| .. | .. |
|---|
| 17 | 17 | * # echo -n "ed963694-e847-4b2a-85af-bc9cfc11d6f3" \ |
|---|
| 18 | 18 | * > /sys/bus/vmbus/drivers/uio_hv_generic/bind |
|---|
| 19 | 19 | */ |
|---|
| 20 | | -#define DEBUG 1 |
|---|
| 21 | 20 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
|---|
| 22 | 21 | |
|---|
| 23 | 22 | #include <linux/device.h> |
|---|
| .. | .. |
|---|
| 33 | 32 | |
|---|
| 34 | 33 | #include "../hv/hyperv_vmbus.h" |
|---|
| 35 | 34 | |
|---|
| 36 | | -#define DRIVER_VERSION "0.02.0" |
|---|
| 35 | +#define DRIVER_VERSION "0.02.1" |
|---|
| 37 | 36 | #define DRIVER_AUTHOR "Stephen Hemminger <sthemmin at microsoft.com>" |
|---|
| 38 | 37 | #define DRIVER_DESC "Generic UIO driver for VMBus devices" |
|---|
| 39 | 38 | |
|---|
| 40 | 39 | #define HV_RING_SIZE 512 /* pages */ |
|---|
| 41 | | -#define SEND_BUFFER_SIZE (15 * 1024 * 1024) |
|---|
| 42 | | -#define RECV_BUFFER_SIZE (15 * 1024 * 1024) |
|---|
| 40 | +#define SEND_BUFFER_SIZE (16 * 1024 * 1024) |
|---|
| 41 | +#define RECV_BUFFER_SIZE (31 * 1024 * 1024) |
|---|
| 43 | 42 | |
|---|
| 44 | 43 | /* |
|---|
| 45 | 44 | * List of resources to be mapped to user space |
|---|
| .. | .. |
|---|
| 56 | 55 | struct hv_uio_private_data { |
|---|
| 57 | 56 | struct uio_info info; |
|---|
| 58 | 57 | struct hv_device *device; |
|---|
| 58 | + atomic_t refcnt; |
|---|
| 59 | 59 | |
|---|
| 60 | 60 | void *recv_buf; |
|---|
| 61 | 61 | u32 recv_gpadl; |
|---|
| .. | .. |
|---|
| 129 | 129 | { |
|---|
| 130 | 130 | struct vmbus_channel *channel |
|---|
| 131 | 131 | = container_of(kobj, struct vmbus_channel, kobj); |
|---|
| 132 | | - struct hv_device *dev = channel->primary_channel->device_obj; |
|---|
| 133 | | - u16 q_idx = channel->offermsg.offer.sub_channel_index; |
|---|
| 134 | 132 | void *ring_buffer = page_address(channel->ringbuffer_page); |
|---|
| 135 | 133 | |
|---|
| 136 | | - dev_dbg(&dev->device, "mmap channel %u pages %#lx at %#lx\n", |
|---|
| 137 | | - q_idx, vma_pages(vma), vma->vm_pgoff); |
|---|
| 134 | + if (channel->state != CHANNEL_OPENED_STATE) |
|---|
| 135 | + return -ENODEV; |
|---|
| 138 | 136 | |
|---|
| 139 | 137 | return vm_iomap_memory(vma, virt_to_phys(ring_buffer), |
|---|
| 140 | 138 | channel->ringbuffer_pagecount << PAGE_SHIFT); |
|---|
| .. | .. |
|---|
| 177 | 175 | } |
|---|
| 178 | 176 | } |
|---|
| 179 | 177 | |
|---|
| 178 | +/* free the reserved buffers for send and receive */ |
|---|
| 180 | 179 | static void |
|---|
| 181 | 180 | hv_uio_cleanup(struct hv_device *dev, struct hv_uio_private_data *pdata) |
|---|
| 182 | 181 | { |
|---|
| 183 | | - if (pdata->send_gpadl) |
|---|
| 182 | + if (pdata->send_gpadl) { |
|---|
| 184 | 183 | vmbus_teardown_gpadl(dev->channel, pdata->send_gpadl); |
|---|
| 185 | | - vfree(pdata->send_buf); |
|---|
| 184 | + pdata->send_gpadl = 0; |
|---|
| 185 | + vfree(pdata->send_buf); |
|---|
| 186 | + } |
|---|
| 186 | 187 | |
|---|
| 187 | | - if (pdata->recv_gpadl) |
|---|
| 188 | + if (pdata->recv_gpadl) { |
|---|
| 188 | 189 | vmbus_teardown_gpadl(dev->channel, pdata->recv_gpadl); |
|---|
| 189 | | - vfree(pdata->recv_buf); |
|---|
| 190 | + pdata->recv_gpadl = 0; |
|---|
| 191 | + vfree(pdata->recv_buf); |
|---|
| 192 | + } |
|---|
| 193 | +} |
|---|
| 194 | + |
|---|
| 195 | +/* VMBus primary channel is opened on first use */ |
|---|
| 196 | +static int |
|---|
| 197 | +hv_uio_open(struct uio_info *info, struct inode *inode) |
|---|
| 198 | +{ |
|---|
| 199 | + struct hv_uio_private_data *pdata |
|---|
| 200 | + = container_of(info, struct hv_uio_private_data, info); |
|---|
| 201 | + struct hv_device *dev = pdata->device; |
|---|
| 202 | + int ret; |
|---|
| 203 | + |
|---|
| 204 | + if (atomic_inc_return(&pdata->refcnt) != 1) |
|---|
| 205 | + return 0; |
|---|
| 206 | + |
|---|
| 207 | + vmbus_set_chn_rescind_callback(dev->channel, hv_uio_rescind); |
|---|
| 208 | + vmbus_set_sc_create_callback(dev->channel, hv_uio_new_channel); |
|---|
| 209 | + |
|---|
| 210 | + ret = vmbus_connect_ring(dev->channel, |
|---|
| 211 | + hv_uio_channel_cb, dev->channel); |
|---|
| 212 | + if (ret == 0) |
|---|
| 213 | + dev->channel->inbound.ring_buffer->interrupt_mask = 1; |
|---|
| 214 | + else |
|---|
| 215 | + atomic_dec(&pdata->refcnt); |
|---|
| 216 | + |
|---|
| 217 | + return ret; |
|---|
| 218 | +} |
|---|
| 219 | + |
|---|
| 220 | +/* VMBus primary channel is closed on last close */ |
|---|
| 221 | +static int |
|---|
| 222 | +hv_uio_release(struct uio_info *info, struct inode *inode) |
|---|
| 223 | +{ |
|---|
| 224 | + struct hv_uio_private_data *pdata |
|---|
| 225 | + = container_of(info, struct hv_uio_private_data, info); |
|---|
| 226 | + struct hv_device *dev = pdata->device; |
|---|
| 227 | + int ret = 0; |
|---|
| 228 | + |
|---|
| 229 | + if (atomic_dec_and_test(&pdata->refcnt)) |
|---|
| 230 | + ret = vmbus_disconnect_ring(dev->channel); |
|---|
| 231 | + |
|---|
| 232 | + return ret; |
|---|
| 190 | 233 | } |
|---|
| 191 | 234 | |
|---|
| 192 | 235 | static int |
|---|
| 193 | 236 | hv_uio_probe(struct hv_device *dev, |
|---|
| 194 | 237 | const struct hv_vmbus_device_id *dev_id) |
|---|
| 195 | 238 | { |
|---|
| 239 | + struct vmbus_channel *channel = dev->channel; |
|---|
| 196 | 240 | struct hv_uio_private_data *pdata; |
|---|
| 241 | + void *ring_buffer; |
|---|
| 197 | 242 | int ret; |
|---|
| 243 | + |
|---|
| 244 | + /* Communicating with host has to be via shared memory not hypercall */ |
|---|
| 245 | + if (!channel->offermsg.monitor_allocated) { |
|---|
| 246 | + dev_err(&dev->device, "vmbus channel requires hypercall\n"); |
|---|
| 247 | + return -ENOTSUPP; |
|---|
| 248 | + } |
|---|
| 198 | 249 | |
|---|
| 199 | 250 | pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); |
|---|
| 200 | 251 | if (!pdata) |
|---|
| 201 | 252 | return -ENOMEM; |
|---|
| 202 | 253 | |
|---|
| 203 | | - ret = vmbus_open(dev->channel, HV_RING_SIZE * PAGE_SIZE, |
|---|
| 204 | | - HV_RING_SIZE * PAGE_SIZE, NULL, 0, |
|---|
| 205 | | - hv_uio_channel_cb, dev->channel); |
|---|
| 254 | + ret = vmbus_alloc_ring(channel, HV_RING_SIZE * PAGE_SIZE, |
|---|
| 255 | + HV_RING_SIZE * PAGE_SIZE); |
|---|
| 206 | 256 | if (ret) |
|---|
| 207 | 257 | goto fail; |
|---|
| 208 | 258 | |
|---|
| 209 | | - /* Communicating with host has to be via shared memory not hypercall */ |
|---|
| 210 | | - if (!dev->channel->offermsg.monitor_allocated) { |
|---|
| 211 | | - dev_err(&dev->device, "vmbus channel requires hypercall\n"); |
|---|
| 212 | | - ret = -ENOTSUPP; |
|---|
| 213 | | - goto fail_close; |
|---|
| 214 | | - } |
|---|
| 215 | | - |
|---|
| 216 | | - dev->channel->inbound.ring_buffer->interrupt_mask = 1; |
|---|
| 217 | | - set_channel_read_mode(dev->channel, HV_CALL_ISR); |
|---|
| 259 | + set_channel_read_mode(channel, HV_CALL_ISR); |
|---|
| 218 | 260 | |
|---|
| 219 | 261 | /* Fill general uio info */ |
|---|
| 220 | 262 | pdata->info.name = "uio_hv_generic"; |
|---|
| 221 | 263 | pdata->info.version = DRIVER_VERSION; |
|---|
| 222 | 264 | pdata->info.irqcontrol = hv_uio_irqcontrol; |
|---|
| 265 | + pdata->info.open = hv_uio_open; |
|---|
| 266 | + pdata->info.release = hv_uio_release; |
|---|
| 223 | 267 | pdata->info.irq = UIO_IRQ_CUSTOM; |
|---|
| 268 | + atomic_set(&pdata->refcnt, 0); |
|---|
| 224 | 269 | |
|---|
| 225 | 270 | /* mem resources */ |
|---|
| 226 | 271 | pdata->info.mem[TXRX_RING_MAP].name = "txrx_rings"; |
|---|
| 272 | + ring_buffer = page_address(channel->ringbuffer_page); |
|---|
| 227 | 273 | pdata->info.mem[TXRX_RING_MAP].addr |
|---|
| 228 | | - = (uintptr_t)page_address(dev->channel->ringbuffer_page); |
|---|
| 274 | + = (uintptr_t)virt_to_phys(ring_buffer); |
|---|
| 229 | 275 | pdata->info.mem[TXRX_RING_MAP].size |
|---|
| 230 | | - = dev->channel->ringbuffer_pagecount << PAGE_SHIFT; |
|---|
| 231 | | - pdata->info.mem[TXRX_RING_MAP].memtype = UIO_MEM_LOGICAL; |
|---|
| 276 | + = channel->ringbuffer_pagecount << PAGE_SHIFT; |
|---|
| 277 | + pdata->info.mem[TXRX_RING_MAP].memtype = UIO_MEM_IOVA; |
|---|
| 232 | 278 | |
|---|
| 233 | 279 | pdata->info.mem[INT_PAGE_MAP].name = "int_page"; |
|---|
| 234 | 280 | pdata->info.mem[INT_PAGE_MAP].addr |
|---|
| .. | .. |
|---|
| 248 | 294 | goto fail_close; |
|---|
| 249 | 295 | } |
|---|
| 250 | 296 | |
|---|
| 251 | | - ret = vmbus_establish_gpadl(dev->channel, pdata->recv_buf, |
|---|
| 297 | + ret = vmbus_establish_gpadl(channel, pdata->recv_buf, |
|---|
| 252 | 298 | RECV_BUFFER_SIZE, &pdata->recv_gpadl); |
|---|
| 253 | | - if (ret) |
|---|
| 299 | + if (ret) { |
|---|
| 300 | + vfree(pdata->recv_buf); |
|---|
| 254 | 301 | goto fail_close; |
|---|
| 302 | + } |
|---|
| 255 | 303 | |
|---|
| 256 | 304 | /* put Global Physical Address Label in name */ |
|---|
| 257 | 305 | snprintf(pdata->recv_name, sizeof(pdata->recv_name), |
|---|
| .. | .. |
|---|
| 262 | 310 | pdata->info.mem[RECV_BUF_MAP].size = RECV_BUFFER_SIZE; |
|---|
| 263 | 311 | pdata->info.mem[RECV_BUF_MAP].memtype = UIO_MEM_VIRTUAL; |
|---|
| 264 | 312 | |
|---|
| 265 | | - |
|---|
| 266 | 313 | pdata->send_buf = vzalloc(SEND_BUFFER_SIZE); |
|---|
| 267 | 314 | if (pdata->send_buf == NULL) { |
|---|
| 268 | 315 | ret = -ENOMEM; |
|---|
| 269 | 316 | goto fail_close; |
|---|
| 270 | 317 | } |
|---|
| 271 | 318 | |
|---|
| 272 | | - ret = vmbus_establish_gpadl(dev->channel, pdata->send_buf, |
|---|
| 319 | + ret = vmbus_establish_gpadl(channel, pdata->send_buf, |
|---|
| 273 | 320 | SEND_BUFFER_SIZE, &pdata->send_gpadl); |
|---|
| 274 | | - if (ret) |
|---|
| 321 | + if (ret) { |
|---|
| 322 | + vfree(pdata->send_buf); |
|---|
| 275 | 323 | goto fail_close; |
|---|
| 324 | + } |
|---|
| 276 | 325 | |
|---|
| 277 | 326 | snprintf(pdata->send_name, sizeof(pdata->send_name), |
|---|
| 278 | 327 | "send:%u", pdata->send_gpadl); |
|---|
| .. | .. |
|---|
| 291 | 340 | goto fail_close; |
|---|
| 292 | 341 | } |
|---|
| 293 | 342 | |
|---|
| 294 | | - vmbus_set_chn_rescind_callback(dev->channel, hv_uio_rescind); |
|---|
| 295 | | - vmbus_set_sc_create_callback(dev->channel, hv_uio_new_channel); |
|---|
| 296 | | - |
|---|
| 297 | | - ret = sysfs_create_bin_file(&dev->channel->kobj, &ring_buffer_bin_attr); |
|---|
| 343 | + ret = sysfs_create_bin_file(&channel->kobj, &ring_buffer_bin_attr); |
|---|
| 298 | 344 | if (ret) |
|---|
| 299 | 345 | dev_notice(&dev->device, |
|---|
| 300 | 346 | "sysfs create ring bin file failed; %d\n", ret); |
|---|
| .. | .. |
|---|
| 305 | 351 | |
|---|
| 306 | 352 | fail_close: |
|---|
| 307 | 353 | hv_uio_cleanup(dev, pdata); |
|---|
| 308 | | - vmbus_close(dev->channel); |
|---|
| 309 | 354 | fail: |
|---|
| 310 | 355 | kfree(pdata); |
|---|
| 311 | 356 | |
|---|
| .. | .. |
|---|
| 320 | 365 | if (!pdata) |
|---|
| 321 | 366 | return 0; |
|---|
| 322 | 367 | |
|---|
| 368 | + sysfs_remove_bin_file(&dev->channel->kobj, &ring_buffer_bin_attr); |
|---|
| 323 | 369 | uio_unregister_device(&pdata->info); |
|---|
| 324 | 370 | hv_uio_cleanup(dev, pdata); |
|---|
| 325 | 371 | hv_set_drvdata(dev, NULL); |
|---|
| 326 | | - vmbus_close(dev->channel); |
|---|
| 372 | + |
|---|
| 373 | + vmbus_free_ring(dev->channel); |
|---|
| 327 | 374 | kfree(pdata); |
|---|
| 328 | 375 | return 0; |
|---|
| 329 | 376 | } |
|---|