diff options
Diffstat (limited to 'drivers/misc/genwqe/card_utils.c')
-rw-r--r-- | drivers/misc/genwqe/card_utils.c | 170 |
1 files changed, 130 insertions, 40 deletions
diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c index 6b1a6ef9f1a8..d049d271699c 100644 --- a/drivers/misc/genwqe/card_utils.c +++ b/drivers/misc/genwqe/card_utils.c | |||
@@ -275,67 +275,107 @@ static int genwqe_sgl_size(int num_pages) | |||
275 | return roundup(len, PAGE_SIZE); | 275 | return roundup(len, PAGE_SIZE); |
276 | } | 276 | } |
277 | 277 | ||
278 | struct sg_entry *genwqe_alloc_sgl(struct genwqe_dev *cd, int num_pages, | 278 | /** |
279 | dma_addr_t *dma_addr, size_t *sgl_size) | 279 | * genwqe_alloc_sync_sgl() - Allocate memory for sgl and overlapping pages |
280 | * | ||
281 | * Allocates memory for sgl and overlapping pages. Pages which might | ||
282 | * overlap other user-space memory blocks are being cached for DMAs, | ||
283 | * such that we do not run into syncronization issues. Data is copied | ||
284 | * from user-space into the cached pages. | ||
285 | */ | ||
286 | int genwqe_alloc_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl, | ||
287 | void __user *user_addr, size_t user_size) | ||
280 | { | 288 | { |
289 | int rc; | ||
281 | struct pci_dev *pci_dev = cd->pci_dev; | 290 | struct pci_dev *pci_dev = cd->pci_dev; |
282 | struct sg_entry *sgl; | ||
283 | 291 | ||
284 | *sgl_size = genwqe_sgl_size(num_pages); | 292 | sgl->fpage_offs = offset_in_page((unsigned long)user_addr); |
285 | if (get_order(*sgl_size) > MAX_ORDER) { | 293 | sgl->fpage_size = min_t(size_t, PAGE_SIZE-sgl->fpage_offs, user_size); |
294 | sgl->nr_pages = DIV_ROUND_UP(sgl->fpage_offs + user_size, PAGE_SIZE); | ||
295 | sgl->lpage_size = (user_size - sgl->fpage_size) % PAGE_SIZE; | ||
296 | |||
297 | dev_dbg(&pci_dev->dev, "[%s] uaddr=%p usize=%8ld nr_pages=%ld " | ||
298 | "fpage_offs=%lx fpage_size=%ld lpage_size=%ld\n", | ||
299 | __func__, user_addr, user_size, sgl->nr_pages, | ||
300 | sgl->fpage_offs, sgl->fpage_size, sgl->lpage_size); | ||
301 | |||
302 | sgl->user_addr = user_addr; | ||
303 | sgl->user_size = user_size; | ||
304 | sgl->sgl_size = genwqe_sgl_size(sgl->nr_pages); | ||
305 | |||
306 | if (get_order(sgl->sgl_size) > MAX_ORDER) { | ||
286 | dev_err(&pci_dev->dev, | 307 | dev_err(&pci_dev->dev, |
287 | "[%s] err: too much memory requested!\n", __func__); | 308 | "[%s] err: too much memory requested!\n", __func__); |
288 | return NULL; | 309 | return -ENOMEM; |
289 | } | 310 | } |
290 | 311 | ||
291 | sgl = __genwqe_alloc_consistent(cd, *sgl_size, dma_addr); | 312 | sgl->sgl = __genwqe_alloc_consistent(cd, sgl->sgl_size, |
292 | if (sgl == NULL) { | 313 | &sgl->sgl_dma_addr); |
314 | if (sgl->sgl == NULL) { | ||
293 | dev_err(&pci_dev->dev, | 315 | dev_err(&pci_dev->dev, |
294 | "[%s] err: no memory available!\n", __func__); | 316 | "[%s] err: no memory available!\n", __func__); |
295 | return NULL; | 317 | return -ENOMEM; |
296 | } | 318 | } |
297 | 319 | ||
298 | return sgl; | 320 | /* Only use buffering on incomplete pages */ |
321 | if ((sgl->fpage_size != 0) && (sgl->fpage_size != PAGE_SIZE)) { | ||
322 | sgl->fpage = __genwqe_alloc_consistent(cd, PAGE_SIZE, | ||
323 | &sgl->fpage_dma_addr); | ||
324 | if (sgl->fpage == NULL) | ||
325 | goto err_out; | ||
326 | |||
327 | /* Sync with user memory */ | ||
328 | if (copy_from_user(sgl->fpage + sgl->fpage_offs, | ||
329 | user_addr, sgl->fpage_size)) { | ||
330 | rc = -EFAULT; | ||
331 | goto err_out; | ||
332 | } | ||
333 | } | ||
334 | if (sgl->lpage_size != 0) { | ||
335 | sgl->lpage = __genwqe_alloc_consistent(cd, PAGE_SIZE, | ||
336 | &sgl->lpage_dma_addr); | ||
337 | if (sgl->lpage == NULL) | ||
338 | goto err_out1; | ||
339 | |||
340 | /* Sync with user memory */ | ||
341 | if (copy_from_user(sgl->lpage, user_addr + user_size - | ||
342 | sgl->lpage_size, sgl->lpage_size)) { | ||
343 | rc = -EFAULT; | ||
344 | goto err_out1; | ||
345 | } | ||
346 | } | ||
347 | return 0; | ||
348 | |||
349 | err_out1: | ||
350 | __genwqe_free_consistent(cd, PAGE_SIZE, sgl->fpage, | ||
351 | sgl->fpage_dma_addr); | ||
352 | err_out: | ||
353 | __genwqe_free_consistent(cd, sgl->sgl_size, sgl->sgl, | ||
354 | sgl->sgl_dma_addr); | ||
355 | return -ENOMEM; | ||
299 | } | 356 | } |
300 | 357 | ||
301 | int genwqe_setup_sgl(struct genwqe_dev *cd, | 358 | int genwqe_setup_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl, |
302 | unsigned long offs, | 359 | dma_addr_t *dma_list) |
303 | unsigned long size, | ||
304 | struct sg_entry *sgl, | ||
305 | dma_addr_t dma_addr, size_t sgl_size, | ||
306 | dma_addr_t *dma_list, int page_offs, int num_pages) | ||
307 | { | 360 | { |
308 | int i = 0, j = 0, p; | 361 | int i = 0, j = 0, p; |
309 | unsigned long dma_offs, map_offs; | 362 | unsigned long dma_offs, map_offs; |
310 | struct pci_dev *pci_dev = cd->pci_dev; | ||
311 | dma_addr_t prev_daddr = 0; | 363 | dma_addr_t prev_daddr = 0; |
312 | struct sg_entry *s, *last_s = NULL; | 364 | struct sg_entry *s, *last_s = NULL; |
313 | 365 | size_t size = sgl->user_size; | |
314 | /* sanity checks */ | ||
315 | if (offs > PAGE_SIZE) { | ||
316 | dev_err(&pci_dev->dev, | ||
317 | "[%s] too large start offs %08lx\n", __func__, offs); | ||
318 | return -EFAULT; | ||
319 | } | ||
320 | if (sgl_size < genwqe_sgl_size(num_pages)) { | ||
321 | dev_err(&pci_dev->dev, | ||
322 | "[%s] sgl_size too small %08lx for %d pages\n", | ||
323 | __func__, sgl_size, num_pages); | ||
324 | return -EFAULT; | ||
325 | } | ||
326 | 366 | ||
327 | dma_offs = 128; /* next block if needed/dma_offset */ | 367 | dma_offs = 128; /* next block if needed/dma_offset */ |
328 | map_offs = offs; /* offset in first page */ | 368 | map_offs = sgl->fpage_offs; /* offset in first page */ |
329 | 369 | ||
330 | s = &sgl[0]; /* first set of 8 entries */ | 370 | s = &sgl->sgl[0]; /* first set of 8 entries */ |
331 | p = 0; /* page */ | 371 | p = 0; /* page */ |
332 | while (p < num_pages) { | 372 | while (p < sgl->nr_pages) { |
333 | dma_addr_t daddr; | 373 | dma_addr_t daddr; |
334 | unsigned int size_to_map; | 374 | unsigned int size_to_map; |
335 | 375 | ||
336 | /* always write the chaining entry, cleanup is done later */ | 376 | /* always write the chaining entry, cleanup is done later */ |
337 | j = 0; | 377 | j = 0; |
338 | s[j].target_addr = cpu_to_be64(dma_addr + dma_offs); | 378 | s[j].target_addr = cpu_to_be64(sgl->sgl_dma_addr + dma_offs); |
339 | s[j].len = cpu_to_be32(128); | 379 | s[j].len = cpu_to_be32(128); |
340 | s[j].flags = cpu_to_be32(SG_CHAINED); | 380 | s[j].flags = cpu_to_be32(SG_CHAINED); |
341 | j++; | 381 | j++; |
@@ -343,7 +383,17 @@ int genwqe_setup_sgl(struct genwqe_dev *cd, | |||
343 | while (j < 8) { | 383 | while (j < 8) { |
344 | /* DMA mapping for requested page, offs, size */ | 384 | /* DMA mapping for requested page, offs, size */ |
345 | size_to_map = min(size, PAGE_SIZE - map_offs); | 385 | size_to_map = min(size, PAGE_SIZE - map_offs); |
346 | daddr = dma_list[page_offs + p] + map_offs; | 386 | |
387 | if ((p == 0) && (sgl->fpage != NULL)) { | ||
388 | daddr = sgl->fpage_dma_addr + map_offs; | ||
389 | |||
390 | } else if ((p == sgl->nr_pages - 1) && | ||
391 | (sgl->lpage != NULL)) { | ||
392 | daddr = sgl->lpage_dma_addr; | ||
393 | } else { | ||
394 | daddr = dma_list[p] + map_offs; | ||
395 | } | ||
396 | |||
347 | size -= size_to_map; | 397 | size -= size_to_map; |
348 | map_offs = 0; | 398 | map_offs = 0; |
349 | 399 | ||
@@ -358,7 +408,7 @@ int genwqe_setup_sgl(struct genwqe_dev *cd, | |||
358 | size_to_map); | 408 | size_to_map); |
359 | 409 | ||
360 | p++; /* process next page */ | 410 | p++; /* process next page */ |
361 | if (p == num_pages) | 411 | if (p == sgl->nr_pages) |
362 | goto fixup; /* nothing to do */ | 412 | goto fixup; /* nothing to do */ |
363 | 413 | ||
364 | prev_daddr = daddr + size_to_map; | 414 | prev_daddr = daddr + size_to_map; |
@@ -374,7 +424,7 @@ int genwqe_setup_sgl(struct genwqe_dev *cd, | |||
374 | j++; | 424 | j++; |
375 | 425 | ||
376 | p++; /* process next page */ | 426 | p++; /* process next page */ |
377 | if (p == num_pages) | 427 | if (p == sgl->nr_pages) |
378 | goto fixup; /* nothing to do */ | 428 | goto fixup; /* nothing to do */ |
379 | } | 429 | } |
380 | dma_offs += 128; | 430 | dma_offs += 128; |
@@ -395,10 +445,50 @@ int genwqe_setup_sgl(struct genwqe_dev *cd, | |||
395 | return 0; | 445 | return 0; |
396 | } | 446 | } |
397 | 447 | ||
398 | void genwqe_free_sgl(struct genwqe_dev *cd, struct sg_entry *sg_list, | 448 | /** |
399 | dma_addr_t dma_addr, size_t size) | 449 | * genwqe_free_sync_sgl() - Free memory for sgl and overlapping pages |
450 | * | ||
451 | * After the DMA transfer has been completed we free the memory for | ||
452 | * the sgl and the cached pages. Data is being transfered from cached | ||
453 | * pages into user-space buffers. | ||
454 | */ | ||
455 | int genwqe_free_sync_sgl(struct genwqe_dev *cd, struct genwqe_sgl *sgl) | ||
400 | { | 456 | { |
401 | __genwqe_free_consistent(cd, size, sg_list, dma_addr); | 457 | int rc; |
458 | struct pci_dev *pci_dev = cd->pci_dev; | ||
459 | |||
460 | if (sgl->fpage) { | ||
461 | if (copy_to_user(sgl->user_addr, sgl->fpage + sgl->fpage_offs, | ||
462 | sgl->fpage_size)) { | ||
463 | dev_err(&pci_dev->dev, "[%s] err: copying fpage!\n", | ||
464 | __func__); | ||
465 | rc = -EFAULT; | ||
466 | } | ||
467 | __genwqe_free_consistent(cd, PAGE_SIZE, sgl->fpage, | ||
468 | sgl->fpage_dma_addr); | ||
469 | sgl->fpage = NULL; | ||
470 | sgl->fpage_dma_addr = 0; | ||
471 | } | ||
472 | if (sgl->lpage) { | ||
473 | if (copy_to_user(sgl->user_addr + sgl->user_size - | ||
474 | sgl->lpage_size, sgl->lpage, | ||
475 | sgl->lpage_size)) { | ||
476 | dev_err(&pci_dev->dev, "[%s] err: copying lpage!\n", | ||
477 | __func__); | ||
478 | rc = -EFAULT; | ||
479 | } | ||
480 | __genwqe_free_consistent(cd, PAGE_SIZE, sgl->lpage, | ||
481 | sgl->lpage_dma_addr); | ||
482 | sgl->lpage = NULL; | ||
483 | sgl->lpage_dma_addr = 0; | ||
484 | } | ||
485 | __genwqe_free_consistent(cd, sgl->sgl_size, sgl->sgl, | ||
486 | sgl->sgl_dma_addr); | ||
487 | |||
488 | sgl->sgl = NULL; | ||
489 | sgl->sgl_dma_addr = 0x0; | ||
490 | sgl->sgl_size = 0; | ||
491 | return rc; | ||
402 | } | 492 | } |
403 | 493 | ||
404 | /** | 494 | /** |