@@ -14,6 +14,7 @@ config HYPER_DMABUF_XEN
config HYPER_DMABUF_SYSFS
bool "Enable sysfs information about hyper DMA buffers"
default y
+ depends on HYPER_DMABUF
help
Expose information about imported and exported buffers using
hyper_dmabuf driver
@@ -66,6 +66,15 @@ static int __init hyper_dmabuf_drv_init(void)
#ifdef CONFIG_HYPER_DMABUF_XEN
hyper_dmabuf_private.backend_ops = &xen_backend_ops;
#endif
+ /*
+ * Defer backend setup to first open call.
+ * Due to fact that some hypervisors eg. Xen, may have dependencies
+ * to userspace daemons like xenstored, in that case all xenstore
+ * calls done from kernel will block until that deamon will be
+ * started, in case where module is built in that will block entire
+ * kernel initialization.
+ */
+ hyper_dmabuf_private.backend_initialized = false;
dev_info(hyper_dmabuf_private.device,
"initializing database for imported/exported dmabufs\n");
@@ -73,7 +82,6 @@ static int __init hyper_dmabuf_drv_init(void)
/* device structure initialization */
/* currently only does work-queue initialization */
hyper_dmabuf_private.work_queue = create_workqueue("hyper_dmabuf_wqueue");
- hyper_dmabuf_private.domid = hyper_dmabuf_private.backend_ops->get_vm_id();
ret = hyper_dmabuf_table_init();
if (ret < 0) {
@@ -82,13 +90,6 @@ static int __init hyper_dmabuf_drv_init(void)
return ret;
}
- ret = hyper_dmabuf_private.backend_ops->init_comm_env();
- if (ret < 0) {
- dev_err(hyper_dmabuf_private.device,
- "failed to initiailize hypervisor-specific comm env\n");
- return ret;
- }
-
#ifdef CONFIG_HYPER_DMABUF_SYSFS
ret = hyper_dmabuf_register_sysfs(hyper_dmabuf_private.device);
if (ret < 0) {
@@ -77,6 +77,7 @@ struct hyper_dmabuf_private {
/* backend ops - hypervisor specific */
struct hyper_dmabuf_backend_ops *backend_ops;
struct mutex lock;
+ bool backend_initialized;
};
#endif /* __LINUX_PUBLIC_HYPER_DMABUF_DRV_H__ */
@@ -40,6 +40,13 @@ void store_reusable_id(int id)
struct list_reusable_id *new_reusable;
new_reusable = kmalloc(sizeof(*new_reusable), GFP_KERNEL);
+
+ if (!new_reusable) {
+ dev_err(hyper_dmabuf_private.device,
+ "No memory left to be allocated\n");
+ return;
+ }
+
new_reusable->id = id;
list_add(&new_reusable->list, &reusable_head->list);
@@ -94,6 +101,13 @@ int hyper_dmabuf_get_id(void)
/* first cla to hyper_dmabuf_get_id */
if (id == 0) {
reusable_head = kmalloc(sizeof(*reusable_head), GFP_KERNEL);
+
+ if (!reusable_head) {
+ dev_err(hyper_dmabuf_private.device,
+ "No memory left to be allocated\n");
+ return -ENOMEM;
+ }
+
reusable_head->id = -1; /* list head have invalid id */
INIT_LIST_HEAD(&reusable_head->list);
hyper_dmabuf_private.id_queue = reusable_head;
@@ -270,6 +270,12 @@ inline int hyper_dmabuf_sync_request(int id, int dmabuf_ops)
req = kcalloc(1, sizeof(*req), GFP_KERNEL);
+ if (!req) {
+ dev_err(hyper_dmabuf_private.device,
+ "No memory left to be allocated\n");
+ return -ENOMEM;
+ }
+
hyper_dmabuf_create_request(req, HYPER_DMABUF_OPS_TO_SOURCE, &operands[0]);
/* send request and wait for a response */
@@ -366,8 +372,11 @@ static struct sg_table* hyper_dmabuf_ops_map(struct dma_buf_attachment *attachme
return st;
err_free_sg:
- sg_free_table(st);
- kfree(st);
+ if (st) {
+ sg_free_table(st);
+ kfree(st);
+ }
+
return NULL;
}
@@ -115,22 +115,24 @@ static int hyper_dmabuf_export_remote(struct file *filp, void *data)
*/
ret = hyper_dmabuf_find_id_exported(dma_buf, export_remote_attr->remote_domain);
sgt_info = hyper_dmabuf_find_exported(ret);
- if (ret != -ENOENT && sgt_info->valid) {
- /*
- * Check if unexport is already scheduled for that buffer,
- * if so try to cancel it. If that will fail, buffer needs
- * to be reexport once again.
- */
- if (sgt_info->unexport_scheduled) {
- if (!cancel_delayed_work_sync(&sgt_info->unexport_work)) {
- dma_buf_put(dma_buf);
- goto reexport;
+ if (ret != -ENOENT && sgt_info != NULL) {
+ if (sgt_info->valid) {
+ /*
+ * Check if unexport is already scheduled for that buffer,
+ * if so try to cancel it. If that will fail, buffer needs
+ * to be reexport once again.
+ */
+ if (sgt_info->unexport_scheduled) {
+ if (!cancel_delayed_work_sync(&sgt_info->unexport_work)) {
+ dma_buf_put(dma_buf);
+ goto reexport;
+ }
+ sgt_info->unexport_scheduled = 0;
}
- sgt_info->unexport_scheduled = 0;
+ dma_buf_put(dma_buf);
+ export_remote_attr->hyper_dmabuf_id = ret;
+ return 0;
}
- dma_buf_put(dma_buf);
- export_remote_attr->hyper_dmabuf_id = ret;
- return 0;
}
reexport:
@@ -162,9 +164,32 @@ static int hyper_dmabuf_export_remote(struct file *filp, void *data)
sgt_info->valid = 1;
sgt_info->active_sgts = kmalloc(sizeof(struct sgt_list), GFP_KERNEL);
+ if (!sgt_info->active_sgts) {
+ dev_err(hyper_dmabuf_private.device, "no more space left\n");
+ ret = -ENOMEM;
+ goto fail_map_active_sgts;
+ }
+
sgt_info->active_attached = kmalloc(sizeof(struct attachment_list), GFP_KERNEL);
+ if (!sgt_info->active_attached) {
+ dev_err(hyper_dmabuf_private.device, "no more space left\n");
+ ret = -ENOMEM;
+ goto fail_map_active_attached;
+ }
+
sgt_info->va_kmapped = kmalloc(sizeof(struct kmap_vaddr_list), GFP_KERNEL);
+ if (!sgt_info->va_kmapped) {
+ dev_err(hyper_dmabuf_private.device, "no more space left\n");
+ ret = -ENOMEM;
+ goto fail_map_va_kmapped;
+ }
+
sgt_info->va_vmapped = kmalloc(sizeof(struct vmap_vaddr_list), GFP_KERNEL);
+ if (!sgt_info->va_vmapped) {
+ dev_err(hyper_dmabuf_private.device, "no more space left\n");
+ ret = -ENOMEM;
+ goto fail_map_va_vmapped;
+ }
sgt_info->active_sgts->sgt = sgt;
sgt_info->active_attached->attach = attachment;
@@ -211,6 +236,11 @@ static int hyper_dmabuf_export_remote(struct file *filp, void *data)
req = kcalloc(1, sizeof(*req), GFP_KERNEL);
+ if(!req) {
+ dev_err(hyper_dmabuf_private.device, "no more space left\n");
+ goto fail_map_req;
+ }
+
/* composing a message to the importer */
hyper_dmabuf_create_request(req, HYPER_DMABUF_EXPORT, &operands[0]);
@@ -233,6 +263,8 @@ static int hyper_dmabuf_export_remote(struct file *filp, void *data)
fail_send_request:
kfree(req);
+
+fail_map_req:
hyper_dmabuf_remove_exported(sgt_info->hyper_dmabuf_id);
fail_export:
@@ -242,10 +274,14 @@ static int hyper_dmabuf_export_remote(struct file *filp, void *data)
dma_buf_detach(sgt_info->dma_buf, sgt_info->active_attached->attach);
dma_buf_put(sgt_info->dma_buf);
- kfree(sgt_info->active_attached);
- kfree(sgt_info->active_sgts);
- kfree(sgt_info->va_kmapped);
kfree(sgt_info->va_vmapped);
+fail_map_va_vmapped:
+ kfree(sgt_info->va_kmapped);
+fail_map_va_kmapped:
+ kfree(sgt_info->active_sgts);
+fail_map_active_sgts:
+ kfree(sgt_info->active_attached);
+fail_map_active_attached:
return ret;
}
@@ -288,6 +324,13 @@ static int hyper_dmabuf_export_fd_ioctl(struct file *filp, void *data)
dev_dbg(hyper_dmabuf_private.device, "Exporting fd of buffer %d\n", operand);
req = kcalloc(1, sizeof(*req), GFP_KERNEL);
+
+ if (!req) {
+ dev_err(hyper_dmabuf_private.device,
+ "No memory left to be allocated\n");
+ return -ENOMEM;
+ }
+
hyper_dmabuf_create_request(req, HYPER_DMABUF_EXPORT_FD, &operand);
ret = ops->send_req(HYPER_DMABUF_DOM_ID(operand), req, true);
@@ -381,6 +424,12 @@ static void hyper_dmabuf_delayed_unexport(struct work_struct *work)
req = kcalloc(1, sizeof(*req), GFP_KERNEL);
+ if (!req) {
+ dev_err(hyper_dmabuf_private.device,
+ "No memory left to be allocated\n");
+ return;
+ }
+
hyper_dmabuf_create_request(req, HYPER_DMABUF_NOTIFY_UNEXPORT, &hyper_dmabuf_id);
/* Now send unexport request to remote domain, marking that buffer should not be used anymore */
@@ -540,6 +589,11 @@ static long hyper_dmabuf_ioctl(struct file *filp,
hyper_dmabuf_ioctl_t func;
char *kdata;
+ if (nr > ARRAY_SIZE(hyper_dmabuf_ioctls)) {
+ dev_err(hyper_dmabuf_private.device, "invalid ioctl\n");
+ return -EINVAL;
+ }
+
ioctl = &hyper_dmabuf_ioctls[nr];
func = ioctl->func;
@@ -574,11 +628,34 @@ static long hyper_dmabuf_ioctl(struct file *filp,
int hyper_dmabuf_open(struct inode *inode, struct file *filp)
{
+ int ret = 0;
+
/* Do not allow exclusive open */
if (filp->f_flags & O_EXCL)
return -EBUSY;
- return 0;
+ /*
+ * Initialize backend if neededm,
+ * use mutex to prevent race conditions when
+ * two userspace apps will open device at the same time
+ */
+ mutex_lock(&hyper_dmabuf_private.lock);
+
+ if (!hyper_dmabuf_private.backend_initialized) {
+ hyper_dmabuf_private.domid = hyper_dmabuf_private.backend_ops->get_vm_id();
+
+ ret = hyper_dmabuf_private.backend_ops->init_comm_env();
+ if (ret < 0) {
+ dev_err(hyper_dmabuf_private.device,
+ "failed to initiailize hypervisor-specific comm env\n");
+ } else {
+ hyper_dmabuf_private.backend_initialized = true;
+ }
+ }
+
+ mutex_unlock(&hyper_dmabuf_private.lock);
+
+ return ret;
}
static void hyper_dmabuf_emergency_release(struct hyper_dmabuf_sgt_info* sgt_info,
@@ -34,8 +34,11 @@
#include <asm/uaccess.h>
#include <linux/hashtable.h>
#include <linux/dma-buf.h>
+#include "hyper_dmabuf_drv.h"
#include "hyper_dmabuf_list.h"
+extern struct hyper_dmabuf_private hyper_dmabuf_private;
+
DECLARE_HASHTABLE(hyper_dmabuf_hash_imported, MAX_ENTRY_IMPORTED);
DECLARE_HASHTABLE(hyper_dmabuf_hash_exported, MAX_ENTRY_EXPORTED);
@@ -132,6 +135,12 @@ int hyper_dmabuf_register_exported(struct hyper_dmabuf_sgt_info *info)
info_entry = kmalloc(sizeof(*info_entry), GFP_KERNEL);
+ if (!info_entry) {
+ dev_err(hyper_dmabuf_private.device,
+ "No memory left to be allocated\n");
+ return -ENOMEM;
+ }
+
info_entry->info = info;
hash_add(hyper_dmabuf_hash_exported, &info_entry->node,
@@ -146,6 +155,12 @@ int hyper_dmabuf_register_imported(struct hyper_dmabuf_imported_sgt_info* info)
info_entry = kmalloc(sizeof(*info_entry), GFP_KERNEL);
+ if (!info_entry) {
+ dev_err(hyper_dmabuf_private.device,
+ "No memory left to be allocated\n");
+ return -ENOMEM;
+ }
+
info_entry->info = info;
hash_add(hyper_dmabuf_hash_imported, &info_entry->node,
@@ -134,6 +134,13 @@ void cmd_process_work(struct work_struct *work)
* operands5~8 : Driver-specific private data (e.g. graphic buffer's meta info)
*/
imported_sgt_info = kcalloc(1, sizeof(*imported_sgt_info), GFP_KERNEL);
+
+ if (!imported_sgt_info) {
+ dev_err(hyper_dmabuf_private.device,
+ "No memory left to be allocated\n");
+ break;
+ }
+
imported_sgt_info->hyper_dmabuf_id = req->operands[0];
imported_sgt_info->frst_ofst = req->operands[2];
imported_sgt_info->last_len = req->operands[3];
@@ -288,9 +295,22 @@ int hyper_dmabuf_msg_parse(int domid, struct hyper_dmabuf_req *req)
"%s: putting request to workqueue\n", __func__);
temp_req = kmalloc(sizeof(*temp_req), GFP_KERNEL);
+ if (!temp_req) {
+ dev_err(hyper_dmabuf_private.device,
+ "No memory left to be allocated\n");
+ return -ENOMEM;
+ }
+
memcpy(temp_req, req, sizeof(*temp_req));
proc = kcalloc(1, sizeof(struct cmd_process), GFP_KERNEL);
+
+ if (!proc) {
+ dev_err(hyper_dmabuf_private.device,
+ "No memory left to be allocated\n");
+ return -ENOMEM;
+ }
+
proc->rq = temp_req;
proc->domid = domid;
@@ -78,6 +78,12 @@ int hyper_dmabuf_remote_sync(int id, int ops)
case HYPER_DMABUF_OPS_ATTACH:
attachl = kcalloc(1, sizeof(*attachl), GFP_KERNEL);
+ if (!attachl) {
+ dev_err(hyper_dmabuf_private.device,
+ "dmabuf remote sync::error while processing HYPER_DMABUF_OPS_ATTACH\n");
+ return -ENOMEM;
+ }
+
attachl->attach = dma_buf_attach(sgt_info->dma_buf,
hyper_dmabuf_private.device);
@@ -85,7 +91,7 @@ int hyper_dmabuf_remote_sync(int id, int ops)
kfree(attachl);
dev_err(hyper_dmabuf_private.device,
"dmabuf remote sync::error while processing HYPER_DMABUF_OPS_ATTACH\n");
- return PTR_ERR(attachl->attach);
+ return -ENOMEM;
}
list_add(&attachl->list, &sgt_info->active_attached->list);
@@ -121,12 +127,19 @@ int hyper_dmabuf_remote_sync(int id, int ops)
struct attachment_list, list);
sgtl = kcalloc(1, sizeof(*sgtl), GFP_KERNEL);
+
+ if (!sgtl) {
+ dev_err(hyper_dmabuf_private.device,
+ "dmabuf remote sync::error while processing HYPER_DMABUF_OPS_MAP\n");
+ return -ENOMEM;
+ }
+
sgtl->sgt = dma_buf_map_attachment(attachl->attach, DMA_BIDIRECTIONAL);
if (!sgtl->sgt) {
kfree(sgtl);
dev_err(hyper_dmabuf_private.device,
"dmabuf remote sync::error while processing HYPER_DMABUF_OPS_MAP\n");
- return PTR_ERR(sgtl->sgt);
+ return -ENOMEM;
}
list_add(&sgtl->list, &sgt_info->active_sgts->list);
break;
@@ -201,6 +214,11 @@ int hyper_dmabuf_remote_sync(int id, int ops)
case HYPER_DMABUF_OPS_KMAP_ATOMIC:
case HYPER_DMABUF_OPS_KMAP:
va_kmapl = kcalloc(1, sizeof(*va_kmapl), GFP_KERNEL);
+ if (!va_kmapl) {
+ dev_err(hyper_dmabuf_private.device,
+ "dmabuf remote sync::error while processing HYPER_DMABUF_OPS_KMAP(_ATOMIC)\n");
+ return -ENOMEM;
+ }
/* dummy kmapping of 1 page */
if (ops == HYPER_DMABUF_OPS_KMAP_ATOMIC)
@@ -212,7 +230,7 @@ int hyper_dmabuf_remote_sync(int id, int ops)
kfree(va_kmapl);
dev_err(hyper_dmabuf_private.device,
"dmabuf remote sync::error while processing HYPER_DMABUF_OPS_KMAP(_ATOMIC)\n");
- return PTR_ERR(va_kmapl->vaddr);
+ return -ENOMEM;
}
list_add(&va_kmapl->list, &sgt_info->va_kmapped->list);
break;
@@ -255,6 +273,12 @@ int hyper_dmabuf_remote_sync(int id, int ops)
case HYPER_DMABUF_OPS_VMAP:
va_vmapl = kcalloc(1, sizeof(*va_vmapl), GFP_KERNEL);
+ if (!va_vmapl) {
+ dev_err(hyper_dmabuf_private.device,
+ "dmabuf remote sync::error while processing HYPER_DMABUF_OPS_VMAP\n");
+ return -ENOMEM;
+ }
+
/* dummy vmapping */
va_vmapl->vaddr = dma_buf_vmap(sgt_info->dma_buf);
@@ -262,7 +286,7 @@ int hyper_dmabuf_remote_sync(int id, int ops)
kfree(va_vmapl);
dev_err(hyper_dmabuf_private.device,
"dmabuf remote sync::error while processing HYPER_DMABUF_OPS_VMAP\n");
- return PTR_ERR(va_vmapl->vaddr);
+ return -ENOMEM;
}
list_add(&va_vmapl->list, &sgt_info->va_vmapped->list);
break;
@@ -381,6 +381,12 @@ int hyper_dmabuf_xen_init_rx_rbuf(int domid)
ring_info = kmalloc(sizeof(*ring_info), GFP_KERNEL);
+ if (!ring_info) {
+ dev_err(hyper_dmabuf_private.device,
+ "No memory left to be allocated\n");
+ return -ENOMEM;
+ }
+
ring_info->sdomain = domid;
ring_info->evtchn = rx_port;
@@ -34,9 +34,12 @@
#include <asm/uaccess.h>
#include <linux/hashtable.h>
#include <xen/grant_table.h>
+#include "../hyper_dmabuf_drv.h"
#include "hyper_dmabuf_xen_comm.h"
#include "hyper_dmabuf_xen_comm_list.h"
+extern struct hyper_dmabuf_private hyper_dmabuf_private;
+
DECLARE_HASHTABLE(xen_comm_tx_ring_hash, MAX_ENTRY_TX_RING);
DECLARE_HASHTABLE(xen_comm_rx_ring_hash, MAX_ENTRY_RX_RING);
@@ -52,6 +55,12 @@ int xen_comm_add_tx_ring(struct xen_comm_tx_ring_info *ring_info)
info_entry = kmalloc(sizeof(*info_entry), GFP_KERNEL);
+ if (!info_entry) {
+ dev_err(hyper_dmabuf_private.device,
+ "No memory left to be allocated\n");
+ return -ENOMEM;
+ }
+
info_entry->info = ring_info;
hash_add(xen_comm_tx_ring_hash, &info_entry->node,
@@ -66,6 +75,12 @@ int xen_comm_add_rx_ring(struct xen_comm_rx_ring_info *ring_info)
info_entry = kmalloc(sizeof(*info_entry), GFP_KERNEL);
+ if (!info_entry) {
+ dev_err(hyper_dmabuf_private.device,
+ "No memory left to be allocated\n");
+ return -ENOMEM;
+ }
+
info_entry->info = ring_info;
hash_add(xen_comm_rx_ring_hash, &info_entry->node,
@@ -96,6 +96,12 @@ int hyper_dmabuf_xen_share_pages(struct page **pages, int domid, int nents,
lvl2_table = (grant_ref_t *)__get_free_pages(GFP_KERNEL, n_lvl2_grefs);
sh_pages_info = kmalloc(sizeof(*sh_pages_info), GFP_KERNEL);
+
+ if (!sh_pages_info) {
+ dev_err(hyper_dmabuf_private.device, "No more space left\n");
+ return -ENOMEM;
+ }
+
*refs_info = (void *)sh_pages_info;
/* share data pages in rw mode*/
new file mode 100644
@@ -0,0 +1,6 @@
+# UAPI Header export list
+header-y += evtchn.h
+header-y += gntalloc.h
+header-y += gntdev.h
+header-y += privcmd.h
+header-y += hyper_dmabuf.h