Use sysfs to allow supporting sub-channels. The userspace application makes request to host to create sub-channels and the UIO kernel driver populates the sysfs per-channel directory with a binary attribute file that can be used to read/write ring.
Signed-off-by: Stephen Hemminger <sthem...@microsoft.com> --- Documentation/driver-api/uio-howto.rst | 5 ++ drivers/uio/uio_hv_generic.c | 95 ++++++++++++++++++++++++++++++++-- 2 files changed, 97 insertions(+), 3 deletions(-) diff --git a/Documentation/driver-api/uio-howto.rst b/Documentation/driver-api/uio-howto.rst index 693e3bd84e79..7d36a50c0484 100644 --- a/Documentation/driver-api/uio-howto.rst +++ b/Documentation/driver-api/uio-howto.rst @@ -709,6 +709,11 @@ The vmbus device regions are mapped into uio device resources: 3) Network receive buffer region 4) Network send buffer region +If a subchannel is created by a request to host, then the uio_hv_generic +device driver will create a sysfs binary file for the per-channel ring buffer. +For example: + /sys/bus/vmbus/devices/3811fe4d-0fa0-4b62-981a-74fc1084c757/channels/21/ring_buffer + Further information =================== diff --git a/drivers/uio/uio_hv_generic.c b/drivers/uio/uio_hv_generic.c index 8c6b04a26c47..2c98b9d75e0e 100644 --- a/drivers/uio/uio_hv_generic.c +++ b/drivers/uio/uio_hv_generic.c @@ -39,7 +39,8 @@ #define DRIVER_AUTHOR "Stephen Hemminger <sthemmin at microsoft.com>" #define DRIVER_DESC "Generic UIO driver for VMBus devices" -#define HV_RING_SIZE 512 /* pages */ +#define HV_RING_SIZE 512 +#define HV_RING_BYTES (HV_RING_SIZE * PAGE_SIZE) #define SEND_BUFFER_SIZE (15 * 1024 * 1024) #define RECV_BUFFER_SIZE (15 * 1024 * 1024) @@ -121,6 +122,93 @@ static void hv_uio_rescind(struct vmbus_channel *channel) uio_event_notify(&pdata->info); } +/* + * Handle fault when looking for sub channel ring buffer + * Subchannel ring buffer is same as resource 0 which is main ring buffer + * This is derived from uio_vma_fault + */ +static int hv_uio_vma_fault(struct vm_fault *vmf) +{ + struct vm_area_struct *vma = vmf->vma; + void *ring_buffer = vma->vm_private_data; + struct page *page; + void *addr; + + addr = ring_buffer + (vmf->pgoff << PAGE_SHIFT); + page = virt_to_page(addr); + get_page(page); + vmf->page = page; + return 0; +} + +static const struct vm_operations_struct hv_uio_vm_ops = { + .fault = hv_uio_vma_fault, +}; + +/* Sysfs API to allow mmap of the ring buffers */ +static int hv_uio_ring_mmap(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + struct vm_area_struct *vma) +{ + struct vmbus_channel *channel + = container_of(kobj, struct vmbus_channel, kobj); + unsigned long requested_pages; + + if (vma->vm_end < vma->vm_start) + return -EINVAL; + + /* only allow 0 for now */ + if (vma->vm_pgoff > 0) + return -EINVAL; + + requested_pages = vma_pages(vma); + if (requested_pages > 2 * HV_RING_SIZE) + return -EINVAL; + + vma->vm_private_data = channel->ringbuffer_pages; + vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; + vma->vm_ops = &hv_uio_vm_ops; + return 0; +} + +static struct bin_attribute ring_buffer_bin_attr __ro_after_init = { + .attr = { + .name = "ring", + .mode = 0600, + }, + .mmap = hv_uio_ring_mmap, + .size = 2 * HV_RING_BYTES, +}; + +/* Callback from VMBUS subystem when new channel created. */ +static void +hv_uio_new_channel(struct vmbus_channel *new_sc) +{ + struct hv_device *hv_dev = new_sc->primary_channel->device_obj; + struct device *device = &hv_dev->device; + struct hv_uio_private_data *pdata = hv_get_drvdata(hv_dev); + int ret; + + /* Create host communication ring */ + ret = vmbus_open(new_sc, HV_RING_BYTES, + HV_RING_BYTES, NULL, 0, + hv_uio_channel_cb, pdata); + if (ret) { + dev_err(device, "vmbus_open subchannel failed: %d\n", ret); + return; + } + + /* Disable interrupts on sub channel */ + new_sc->inbound.ring_buffer->interrupt_mask = 1; + set_channel_read_mode(new_sc, HV_CALL_ISR); + + ret = sysfs_create_bin_file(&new_sc->kobj, &ring_buffer_bin_attr); + if (ret) { + dev_err(device, "sysfs create ring bin file failed; %d\n", ret); + vmbus_close(new_sc); + } +} + static void hv_uio_cleanup(struct hv_device *dev, struct hv_uio_private_data *pdata) { @@ -144,8 +232,8 @@ hv_uio_probe(struct hv_device *dev, if (!pdata) return -ENOMEM; - ret = vmbus_open(dev->channel, HV_RING_SIZE * PAGE_SIZE, - HV_RING_SIZE * PAGE_SIZE, NULL, 0, + ret = vmbus_open(dev->channel, HV_RING_BYTES, + HV_RING_BYTES, NULL, 0, hv_uio_channel_cb, pdata); if (ret) goto fail; @@ -236,6 +324,7 @@ hv_uio_probe(struct hv_device *dev, } vmbus_set_chn_rescind_callback(dev->channel, hv_uio_rescind); + vmbus_set_sc_create_callback(dev->channel, hv_uio_new_channel); hv_set_drvdata(dev, pdata); -- 2.15.1 _______________________________________________ devel mailing list de...@linuxdriverproject.org http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel