@@ -6,6 +6,7 @@
#include "hyper_dmabuf_conf.h"
#include "hyper_dmabuf_list.h"
#include "xen/hyper_dmabuf_xen_comm_list.h"
+#include "xen/hyper_dmabuf_xen_comm.h"
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("IOTG-PED, INTEL");
@@ -43,6 +44,11 @@ static int hyper_dmabuf_drv_init(void)
return -EINVAL;
}
+ ret = hyper_dmabuf_setup_data_dir();
+ if (ret < 0) {
+ return -EINVAL;
+ }
+
/* interrupt for comm should be registered here: */
return ret;
}
@@ -52,12 +58,15 @@ static void hyper_dmabuf_drv_exit(void)
{
/* hash tables for export/import entries and ring_infos */
hyper_dmabuf_table_destroy();
- hyper_dmabuf_ring_table_init();
+
+ hyper_dmabuf_cleanup_ringbufs();
+ hyper_dmabuf_ring_table_destroy();
/* destroy workqueue */
if (hyper_dmabuf_private.work_queue)
destroy_workqueue(hyper_dmabuf_private.work_queue);
+ hyper_dmabuf_destroy_data_dir();
printk( KERN_NOTICE "dma_buf-src_sink model: Exiting" );
unregister_device();
}
@@ -29,8 +29,6 @@ struct ioctl_hyper_dmabuf_exporter_ring_setup {
/* IN parameters */
/* Remote domain id */
uint32_t remote_domain;
- grant_ref_t ring_refid; /* assigned by driver, copied to userspace after initialization */
- uint32_t port; /* assigned by driver, copied to userspace after initialization */
};
#define IOCTL_HYPER_DMABUF_IMPORTER_RING_SETUP \
@@ -39,10 +37,6 @@ struct ioctl_hyper_dmabuf_importer_ring_setup {
/* IN parameters */
/* Source domain id */
uint32_t source_domain;
- /* Ring shared page refid */
- grant_ref_t ring_refid;
- /* Port number */
- uint32_t port;
};
#define IOCTL_HYPER_DMABUF_EXPORT_REMOTE \
@@ -95,12 +89,4 @@ struct ioctl_hyper_dmabuf_query {
uint32_t info;
};
-#define IOCTL_HYPER_DMABUF_REMOTE_EXPORTER_RING_SETUP \
-_IOC(_IOC_NONE, 'G', 6, sizeof(struct ioctl_hyper_dmabuf_remote_exporter_ring_setup))
-struct ioctl_hyper_dmabuf_remote_exporter_ring_setup {
- /* in parameters */
- uint32_t rdomain; /* id of remote domain where exporter's ring need to be setup */
- uint32_t info;
-};
-
#endif //__LINUX_PUBLIC_HYPER_DMABUF_DRV_H__
@@ -48,9 +48,7 @@ static int hyper_dmabuf_exporter_ring_setup(void *data)
return 0;
}
- ret = hyper_dmabuf_exporter_ringbuf_init(ring_attr->remote_domain,
- &ring_attr->ring_refid,
- &ring_attr->port);
+ ret = hyper_dmabuf_exporter_ringbuf_init(ring_attr->remote_domain);
return ret;
}
@@ -76,10 +74,7 @@ static int hyper_dmabuf_importer_ring_setup(void *data)
return 0;
}
- /* user need to provide a port number and ref # for the page used as ring buffer */
- ret = hyper_dmabuf_importer_ringbuf_init(setup_imp_ring_attr->source_domain,
- setup_imp_ring_attr->ring_refid,
- setup_imp_ring_attr->port);
+ ret = hyper_dmabuf_importer_ringbuf_init(setup_imp_ring_attr->source_domain);
return ret;
}
@@ -355,26 +350,6 @@ static int hyper_dmabuf_query(void *data)
return ret;
}
-static int hyper_dmabuf_remote_exporter_ring_setup(void *data)
-{
- struct ioctl_hyper_dmabuf_remote_exporter_ring_setup *remote_exporter_ring_setup;
- struct hyper_dmabuf_ring_rq *req;
-
- remote_exporter_ring_setup = (struct ioctl_hyper_dmabuf_remote_exporter_ring_setup *)data;
-
- req = kcalloc(1, sizeof(*req), GFP_KERNEL);
- hyper_dmabuf_create_request(req, HYPER_DMABUF_EXPORTER_RING_SETUP, NULL);
-
- /* requesting remote domain to set-up exporter's ring */
- if(hyper_dmabuf_send_request(remote_exporter_ring_setup->rdomain, req) < 0) {
- kfree(req);
- return -EINVAL;
- }
-
- kfree(req);
- return 0;
-}
-
static const struct hyper_dmabuf_ioctl_desc hyper_dmabuf_ioctls[] = {
HYPER_DMABUF_IOCTL_DEF(IOCTL_HYPER_DMABUF_EXPORTER_RING_SETUP, hyper_dmabuf_exporter_ring_setup, 0),
HYPER_DMABUF_IOCTL_DEF(IOCTL_HYPER_DMABUF_IMPORTER_RING_SETUP, hyper_dmabuf_importer_ring_setup, 0),
@@ -382,7 +357,6 @@ static const struct hyper_dmabuf_ioctl_desc hyper_dmabuf_ioctls[] = {
HYPER_DMABUF_IOCTL_DEF(IOCTL_HYPER_DMABUF_EXPORT_FD, hyper_dmabuf_export_fd_ioctl, 0),
HYPER_DMABUF_IOCTL_DEF(IOCTL_HYPER_DMABUF_DESTROY, hyper_dmabuf_destroy, 0),
HYPER_DMABUF_IOCTL_DEF(IOCTL_HYPER_DMABUF_QUERY, hyper_dmabuf_query, 0),
- HYPER_DMABUF_IOCTL_DEF(IOCTL_HYPER_DMABUF_REMOTE_EXPORTER_RING_SETUP, hyper_dmabuf_remote_exporter_ring_setup, 0),
};
static long hyper_dmabuf_ioctl(struct file *filp,
@@ -70,12 +70,6 @@ void hyper_dmabuf_create_request(struct hyper_dmabuf_ring_rq *request,
request->operands[i] = operands[i];
break;
- /* requesting the other side to setup another ring channel for reverse direction */
- case HYPER_DMABUF_EXPORTER_RING_SETUP:
- /* command : HYPER_DMABUF_EXPORTER_RING_SETUP */
- /* no operands needed */
- break;
-
default:
/* no command found */
return;
@@ -163,13 +157,6 @@ void cmd_process_work(struct work_struct *work)
*/
break;
- case HYPER_DMABUF_IMPORTER_RING_SETUP:
- /* command: HYPER_DMABUF_IMPORTER_RING_SETUP */
- /* no operands needed */
- hyper_dmabuf_importer_ringbuf_init(domid, req->operands[0], req->operands[1]);
-
- break;
-
default:
/* shouldn't get here */
/* no matched command, nothing to do.. just return error */
@@ -185,7 +172,6 @@ int hyper_dmabuf_msg_parse(int domid, struct hyper_dmabuf_ring_rq *req)
struct cmd_process *proc;
struct hyper_dmabuf_ring_rq *temp_req;
struct hyper_dmabuf_imported_sgt_info *imported_sgt_info;
- int ret;
if (!req) {
printk("request is NULL\n");
@@ -193,28 +179,13 @@ int hyper_dmabuf_msg_parse(int domid, struct hyper_dmabuf_ring_rq *req)
}
if ((req->command < HYPER_DMABUF_EXPORT) ||
- (req->command > HYPER_DMABUF_IMPORTER_RING_SETUP)) {
+ (req->command > HYPER_DMABUF_OPS_TO_SOURCE)) {
printk("invalid command\n");
return -EINVAL;
}
req->status = HYPER_DMABUF_REQ_PROCESSED;
- /* HYPER_DMABUF_EXPORTER_RING_SETUP requires immediate
- * follow up so can't be processed in workqueue
- */
- if (req->command == HYPER_DMABUF_EXPORTER_RING_SETUP) {
- ret = hyper_dmabuf_exporter_ringbuf_init(domid, &req->operands[0], &req->operands[1]);
- if (ret < 0) {
- req->status = HYPER_DMABUF_REQ_ERROR;
- }
-
- req->status = HYPER_DMABUF_REQ_NEEDS_FOLLOW_UP;
- req->command = HYPER_DMABUF_IMPORTER_RING_SETUP;
-
- return req->command;
- }
-
/* HYPER_DMABUF_DESTROY requires immediate
* follow up so can't be processed in workqueue
*/
@@ -7,8 +7,6 @@ enum hyper_dmabuf_command {
HYPER_DMABUF_DESTROY_FINISH,
HYPER_DMABUF_OPS_TO_REMOTE,
HYPER_DMABUF_OPS_TO_SOURCE,
- HYPER_DMABUF_EXPORTER_RING_SETUP, /* requesting remote domain to set up exporter's ring */
- HYPER_DMABUF_IMPORTER_RING_SETUP, /* requesting remote domain to set up importer's ring */
};
enum hyper_dmabuf_ops {
@@ -15,6 +15,83 @@
static int export_req_id = 0;
+/* Creates entry in xen store that will keep details of all exporter rings created by this domain */
+int32_t hyper_dmabuf_setup_data_dir()
+{
+ char buf[255];
+
+ sprintf(buf, "/local/domain/%d/data/hyper_dmabuf", hyper_dmabuf_get_domid());
+ return xenbus_mkdir(XBT_NIL, buf, "");
+}
+
+
+/* Removes entry from xenstore with exporter ring details.
+ * Other domains that has connected to any of exporter rings created by this domain,
+ * will be notified about removal of this entry and will treat that as signal to
+ * cleanup importer rings created for this domain
+ */
+int32_t hyper_dmabuf_destroy_data_dir()
+{
+ char buf[255];
+
+ sprintf(buf, "/local/domain/%d/data/hyper_dmabuf", hyper_dmabuf_get_domid());
+ return xenbus_rm(XBT_NIL, buf, "");
+}
+
+/*
+ * Adds xenstore entries with details of exporter ring created for given remote domain.
+ * It requires special daemon running in dom0 to make sure that given remote domain will
+ * have right permissions to access that data.
+ */
+static int32_t hyper_dmabuf_expose_ring_details(uint32_t domid, uint32_t rdomid, uint32_t grefid, uint32_t port)
+{
+ char buf[255];
+ int ret;
+
+ sprintf(buf, "/local/domain/%d/data/hyper_dmabuf/%d", domid, rdomid);
+ ret = xenbus_printf(XBT_NIL, buf, "grefid", "%d", grefid);
+
+ if (ret) {
+ printk("Failed to write xenbus entry %s: %d\n", buf, ret);
+ return ret;
+ }
+
+ ret = xenbus_printf(XBT_NIL, buf, "port", "%d", port);
+
+ if (ret) {
+ printk("Failed to write xenbus entry %s: %d\n", buf, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Queries details of ring exposed by remote domain.
+ */
+static int32_t hyper_dmabuf_get_ring_details(uint32_t domid, uint32_t rdomid, uint32_t *grefid, uint32_t *port)
+{
+ char buf[255];
+ int ret;
+
+ sprintf(buf, "/local/domain/%d/data/hyper_dmabuf/%d", rdomid, domid);
+ ret = xenbus_scanf(XBT_NIL, buf, "grefid", "%d", grefid);
+
+ if (ret <= 0) {
+ printk("Failed to read xenbus entry %s: %d\n", buf, ret);
+ return ret;
+ }
+
+ ret = xenbus_scanf(XBT_NIL, buf, "port", "%d", port);
+
+ if (ret <= 0) {
+ printk("Failed to read xenbus entry %s: %d\n", buf, ret);
+ return ret;
+ }
+
+ return (ret <= 0 ? 1 : 0);
+}
+
int32_t hyper_dmabuf_get_domid(void)
{
struct xenbus_transaction xbt;
@@ -40,8 +117,49 @@ int hyper_dmabuf_next_req_id_export(void)
static irqreturn_t hyper_dmabuf_front_ring_isr(int irq, void *dev_id);
static irqreturn_t hyper_dmabuf_back_ring_isr(int irq, void *dev_id);
+/*
+ * Callback function that will be called on any change of xenbus path being watched.
+ * Used for detecting creation/destruction of remote domain exporter ring.
+ * When remote domain's exporter ring will be detected, importer ring on this domain will be created.
+ * When remote domain's exporter ring destruction will be detected it will celanup this domain importer ring.
+ * Destruction can be caused by unloading module by remote domain or it's crash/force shutdown.
+ */
+static void remote_domain_exporter_watch_cb(struct xenbus_watch *watch,
+ const char *path, const char *token)
+{
+ int rdom,ret;
+ uint32_t grefid, port;
+ struct hyper_dmabuf_ring_info_import *ring_info;
+
+ /* Check which domain has changed its exporter rings */
+ ret = sscanf(watch->node, "/local/domain/%d/", &rdom);
+ if (ret <= 0) {
+ return;
+ }
+
+ /* Check if we have importer ring for given remote domain alrady created */
+ ring_info = hyper_dmabuf_find_importer_ring(rdom);
+
+ /*
+ * Try to query remote domain exporter ring details - if that will fail and we have
+ * importer ring that means remote domains has cleanup its exporter ring, so our
+ * importer ring is no longer useful.
+ * If querying details will succeed and we don't have importer ring, it means that
+ * remote domain has setup it for us and we should connect to it.
+ */
+ ret = hyper_dmabuf_get_ring_details(hyper_dmabuf_get_domid(), rdom, &grefid, &port);
+
+ if (ring_info && ret != 0) {
+ printk("Remote exporter closed, cleaninup importer\n");
+ hyper_dmabuf_importer_ringbuf_cleanup(rdom);
+ } else if (!ring_info && ret == 0) {
+ printk("Registering importer\n");
+ hyper_dmabuf_importer_ringbuf_init(rdom);
+ }
+}
+
/* exporter needs to generated info for page sharing */
-int hyper_dmabuf_exporter_ringbuf_init(int rdomain, grant_ref_t *refid, int *port)
+int hyper_dmabuf_exporter_ringbuf_init(int rdomain)
{
struct hyper_dmabuf_ring_info_export *ring_info;
struct hyper_dmabuf_sring *sring;
@@ -99,24 +217,58 @@ int hyper_dmabuf_exporter_ringbuf_init(int rdomain, grant_ref_t *refid, int *por
ring_info->irq = ret;
ring_info->port = alloc_unbound.port;
- /* store refid and port numbers for userspace's use */
- *refid = ring_info->gref_ring;
- *port = ring_info->port;
-
printk("%s: allocated eventchannel gref %d port: %d irq: %d\n",
__func__,
ring_info->gref_ring,
ring_info->port,
ring_info->irq);
- /* register ring info */
ret = hyper_dmabuf_register_exporter_ring(ring_info);
+ ret = hyper_dmabuf_expose_ring_details(hyper_dmabuf_get_domid(), rdomain,
+ ring_info->gref_ring, ring_info->port);
+
+ /*
+ * Register watch for remote domain exporter ring.
+ * When remote domain will setup its exporter ring, we will automatically connect our importer ring to it.
+ */
+ ring_info->watch.callback = remote_domain_exporter_watch_cb;
+ ring_info->watch.node = (const char*) kmalloc(sizeof(char) * 255, GFP_KERNEL);
+ sprintf((char*)ring_info->watch.node, "/local/domain/%d/data/hyper_dmabuf/%d/port", rdomain, hyper_dmabuf_get_domid());
+ register_xenbus_watch(&ring_info->watch);
+
return ret;
}
+/* cleans up exporter ring created for given remote domain */
+void hyper_dmabuf_exporter_ringbuf_cleanup(int rdomain)
+{
+ struct hyper_dmabuf_ring_info_export *ring_info;
+
+ /* check if we at all have exporter ring for given rdomain */
+ ring_info = hyper_dmabuf_find_exporter_ring(rdomain);
+
+ if (!ring_info) {
+ return;
+ }
+
+ hyper_dmabuf_remove_exporter_ring(rdomain);
+
+ unregister_xenbus_watch(&ring_info->watch);
+ kfree(ring_info->watch.node);
+
+ /* No need to close communication channel, will be done by this function */
+ unbind_from_irqhandler(ring_info->irq, (void*) ring_info);
+
+ /* No need to free sring page, will be freed by this function when other side will end its access */
+ gnttab_end_foreign_access(ring_info->gref_ring, 0,
+ (unsigned long) ring_info->ring_front.sring);
+
+ kfree(ring_info);
+}
+
/* importer needs to know about shared page and port numbers for ring buffer and event channel */
-int hyper_dmabuf_importer_ringbuf_init(int sdomain, grant_ref_t gref, int port)
+int hyper_dmabuf_importer_ringbuf_init(int sdomain)
{
struct hyper_dmabuf_ring_info_import *ring_info;
struct hyper_dmabuf_sring *sring;
@@ -124,24 +276,33 @@ int hyper_dmabuf_importer_ringbuf_init(int sdomain, grant_ref_t gref, int port)
struct page *shared_ring;
struct gnttab_map_grant_ref *ops;
- struct gnttab_unmap_grant_ref *unmap_ops;
int ret;
+ int importer_gref, importer_port;
+
+ ret = hyper_dmabuf_get_ring_details(hyper_dmabuf_get_domid(), sdomain,
+ &importer_gref, &importer_port);
+
+ if (ret) {
+ printk("Domain %d has not created exporter ring for current domain\n", sdomain);
+ return ret;
+ }
ring_info = (struct hyper_dmabuf_ring_info_import *)
kmalloc(sizeof(*ring_info), GFP_KERNEL);
ring_info->sdomain = sdomain;
- ring_info->evtchn = port;
+ ring_info->evtchn = importer_port;
ops = (struct gnttab_map_grant_ref*)kmalloc(sizeof(*ops), GFP_KERNEL);
- unmap_ops = (struct gnttab_unmap_grant_ref*)kmalloc(sizeof(*unmap_ops), GFP_KERNEL);
if (gnttab_alloc_pages(1, &shared_ring)) {
return -EINVAL;
}
gnttab_set_map_op(&ops[0], (unsigned long)pfn_to_kaddr(page_to_pfn(shared_ring)),
- GNTMAP_host_map, gref, sdomain);
+ GNTMAP_host_map, importer_gref, sdomain);
+ gnttab_set_unmap_op(&ring_info->unmap_op, (unsigned long)pfn_to_kaddr(page_to_pfn(shared_ring)),
+ GNTMAP_host_map, -1);
ret = gnttab_map_refs(ops, NULL, &shared_ring, 1);
if (ret < 0) {
@@ -152,13 +313,15 @@ int hyper_dmabuf_importer_ringbuf_init(int sdomain, grant_ref_t gref, int port)
if (ops[0].status) {
printk("Ring mapping failed\n");
return -EINVAL;
+ } else {
+ ring_info->unmap_op.handle = ops[0].handle;
}
sring = (struct hyper_dmabuf_sring*) pfn_to_kaddr(page_to_pfn(shared_ring));
BACK_RING_INIT(&ring_info->ring_back, sring, PAGE_SIZE);
- ret = bind_interdomain_evtchn_to_irqhandler(sdomain, port,
+ ret = bind_interdomain_evtchn_to_irqhandler(sdomain, importer_port,
hyper_dmabuf_back_ring_isr, 0,
NULL, (void*)ring_info);
if (ret < 0) {
@@ -168,14 +331,51 @@ int hyper_dmabuf_importer_ringbuf_init(int sdomain, grant_ref_t gref, int port)
ring_info->irq = ret;
printk("%s: bound to eventchannel port: %d irq: %d\n", __func__,
- port,
+ importer_port,
ring_info->irq);
ret = hyper_dmabuf_register_importer_ring(ring_info);
+ /* Setup communcation channel in opposite direction */
+ if (!hyper_dmabuf_find_exporter_ring(sdomain)) {
+ ret = hyper_dmabuf_exporter_ringbuf_init(sdomain);
+ }
+
return ret;
}
+/* clenas up importer ring create for given source domain */
+void hyper_dmabuf_importer_ringbuf_cleanup(int sdomain)
+{
+ struct hyper_dmabuf_ring_info_import *ring_info;
+ struct page *shared_ring;
+
+ /* check if we have importer ring created for given sdomain */
+ ring_info = hyper_dmabuf_find_importer_ring(sdomain);
+
+ if (!ring_info)
+ return;
+
+ hyper_dmabuf_remove_importer_ring(sdomain);
+
+ /* no need to close event channel, will be done by that function */
+ unbind_from_irqhandler(ring_info->irq, (void*) ring_info);
+
+ /* unmapping shared ring page */
+ shared_ring = virt_to_page(ring_info->ring_back.sring);
+ gnttab_unmap_refs(&ring_info->unmap_op, NULL, &shared_ring, 1);
+ gnttab_free_pages(1, &shared_ring);
+
+ kfree(ring_info);
+}
+
+/* cleans up all exporter/importer rings */
+void hyper_dmabuf_cleanup_ringbufs(void)
+{
+ hyper_dmabuf_foreach_exporter_ring(hyper_dmabuf_exporter_ringbuf_cleanup);
+ hyper_dmabuf_foreach_importer_ring(hyper_dmabuf_importer_ringbuf_cleanup);
+}
+
int hyper_dmabuf_send_request(int domain, struct hyper_dmabuf_ring_rq *req)
{
struct hyper_dmabuf_front_ring *ring;
@@ -2,6 +2,7 @@
#define __HYPER_DMABUF_XEN_COMM_H__
#include "xen/interface/io/ring.h"
+#include "xen/xenbus.h"
#define MAX_NUMBER_OF_OPERANDS 9
@@ -27,6 +28,7 @@ struct hyper_dmabuf_ring_info_export {
int gref_ring;
int irq;
int port;
+ struct xenbus_watch watch;
};
struct hyper_dmabuf_ring_info_import {
@@ -34,17 +36,29 @@ struct hyper_dmabuf_ring_info_import {
int irq;
int evtchn;
struct hyper_dmabuf_back_ring ring_back;
+ struct gnttab_unmap_grant_ref unmap_op;
};
int32_t hyper_dmabuf_get_domid(void);
+int32_t hyper_dmabuf_setup_data_dir(void);
+int32_t hyper_dmabuf_destroy_data_dir(void);
int hyper_dmabuf_next_req_id_export(void);
/* exporter needs to generated info for page sharing */
-int hyper_dmabuf_exporter_ringbuf_init(int rdomain, grant_ref_t *gref, int *port);
+int hyper_dmabuf_exporter_ringbuf_init(int rdomain);
/* importer needs to know about shared page and port numbers for ring buffer and event channel */
-int hyper_dmabuf_importer_ringbuf_init(int sdomain, grant_ref_t gref, int port);
+int hyper_dmabuf_importer_ringbuf_init(int sdomain);
+
+/* cleans up exporter ring created for given domain */
+void hyper_dmabuf_exporter_ringbuf_cleanup(int rdomain);
+
+/* cleans up importer ring created for given domain */
+void hyper_dmabuf_importer_ringbuf_cleanup(int sdomain);
+
+/* cleans up all exporter/importer rings */
+void hyper_dmabuf_cleanup_ringbufs(void);
/* send request to the remote domain */
int hyper_dmabuf_send_request(int domain, struct hyper_dmabuf_ring_rq *req);
@@ -104,3 +104,25 @@ int hyper_dmabuf_remove_importer_ring(int domid)
return -1;
}
+
+void hyper_dmabuf_foreach_exporter_ring(void (*func)(int rdom))
+{
+ struct hyper_dmabuf_exporter_ring_info *info_entry;
+ struct hlist_node *tmp;
+ int bkt;
+
+ hash_for_each_safe(hyper_dmabuf_hash_exporter_ring, bkt, tmp, info_entry, node) {
+ func(info_entry->info->rdomain);
+ }
+}
+
+void hyper_dmabuf_foreach_importer_ring(void (*func)(int sdom))
+{
+ struct hyper_dmabuf_importer_ring_info *info_entry;
+ struct hlist_node *tmp;
+ int bkt;
+
+ hash_for_each_safe(hyper_dmabuf_hash_importer_ring, bkt, tmp, info_entry, node) {
+ func(info_entry->info->sdomain);
+ }
+}
@@ -32,4 +32,10 @@ int hyper_dmabuf_remove_exporter_ring(int domid);
int hyper_dmabuf_remove_importer_ring(int domid);
+/* iterates over all exporter rings and calls provided function for each of them */
+void hyper_dmabuf_foreach_exporter_ring(void (*func)(int rdom));
+
+/* iterates over all importer rings and calls provided function for each of them */
+void hyper_dmabuf_foreach_importer_ring(void (*func)(int sdom));
+
#endif // __HYPER_DMABUF_XEN_COMM_LIST_H__