diff options
-rw-r--r-- | drivers/vhost/tcm_vhost.c | 54 |
1 files changed, 30 insertions, 24 deletions
diff --git a/drivers/vhost/tcm_vhost.c b/drivers/vhost/tcm_vhost.c index 62058d7a562f..704e4f674776 100644 --- a/drivers/vhost/tcm_vhost.c +++ b/drivers/vhost/tcm_vhost.c | |||
@@ -430,40 +430,47 @@ static struct tcm_vhost_cmd *vhost_scsi_allocate_cmd( | |||
430 | * Returns the number of scatterlist entries used or -errno on error. | 430 | * Returns the number of scatterlist entries used or -errno on error. |
431 | */ | 431 | */ |
432 | static int vhost_scsi_map_to_sgl(struct scatterlist *sgl, | 432 | static int vhost_scsi_map_to_sgl(struct scatterlist *sgl, |
433 | unsigned int sgl_count, void __user *ptr, size_t len, int write) | 433 | unsigned int sgl_count, struct iovec *iov, int write) |
434 | { | 434 | { |
435 | unsigned int npages = 0, pages_nr, offset, nbytes; | ||
435 | struct scatterlist *sg = sgl; | 436 | struct scatterlist *sg = sgl; |
436 | unsigned int npages = 0; | 437 | void __user *ptr = iov->iov_base; |
437 | int ret; | 438 | size_t len = iov->iov_len; |
439 | struct page **pages; | ||
440 | int ret, i; | ||
438 | 441 | ||
439 | while (len > 0) { | 442 | pages_nr = iov_num_pages(iov); |
440 | struct page *page; | 443 | if (pages_nr > sgl_count) |
441 | unsigned int offset = (uintptr_t)ptr & ~PAGE_MASK; | 444 | return -ENOBUFS; |
442 | unsigned int nbytes = min_t(unsigned int, | ||
443 | PAGE_SIZE - offset, len); | ||
444 | 445 | ||
445 | if (npages == sgl_count) { | 446 | pages = kmalloc(pages_nr * sizeof(struct page *), GFP_KERNEL); |
446 | ret = -ENOBUFS; | 447 | if (!pages) |
447 | goto err; | 448 | return -ENOMEM; |
448 | } | ||
449 | 449 | ||
450 | ret = get_user_pages_fast((unsigned long)ptr, 1, write, &page); | 450 | ret = get_user_pages_fast((unsigned long)ptr, pages_nr, write, pages); |
451 | BUG_ON(ret == 0); /* we should either get our page or fail */ | 451 | /* No pages were pinned */ |
452 | if (ret < 0) | 452 | if (ret < 0) |
453 | goto err; | 453 | goto out; |
454 | /* Less pages pinned than wanted */ | ||
455 | if (ret != pages_nr) { | ||
456 | for (i = 0; i < ret; i++) | ||
457 | put_page(pages[i]); | ||
458 | ret = -EFAULT; | ||
459 | goto out; | ||
460 | } | ||
454 | 461 | ||
455 | sg_set_page(sg, page, nbytes, offset); | 462 | while (len > 0) { |
463 | offset = (uintptr_t)ptr & ~PAGE_MASK; | ||
464 | nbytes = min_t(unsigned int, PAGE_SIZE - offset, len); | ||
465 | sg_set_page(sg, pages[npages], nbytes, offset); | ||
456 | ptr += nbytes; | 466 | ptr += nbytes; |
457 | len -= nbytes; | 467 | len -= nbytes; |
458 | sg++; | 468 | sg++; |
459 | npages++; | 469 | npages++; |
460 | } | 470 | } |
461 | return npages; | ||
462 | 471 | ||
463 | err: | 472 | out: |
464 | /* Put pages that we hold */ | 473 | kfree(pages); |
465 | for (sg = sgl; sg != &sgl[npages]; sg++) | ||
466 | put_page(sg_page(sg)); | ||
467 | return ret; | 474 | return ret; |
468 | } | 475 | } |
469 | 476 | ||
@@ -496,8 +503,7 @@ static int vhost_scsi_map_iov_to_sgl(struct tcm_vhost_cmd *tv_cmd, | |||
496 | 503 | ||
497 | pr_debug("Mapping %u iovecs for %u pages\n", niov, sgl_count); | 504 | pr_debug("Mapping %u iovecs for %u pages\n", niov, sgl_count); |
498 | for (i = 0; i < niov; i++) { | 505 | for (i = 0; i < niov; i++) { |
499 | ret = vhost_scsi_map_to_sgl(sg, sgl_count, iov[i].iov_base, | 506 | ret = vhost_scsi_map_to_sgl(sg, sgl_count, &iov[i], write); |
500 | iov[i].iov_len, write); | ||
501 | if (ret < 0) { | 507 | if (ret < 0) { |
502 | for (i = 0; i < tv_cmd->tvc_sgl_count; i++) | 508 | for (i = 0; i < tv_cmd->tvc_sgl_count; i++) |
503 | put_page(sg_page(&tv_cmd->tvc_sgl[i])); | 509 | put_page(sg_page(&tv_cmd->tvc_sgl[i])); |