[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [RFC PATCH 01/60] hyper_dmabuf: initial working version of hyper_dmabuf drv
Upload of intial version of hyper_DMABUF driver enabling DMA_BUF exchange between two different VMs in virtualized platform based on hypervisor such as KVM or XEN. Hyper_DMABUF drv's primary role is to import a DMA_BUF from originator then re-export it to another Linux VM so that it can be mapped and accessed by it. The functionality of this driver highly depends on Hypervisor's native page sharing mechanism and inter-VM communication support. This driver has two layers, one is main hyper_DMABUF framework for scatter-gather list management that handles actual import and export of DMA_BUF. Lower layer is about actual memory sharing and communication between two VMs, which is hypervisor-specific interface. This driver is initially designed to enable DMA_BUF sharing across VMs in Xen environment, so currently working with Xen only. This also adds Kernel configuration for hyper_DMABUF drv under Device Drivers->Xen driver support->hyper_dmabuf options. To give some brief information about each source file, hyper_dmabuf/hyper_dmabuf_conf.h : configuration info hyper_dmabuf/hyper_dmabuf_drv.c : driver interface and initialization hyper_dmabuf/hyper_dmabuf_imp.c : scatter-gather list generation and management. DMA_BUF ops for DMA_BUF reconstructed from hyper_DMABUF hyper_dmabuf/hyper_dmabuf_ioctl.c : IOCTLs calls for export/import and comm channel creation unexport. hyper_dmabuf/hyper_dmabuf_list.c : Database (linked-list) for exported and imported hyper_DMABUF hyper_dmabuf/hyper_dmabuf_msg.c : creation and management of messages between exporter and importer hyper_dmabuf/xen/hyper_dmabuf_xen_comm.c : comm ch management and ISRs for incoming messages. hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.c : Database (linked-list) for keeping information about existing comm channels among VMs Signed-off-by: Dongwon Kim <dongwon.kim@xxxxxxxxx> Signed-off-by: Mateusz Polrola <mateuszx.potrola@xxxxxxxxx> --- drivers/xen/Kconfig | 2 + drivers/xen/Makefile | 1 + drivers/xen/hyper_dmabuf/Kconfig | 14 + drivers/xen/hyper_dmabuf/Makefile | 34 + drivers/xen/hyper_dmabuf/hyper_dmabuf_conf.h | 2 + drivers/xen/hyper_dmabuf/hyper_dmabuf_drv.c | 54 ++ drivers/xen/hyper_dmabuf/hyper_dmabuf_drv.h | 101 +++ drivers/xen/hyper_dmabuf/hyper_dmabuf_imp.c | 852 +++++++++++++++++++++ drivers/xen/hyper_dmabuf/hyper_dmabuf_imp.h | 31 + drivers/xen/hyper_dmabuf/hyper_dmabuf_ioctl.c | 462 +++++++++++ drivers/xen/hyper_dmabuf/hyper_dmabuf_list.c | 119 +++ drivers/xen/hyper_dmabuf/hyper_dmabuf_list.h | 40 + drivers/xen/hyper_dmabuf/hyper_dmabuf_msg.c | 212 +++++ drivers/xen/hyper_dmabuf/hyper_dmabuf_msg.h | 45 ++ drivers/xen/hyper_dmabuf/hyper_dmabuf_query.h | 16 + drivers/xen/hyper_dmabuf/hyper_dmabuf_struct.h | 70 ++ .../xen/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.c | 328 ++++++++ .../xen/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.h | 62 ++ .../hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.c | 106 +++ .../hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.h | 35 + 20 files changed, 2586 insertions(+) create mode 100644 drivers/xen/hyper_dmabuf/Kconfig create mode 100644 drivers/xen/hyper_dmabuf/Makefile create mode 100644 drivers/xen/hyper_dmabuf/hyper_dmabuf_conf.h create mode 100644 drivers/xen/hyper_dmabuf/hyper_dmabuf_drv.c create mode 100644 drivers/xen/hyper_dmabuf/hyper_dmabuf_drv.h create mode 100644 drivers/xen/hyper_dmabuf/hyper_dmabuf_imp.c create mode 100644 drivers/xen/hyper_dmabuf/hyper_dmabuf_imp.h create mode 100644 drivers/xen/hyper_dmabuf/hyper_dmabuf_ioctl.c create mode 100644 drivers/xen/hyper_dmabuf/hyper_dmabuf_list.c create mode 100644 drivers/xen/hyper_dmabuf/hyper_dmabuf_list.h create mode 100644 drivers/xen/hyper_dmabuf/hyper_dmabuf_msg.c create mode 100644 drivers/xen/hyper_dmabuf/hyper_dmabuf_msg.h create mode 100644 drivers/xen/hyper_dmabuf/hyper_dmabuf_query.h create mode 100644 drivers/xen/hyper_dmabuf/hyper_dmabuf_struct.h create mode 100644 drivers/xen/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.c create mode 100644 drivers/xen/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.h create mode 100644 drivers/xen/hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.c create mode 100644 drivers/xen/hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.h diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index d8dd546..b59b0e3 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig @@ -321,4 +321,6 @@ config XEN_SYMS config XEN_HAVE_VPMU bool +source "drivers/xen/hyper_dmabuf/Kconfig" + endmenu diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile index 451e833..a6e253a 100644 --- a/drivers/xen/Makefile +++ b/drivers/xen/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_X86) += fallback.o obj-y += grant-table.o features.o balloon.o manage.o preempt.o time.o obj-y += events/ obj-y += xenbus/ +obj-y += hyper_dmabuf/ nostackp := $(call cc-option, -fno-stack-protector) CFLAGS_features.o := $(nostackp) diff --git a/drivers/xen/hyper_dmabuf/Kconfig b/drivers/xen/hyper_dmabuf/Kconfig new file mode 100644 index 0000000..75e1f96 --- /dev/null +++ b/drivers/xen/hyper_dmabuf/Kconfig @@ -0,0 +1,14 @@ +menu "hyper_dmabuf options" + +config HYPER_DMABUF + tristate "Enables hyper dmabuf driver" + default y + +config HYPER_DMABUF_XEN + bool "Configure hyper_dmabuf for XEN hypervisor" + default y + depends on HYPER_DMABUF + help + Configuring hyper_dmabuf driver for XEN hypervisor + +endmenu diff --git a/drivers/xen/hyper_dmabuf/Makefile b/drivers/xen/hyper_dmabuf/Makefile new file mode 100644 index 0000000..0be7445 --- /dev/null +++ b/drivers/xen/hyper_dmabuf/Makefile @@ -0,0 +1,34 @@ +TARGET_MODULE:=hyper_dmabuf + +# If we running by kernel building system +ifneq ($(KERNELRELEASE),) + $(TARGET_MODULE)-objs := hyper_dmabuf_drv.o \ + hyper_dmabuf_ioctl.o \ + hyper_dmabuf_list.o \ + hyper_dmabuf_imp.o \ + hyper_dmabuf_msg.o \ + xen/hyper_dmabuf_xen_comm.o \ + xen/hyper_dmabuf_xen_comm_list.o + +obj-$(CONFIG_HYPER_DMABUF) := $(TARGET_MODULE).o + +# If we are running without kernel build system +else +BUILDSYSTEM_DIR?=../../../ +PWD:=$(shell pwd) + +all : +# run kernel build system to make module +$(MAKE) -C $(BUILDSYSTEM_DIR) M=$(PWD) modules + +clean: +# run kernel build system to cleanup in current directory +$(MAKE) -C $(BUILDSYSTEM_DIR) M=$(PWD) clean + +load: + insmod ./$(TARGET_MODULE).ko + +unload: + rmmod ./$(TARGET_MODULE).ko + +endif diff --git a/drivers/xen/hyper_dmabuf/hyper_dmabuf_conf.h b/drivers/xen/hyper_dmabuf/hyper_dmabuf_conf.h new file mode 100644 index 0000000..3d9b2d6 --- /dev/null +++ b/drivers/xen/hyper_dmabuf/hyper_dmabuf_conf.h @@ -0,0 +1,2 @@ +#define CURRENT_TARGET XEN +#define INTER_DOMAIN_DMABUF_SYNCHRONIZATION diff --git a/drivers/xen/hyper_dmabuf/hyper_dmabuf_drv.c b/drivers/xen/hyper_dmabuf/hyper_dmabuf_drv.c new file mode 100644 index 0000000..0698327 --- /dev/null +++ b/drivers/xen/hyper_dmabuf/hyper_dmabuf_drv.c @@ -0,0 +1,54 @@ +#include <linux/init.h> /* module_init, module_exit */ +#include <linux/module.h> /* version info, MODULE_LICENSE, MODULE_AUTHOR, printk() */ +#include "hyper_dmabuf_conf.h" +#include "hyper_dmabuf_list.h" +#include "xen/hyper_dmabuf_xen_comm_list.h" + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("IOTG-PED, INTEL"); + +int register_device(void); +int unregister_device(void); + +/*===============================================================================================*/ +static int hyper_dmabuf_drv_init(void) +{ + int ret = 0; + + printk( KERN_NOTICE "hyper_dmabuf_starting: Initialization started" ); + + ret = register_device(); + if (ret < 0) { + return -EINVAL; + } + + printk( KERN_NOTICE "initializing database for imported/exported dmabufs\n"); + + ret = hyper_dmabuf_table_init(); + if (ret < 0) { + return -EINVAL; + } + + ret = hyper_dmabuf_ring_table_init(); + if (ret < 0) { + return -EINVAL; + } + + /* interrupt for comm should be registered here: */ + return ret; +} + +/*-----------------------------------------------------------------------------------------------*/ +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(); + + printk( KERN_NOTICE "dma_buf-src_sink model: Exiting" ); + unregister_device(); +} +/*===============================================================================================*/ + +module_init(hyper_dmabuf_drv_init); +module_exit(hyper_dmabuf_drv_exit); diff --git a/drivers/xen/hyper_dmabuf/hyper_dmabuf_drv.h b/drivers/xen/hyper_dmabuf/hyper_dmabuf_drv.h new file mode 100644 index 0000000..2dad9a6 --- /dev/null +++ b/drivers/xen/hyper_dmabuf/hyper_dmabuf_drv.h @@ -0,0 +1,101 @@ +#ifndef __LINUX_PUBLIC_HYPER_DMABUF_DRV_H__ +#define __LINUX_PUBLIC_HYPER_DMABUF_DRV_H__ + +typedef int (*hyper_dmabuf_ioctl_t)(void *data); + +struct hyper_dmabuf_ioctl_desc { + unsigned int cmd; + int flags; + hyper_dmabuf_ioctl_t func; + const char *name; +}; + +#define HYPER_DMABUF_IOCTL_DEF(ioctl, _func, _flags) \ + [_IOC_NR(ioctl)] = { \ + .cmd = ioctl, \ + .func = _func, \ + .flags = _flags, \ + .name = #ioctl \ + } + +#define IOCTL_HYPER_DMABUF_EXPORTER_RING_SETUP \ +_IOC(_IOC_NONE, 'G', 0, sizeof(struct ioctl_hyper_dmabuf_exporter_ring_setup)) +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 \ +_IOC(_IOC_NONE, 'G', 1, sizeof(struct ioctl_hyper_dmabuf_importer_ring_setup)) +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 \ +_IOC(_IOC_NONE, 'G', 2, sizeof(struct ioctl_hyper_dmabuf_export_remote)) +struct ioctl_hyper_dmabuf_export_remote { + /* IN parameters */ + /* DMA buf fd to be exported */ + uint32_t dmabuf_fd; + /* Domain id to which buffer should be exported */ + uint32_t remote_domain; + /* exported dma buf id */ + uint32_t hyper_dmabuf_id; + uint32_t private[4]; +}; + +#define IOCTL_HYPER_DMABUF_EXPORT_FD \ +_IOC(_IOC_NONE, 'G', 3, sizeof(struct ioctl_hyper_dmabuf_export_fd)) +struct ioctl_hyper_dmabuf_export_fd { + /* IN parameters */ + /* hyper dmabuf id to be imported */ + uint32_t hyper_dmabuf_id; + /* flags */ + uint32_t flags; + /* OUT parameters */ + /* exported dma buf fd */ + uint32_t fd; +}; + +#define IOCTL_HYPER_DMABUF_DESTROY \ +_IOC(_IOC_NONE, 'G', 4, sizeof(struct ioctl_hyper_dmabuf_destroy)) +struct ioctl_hyper_dmabuf_destroy { + /* IN parameters */ + /* hyper dmabuf id to be destroyed */ + uint32_t hyper_dmabuf_id; + /* OUT parameters */ + /* Status of request */ + uint32_t status; +}; + +#define IOCTL_HYPER_DMABUF_QUERY \ +_IOC(_IOC_NONE, 'G', 5, sizeof(struct ioctl_hyper_dmabuf_query)) +struct ioctl_hyper_dmabuf_query { + /* in parameters */ + /* hyper dmabuf id to be queried */ + uint32_t hyper_dmabuf_id; + /* item to be queried */ + uint32_t item; + /* OUT parameters */ + /* Value of queried item */ + 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__ diff --git a/drivers/xen/hyper_dmabuf/hyper_dmabuf_imp.c b/drivers/xen/hyper_dmabuf/hyper_dmabuf_imp.c new file mode 100644 index 0000000..faa5c1b --- /dev/null +++ b/drivers/xen/hyper_dmabuf/hyper_dmabuf_imp.c @@ -0,0 +1,852 @@ +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/dma-buf.h> +#include <xen/grant_table.h> +#include <asm/xen/page.h> +#include "hyper_dmabuf_struct.h" +#include "hyper_dmabuf_imp.h" +#include "xen/hyper_dmabuf_xen_comm.h" +#include "hyper_dmabuf_msg.h" + +#define REFS_PER_PAGE (PAGE_SIZE/sizeof(grant_ref_t)) + +/* return total number of pages referecned by a sgt + * for pre-calculation of # of pages behind a given sgt + */ +static int hyper_dmabuf_get_num_pgs(struct sg_table *sgt) +{ + struct scatterlist *sgl; + int length, i; + /* at least one page */ + int num_pages = 1; + + sgl = sgt->sgl; + + length = sgl->length - PAGE_SIZE + sgl->offset; + num_pages += ((length + PAGE_SIZE - 1)/PAGE_SIZE); /* round-up */ + + for (i = 1; i < sgt->nents; i++) { + sgl = sg_next(sgl); + num_pages += ((sgl->length + PAGE_SIZE - 1) / PAGE_SIZE); /* round-up */ + } + + return num_pages; +} + +/* extract pages directly from struct sg_table */ +struct hyper_dmabuf_pages_info *hyper_dmabuf_ext_pgs(struct sg_table *sgt) +{ + struct hyper_dmabuf_pages_info *pinfo; + int i, j; + int length; + struct scatterlist *sgl; + + pinfo = kmalloc(sizeof(*pinfo), GFP_KERNEL); + if (pinfo == NULL) + return NULL; + + pinfo->pages = kmalloc(sizeof(struct page *)*hyper_dmabuf_get_num_pgs(sgt), GFP_KERNEL); + if (pinfo->pages == NULL) + return NULL; + + sgl = sgt->sgl; + + pinfo->nents = 1; + pinfo->frst_ofst = sgl->offset; + pinfo->pages[0] = sg_page(sgl); + length = sgl->length - PAGE_SIZE + sgl->offset; + i=1; + + while (length > 0) { + pinfo->pages[i] = nth_page(sg_page(sgl), i); + length -= PAGE_SIZE; + pinfo->nents++; + i++; + } + + for (j = 1; j < sgt->nents; j++) { + sgl = sg_next(sgl); + pinfo->pages[i++] = sg_page(sgl); + length = sgl->length - PAGE_SIZE; + pinfo->nents++; + + while (length > 0) { + pinfo->pages[i] = nth_page(sg_page(sgl), i); + length -= PAGE_SIZE; + pinfo->nents++; + i++; + } + } + + /* + * lenght at that point will be 0 or negative, + * so to calculate last page size just add it to PAGE_SIZE + */ + pinfo->last_len = PAGE_SIZE + length; + + return pinfo; +} + +/* create sg_table with given pages and other parameters */ +struct sg_table* hyper_dmabuf_create_sgt(struct page **pages, + int frst_ofst, int last_len, int nents) +{ + struct sg_table *sgt; + struct scatterlist *sgl; + int i, ret; + + sgt = kmalloc(sizeof(struct sg_table), GFP_KERNEL); + if (sgt == NULL) { + return NULL; + } + + ret = sg_alloc_table(sgt, nents, GFP_KERNEL); + if (ret) { + kfree(sgt); + return NULL; + } + + sgl = sgt->sgl; + + sg_set_page(sgl, pages[0], PAGE_SIZE-frst_ofst, frst_ofst); + + for (i=1; i<nents-1; i++) { + sgl = sg_next(sgl); + sg_set_page(sgl, pages[i], PAGE_SIZE, 0); + } + + if (i > 1) /* more than one page */ { + sgl = sg_next(sgl); + sg_set_page(sgl, pages[i], last_len, 0); + } + + return sgt; +} + +/* + * Creates 2 level page directory structure for referencing shared pages. + * Top level page is a single page that contains up to 1024 refids that + * point to 2nd level pages. + * Each 2nd level page contains up to 1024 refids that point to shared + * data pages. + * There will always be one top level page and number of 2nd level pages + * depends on number of shared data pages. + * + * Top level page 2nd level pages Data pages + * +-------------------------+ ┌>+--------------------+ ┌--->+------------+ + * |2nd level page 0 refid |---┘ |Data page 0 refid |-┘ |Data page 0 | + * |2nd level page 1 refid |---┐ |Data page 1 refid |-┐ +------------+ + * | ... | | | .... | | + * |2nd level page 1023 refid|-┐ | |Data page 1023 refid| └--->+------------+ + * +-------------------------+ | | +--------------------+ |Data page 1 | + * | | +------------+ + * | └>+--------------------+ + * | |Data page 1024 refid| + * | |Data page 1025 refid| + * | | ... | + * | |Data page 2047 refid| + * | +--------------------+ + * | + * | ..... + * └-->+-----------------------+ + * |Data page 1047552 refid| + * |Data page 1047553 refid| + * | ... | + * |Data page 1048575 refid|-->+------------------+ + * +-----------------------+ |Data page 1048575 | + * +------------------+ + * + * Using such 2 level structure it is possible to reference up to 4GB of + * shared data using single refid pointing to top level page. + * + * Returns refid of top level page. + */ +grant_ref_t hyper_dmabuf_create_addressing_tables(grant_ref_t *data_refs, int nents, int rdomain, + struct hyper_dmabuf_shared_pages_info *shared_pages_info) +{ + /* + * Calculate number of pages needed for 2nd level addresing: + */ + int n_2nd_level_pages = (nents/REFS_PER_PAGE + ((nents % REFS_PER_PAGE) ? 1: 0));/* rounding */ + int i; + unsigned long gref_page_start; + grant_ref_t *tmp_page; + grant_ref_t top_level_ref; + grant_ref_t * addr_refs; + addr_refs = kcalloc(sizeof(grant_ref_t), n_2nd_level_pages, GFP_KERNEL); + + gref_page_start = __get_free_pages(GFP_KERNEL, n_2nd_level_pages); + tmp_page = (grant_ref_t *)gref_page_start; + + /* Store 2nd level pages to be freed later */ + shared_pages_info->addr_pages = tmp_page; + + /*TODO: make sure that allocated memory is filled with 0*/ + + /* Share 2nd level addressing pages in readonly mode*/ + for (i=0; i< n_2nd_level_pages; i++) { + addr_refs[i] = gnttab_grant_foreign_access(rdomain, virt_to_mfn((unsigned long)tmp_page+i*PAGE_SIZE ), 1); + } + + /* + * fill second level pages with data refs + */ + for (i = 0; i < nents; i++) { + tmp_page[i] = data_refs[i]; + } + + + /* allocate top level page */ + gref_page_start = __get_free_pages(GFP_KERNEL, 1); + tmp_page = (grant_ref_t *)gref_page_start; + + /* Store top level page to be freed later */ + shared_pages_info->top_level_page = tmp_page; + + /* + * fill top level page with reference numbers of second level pages refs. + */ + for (i=0; i< n_2nd_level_pages; i++) { + tmp_page[i] = addr_refs[i]; + } + + /* Share top level addressing page in readonly mode*/ + top_level_ref = gnttab_grant_foreign_access(rdomain, virt_to_mfn((unsigned long)tmp_page), 1); + + kfree(addr_refs); + + return top_level_ref; +} + +/* + * Maps provided top level ref id and then return array of pages containing data refs. + */ +struct page** hyper_dmabuf_get_data_refs(grant_ref_t top_level_ref, int domid, int nents, + struct hyper_dmabuf_shared_pages_info *shared_pages_info) +{ + struct page *top_level_page; + struct page **level2_pages; + + grant_ref_t *top_level_refs; + + struct gnttab_map_grant_ref top_level_map_ops; + struct gnttab_unmap_grant_ref top_level_unmap_ops; + + struct gnttab_map_grant_ref *map_ops; + struct gnttab_unmap_grant_ref *unmap_ops; + + unsigned long addr; + int n_level2_refs = 0; + int i; + + n_level2_refs = (nents / REFS_PER_PAGE) + ((nents % REFS_PER_PAGE) ? 1 : 0); + + level2_pages = kcalloc(sizeof(struct page*), n_level2_refs, GFP_KERNEL); + + map_ops = kcalloc(sizeof(map_ops[0]), REFS_PER_PAGE, GFP_KERNEL); + unmap_ops = kcalloc(sizeof(unmap_ops[0]), REFS_PER_PAGE, GFP_KERNEL); + + /* Map top level addressing page */ + if (gnttab_alloc_pages(1, &top_level_page)) { + printk("Cannot allocate pages\n"); + return NULL; + } + + addr = (unsigned long)pfn_to_kaddr(page_to_pfn(top_level_page)); + gnttab_set_map_op(&top_level_map_ops, addr, GNTMAP_host_map | GNTMAP_readonly, top_level_ref, domid); + gnttab_set_unmap_op(&top_level_unmap_ops, addr, GNTMAP_host_map | GNTMAP_readonly, -1); + + if (gnttab_map_refs(&top_level_map_ops, NULL, &top_level_page, 1)) { + printk("\nxen: dom0: HYPERVISOR map grant ref failed"); + return NULL; + } + + if (top_level_map_ops.status) { + printk("\nxen: dom0: HYPERVISOR map grant ref failed status = %d", + top_level_map_ops.status); + return NULL; + } else { + top_level_unmap_ops.handle = top_level_map_ops.handle; + } + + /* Parse contents of top level addressing page to find how many second level pages is there*/ + top_level_refs = pfn_to_kaddr(page_to_pfn(top_level_page)); + + /* Map all second level pages */ + if (gnttab_alloc_pages(n_level2_refs, level2_pages)) { + printk("Cannot allocate pages\n"); + return NULL; + } + + for (i = 0; i < n_level2_refs; i++) { + addr = (unsigned long)pfn_to_kaddr(page_to_pfn(level2_pages[i])); + gnttab_set_map_op(&map_ops[i], addr, GNTMAP_host_map | GNTMAP_readonly, top_level_refs[i], domid); + gnttab_set_unmap_op(&unmap_ops[i], addr, GNTMAP_host_map | GNTMAP_readonly, -1); + } + + if (gnttab_map_refs(map_ops, NULL, level2_pages, n_level2_refs)) { + printk("\nxen: dom0: HYPERVISOR map grant ref failed"); + return NULL; + } + + /* Checks if pages were mapped correctly and at the same time is calculating total number of data refids*/ + for (i = 0; i < n_level2_refs; i++) { + if (map_ops[i].status) { + printk("\nxen: dom0: HYPERVISOR map grant ref failed status = %d", + map_ops[i].status); + return NULL; + } else { + unmap_ops[i].handle = map_ops[i].handle; + } + } + + /* Unmap top level page, as it won't be needed any longer */ + if (gnttab_unmap_refs(&top_level_unmap_ops, NULL, &top_level_page, 1)) { + printk("\xen: cannot unmap top level page\n"); + return NULL; + } + + gnttab_free_pages(1, &top_level_page); + kfree(map_ops); + shared_pages_info->unmap_ops = unmap_ops; + + return level2_pages; +} + + +/* This collects all reference numbers for 2nd level shared pages and create a table + * with those in 1st level shared pages then return reference numbers for this top level + * table. */ +grant_ref_t hyper_dmabuf_create_gref_table(struct page **pages, int rdomain, int nents, + struct hyper_dmabuf_shared_pages_info *shared_pages_info) +{ + int i = 0; + grant_ref_t *data_refs; + grant_ref_t top_level_ref; + + /* allocate temp array for refs of shared data pages */ + data_refs = kcalloc(nents, sizeof(grant_ref_t), GFP_KERNEL); + + /* share data pages in rw mode*/ + for (i=0; i<nents; i++) { + data_refs[i] = gnttab_grant_foreign_access(rdomain, pfn_to_mfn(page_to_pfn(pages[i])), 0); + } + + /* create additional shared pages with 2 level addressing of data pages */ + top_level_ref = hyper_dmabuf_create_addressing_tables(data_refs, nents, rdomain, + shared_pages_info); + + /* Store exported pages refid to be unshared later */ + shared_pages_info->data_refs = data_refs; + shared_pages_info->top_level_ref = top_level_ref; + + return top_level_ref; +} + +int hyper_dmabuf_cleanup_gref_table(struct hyper_dmabuf_sgt_info *sgt_info) { + uint32_t i = 0; + struct hyper_dmabuf_shared_pages_info *shared_pages_info = &sgt_info->shared_pages_info; + + grant_ref_t *ref = shared_pages_info->top_level_page; + int n_2nd_level_pages = (sgt_info->sgt->nents/REFS_PER_PAGE + ((sgt_info->sgt->nents % REFS_PER_PAGE) ? 1: 0));/* rounding */ + + + if (shared_pages_info->data_refs == NULL || + shared_pages_info->addr_pages == NULL || + shared_pages_info->top_level_page == NULL || + shared_pages_info->top_level_ref == -1) { + printk("gref table for hyper_dmabuf already cleaned up\n"); + return 0; + } + + /* End foreign access for 2nd level addressing pages */ + while(ref[i] != 0 && i < n_2nd_level_pages) { + if (gnttab_query_foreign_access(ref[i])) { + printk("refid not shared !!\n"); + } + if (!gnttab_end_foreign_access_ref(ref[i], 1)) { + printk("refid still in use!!!\n"); + } + i++; + } + free_pages((unsigned long)shared_pages_info->addr_pages, i); + + /* End foreign access for top level addressing page */ + if (gnttab_query_foreign_access(shared_pages_info->top_level_ref)) { + printk("refid not shared !!\n"); + } + if (!gnttab_end_foreign_access_ref(shared_pages_info->top_level_ref, 1)) { + printk("refid still in use!!!\n"); + } + gnttab_end_foreign_access_ref(shared_pages_info->top_level_ref, 1); + free_pages((unsigned long)shared_pages_info->top_level_page, 1); + + /* End foreign access for data pages, but do not free them */ + for (i = 0; i < sgt_info->sgt->nents; i++) { + if (gnttab_query_foreign_access(shared_pages_info->data_refs[i])) { + printk("refid not shared !!\n"); + } + gnttab_end_foreign_access_ref(shared_pages_info->data_refs[i], 0); + } + + kfree(shared_pages_info->data_refs); + + shared_pages_info->data_refs = NULL; + shared_pages_info->addr_pages = NULL; + shared_pages_info->top_level_page = NULL; + shared_pages_info->top_level_ref = -1; + + return 0; +} + +int hyper_dmabuf_cleanup_imported_pages(struct hyper_dmabuf_imported_sgt_info *sgt_info) { + struct hyper_dmabuf_shared_pages_info *shared_pages_info = &sgt_info->shared_pages_info; + + if(shared_pages_info->unmap_ops == NULL || shared_pages_info->data_pages == NULL) { + printk("Imported pages already cleaned up or buffer was not imported yet\n"); + return 0; + } + + if (gnttab_unmap_refs(shared_pages_info->unmap_ops, NULL, shared_pages_info->data_pages, sgt_info->nents) ) { + printk("Cannot unmap data pages\n"); + return -EINVAL; + } + + gnttab_free_pages(sgt_info->nents, shared_pages_info->data_pages); + kfree(shared_pages_info->data_pages); + kfree(shared_pages_info->unmap_ops); + shared_pages_info->unmap_ops = NULL; + shared_pages_info->data_pages = NULL; + + return 0; +} + +/* map and construct sg_lists from reference numbers */ +struct sg_table* hyper_dmabuf_map_pages(grant_ref_t top_level_gref, int frst_ofst, int last_len, int nents, int sdomain, + struct hyper_dmabuf_shared_pages_info *shared_pages_info) +{ + struct sg_table *st; + struct page **pages; + struct gnttab_map_grant_ref *ops; + struct gnttab_unmap_grant_ref *unmap_ops; + unsigned long addr; + grant_ref_t *refs; + int i; + int n_level2_refs = (nents / REFS_PER_PAGE) + ((nents % REFS_PER_PAGE) ? 1 : 0); + + /* Get data refids */ + struct page** refid_pages = hyper_dmabuf_get_data_refs(top_level_gref, sdomain, nents, + shared_pages_info); + + pages = kcalloc(sizeof(struct page*), nents, GFP_KERNEL); + if (pages == NULL) { + return NULL; + } + + /* allocate new pages that are mapped to shared pages via grant-table */ + if (gnttab_alloc_pages(nents, pages)) { + printk("Cannot allocate pages\n"); + return NULL; + } + + ops = (struct gnttab_map_grant_ref *)kcalloc(nents, sizeof(struct gnttab_map_grant_ref), GFP_KERNEL); + unmap_ops = (struct gnttab_unmap_grant_ref *)kcalloc(nents, sizeof(struct gnttab_unmap_grant_ref), GFP_KERNEL); + + for (i=0; i<nents; i++) { + addr = (unsigned long)pfn_to_kaddr(page_to_pfn(pages[i])); + refs = pfn_to_kaddr(page_to_pfn(refid_pages[i / REFS_PER_PAGE])); + gnttab_set_map_op(&ops[i], addr, GNTMAP_host_map | GNTMAP_readonly, refs[i % REFS_PER_PAGE], sdomain); + gnttab_set_unmap_op(&unmap_ops[i], addr, GNTMAP_host_map | GNTMAP_readonly, -1); + } + + if (gnttab_map_refs(ops, NULL, pages, nents)) { + printk("\nxen: dom0: HYPERVISOR map grant ref failed\n"); + return NULL; + } + + for (i=0; i<nents; i++) { + if (ops[i].status) { + printk("\nxen: dom0: HYPERVISOR map grant ref failed status = %d\n", + ops[0].status); + return NULL; + } else { + unmap_ops[i].handle = ops[i].handle; + } + } + + st = hyper_dmabuf_create_sgt(pages, frst_ofst, last_len, nents); + + if (gnttab_unmap_refs(shared_pages_info->unmap_ops, NULL, refid_pages, n_level2_refs) ) { + printk("Cannot unmap 2nd level refs\n"); + return NULL; + } + + gnttab_free_pages(n_level2_refs, refid_pages); + kfree(refid_pages); + + kfree(shared_pages_info->unmap_ops); + shared_pages_info->unmap_ops = unmap_ops; + shared_pages_info->data_pages = pages; + kfree(ops); + + return st; +} + +inline int hyper_dmabuf_sync_request_and_wait(int id, int ops) +{ + struct hyper_dmabuf_ring_rq *req; + int operands[2]; + int ret; + + operands[0] = id; + operands[1] = ops; + + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + + hyper_dmabuf_create_request(req, HYPER_DMABUF_OPS_TO_SOURCE, &operands[0]); + + /* send request */ + ret = hyper_dmabuf_send_request(id, req); + + /* TODO: wait until it gets response.. or can we just move on? */ + + kfree(req); + + return ret; +} + +static int hyper_dmabuf_ops_attach(struct dma_buf* dmabuf, struct device* dev, + struct dma_buf_attachment *attach) +{ + struct hyper_dmabuf_imported_sgt_info *sgt_info; + int ret; + + if (!attach->dmabuf->priv) + return -EINVAL; + + sgt_info = (struct hyper_dmabuf_imported_sgt_info *)attach->dmabuf->priv; + + ret = hyper_dmabuf_sync_request_and_wait(HYPER_DMABUF_ID_IMPORTER_GET_SDOMAIN_ID(sgt_info->hyper_dmabuf_id), + HYPER_DMABUF_OPS_ATTACH); + + if (ret < 0) { + printk("send dmabuf sync request failed\n"); + } + + return ret; +} + +static void hyper_dmabuf_ops_detach(struct dma_buf* dmabuf, struct dma_buf_attachment *attach) +{ + struct hyper_dmabuf_imported_sgt_info *sgt_info; + int ret; + + if (!attach->dmabuf->priv) + return; + + sgt_info = (struct hyper_dmabuf_imported_sgt_info *)attach->dmabuf->priv; + + ret = hyper_dmabuf_sync_request_and_wait(HYPER_DMABUF_ID_IMPORTER_GET_SDOMAIN_ID(sgt_info->hyper_dmabuf_id), + HYPER_DMABUF_OPS_DETACH); + + if (ret < 0) { + printk("send dmabuf sync request failed\n"); + } +} + +static struct sg_table* hyper_dmabuf_ops_map(struct dma_buf_attachment *attachment, + enum dma_data_direction dir) +{ + struct sg_table *st; + struct hyper_dmabuf_imported_sgt_info *sgt_info; + struct hyper_dmabuf_pages_info *page_info; + int ret; + + if (!attachment->dmabuf->priv) + return NULL; + + sgt_info = (struct hyper_dmabuf_imported_sgt_info *)attachment->dmabuf->priv; + + /* extract pages from sgt */ + page_info = hyper_dmabuf_ext_pgs(sgt_info->sgt); + + /* 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); + if (st == NULL) + goto err_free_sg; + + if (!dma_map_sg(attachment->dev, st->sgl, st->nents, dir)) { + goto err_free_sg; + } + + ret = hyper_dmabuf_sync_request_and_wait(HYPER_DMABUF_ID_IMPORTER_GET_SDOMAIN_ID(sgt_info->hyper_dmabuf_id), + HYPER_DMABUF_OPS_MAP); + + if (ret < 0) { + printk("send dmabuf sync request failed\n"); + } + + return st; + +err_free_sg: + sg_free_table(st); + kfree(st); + return NULL; +} + +static void hyper_dmabuf_ops_unmap(struct dma_buf_attachment *attachment, + struct sg_table *sg, + enum dma_data_direction dir) +{ + struct hyper_dmabuf_imported_sgt_info *sgt_info; + int ret; + + if (!attachment->dmabuf->priv) + return; + + sgt_info = (struct hyper_dmabuf_imported_sgt_info *)attachment->dmabuf->priv; + + dma_unmap_sg(attachment->dev, sg->sgl, sg->nents, dir); + + sg_free_table(sg); + kfree(sg); + + ret = hyper_dmabuf_sync_request_and_wait(HYPER_DMABUF_ID_IMPORTER_GET_SDOMAIN_ID(sgt_info->hyper_dmabuf_id), + HYPER_DMABUF_OPS_UNMAP); + + if (ret < 0) { + printk("send dmabuf sync request failed\n"); + } +} + +static void hyper_dmabuf_ops_release(struct dma_buf *dmabuf) +{ + struct hyper_dmabuf_imported_sgt_info *sgt_info; + int ret; + + if (!dmabuf->priv) + return; + + sgt_info = (struct hyper_dmabuf_imported_sgt_info *)dmabuf->priv; + + ret = hyper_dmabuf_sync_request_and_wait(HYPER_DMABUF_ID_IMPORTER_GET_SDOMAIN_ID(sgt_info->hyper_dmabuf_id), + HYPER_DMABUF_OPS_RELEASE); + + if (ret < 0) { + printk("send dmabuf sync request failed\n"); + } +} + +static int hyper_dmabuf_ops_begin_cpu_access(struct dma_buf *dmabuf, enum dma_data_direction dir) +{ + struct hyper_dmabuf_imported_sgt_info *sgt_info; + int ret; + + if (!dmabuf->priv) + return -EINVAL; + + sgt_info = (struct hyper_dmabuf_imported_sgt_info *)dmabuf->priv; + + ret = hyper_dmabuf_sync_request_and_wait(HYPER_DMABUF_ID_IMPORTER_GET_SDOMAIN_ID(sgt_info->hyper_dmabuf_id), + HYPER_DMABUF_OPS_BEGIN_CPU_ACCESS); + if (ret < 0) { + printk("send dmabuf sync request failed\n"); + } + + return ret; +} + +static int hyper_dmabuf_ops_end_cpu_access(struct dma_buf *dmabuf, enum dma_data_direction dir) +{ + struct hyper_dmabuf_imported_sgt_info *sgt_info; + int ret; + + if (!dmabuf->priv) + return -EINVAL; + + sgt_info = (struct hyper_dmabuf_imported_sgt_info *)dmabuf->priv; + + ret = hyper_dmabuf_sync_request_and_wait(HYPER_DMABUF_ID_IMPORTER_GET_SDOMAIN_ID(sgt_info->hyper_dmabuf_id), + HYPER_DMABUF_OPS_END_CPU_ACCESS); + if (ret < 0) { + printk("send dmabuf sync request failed\n"); + } + + return 0; +} + +static void *hyper_dmabuf_ops_kmap_atomic(struct dma_buf *dmabuf, unsigned long pgnum) +{ + struct hyper_dmabuf_imported_sgt_info *sgt_info; + int ret; + + if (!dmabuf->priv) + return NULL; + + sgt_info = (struct hyper_dmabuf_imported_sgt_info *)dmabuf->priv; + + ret = hyper_dmabuf_sync_request_and_wait(HYPER_DMABUF_ID_IMPORTER_GET_SDOMAIN_ID(sgt_info->hyper_dmabuf_id), + HYPER_DMABUF_OPS_KMAP_ATOMIC); + if (ret < 0) { + printk("send dmabuf sync request failed\n"); + } + + return NULL; /* for now NULL.. need to return the address of mapped region */ +} + +static void hyper_dmabuf_ops_kunmap_atomic(struct dma_buf *dmabuf, unsigned long pgnum, void *vaddr) +{ + struct hyper_dmabuf_imported_sgt_info *sgt_info; + int ret; + + if (!dmabuf->priv) + return; + + sgt_info = (struct hyper_dmabuf_imported_sgt_info *)dmabuf->priv; + + ret = hyper_dmabuf_sync_request_and_wait(HYPER_DMABUF_ID_IMPORTER_GET_SDOMAIN_ID(sgt_info->hyper_dmabuf_id), + HYPER_DMABUF_OPS_KUNMAP_ATOMIC); + if (ret < 0) { + printk("send dmabuf sync request failed\n"); + } +} + +static void *hyper_dmabuf_ops_kmap(struct dma_buf *dmabuf, unsigned long pgnum) +{ + struct hyper_dmabuf_imported_sgt_info *sgt_info; + int ret; + + if (!dmabuf->priv) + return NULL; + + sgt_info = (struct hyper_dmabuf_imported_sgt_info *)dmabuf->priv; + + ret = hyper_dmabuf_sync_request_and_wait(HYPER_DMABUF_ID_IMPORTER_GET_SDOMAIN_ID(sgt_info->hyper_dmabuf_id), + HYPER_DMABUF_OPS_KMAP); + if (ret < 0) { + printk("send dmabuf sync request failed\n"); + } + + return NULL; /* for now NULL.. need to return the address of mapped region */ +} + +static void hyper_dmabuf_ops_kunmap(struct dma_buf *dmabuf, unsigned long pgnum, void *vaddr) +{ + struct hyper_dmabuf_imported_sgt_info *sgt_info; + int ret; + + if (!dmabuf->priv) + return; + + sgt_info = (struct hyper_dmabuf_imported_sgt_info *)dmabuf->priv; + + ret = hyper_dmabuf_sync_request_and_wait(HYPER_DMABUF_ID_IMPORTER_GET_SDOMAIN_ID(sgt_info->hyper_dmabuf_id), + HYPER_DMABUF_OPS_KUNMAP); + if (ret < 0) { + printk("send dmabuf sync request failed\n"); + } +} + +static int hyper_dmabuf_ops_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) +{ + struct hyper_dmabuf_imported_sgt_info *sgt_info; + int ret; + + if (!dmabuf->priv) + return -EINVAL; + + sgt_info = (struct hyper_dmabuf_imported_sgt_info *)dmabuf->priv; + + ret = hyper_dmabuf_sync_request_and_wait(HYPER_DMABUF_ID_IMPORTER_GET_SDOMAIN_ID(sgt_info->hyper_dmabuf_id), + HYPER_DMABUF_OPS_MMAP); + if (ret < 0) { + printk("send dmabuf sync request failed\n"); + } + + return ret; +} + +static void *hyper_dmabuf_ops_vmap(struct dma_buf *dmabuf) +{ + struct hyper_dmabuf_imported_sgt_info *sgt_info; + int ret; + + if (!dmabuf->priv) + return NULL; + + sgt_info = (struct hyper_dmabuf_imported_sgt_info *)dmabuf->priv; + + ret = hyper_dmabuf_sync_request_and_wait(HYPER_DMABUF_ID_IMPORTER_GET_SDOMAIN_ID(sgt_info->hyper_dmabuf_id), + HYPER_DMABUF_OPS_VMAP); + if (ret < 0) { + printk("send dmabuf sync request failed\n"); + } + + return NULL; +} + +static void hyper_dmabuf_ops_vunmap(struct dma_buf *dmabuf, void *vaddr) +{ + struct hyper_dmabuf_imported_sgt_info *sgt_info; + int ret; + + if (!dmabuf->priv) + return; + + sgt_info = (struct hyper_dmabuf_imported_sgt_info *)dmabuf->priv; + + ret = hyper_dmabuf_sync_request_and_wait(HYPER_DMABUF_ID_IMPORTER_GET_SDOMAIN_ID(sgt_info->hyper_dmabuf_id), + HYPER_DMABUF_OPS_VUNMAP); + if (ret < 0) { + printk("send dmabuf sync request failed\n"); + } +} + +static const struct dma_buf_ops hyper_dmabuf_ops = { + .attach = hyper_dmabuf_ops_attach, + .detach = hyper_dmabuf_ops_detach, + .map_dma_buf = hyper_dmabuf_ops_map, + .unmap_dma_buf = hyper_dmabuf_ops_unmap, + .release = hyper_dmabuf_ops_release, + .begin_cpu_access = (void*)hyper_dmabuf_ops_begin_cpu_access, + .end_cpu_access = (void*)hyper_dmabuf_ops_end_cpu_access, + .map_atomic = hyper_dmabuf_ops_kmap_atomic, + .unmap_atomic = hyper_dmabuf_ops_kunmap_atomic, + .map = hyper_dmabuf_ops_kmap, + .unmap = hyper_dmabuf_ops_kunmap, + .mmap = hyper_dmabuf_ops_mmap, + .vmap = hyper_dmabuf_ops_vmap, + .vunmap = hyper_dmabuf_ops_vunmap, +}; + +/* exporting dmabuf as fd */ +int hyper_dmabuf_export_fd(struct hyper_dmabuf_imported_sgt_info *dinfo, int flags) +{ + int fd; + + struct dma_buf* dmabuf; + +/* call hyper_dmabuf_export_dmabuf and create and bind a handle for it + * then release */ + + dmabuf = hyper_dmabuf_export_dma_buf(dinfo); + + fd = dma_buf_fd(dmabuf, flags); + + return fd; +} + +struct dma_buf* hyper_dmabuf_export_dma_buf(struct hyper_dmabuf_imported_sgt_info *dinfo) +{ + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + + exp_info.ops = &hyper_dmabuf_ops; + exp_info.size = dinfo->sgt->nents * PAGE_SIZE; /* multiple of PAGE_SIZE, not considering offset */ + exp_info.flags = /* not sure about flag */0; + exp_info.priv = dinfo; + + return dma_buf_export(&exp_info); +}; diff --git a/drivers/xen/hyper_dmabuf/hyper_dmabuf_imp.h b/drivers/xen/hyper_dmabuf/hyper_dmabuf_imp.h new file mode 100644 index 0000000..003c158 --- /dev/null +++ b/drivers/xen/hyper_dmabuf/hyper_dmabuf_imp.h @@ -0,0 +1,31 @@ +#ifndef __HYPER_DMABUF_IMP_H__ +#define __HYPER_DMABUF_IMP_H__ + +#include "hyper_dmabuf_struct.h" + +/* extract pages directly from struct sg_table */ +struct hyper_dmabuf_pages_info *hyper_dmabuf_ext_pgs(struct sg_table *sgt); + +/* create sg_table with given pages and other parameters */ +struct sg_table* hyper_dmabuf_create_sgt(struct page **pages, + int frst_ofst, int last_len, int nents); + +grant_ref_t hyper_dmabuf_create_gref_table(struct page **pages, int rdomain, int nents, + struct hyper_dmabuf_shared_pages_info *shared_pages_info); + +int hyper_dmabuf_cleanup_gref_table(struct hyper_dmabuf_sgt_info *sgt_info); + +int hyper_dmabuf_cleanup_imported_pages(struct hyper_dmabuf_imported_sgt_info *sgt_info); + +/* map first level tables that contains reference numbers for actual shared pages */ +grant_ref_t *hyper_dmabuf_map_gref_table(grant_ref_t *gref_table, int n_pages_table); + +/* map and construct sg_lists from reference numbers */ +struct sg_table* hyper_dmabuf_map_pages(grant_ref_t gref, int frst_ofst, int last_len, int nents, int sdomain, + struct hyper_dmabuf_shared_pages_info *shared_pages_info); + +int hyper_dmabuf_export_fd(struct hyper_dmabuf_imported_sgt_info *dinfo, int flags); + +struct dma_buf* hyper_dmabuf_export_dma_buf(struct hyper_dmabuf_imported_sgt_info *dinfo); + +#endif /* __HYPER_DMABUF_IMP_H__ */ diff --git a/drivers/xen/hyper_dmabuf/hyper_dmabuf_ioctl.c b/drivers/xen/hyper_dmabuf/hyper_dmabuf_ioctl.c new file mode 100644 index 0000000..5e50908 --- /dev/null +++ b/drivers/xen/hyper_dmabuf/hyper_dmabuf_ioctl.c @@ -0,0 +1,462 @@ +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/miscdevice.h> +#include <linux/uaccess.h> +#include <linux/dma-buf.h> +#include <linux/delay.h> +#include "hyper_dmabuf_struct.h" +#include "hyper_dmabuf_imp.h" +#include "hyper_dmabuf_list.h" +#include "hyper_dmabuf_drv.h" +#include "hyper_dmabuf_query.h" +#include "xen/hyper_dmabuf_xen_comm.h" +#include "hyper_dmabuf_msg.h" + +struct hyper_dmabuf_private { + struct device *device; +} hyper_dmabuf_private; + +static uint32_t hyper_dmabuf_id_gen(void) { + /* TODO: add proper implementation */ + static uint32_t id = 0; + static int32_t domid = -1; + if (domid == -1) { + domid = hyper_dmabuf_get_domid(); + } + return HYPER_DMABUF_ID_IMPORTER(domid, id++); +} + +static int hyper_dmabuf_exporter_ring_setup(void *data) +{ + struct ioctl_hyper_dmabuf_exporter_ring_setup *ring_attr; + int ret = 0; + + if (!data) { + printk("user data is NULL\n"); + return -1; + } + ring_attr = (struct ioctl_hyper_dmabuf_exporter_ring_setup *)data; + + ret = hyper_dmabuf_exporter_ringbuf_init(ring_attr->remote_domain, + &ring_attr->ring_refid, + &ring_attr->port); + + return ret; +} + +static int hyper_dmabuf_importer_ring_setup(void *data) +{ + struct ioctl_hyper_dmabuf_importer_ring_setup *setup_imp_ring_attr; + int ret = 0; + + if (!data) { + printk("user data is NULL\n"); + return -1; + } + + setup_imp_ring_attr = (struct ioctl_hyper_dmabuf_importer_ring_setup *)data; + + /* 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); + + return ret; +} + +static int hyper_dmabuf_export_remote(void *data) +{ + struct ioctl_hyper_dmabuf_export_remote *export_remote_attr; + struct dma_buf *dma_buf; + struct dma_buf_attachment *attachment; + struct sg_table *sgt; + struct hyper_dmabuf_pages_info *page_info; + struct hyper_dmabuf_sgt_info *sgt_info; + struct hyper_dmabuf_ring_rq *req; + int operands[9]; + int ret = 0; + + if (!data) { + printk("user data is NULL\n"); + return -1; + } + + export_remote_attr = (struct ioctl_hyper_dmabuf_export_remote *)data; + + dma_buf = dma_buf_get(export_remote_attr->dmabuf_fd); + if (!dma_buf) { + printk("Cannot get dma buf\n"); + return -1; + } + + attachment = dma_buf_attach(dma_buf, hyper_dmabuf_private.device); + if (!attachment) { + printk("Cannot get attachment\n"); + return -1; + } + + /* we check if this specific attachment was already exported + * to the same domain and if yes, it returns hyper_dmabuf_id + * of pre-exported sgt */ + ret = hyper_dmabuf_find_id(attachment, export_remote_attr->remote_domain); + if (ret != -1) { + dma_buf_detach(dma_buf, attachment); + dma_buf_put(dma_buf); + export_remote_attr->hyper_dmabuf_id = ret; + return 0; + } + /* Clear ret, as that will cause whole ioctl to return failure to userspace, which is not true */ + ret = 0; + + sgt = dma_buf_map_attachment(attachment, DMA_BIDIRECTIONAL); + + sgt_info = kmalloc(sizeof(*sgt_info), GFP_KERNEL); + + sgt_info->hyper_dmabuf_id = hyper_dmabuf_id_gen(); + /* TODO: We might need to consider using port number on event channel? */ + sgt_info->hyper_dmabuf_rdomain = export_remote_attr->remote_domain; + sgt_info->sgt = sgt; + sgt_info->attachment = attachment; + sgt_info->dma_buf = dma_buf; + + page_info = hyper_dmabuf_ext_pgs(sgt); + if (page_info == NULL) + goto fail_export; + + /* now register it to export list */ + hyper_dmabuf_register_exported(sgt_info); + + page_info->hyper_dmabuf_rdomain = sgt_info->hyper_dmabuf_rdomain; + page_info->hyper_dmabuf_id = sgt_info->hyper_dmabuf_id; /* may not be needed */ + + export_remote_attr->hyper_dmabuf_id = sgt_info->hyper_dmabuf_id; + + /* now create table of grefs for shared pages and */ + + /* now create request for importer via ring */ + operands[0] = page_info->hyper_dmabuf_id; + operands[1] = page_info->nents; + operands[2] = page_info->frst_ofst; + operands[3] = page_info->last_len; + operands[4] = hyper_dmabuf_create_gref_table(page_info->pages, export_remote_attr->remote_domain, + page_info->nents, &sgt_info->shared_pages_info); + /* driver/application specific private info, max 32 bytes */ + operands[5] = export_remote_attr->private[0]; + operands[6] = export_remote_attr->private[1]; + operands[7] = export_remote_attr->private[2]; + operands[8] = export_remote_attr->private[3]; + + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + + /* composing a message to the importer */ + hyper_dmabuf_create_request(req, HYPER_DMABUF_EXPORT, &operands[0]); + if(hyper_dmabuf_send_request(export_remote_attr->remote_domain, req)) + goto fail_send_request; + + /* free msg */ + kfree(req); + /* free page_info */ + kfree(page_info); + + return ret; + +fail_send_request: + kfree(req); + hyper_dmabuf_remove_exported(sgt_info->hyper_dmabuf_id); + +fail_export: + dma_buf_unmap_attachment(sgt_info->attachment, sgt_info->sgt, DMA_BIDIRECTIONAL); + dma_buf_detach(sgt_info->dma_buf, sgt_info->attachment); + dma_buf_put(sgt_info->dma_buf); + + return -EINVAL; +} + +static int hyper_dmabuf_export_fd_ioctl(void *data) +{ + struct ioctl_hyper_dmabuf_export_fd *export_fd_attr; + struct hyper_dmabuf_imported_sgt_info *imported_sgt_info; + int ret = 0; + + if (!data) { + printk("user data is NULL\n"); + return -1; + } + + export_fd_attr = (struct ioctl_hyper_dmabuf_export_fd *)data; + + /* look for dmabuf for the id */ + imported_sgt_info = hyper_dmabuf_find_imported(export_fd_attr->hyper_dmabuf_id); + if (imported_sgt_info == NULL) /* can't find sgt from the table */ + return -1; + + printk("%s Found buffer gref %d off %d last len %d nents %d domain %d\n", __func__, + imported_sgt_info->gref, imported_sgt_info->frst_ofst, + imported_sgt_info->last_len, imported_sgt_info->nents, + HYPER_DMABUF_ID_IMPORTER_GET_SDOMAIN_ID(imported_sgt_info->hyper_dmabuf_id)); + + imported_sgt_info->sgt = hyper_dmabuf_map_pages(imported_sgt_info->gref, + imported_sgt_info->frst_ofst, + imported_sgt_info->last_len, + imported_sgt_info->nents, + HYPER_DMABUF_ID_IMPORTER_GET_SDOMAIN_ID(imported_sgt_info->hyper_dmabuf_id), + &imported_sgt_info->shared_pages_info); + + if (!imported_sgt_info->sgt) { + return -1; + } + + export_fd_attr->fd = hyper_dmabuf_export_fd(imported_sgt_info, export_fd_attr->flags); + if (export_fd_attr < 0) { + ret = export_fd_attr->fd; + } + + return ret; +} + +/* removing dmabuf from the database and send int req to the source domain +* to unmap it. */ +static int hyper_dmabuf_destroy(void *data) +{ + struct ioctl_hyper_dmabuf_destroy *destroy_attr; + struct hyper_dmabuf_sgt_info *sgt_info; + struct hyper_dmabuf_ring_rq *req; + int ret; + + if (!data) { + printk("user data is NULL\n"); + return -EINVAL; + } + + destroy_attr = (struct ioctl_hyper_dmabuf_destroy *)data; + + /* find dmabuf in export list */ + sgt_info = hyper_dmabuf_find_exported(destroy_attr->hyper_dmabuf_id); + if (sgt_info == NULL) { /* failed to find corresponding entry in export list */ + destroy_attr->status = -EINVAL; + return -EFAULT; + } + + req = kcalloc(1, sizeof(*req), GFP_KERNEL); + + hyper_dmabuf_create_request(req, HYPER_DMABUF_DESTROY, &destroy_attr->hyper_dmabuf_id); + + /* now send destroy request to remote domain + * currently assuming there's only one importer exist */ + ret = hyper_dmabuf_send_request(sgt_info->hyper_dmabuf_rdomain, req); + if (ret < 0) { + kfree(req); + return -EFAULT; + } + + /* free msg */ + kfree(req); + destroy_attr->status = ret; + + /* Rest of cleanup will follow when importer will free it's buffer, + * current implementation assumes that there is only one importer + */ + + return ret; +} + +static int hyper_dmabuf_query(void *data) +{ + struct ioctl_hyper_dmabuf_query *query_attr; + struct hyper_dmabuf_sgt_info *sgt_info; + struct hyper_dmabuf_imported_sgt_info *imported_sgt_info; + int ret = 0; + + if (!data) { + printk("user data is NULL\n"); + return -EINVAL; + } + + query_attr = (struct ioctl_hyper_dmabuf_query *)data; + + sgt_info = hyper_dmabuf_find_exported(query_attr->hyper_dmabuf_id); + imported_sgt_info = hyper_dmabuf_find_imported(query_attr->hyper_dmabuf_id); + + /* if dmabuf can't be found in both lists, return */ + if (!(sgt_info && imported_sgt_info)) { + printk("can't find entry anywhere\n"); + return -EINVAL; + } + + /* not considering the case where a dmabuf is found on both queues + * in one domain */ + switch (query_attr->item) + { + case DMABUF_QUERY_TYPE_LIST: + if (sgt_info) { + query_attr->info = EXPORTED; + } else { + query_attr->info = IMPORTED; + } + break; + + /* exporting domain of this specific dmabuf*/ + case DMABUF_QUERY_EXPORTER: + if (sgt_info) { + query_attr->info = 0xFFFFFFFF; /* myself */ + } else { + query_attr->info = (HYPER_DMABUF_ID_IMPORTER_GET_SDOMAIN_ID(imported_sgt_info->hyper_dmabuf_id)); + } + break; + + /* importing domain of this specific dmabuf */ + case DMABUF_QUERY_IMPORTER: + if (sgt_info) { + query_attr->info = sgt_info->hyper_dmabuf_rdomain; + } else { +#if 0 /* TODO: a global variable, current_domain does not exist yet*/ + query_attr->info = current_domain; +#endif + } + break; + + /* size of dmabuf in byte */ + case DMABUF_QUERY_SIZE: + if (sgt_info) { +#if 0 /* TODO: hyper_dmabuf_buf_size is not implemented yet */ + query_attr->info = hyper_dmabuf_buf_size(sgt_info->sgt); +#endif + } else { + query_attr->info = imported_sgt_info->nents * 4096 - + imported_sgt_info->frst_ofst - 4096 + + imported_sgt_info->last_len; + } + break; + } + + 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), + HYPER_DMABUF_IOCTL_DEF(IOCTL_HYPER_DMABUF_EXPORT_REMOTE, hyper_dmabuf_export_remote, 0), + 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, + unsigned int cmd, unsigned long param) +{ + const struct hyper_dmabuf_ioctl_desc *ioctl = NULL; + unsigned int nr = _IOC_NR(cmd); + int ret = -EINVAL; + hyper_dmabuf_ioctl_t func; + char *kdata; + + ioctl = &hyper_dmabuf_ioctls[nr]; + + func = ioctl->func; + + if (unlikely(!func)) { + printk("no function\n"); + return -EINVAL; + } + + kdata = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL); + if (!kdata) { + printk("no memory\n"); + return -ENOMEM; + } + + if (copy_from_user(kdata, (void __user *)param, _IOC_SIZE(cmd)) != 0) { + printk("failed to copy from user arguments\n"); + return -EFAULT; + } + + ret = func(kdata); + + if (copy_to_user((void __user *)param, kdata, _IOC_SIZE(cmd)) != 0) { + printk("failed to copy to user arguments\n"); + return -EFAULT; + } + + kfree(kdata); + + return ret; +} + +struct device_info { + int curr_domain; +}; + +/*===============================================================================================*/ +static struct file_operations hyper_dmabuf_driver_fops = +{ + .owner = THIS_MODULE, + .unlocked_ioctl = hyper_dmabuf_ioctl, +}; + +static struct miscdevice hyper_dmabuf_miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "xen/hyper_dmabuf", + .fops = &hyper_dmabuf_driver_fops, +}; + +static const char device_name[] = "hyper_dmabuf"; + +/*===============================================================================================*/ +int register_device(void) +{ + int result = 0; + + result = misc_register(&hyper_dmabuf_miscdev); + + if (result != 0) { + printk(KERN_WARNING "hyper_dmabuf: driver can't be registered\n"); + return result; + } + + hyper_dmabuf_private.device = hyper_dmabuf_miscdev.this_device; + + /* TODO: Check if there is a different way to initialize dma mask nicely */ + dma_coerce_mask_and_coherent(hyper_dmabuf_private.device, 0xFFFFFFFF); + + /* TODO find a way to provide parameters for below function or move that to ioctl */ +/* err = bind_interdomain_evtchn_to_irqhandler(rdomain, evtchn, + src_sink_isr, PORT_NUM, "remote_domain", &info); + if (err < 0) { + printk("hyper_dmabuf: can't register interrupt handlers\n"); + return -EFAULT; + } + + info.irq = err; +*/ + return result; +} + +/*-----------------------------------------------------------------------------------------------*/ +void unregister_device(void) +{ + printk( KERN_NOTICE "hyper_dmabuf: unregister_device() is called" ); + misc_deregister(&hyper_dmabuf_miscdev); +} diff --git a/drivers/xen/hyper_dmabuf/hyper_dmabuf_list.c b/drivers/xen/hyper_dmabuf/hyper_dmabuf_list.c new file mode 100644 index 0000000..77a7e65 --- /dev/null +++ b/drivers/xen/hyper_dmabuf/hyper_dmabuf_list.c @@ -0,0 +1,119 @@ +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/cdev.h> +#include <asm/uaccess.h> +#include <linux/hashtable.h> +#include <linux/dma-buf.h> +#include "hyper_dmabuf_list.h" + +DECLARE_HASHTABLE(hyper_dmabuf_hash_imported, MAX_ENTRY_IMPORTED); +DECLARE_HASHTABLE(hyper_dmabuf_hash_exported, MAX_ENTRY_EXPORTED); + +int hyper_dmabuf_table_init() +{ + hash_init(hyper_dmabuf_hash_imported); + hash_init(hyper_dmabuf_hash_exported); + return 0; +} + +int hyper_dmabuf_table_destroy() +{ + /* TODO: cleanup hyper_dmabuf_hash_imported and hyper_dmabuf_hash_exported */ + return 0; +} + +int hyper_dmabuf_register_exported(struct hyper_dmabuf_sgt_info *info) +{ + struct hyper_dmabuf_info_entry_exported *info_entry; + + info_entry = kmalloc(sizeof(*info_entry), GFP_KERNEL); + + info_entry->info = info; + + hash_add(hyper_dmabuf_hash_exported, &info_entry->node, + info_entry->info->hyper_dmabuf_id); + + return 0; +} + +int hyper_dmabuf_register_imported(struct hyper_dmabuf_imported_sgt_info* info) +{ + struct hyper_dmabuf_info_entry_imported *info_entry; + + info_entry = kmalloc(sizeof(*info_entry), GFP_KERNEL); + + info_entry->info = info; + + hash_add(hyper_dmabuf_hash_imported, &info_entry->node, + info_entry->info->hyper_dmabuf_id); + + return 0; +} + +struct hyper_dmabuf_sgt_info *hyper_dmabuf_find_exported(int id) +{ + struct hyper_dmabuf_info_entry_exported *info_entry; + int bkt; + + hash_for_each(hyper_dmabuf_hash_exported, bkt, info_entry, node) + if(info_entry->info->hyper_dmabuf_id == id) + return info_entry->info; + + return NULL; +} + +/* search for pre-exported sgt and return id of it if it exist */ +int hyper_dmabuf_find_id(struct dma_buf_attachment *attach, int domid) +{ + struct hyper_dmabuf_info_entry_exported *info_entry; + int bkt; + + hash_for_each(hyper_dmabuf_hash_exported, bkt, info_entry, node) + if(info_entry->info->attachment == attach && + info_entry->info->hyper_dmabuf_rdomain == domid) + return info_entry->info->hyper_dmabuf_id; + + return -1; +} + +struct hyper_dmabuf_imported_sgt_info *hyper_dmabuf_find_imported(int id) +{ + struct hyper_dmabuf_info_entry_imported *info_entry; + int bkt; + + hash_for_each(hyper_dmabuf_hash_imported, bkt, info_entry, node) + if(info_entry->info->hyper_dmabuf_id == id) + return info_entry->info; + + return NULL; +} + +int hyper_dmabuf_remove_exported(int id) +{ + struct hyper_dmabuf_info_entry_exported *info_entry; + int bkt; + + hash_for_each(hyper_dmabuf_hash_exported, bkt, info_entry, node) + if(info_entry->info->hyper_dmabuf_id == id) { + hash_del(&info_entry->node); + return 0; + } + + return -1; +} + +int hyper_dmabuf_remove_imported(int id) +{ + struct hyper_dmabuf_info_entry_imported *info_entry; + int bkt; + + hash_for_each(hyper_dmabuf_hash_imported, bkt, info_entry, node) + if(info_entry->info->hyper_dmabuf_id == id) { + hash_del(&info_entry->node); + return 0; + } + + return -1; +} diff --git a/drivers/xen/hyper_dmabuf/hyper_dmabuf_list.h b/drivers/xen/hyper_dmabuf/hyper_dmabuf_list.h new file mode 100644 index 0000000..869cd9a --- /dev/null +++ b/drivers/xen/hyper_dmabuf/hyper_dmabuf_list.h @@ -0,0 +1,40 @@ +#ifndef __HYPER_DMABUF_LIST_H__ +#define __HYPER_DMABUF_LIST_H__ + +#include "hyper_dmabuf_struct.h" + +/* number of bits to be used for exported dmabufs hash table */ +#define MAX_ENTRY_EXPORTED 7 +/* number of bits to be used for imported dmabufs hash table */ +#define MAX_ENTRY_IMPORTED 7 + +struct hyper_dmabuf_info_entry_exported { + struct hyper_dmabuf_sgt_info *info; + struct hlist_node node; +}; + +struct hyper_dmabuf_info_entry_imported { + struct hyper_dmabuf_imported_sgt_info *info; + struct hlist_node node; +}; + +int hyper_dmabuf_table_init(void); + +int hyper_dmabuf_table_destroy(void); + +int hyper_dmabuf_register_exported(struct hyper_dmabuf_sgt_info *info); + +/* search for pre-exported sgt and return id of it if it exist */ +int hyper_dmabuf_find_id(struct dma_buf_attachment *attach, int domid); + +int hyper_dmabuf_register_imported(struct hyper_dmabuf_imported_sgt_info* info); + +struct hyper_dmabuf_sgt_info *hyper_dmabuf_find_exported(int id); + +struct hyper_dmabuf_imported_sgt_info *hyper_dmabuf_find_imported(int id); + +int hyper_dmabuf_remove_exported(int id); + +int hyper_dmabuf_remove_imported(int id); + +#endif // __HYPER_DMABUF_LIST_H__ diff --git a/drivers/xen/hyper_dmabuf/hyper_dmabuf_msg.c b/drivers/xen/hyper_dmabuf/hyper_dmabuf_msg.c new file mode 100644 index 0000000..3237e50 --- /dev/null +++ b/drivers/xen/hyper_dmabuf/hyper_dmabuf_msg.c @@ -0,0 +1,212 @@ +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/dma-buf.h> +#include "hyper_dmabuf_imp.h" +//#include "hyper_dmabuf_remote_sync.h" +#include "xen/hyper_dmabuf_xen_comm.h" +#include "hyper_dmabuf_msg.h" +#include "hyper_dmabuf_list.h" + +void hyper_dmabuf_create_request(struct hyper_dmabuf_ring_rq *request, + enum hyper_dmabuf_command command, int *operands) +{ + int i; + + request->request_id = hyper_dmabuf_next_req_id_export(); + request->status = HYPER_DMABUF_REQ_NOT_RESPONDED; + request->command = command; + + switch(command) { + /* as exporter, commands to importer */ + case HYPER_DMABUF_EXPORT: + /* exporting pages for dmabuf */ + /* command : HYPER_DMABUF_EXPORT, + * operands0 : hyper_dmabuf_id + * operands1 : number of pages to be shared + * operands2 : offset of data in the first page + * operands3 : length of data in the last page + * operands4 : top-level reference number for shared pages + * operands5~8 : Driver-specific private data (e.g. graphic buffer's meta info) + */ + for (i=0; i < 8; i++) + request->operands[i] = operands[i]; + break; + + case HYPER_DMABUF_DESTROY: + /* destroy sg_list for hyper_dmabuf_id on remote side */ + /* command : DMABUF_DESTROY, + * operands0 : hyper_dmabuf_id + */ + request->operands[0] = operands[0]; + break; + + case HYPER_DMABUF_OPS_TO_REMOTE: + /* notifying dmabuf map/unmap to importer (probably not needed) */ + /* for dmabuf synchronization */ + break; + + /* as importer, command to exporter */ + case HYPER_DMABUF_OPS_TO_SOURCE: + /* notifying dmabuf map/unmap to exporter, map will make the driver to do shadow mapping + * or unmapping for synchronization with original exporter (e.g. i915) */ + /* command : DMABUF_OPS_TO_SOURCE. + * operands0 : hyper_dmabuf_id + * operands1 : map(=1)/unmap(=2)/attach(=3)/detach(=4) + */ + for (i=0; i<2; i++) + 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; + } +} + +int hyper_dmabuf_msg_parse(int domid, struct hyper_dmabuf_ring_rq *req) +{ + uint32_t i, ret; + struct hyper_dmabuf_imported_sgt_info *imported_sgt_info; + struct hyper_dmabuf_sgt_info *sgt_info; + + /* make sure req is not NULL (may not be needed) */ + if (!req) { + return -EINVAL; + } + + req->status = HYPER_DMABUF_REQ_PROCESSED; + + switch (req->command) { + case HYPER_DMABUF_EXPORT: + /* exporting pages for dmabuf */ + /* command : HYPER_DMABUF_EXPORT, + * operands0 : hyper_dmabuf_id + * operands1 : number of pages to be shared + * operands2 : offset of data in the first page + * operands3 : length of data in the last page + * operands4 : top-level reference number for shared pages + * operands5~8 : Driver-specific private data (e.g. graphic buffer's meta info) + */ + imported_sgt_info = (struct hyper_dmabuf_imported_sgt_info*)kcalloc(1, sizeof(*imported_sgt_info), GFP_KERNEL); + 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]; + imported_sgt_info->nents = req->operands[1]; + imported_sgt_info->gref = req->operands[4]; + + printk("DMABUF was exported\n"); + printk("\thyper_dmabuf_id %d\n", req->operands[0]); + printk("\tnents %d\n", req->operands[1]); + printk("\tfirst offset %d\n", req->operands[2]); + printk("\tlast len %d\n", req->operands[3]); + printk("\tgrefid %d\n", req->operands[4]); + + for (i=0; i<4; i++) + imported_sgt_info->private[i] = req->operands[5+i]; + + hyper_dmabuf_register_imported(imported_sgt_info); + break; + + case HYPER_DMABUF_DESTROY: + /* destroy sg_list for hyper_dmabuf_id on remote side */ + /* command : DMABUF_DESTROY, + * operands0 : hyper_dmabuf_id + */ + + imported_sgt_info = + hyper_dmabuf_find_imported(req->operands[0]); + + if (imported_sgt_info) { + hyper_dmabuf_cleanup_imported_pages(imported_sgt_info); + + hyper_dmabuf_remove_imported(req->operands[0]); + + /* TODO: cleanup sgt on importer side etc */ + } + + /* Notify exporter that buffer is freed and it can cleanup it */ + req->status = HYPER_DMABUF_REQ_NEEDS_FOLLOW_UP; + req->command = HYPER_DMABUF_DESTROY_FINISH; + +#if 0 /* function is not implemented yet */ + + ret = hyper_dmabuf_destroy_sgt(req->hyper_dmabuf_id); +#endif + break; + + case HYPER_DMABUF_DESTROY_FINISH: + /* destroy sg_list for hyper_dmabuf_id on local side */ + /* command : DMABUF_DESTROY_FINISH, + * operands0 : hyper_dmabuf_id + */ + + /* TODO: that should be done on workqueue, when received ack from all importers that buffer is no longer used */ + sgt_info = + hyper_dmabuf_find_exported(req->operands[0]); + + if (sgt_info) { + hyper_dmabuf_cleanup_gref_table(sgt_info); + + /* unmap dmabuf */ + dma_buf_unmap_attachment(sgt_info->attachment, sgt_info->sgt, DMA_BIDIRECTIONAL); + dma_buf_detach(sgt_info->dma_buf, sgt_info->attachment); + dma_buf_put(sgt_info->dma_buf); + + /* TODO: Rest of cleanup, sgt cleanup etc */ + } + + break; + + case HYPER_DMABUF_OPS_TO_REMOTE: + /* notifying dmabuf map/unmap to importer (probably not needed) */ + /* for dmabuf synchronization */ + break; + + /* as importer, command to exporter */ + case HYPER_DMABUF_OPS_TO_SOURCE: + /* notifying dmabuf map/unmap to exporter, map will make the driver to do shadow mapping + * or unmapping for synchronization with original exporter (e.g. i915) */ + /* command : DMABUF_OPS_TO_SOURCE. + * operands0 : hyper_dmabuf_id + * operands1 : map(=1)/unmap(=2)/attach(=3)/detach(=4) + */ + 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 */ + ret = hyper_dmabuf_exporter_ringbuf_init(domid, &req->operands[0], &req->operands[1]); + if (ret < 0) { + req->status = HYPER_DMABUF_REQ_ERROR; + return -EINVAL; + } + + req->status = HYPER_DMABUF_REQ_NEEDS_FOLLOW_UP; + req->command = HYPER_DMABUF_IMPORTER_RING_SETUP; + break; + + case HYPER_DMABUF_IMPORTER_RING_SETUP: + /* command: HYPER_DMABUF_IMPORTER_RING_SETUP */ + /* no operands needed */ + ret = hyper_dmabuf_importer_ringbuf_init(domid, req->operands[0], req->operands[1]); + if (ret < 0) + return -EINVAL; + + break; + + default: + /* no matched command, nothing to do.. just return error */ + return -EINVAL; + } + + return req->command; +} diff --git a/drivers/xen/hyper_dmabuf/hyper_dmabuf_msg.h b/drivers/xen/hyper_dmabuf/hyper_dmabuf_msg.h new file mode 100644 index 0000000..44bfb70 --- /dev/null +++ b/drivers/xen/hyper_dmabuf/hyper_dmabuf_msg.h @@ -0,0 +1,45 @@ +#ifndef __HYPER_DMABUF_MSG_H__ +#define __HYPER_DMABUF_MSG_H__ + +enum hyper_dmabuf_command { + HYPER_DMABUF_EXPORT = 0x10, + HYPER_DMABUF_DESTROY, + 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 { + HYPER_DMABUF_OPS_ATTACH = 0x1000, + HYPER_DMABUF_OPS_DETACH, + HYPER_DMABUF_OPS_MAP, + HYPER_DMABUF_OPS_UNMAP, + HYPER_DMABUF_OPS_RELEASE, + HYPER_DMABUF_OPS_BEGIN_CPU_ACCESS, + HYPER_DMABUF_OPS_END_CPU_ACCESS, + HYPER_DMABUF_OPS_KMAP_ATOMIC, + HYPER_DMABUF_OPS_KUNMAP_ATOMIC, + HYPER_DMABUF_OPS_KMAP, + HYPER_DMABUF_OPS_KUNMAP, + HYPER_DMABUF_OPS_MMAP, + HYPER_DMABUF_OPS_VMAP, + HYPER_DMABUF_OPS_VUNMAP, +}; + +enum hyper_dmabuf_req_feedback { + HYPER_DMABUF_REQ_PROCESSED = 0x100, + HYPER_DMABUF_REQ_NEEDS_FOLLOW_UP, + HYPER_DMABUF_REQ_ERROR, + HYPER_DMABUF_REQ_NOT_RESPONDED +}; + +/* create a request packet with given command and operands */ +void hyper_dmabuf_create_request(struct hyper_dmabuf_ring_rq *request, + enum hyper_dmabuf_command command, int *operands); + +/* parse incoming request packet (or response) and take appropriate actions for those */ +int hyper_dmabuf_msg_parse(int domid, struct hyper_dmabuf_ring_rq *req); + +#endif // __HYPER_DMABUF_MSG_H__ diff --git a/drivers/xen/hyper_dmabuf/hyper_dmabuf_query.h b/drivers/xen/hyper_dmabuf/hyper_dmabuf_query.h new file mode 100644 index 0000000..a577167 --- /dev/null +++ b/drivers/xen/hyper_dmabuf/hyper_dmabuf_query.h @@ -0,0 +1,16 @@ +#ifndef __HYPER_DMABUF_QUERY_H__ +#define __HYPER_DMABUF_QUERY_H__ + +enum hyper_dmabuf_query { + DMABUF_QUERY_TYPE_LIST = 0x10, + DMABUF_QUERY_EXPORTER, + DMABUF_QUERY_IMPORTER, + DMABUF_QUERY_SIZE +}; + +enum hyper_dmabuf_status { + EXPORTED = 0x01, + IMPORTED +}; + +#endif /* __HYPER_DMABUF_QUERY_H__ */ diff --git a/drivers/xen/hyper_dmabuf/hyper_dmabuf_struct.h b/drivers/xen/hyper_dmabuf/hyper_dmabuf_struct.h new file mode 100644 index 0000000..c8a2f4d --- /dev/null +++ b/drivers/xen/hyper_dmabuf/hyper_dmabuf_struct.h @@ -0,0 +1,70 @@ +#ifndef __HYPER_DMABUF_STRUCT_H__ +#define __HYPER_DMABUF_STRUCT_H__ + +#include <xen/interface/grant_table.h> + +/* Importer combine source domain id with given hyper_dmabuf_id + * to make it unique in case there are multiple exporters */ + +#define HYPER_DMABUF_ID_IMPORTER(sdomain, id) \ + ((((sdomain) & 0xFF) << 24) | ((id) & 0xFFFFFF)) + +#define HYPER_DMABUF_ID_IMPORTER_GET_SDOMAIN_ID(id) \ + (((id) >> 24) & 0xFF) + +/* each grant_ref_t is 4 bytes, so total 4096 grant_ref_t can be + * in this block meaning we can share 4KB*4096 = 16MB of buffer + * (needs to be increased for large buffer use-cases such as 4K + * frame buffer) */ +#define MAX_ALLOWED_NUM_PAGES_FOR_GREF_NUM_ARRAYS 4 + +struct hyper_dmabuf_shared_pages_info { + grant_ref_t *data_refs; /* table with shared buffer pages refid */ + grant_ref_t *addr_pages; /* pages of 2nd level addressing */ + grant_ref_t *top_level_page; /* page of top level addressing, it contains refids of 2nd level pages */ + grant_ref_t top_level_ref; /* top level refid */ + struct gnttab_unmap_grant_ref* unmap_ops; /* unmap ops for mapped pages */ + struct page **data_pages; /* data pages to be unmapped */ +}; + +/* Exporter builds pages_info before sharing pages */ +struct hyper_dmabuf_pages_info { + int hyper_dmabuf_id; /* unique id to reference dmabuf in source domain */ + int hyper_dmabuf_rdomain; /* currenting considering just one remote domain access it */ + int frst_ofst; /* offset of data in the first page */ + int last_len; /* length of data in the last page */ + int nents; /* # of pages */ + struct page **pages; /* pages that contains reference numbers of shared pages*/ +}; + +/* Both importer and exporter use this structure to point to sg lists + * + * Exporter stores references to sgt in a hash table + * Exporter keeps these references for synchronization and tracking purposes + * + * Importer use this structure exporting to other drivers in the same domain */ +struct hyper_dmabuf_sgt_info { + int hyper_dmabuf_id; /* unique id to reference dmabuf in remote domain */ + int hyper_dmabuf_rdomain; /* domain importing this sgt */ + struct sg_table *sgt; /* pointer to sgt */ + struct dma_buf *dma_buf; /* needed to store this for freeing it later */ + struct dma_buf_attachment *attachment; /* needed to store this for freeing this later */ + struct hyper_dmabuf_shared_pages_info shared_pages_info; + int private[4]; /* device specific info (e.g. image's meta info?) */ +}; + +/* Importer store references (before mapping) on shared pages + * Importer store these references in the table and map it in + * its own memory map once userspace asks for reference for the buffer */ +struct hyper_dmabuf_imported_sgt_info { + int hyper_dmabuf_id; /* unique id to reference dmabuf (HYPER_DMABUF_ID_IMPORTER(source domain id, exporter's hyper_dmabuf_id */ + int frst_ofst; /* start offset in shared page #1 */ + int last_len; /* length of data in the last shared page */ + int nents; /* number of pages to be shared */ + grant_ref_t gref; /* reference number of top level addressing page of shared pages */ + struct sg_table *sgt; /* sgt pointer after importing buffer */ + struct hyper_dmabuf_shared_pages_info shared_pages_info; + int private[4]; /* device specific info (e.g. image's meta info?) */ +}; + +#endif /* __HYPER_DMABUF_STRUCT_H__ */ diff --git a/drivers/xen/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.c b/drivers/xen/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.c new file mode 100644 index 0000000..22f2ef0 --- /dev/null +++ b/drivers/xen/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.c @@ -0,0 +1,328 @@ +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/workqueue.h> +#include <xen/grant_table.h> +#include <xen/events.h> +#include <xen/xenbus.h> +#include <asm/xen/page.h> +#include "hyper_dmabuf_xen_comm.h" +#include "hyper_dmabuf_xen_comm_list.h" +#include "../hyper_dmabuf_imp.h" +#include "../hyper_dmabuf_list.h" +#include "../hyper_dmabuf_msg.h" + +static int export_req_id = 0; +static int import_req_id = 0; + +int32_t hyper_dmabuf_get_domid(void) +{ + struct xenbus_transaction xbt; + int32_t domid; + + xenbus_transaction_start(&xbt); + + if (!xenbus_scanf(xbt, "domid","", "%d", &domid)) { + domid = -1; + } + xenbus_transaction_end(xbt, 0); + + return domid; +} + +int hyper_dmabuf_next_req_id_export(void) +{ + export_req_id++; + return export_req_id; +} + +int hyper_dmabuf_next_req_id_import(void) +{ + import_req_id++; + return import_req_id; +} + +/* For now cache latast rings as global variables TODO: keep them in list*/ +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); + +/* exporter needs to generated info for page sharing */ +int hyper_dmabuf_exporter_ringbuf_init(int rdomain, grant_ref_t *refid, int *port) +{ + struct hyper_dmabuf_ring_info_export *ring_info; + struct hyper_dmabuf_sring *sring; + struct evtchn_alloc_unbound alloc_unbound; + struct evtchn_close close; + + void *shared_ring; + int ret; + + ring_info = (struct hyper_dmabuf_ring_info_export*) + kmalloc(sizeof(*ring_info), GFP_KERNEL); + + /* from exporter to importer */ + shared_ring = (void *)__get_free_pages(GFP_KERNEL, 1); + if (shared_ring == 0) { + return -EINVAL; + } + + sring = (struct hyper_dmabuf_sring *) shared_ring; + + SHARED_RING_INIT(sring); + + FRONT_RING_INIT(&(ring_info->ring_front), sring, PAGE_SIZE); + + ring_info->gref_ring = gnttab_grant_foreign_access(rdomain, + virt_to_mfn(shared_ring), 0); + if (ring_info->gref_ring < 0) { + return -EINVAL; /* fail to get gref */ + } + + alloc_unbound.dom = DOMID_SELF; + alloc_unbound.remote_dom = rdomain; + ret = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, &alloc_unbound); + if (ret != 0) { + printk("Cannot allocate event channel\n"); + return -EINVAL; + } + + /* setting up interrupt */ + ret = bind_evtchn_to_irqhandler(alloc_unbound.port, + hyper_dmabuf_front_ring_isr, 0, + NULL, (void*) ring_info); + + if (ret < 0) { + printk("Failed to setup event channel\n"); + close.port = alloc_unbound.port; + HYPERVISOR_event_channel_op(EVTCHNOP_close, &close); + gnttab_end_foreign_access(ring_info->gref_ring, 0, virt_to_mfn(shared_ring)); + return -EINVAL; + } + + ring_info->rdomain = rdomain; + 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); + + return ret; +} + +/* 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) +{ + struct hyper_dmabuf_ring_info_import *ring_info; + struct hyper_dmabuf_sring *sring; + + struct page *shared_ring; + + struct gnttab_map_grant_ref *ops; + struct gnttab_unmap_grant_ref *unmap_ops; + int ret; + + ring_info = (struct hyper_dmabuf_ring_info_import *) + kmalloc(sizeof(*ring_info), GFP_KERNEL); + + ring_info->sdomain = sdomain; + ring_info->evtchn = 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); + + ret = gnttab_map_refs(ops, NULL, &shared_ring, 1); + if (ret < 0) { + printk("Cannot map ring\n"); + return -EINVAL; + } + + if (ops[0].status) { + printk("Ring mapping failed\n"); + return -EINVAL; + } + + 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, hyper_dmabuf_back_ring_isr, 0, + NULL, (void*)ring_info); + if (ret < 0) { + return -EINVAL; + } + + ring_info->irq = ret; + + printk("%s: bound to eventchannel port: %d irq: %d\n", __func__, + port, + ring_info->irq); + + ret = hyper_dmabuf_register_importer_ring(ring_info); + + return ret; +} + +int hyper_dmabuf_send_request(int domain, struct hyper_dmabuf_ring_rq *req) +{ + struct hyper_dmabuf_front_ring *ring; + struct hyper_dmabuf_ring_rq *new_req; + struct hyper_dmabuf_ring_info_export *ring_info; + int notify; + + /* find a ring info for the channel */ + ring_info = hyper_dmabuf_find_exporter_ring(domain); + if (!ring_info) { + printk("Can't find ring info for the channel\n"); + return -EINVAL; + } + + ring = &ring_info->ring_front; + + if (RING_FULL(ring)) + return -EBUSY; + + new_req = RING_GET_REQUEST(ring, ring->req_prod_pvt); + if (!new_req) { + printk("NULL REQUEST\n"); + return -EIO; + } + + memcpy(new_req, req, sizeof(*new_req)); + + ring->req_prod_pvt++; + + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(ring, notify); + if (notify) { + notify_remote_via_irq(ring_info->irq); + } + + return 0; +} + +/* called by interrupt (WORKQUEUE) */ +int hyper_dmabuf_send_response(struct hyper_dmabuf_ring_rp* response, int domain) +{ + /* as a importer and as a exporter */ + return 0; +} + +/* ISR for request from exporter (as an importer) */ +static irqreturn_t hyper_dmabuf_back_ring_isr(int irq, void *dev_id) +{ + RING_IDX rc, rp; + struct hyper_dmabuf_ring_rq request; + struct hyper_dmabuf_ring_rp response; + int notify, more_to_do; + int ret; +// struct hyper_dmabuf_work *work; + + struct hyper_dmabuf_ring_info_import *ring_info = (struct hyper_dmabuf_ring_info_import *)dev_id; + struct hyper_dmabuf_back_ring *ring; + + ring = &ring_info->ring_back; + + do { + rc = ring->req_cons; + rp = ring->sring->req_prod; + + while (rc != rp) { + if (RING_REQUEST_CONS_OVERFLOW(ring, rc)) + break; + + memcpy(&request, RING_GET_REQUEST(ring, rc), sizeof(request)); + printk("Got request\n"); + ring->req_cons = ++rc; + + /* TODO: probably using linked list for multiple requests then let + * a task in a workqueue to process those is better idea becuase + * we do not want to stay in ISR for long. + */ + ret = hyper_dmabuf_msg_parse(ring_info->sdomain, &request); + + if (ret > 0) { + /* build response */ + memcpy(&response, &request, sizeof(response)); + + /* we sent back modified request as a response.. we might just need to have request only..*/ + memcpy(RING_GET_RESPONSE(ring, ring->rsp_prod_pvt), &response, sizeof(response)); + ring->rsp_prod_pvt++; + + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(ring, notify); + + if (notify) { + printk("Notyfing\n"); + notify_remote_via_irq(ring_info->irq); + } + } + + RING_FINAL_CHECK_FOR_REQUESTS(ring, more_to_do); + printk("Final check for requests %d\n", more_to_do); + } + } while (more_to_do); + + return IRQ_HANDLED; +} + +/* ISR for responses from importer */ +static irqreturn_t hyper_dmabuf_front_ring_isr(int irq, void *dev_id) +{ + /* front ring only care about response from back */ + struct hyper_dmabuf_ring_rp *response; + RING_IDX i, rp; + int more_to_do, ret; + + struct hyper_dmabuf_ring_info_export *ring_info = (struct hyper_dmabuf_ring_info_export *)dev_id; + struct hyper_dmabuf_front_ring *ring; + ring = &ring_info->ring_front; + + do { + more_to_do = 0; + rp = ring->sring->rsp_prod; + for (i = ring->rsp_cons; i != rp; i++) { + unsigned long id; + + response = RING_GET_RESPONSE(ring, i); + id = response->response_id; + + if (response->status == HYPER_DMABUF_REQ_NEEDS_FOLLOW_UP) { + /* parsing response */ + ret = hyper_dmabuf_msg_parse(ring_info->rdomain, (struct hyper_dmabuf_ring_rq*)response); + + if (ret < 0) { + printk("getting error while parsing response\n"); + } + } else if (response->status == HYPER_DMABUF_REQ_ERROR) { + printk("remote domain %d couldn't process request %d\n", ring_info->rdomain, response->command); + } + + } + + ring->rsp_cons = i; + + if (i != ring->req_prod_pvt) { + RING_FINAL_CHECK_FOR_RESPONSES(ring, more_to_do); + printk("more to do %d\n", more_to_do); + } else { + ring->sring->rsp_event = i+1; + } + } while (more_to_do); + + return IRQ_HANDLED; +} diff --git a/drivers/xen/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.h b/drivers/xen/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.h new file mode 100644 index 0000000..2754917 --- /dev/null +++ b/drivers/xen/hyper_dmabuf/xen/hyper_dmabuf_xen_comm.h @@ -0,0 +1,62 @@ +#ifndef __HYPER_DMABUF_XEN_COMM_H__ +#define __HYPER_DMABUF_XEN_COMM_H__ + +#include "xen/interface/io/ring.h" + +#define MAX_NUMBER_OF_OPERANDS 9 + +struct hyper_dmabuf_ring_rq { + unsigned int request_id; + unsigned int status; + unsigned int command; + unsigned int operands[MAX_NUMBER_OF_OPERANDS]; +}; + +struct hyper_dmabuf_ring_rp { + unsigned int response_id; + unsigned int status; + unsigned int command; + unsigned int operands[MAX_NUMBER_OF_OPERANDS]; +}; + +DEFINE_RING_TYPES(hyper_dmabuf, struct hyper_dmabuf_ring_rq, struct hyper_dmabuf_ring_rp); + +struct hyper_dmabuf_ring_info_export { + struct hyper_dmabuf_front_ring ring_front; + int rdomain; + int gref_ring; + int irq; + int port; +}; + +struct hyper_dmabuf_ring_info_import { + int sdomain; + int irq; + int evtchn; + struct hyper_dmabuf_back_ring ring_back; +}; + +//struct hyper_dmabuf_work { +// hyper_dmabuf_ring_rq requrest; +// struct work_struct msg_parse; +//}; + +int32_t hyper_dmabuf_get_domid(void); + +int hyper_dmabuf_next_req_id_export(void); + +int hyper_dmabuf_next_req_id_import(void); + +/* exporter needs to generated info for page sharing */ +int hyper_dmabuf_exporter_ringbuf_init(int rdomain, grant_ref_t *gref, int *port); + +/* 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); + +/* send request to the remote domain */ +int hyper_dmabuf_send_request(int domain, struct hyper_dmabuf_ring_rq *req); + +/* called by interrupt (WORKQUEUE) */ +int hyper_dmabuf_send_response(struct hyper_dmabuf_ring_rp* response, int domain); + +#endif // __HYPER_DMABUF_XEN_COMM_H__ diff --git a/drivers/xen/hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.c b/drivers/xen/hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.c new file mode 100644 index 0000000..15c9d29 --- /dev/null +++ b/drivers/xen/hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.c @@ -0,0 +1,106 @@ +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/cdev.h> +#include <asm/uaccess.h> +#include <linux/hashtable.h> +#include <xen/grant_table.h> +#include "hyper_dmabuf_xen_comm.h" +#include "hyper_dmabuf_xen_comm_list.h" + +DECLARE_HASHTABLE(hyper_dmabuf_hash_importer_ring, MAX_ENTRY_IMPORT_RING); +DECLARE_HASHTABLE(hyper_dmabuf_hash_exporter_ring, MAX_ENTRY_EXPORT_RING); + +int hyper_dmabuf_ring_table_init() +{ + hash_init(hyper_dmabuf_hash_importer_ring); + hash_init(hyper_dmabuf_hash_exporter_ring); + return 0; +} + +int hyper_dmabuf_ring_table_destroy() +{ + /* TODO: cleanup tables*/ + return 0; +} + +int hyper_dmabuf_register_exporter_ring(struct hyper_dmabuf_ring_info_export *ring_info) +{ + struct hyper_dmabuf_exporter_ring_info *info_entry; + + info_entry = kmalloc(sizeof(*info_entry), GFP_KERNEL); + + info_entry->info = ring_info; + + hash_add(hyper_dmabuf_hash_exporter_ring, &info_entry->node, + info_entry->info->rdomain); + + return 0; +} + +int hyper_dmabuf_register_importer_ring(struct hyper_dmabuf_ring_info_import *ring_info) +{ + struct hyper_dmabuf_importer_ring_info *info_entry; + + info_entry = kmalloc(sizeof(*info_entry), GFP_KERNEL); + + info_entry->info = ring_info; + + hash_add(hyper_dmabuf_hash_importer_ring, &info_entry->node, + info_entry->info->sdomain); + + return 0; +} + +struct hyper_dmabuf_ring_info_export *hyper_dmabuf_find_exporter_ring(int domid) +{ + struct hyper_dmabuf_exporter_ring_info *info_entry; + int bkt; + + hash_for_each(hyper_dmabuf_hash_exporter_ring, bkt, info_entry, node) + if(info_entry->info->rdomain == domid) + return info_entry->info; + + return NULL; +} + +struct hyper_dmabuf_ring_info_import *hyper_dmabuf_find_importer_ring(int domid) +{ + struct hyper_dmabuf_importer_ring_info *info_entry; + int bkt; + + hash_for_each(hyper_dmabuf_hash_importer_ring, bkt, info_entry, node) + if(info_entry->info->sdomain == domid) + return info_entry->info; + + return NULL; +} + +int hyper_dmabuf_remove_exporter_ring(int domid) +{ + struct hyper_dmabuf_exporter_ring_info *info_entry; + int bkt; + + hash_for_each(hyper_dmabuf_hash_exporter_ring, bkt, info_entry, node) + if(info_entry->info->rdomain == domid) { + hash_del(&info_entry->node); + return 0; + } + + return -1; +} + +int hyper_dmabuf_remove_importer_ring(int domid) +{ + struct hyper_dmabuf_importer_ring_info *info_entry; + int bkt; + + hash_for_each(hyper_dmabuf_hash_importer_ring, bkt, info_entry, node) + if(info_entry->info->sdomain == domid) { + hash_del(&info_entry->node); + return 0; + } + + return -1; +} diff --git a/drivers/xen/hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.h b/drivers/xen/hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.h new file mode 100644 index 0000000..5929f99 --- /dev/null +++ b/drivers/xen/hyper_dmabuf/xen/hyper_dmabuf_xen_comm_list.h @@ -0,0 +1,35 @@ +#ifndef __HYPER_DMABUF_XEN_COMM_LIST_H__ +#define __HYPER_DMABUF_XEN_COMM_LIST_H__ + +/* number of bits to be used for exported dmabufs hash table */ +#define MAX_ENTRY_EXPORT_RING 7 +/* number of bits to be used for imported dmabufs hash table */ +#define MAX_ENTRY_IMPORT_RING 7 + +struct hyper_dmabuf_exporter_ring_info { + struct hyper_dmabuf_ring_info_export *info; + struct hlist_node node; +}; + +struct hyper_dmabuf_importer_ring_info { + struct hyper_dmabuf_ring_info_import *info; + struct hlist_node node; +}; + +int hyper_dmabuf_ring_table_init(void); + +int hyper_dmabuf_ring_table_destroy(void); + +int hyper_dmabuf_register_exporter_ring(struct hyper_dmabuf_ring_info_export *ring_info); + +int hyper_dmabuf_register_importer_ring(struct hyper_dmabuf_ring_info_import *ring_info); + +struct hyper_dmabuf_ring_info_export *hyper_dmabuf_find_exporter_ring(int domid); + +struct hyper_dmabuf_ring_info_import *hyper_dmabuf_find_importer_ring(int domid); + +int hyper_dmabuf_remove_exporter_ring(int domid); + +int hyper_dmabuf_remove_importer_ring(int domid); + +#endif // __HYPER_DMABUF_XEN_COMM_LIST_H__ -- 2.7.4 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/mailman/listinfo/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |