forked from luck/tmp_suning_uos_patched
xen/privcmd: Corrected error handling path
Previously, if lock_pages() end up partially mapping pages, it used to return -ERRNO due to which unlock_pages() have to go through each pages[i] till *nr_pages* to validate them. This can be avoided by passing correct number of partially mapped pages & -ERRNO separately, while returning from lock_pages() due to error. With this fix unlock_pages() doesn't need to validate pages[i] till *nr_pages* for error scenario and few condition checks can be ignored. Signed-off-by: Souptick Joarder <jrdr.linux@gmail.com> Reviewed-by: Juergen Gross <jgross@suse.com> Reviewed-by: Paul Durrant <paul@xen.org> Cc: John Hubbard <jhubbard@nvidia.com> Cc: Boris Ostrovsky <boris.ostrovsky@oracle.com> Cc: Paul Durrant <xadimgnik@gmail.com> Link: https://lore.kernel.org/r/1594525195-28345-2-git-send-email-jrdr.linux@gmail.com Signed-off-by: Juergen Gross <jgross@suse.com>
This commit is contained in:
parent
bcf876870b
commit
e398fb4bdf
|
@ -580,13 +580,13 @@ static long privcmd_ioctl_mmap_batch(
|
|||
|
||||
static int lock_pages(
|
||||
struct privcmd_dm_op_buf kbufs[], unsigned int num,
|
||||
struct page *pages[], unsigned int nr_pages)
|
||||
struct page *pages[], unsigned int nr_pages, unsigned int *pinned)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
unsigned int requested;
|
||||
int pinned;
|
||||
int page_count;
|
||||
|
||||
requested = DIV_ROUND_UP(
|
||||
offset_in_page(kbufs[i].uptr) + kbufs[i].size,
|
||||
|
@ -594,14 +594,15 @@ static int lock_pages(
|
|||
if (requested > nr_pages)
|
||||
return -ENOSPC;
|
||||
|
||||
pinned = get_user_pages_fast(
|
||||
page_count = get_user_pages_fast(
|
||||
(unsigned long) kbufs[i].uptr,
|
||||
requested, FOLL_WRITE, pages);
|
||||
if (pinned < 0)
|
||||
return pinned;
|
||||
if (page_count < 0)
|
||||
return page_count;
|
||||
|
||||
nr_pages -= pinned;
|
||||
pages += pinned;
|
||||
*pinned += page_count;
|
||||
nr_pages -= page_count;
|
||||
pages += page_count;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -611,13 +612,8 @@ static void unlock_pages(struct page *pages[], unsigned int nr_pages)
|
|||
{
|
||||
unsigned int i;
|
||||
|
||||
if (!pages)
|
||||
return;
|
||||
|
||||
for (i = 0; i < nr_pages; i++) {
|
||||
if (pages[i])
|
||||
put_page(pages[i]);
|
||||
}
|
||||
for (i = 0; i < nr_pages; i++)
|
||||
put_page(pages[i]);
|
||||
}
|
||||
|
||||
static long privcmd_ioctl_dm_op(struct file *file, void __user *udata)
|
||||
|
@ -630,6 +626,7 @@ static long privcmd_ioctl_dm_op(struct file *file, void __user *udata)
|
|||
struct xen_dm_op_buf *xbufs = NULL;
|
||||
unsigned int i;
|
||||
long rc;
|
||||
unsigned int pinned = 0;
|
||||
|
||||
if (copy_from_user(&kdata, udata, sizeof(kdata)))
|
||||
return -EFAULT;
|
||||
|
@ -683,9 +680,11 @@ static long privcmd_ioctl_dm_op(struct file *file, void __user *udata)
|
|||
goto out;
|
||||
}
|
||||
|
||||
rc = lock_pages(kbufs, kdata.num, pages, nr_pages);
|
||||
if (rc)
|
||||
rc = lock_pages(kbufs, kdata.num, pages, nr_pages, &pinned);
|
||||
if (rc < 0) {
|
||||
nr_pages = pinned;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < kdata.num; i++) {
|
||||
set_xen_guest_handle(xbufs[i].h, kbufs[i].uptr);
|
||||
|
|
Loading…
Reference in New Issue
Block a user