@@ -341,6 +341,10 @@ static struct sg_table* hyper_dmabuf_ops_map(struct dma_buf_attachment *attachme
/* extract pages from sgt */
page_info = hyper_dmabuf_ext_pgs(sgt_info->sgt);
+ if (!page_info) {
+ return NULL;
+ }
+
/* create a new sg_table with extracted pages */
st = hyper_dmabuf_create_sgt(page_info->pages, page_info->frst_ofst,
page_info->last_len, page_info->nents);
@@ -115,11 +115,24 @@ static int hyper_dmabuf_export_remote(void *data)
ret = hyper_dmabuf_find_id_exported(dma_buf, export_remote_attr->remote_domain);
sgt_info = hyper_dmabuf_find_exported(ret);
if (ret != -1 && 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;
+ }
dma_buf_put(dma_buf);
export_remote_attr->hyper_dmabuf_id = ret;
return 0;
}
+reexport:
attachment = dma_buf_attach(dma_buf, hyper_dmabuf_private.device);
if (!attachment) {
dev_err(hyper_dmabuf_private.device, "Cannot get attachment\n");
@@ -133,7 +146,7 @@ static int hyper_dmabuf_export_remote(void *data)
sgt = dma_buf_map_attachment(attachment, DMA_BIDIRECTIONAL);
- sgt_info = kmalloc(sizeof(*sgt_info), GFP_KERNEL);
+ sgt_info = kcalloc(1, sizeof(*sgt_info), GFP_KERNEL);
sgt_info->hyper_dmabuf_id = hyper_dmabuf_get_id();
@@ -141,7 +154,6 @@ static int hyper_dmabuf_export_remote(void *data)
sgt_info->hyper_dmabuf_rdomain = export_remote_attr->remote_domain;
sgt_info->dma_buf = dma_buf;
sgt_info->valid = 1;
- sgt_info->importer_exported = 0;
sgt_info->active_sgts = kmalloc(sizeof(struct sgt_list), GFP_KERNEL);
sgt_info->active_attached = kmalloc(sizeof(struct attachment_list), GFP_KERNEL);
@@ -245,8 +257,35 @@ static int hyper_dmabuf_export_fd_ioctl(void *data)
/* look for dmabuf for the id */
sgt_info = hyper_dmabuf_find_imported(export_fd_attr->hyper_dmabuf_id);
- if (sgt_info == NULL) /* can't find sgt from the table */
+ if (sgt_info == NULL || !sgt_info->valid) /* can't find sgt from the table */
+ return -1;
+
+ sgt_info->num_importers++;
+
+ /* send notification for export_fd to exporter */
+ operand = sgt_info->hyper_dmabuf_id;
+
+ req = kcalloc(1, sizeof(*req), GFP_KERNEL);
+ hyper_dmabuf_create_request(req, HYPER_DMABUF_FIRST_EXPORT, &operand);
+
+ ret = ops->send_req(HYPER_DMABUF_DOM_ID(operand), req, true);
+
+ if (ret < 0) {
+ kfree(req);
+ dev_err(hyper_dmabuf_private.device, "Failed to create sgt or notify exporter\n");
+ sgt_info->num_importers--;
+ return -EINVAL;
+ }
+ kfree(req);
+
+ if (ret == HYPER_DMABUF_REQ_ERROR) {
+ dev_err(hyper_dmabuf_private.device,
+ "Buffer invalid\n");
+ sgt_info->num_importers--;
return -1;
+ } else {
+ dev_dbg(hyper_dmabuf_private.device, "Can import buffer\n");
+ }
dev_dbg(hyper_dmabuf_private.device,
"%s Found buffer gref %d off %d last len %d nents %d domain %d\n", __func__,
@@ -262,86 +301,62 @@ static int hyper_dmabuf_export_fd_ioctl(void *data)
sgt_info->nents,
&sgt_info->refs_info);
+ if (!data_pages) {
+ sgt_info->num_importers--;
+ return -EINVAL;
+ }
+
sgt_info->sgt = hyper_dmabuf_create_sgt(data_pages, sgt_info->frst_ofst,
sgt_info->last_len, sgt_info->nents);
}
- /* send notification for export_fd to exporter */
- operand = sgt_info->hyper_dmabuf_id;
-
- req = kcalloc(1, sizeof(*req), GFP_KERNEL);
- hyper_dmabuf_create_request(req, HYPER_DMABUF_FIRST_EXPORT, &operand);
-
- ret = ops->send_req(HYPER_DMABUF_DOM_ID(operand), req, false);
-
- if (!sgt_info->sgt || ret) {
- kfree(req);
- dev_err(hyper_dmabuf_private.device, "Failed to create sgt or notify exporter\n");
- return -EINVAL;
- }
- kfree(req);
-
export_fd_attr->fd = hyper_dmabuf_export_fd(sgt_info, export_fd_attr->flags);
if (export_fd_attr->fd < 0) {
/* fail to get fd */
ret = export_fd_attr->fd;
- } else {
- sgt_info->num_importers++;
}
- dev_dbg(hyper_dmabuf_private.device, "%s entry\n", __func__);
- return ret;
+ dev_dbg(hyper_dmabuf_private.device, "%s exit\n", __func__);
+ return 0;
}
/* unexport dmabuf from the database and send int req to the source domain
* to unmap it.
*/
-static int hyper_dmabuf_unexport(void *data)
+static void hyper_dmabuf_delayed_unexport(struct work_struct *work)
{
- struct ioctl_hyper_dmabuf_unexport *unexport_attr;
- struct hyper_dmabuf_backend_ops *ops = hyper_dmabuf_private.backend_ops;
- struct hyper_dmabuf_sgt_info *sgt_info;
struct hyper_dmabuf_req *req;
+ int hyper_dmabuf_id;
int ret;
+ struct hyper_dmabuf_backend_ops *ops = hyper_dmabuf_private.backend_ops;
+ struct hyper_dmabuf_sgt_info *sgt_info =
+ container_of(work, struct hyper_dmabuf_sgt_info, unexport_work.work);
- dev_dbg(hyper_dmabuf_private.device, "%s entry\n", __func__);
-
- if (!data) {
- dev_err(hyper_dmabuf_private.device, "user data is NULL\n");
- return -EINVAL;
- }
-
- unexport_attr = (struct ioctl_hyper_dmabuf_unexport *)data;
+ if (!sgt_info)
+ return;
- /* find dmabuf in export list */
- sgt_info = hyper_dmabuf_find_exported(unexport_attr->hyper_dmabuf_id);
+ hyper_dmabuf_id = sgt_info->hyper_dmabuf_id;
- /* failed to find corresponding entry in export list */
- if (sgt_info == NULL) {
- unexport_attr->status = -EINVAL;
- return -EFAULT;
- }
+ dev_dbg(hyper_dmabuf_private.device,
+ "Marking buffer %d as invalid\n", hyper_dmabuf_id);
+ /* no longer valid */
+ sgt_info->valid = 0;
req = kcalloc(1, sizeof(*req), GFP_KERNEL);
- hyper_dmabuf_create_request(req, HYPER_DMABUF_NOTIFY_UNEXPORT, &unexport_attr->hyper_dmabuf_id);
+ 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 */
ret = ops->send_req(sgt_info->hyper_dmabuf_rdomain, req, true);
if (ret < 0) {
- kfree(req);
- return -EFAULT;
+ dev_err(hyper_dmabuf_private.device, "unexport message for buffer %d failed\n", hyper_dmabuf_id);
}
/* free msg */
kfree(req);
-
- dev_dbg(hyper_dmabuf_private.device,
- "Marking buffer %d as invalid\n", unexport_attr->hyper_dmabuf_id);
- /* no longer valid */
- sgt_info->valid = 0;
+ sgt_info->unexport_scheduled = 0;
/*
* Immediately clean-up if it has never been exported by importer
@@ -352,16 +367,52 @@ static int hyper_dmabuf_unexport(void *data)
*/
if (!sgt_info->importer_exported) {
dev_dbg(hyper_dmabuf_private.device,
- "claning up buffer %d completly\n", unexport_attr->hyper_dmabuf_id);
+ "claning up buffer %d completly\n", hyper_dmabuf_id);
hyper_dmabuf_cleanup_sgt_info(sgt_info, false);
- hyper_dmabuf_remove_exported(unexport_attr->hyper_dmabuf_id);
+ hyper_dmabuf_remove_exported(hyper_dmabuf_id);
kfree(sgt_info);
/* register hyper_dmabuf_id to the list for reuse */
- store_reusable_id(unexport_attr->hyper_dmabuf_id);
+ store_reusable_id(hyper_dmabuf_id);
}
+}
+
+/* Schedules unexport of dmabuf.
+ */
+static int hyper_dmabuf_unexport(void *data)
+{
+ struct ioctl_hyper_dmabuf_unexport *unexport_attr;
+ struct hyper_dmabuf_sgt_info *sgt_info;
dev_dbg(hyper_dmabuf_private.device, "%s entry\n", __func__);
- return ret;
+
+ if (!data) {
+ dev_err(hyper_dmabuf_private.device, "user data is NULL\n");
+ return -EINVAL;
+ }
+
+ unexport_attr = (struct ioctl_hyper_dmabuf_unexport *)data;
+
+ /* find dmabuf in export list */
+ sgt_info = hyper_dmabuf_find_exported(unexport_attr->hyper_dmabuf_id);
+
+ dev_dbg(hyper_dmabuf_private.device, "scheduling unexport of buffer %d\n", unexport_attr->hyper_dmabuf_id);
+
+ /* failed to find corresponding entry in export list */
+ if (sgt_info == NULL) {
+ unexport_attr->status = -EINVAL;
+ return -EFAULT;
+ }
+
+ if (sgt_info->unexport_scheduled)
+ return 0;
+
+ sgt_info->unexport_scheduled = 1;
+ INIT_DELAYED_WORK(&sgt_info->unexport_work, hyper_dmabuf_delayed_unexport);
+ schedule_delayed_work(&sgt_info->unexport_work,
+ msecs_to_jiffies(unexport_attr->delay_ms));
+
+ dev_dbg(hyper_dmabuf_private.device, "%s exit\n", __func__);
+ return 0;
}
static int hyper_dmabuf_query(void *data)
@@ -90,6 +90,8 @@ struct ioctl_hyper_dmabuf_unexport {
/* IN parameters */
/* hyper dmabuf id to be unexported */
int hyper_dmabuf_id;
+ /* delay in ms by which unexport processing will be postponed */
+ int delay_ms;
/* OUT parameters */
/* Status of request */
int status;
@@ -112,7 +112,6 @@ void hyper_dmabuf_create_request(struct hyper_dmabuf_req *req,
void cmd_process_work(struct work_struct *work)
{
- struct hyper_dmabuf_sgt_info *sgt_info;
struct hyper_dmabuf_imported_sgt_info *imported_sgt_info;
struct cmd_process *proc = container_of(work, struct cmd_process, work);
struct hyper_dmabuf_req *req;
@@ -154,19 +153,6 @@ void cmd_process_work(struct work_struct *work)
hyper_dmabuf_register_imported(imported_sgt_info);
break;
- case HYPER_DMABUF_FIRST_EXPORT:
- /* find a corresponding SGT for the id */
- sgt_info = hyper_dmabuf_find_exported(req->operands[0]);
-
- if (!sgt_info) {
- dev_err(hyper_dmabuf_private.device,
- "critical err: requested sgt_info can't be found %d\n", req->operands[0]);
- break;
- }
-
- sgt_info->importer_exported++;
- break;
-
case HYPER_DMABUF_OPS_TO_REMOTE:
/* notifying dmabuf map/unmap to importer (probably not needed) */
/* for dmabuf synchronization */
@@ -187,6 +173,7 @@ int hyper_dmabuf_msg_parse(int domid, struct hyper_dmabuf_req *req)
struct cmd_process *proc;
struct hyper_dmabuf_req *temp_req;
struct hyper_dmabuf_imported_sgt_info *sgt_info;
+ struct hyper_dmabuf_sgt_info *exp_sgt_info;
int ret;
if (!req) {
@@ -216,8 +203,7 @@ int hyper_dmabuf_msg_parse(int domid, struct hyper_dmabuf_req *req)
if (sgt_info) {
/* if anything is still using dma_buf */
- if (sgt_info->dma_buf &&
- dmabuf_refcount(sgt_info->dma_buf) > 0) {
+ if (sgt_info->num_importers) {
/*
* Buffer is still in use, just mark that it should
* not be allowed to export its fd anymore.
@@ -255,6 +241,29 @@ int hyper_dmabuf_msg_parse(int domid, struct hyper_dmabuf_req *req)
return req->command;
}
+ /* synchronous dma_buf_fd export */
+ if (req->command == HYPER_DMABUF_FIRST_EXPORT) {
+ /* find a corresponding SGT for the id */
+ exp_sgt_info = hyper_dmabuf_find_exported(req->operands[0]);
+
+ if (!exp_sgt_info) {
+ dev_err(hyper_dmabuf_private.device,
+ "critical err: requested sgt_info can't be found %d\n", req->operands[0]);
+ req->status = HYPER_DMABUF_REQ_ERROR;
+ } else if (!exp_sgt_info->valid) {
+ dev_dbg(hyper_dmabuf_private.device,
+ "Buffer no longer valid - cannot export\n");
+ req->status = HYPER_DMABUF_REQ_ERROR;
+ } else {
+ dev_dbg(hyper_dmabuf_private.device,
+ "Buffer still valid - can export\n");
+ exp_sgt_info->importer_exported++;
+ req->status = HYPER_DMABUF_REQ_PROCESSED;
+ }
+ return req->command;
+ }
+
+
dev_dbg(hyper_dmabuf_private.device,
"%s: putting request to workqueue\n", __func__);
temp_req = kmalloc(sizeof(*temp_req), GFP_KERNEL);
@@ -78,6 +78,8 @@ struct hyper_dmabuf_sgt_info {
bool valid;
int importer_exported; /* exported locally on importer's side */
void *refs_info; /* hypervisor-specific info for the references */
+ struct delayed_work unexport_work;
+ bool unexport_scheduled;
int private[4]; /* device specific info (e.g. image's meta info?) */
};
@@ -551,6 +551,8 @@ int hyper_dmabuf_xen_send_req(int domid, struct hyper_dmabuf_req *req, int wait)
dev_err(hyper_dmabuf_private.device, "request timed-out\n");
return -EBUSY;
}
+
+ return req_pending.status;
}
return 0;