.. | .. |
---|
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 | } |
---|