mirror of
https://github.com/torvalds/linux.git
synced 2024-11-27 14:41:39 +00:00
IB/hfi1: Fix expected receive setup error exit issues
Fix three error exit issues in expected receive setup. Re-arrange error exits to increase readability. Issues and fixes: 1. Possible missed page unpin if tidlist copyout fails and not all pinned pages where made part of a TID. Fix: Unpin the unused pages. 2. Return success with unset return values tidcnt and length when no pages were pinned. Fix: Return -ENOSPC if no pages were pinned. 3. Return success with unset return values tidcnt and length when no rcvarray entries available. Fix: Return -ENOSPC if no rcvarray entries are available. Fixes:7e7a436ecb
("staging/hfi1: Add TID entry program function body") Fixes:97736f36db
("IB/hfi1: Validate page aligned for a given virtual addres") Fixes:f404ca4c7e
("IB/hfi1: Refactor hfi_user_exp_rcv_setup() IOCTL") Signed-off-by: Dean Luick <dean.luick@cornelisnetworks.com> Signed-off-by: Dennis Dalessandro <dennis.dalessandro@cornelisnetworks.com> Link: https://lore.kernel.org/r/167328548150.1472310.1492305874804187634.stgit@awfm-02.cornelisnetworks.com Signed-off-by: Leon Romanovsky <leon@kernel.org>
This commit is contained in:
parent
ecf91551cd
commit
e0c4a422f5
@ -268,15 +268,14 @@ int hfi1_user_exp_rcv_setup(struct hfi1_filedata *fd,
|
||||
tidbuf->psets = kcalloc(uctxt->expected_count, sizeof(*tidbuf->psets),
|
||||
GFP_KERNEL);
|
||||
if (!tidbuf->psets) {
|
||||
kfree(tidbuf);
|
||||
return -ENOMEM;
|
||||
ret = -ENOMEM;
|
||||
goto fail_release_mem;
|
||||
}
|
||||
|
||||
pinned = pin_rcv_pages(fd, tidbuf);
|
||||
if (pinned <= 0) {
|
||||
kfree(tidbuf->psets);
|
||||
kfree(tidbuf);
|
||||
return pinned;
|
||||
ret = (pinned < 0) ? pinned : -ENOSPC;
|
||||
goto fail_unpin;
|
||||
}
|
||||
|
||||
/* Find sets of physically contiguous pages */
|
||||
@ -291,14 +290,16 @@ int hfi1_user_exp_rcv_setup(struct hfi1_filedata *fd,
|
||||
fd->tid_used += pageset_count;
|
||||
spin_unlock(&fd->tid_lock);
|
||||
|
||||
if (!pageset_count)
|
||||
goto bail;
|
||||
if (!pageset_count) {
|
||||
ret = -ENOSPC;
|
||||
goto fail_unreserve;
|
||||
}
|
||||
|
||||
ngroups = pageset_count / dd->rcv_entries.group_size;
|
||||
tidlist = kcalloc(pageset_count, sizeof(*tidlist), GFP_KERNEL);
|
||||
if (!tidlist) {
|
||||
ret = -ENOMEM;
|
||||
goto nomem;
|
||||
goto fail_unreserve;
|
||||
}
|
||||
|
||||
tididx = 0;
|
||||
@ -394,44 +395,60 @@ int hfi1_user_exp_rcv_setup(struct hfi1_filedata *fd,
|
||||
}
|
||||
unlock:
|
||||
mutex_unlock(&uctxt->exp_mutex);
|
||||
nomem:
|
||||
hfi1_cdbg(TID, "total mapped: tidpairs:%u pages:%u (%d)", tididx,
|
||||
mapped_pages, ret);
|
||||
|
||||
/* fail if nothing was programmed, set error if none provided */
|
||||
if (tididx == 0) {
|
||||
if (ret >= 0)
|
||||
ret = -ENOSPC;
|
||||
goto fail_unreserve;
|
||||
}
|
||||
|
||||
/* adjust reserved tid_used to actual count */
|
||||
spin_lock(&fd->tid_lock);
|
||||
fd->tid_used -= pageset_count - tididx;
|
||||
spin_unlock(&fd->tid_lock);
|
||||
if (tididx) {
|
||||
tinfo->tidcnt = tididx;
|
||||
tinfo->length = mapped_pages * PAGE_SIZE;
|
||||
|
||||
if (copy_to_user(u64_to_user_ptr(tinfo->tidlist),
|
||||
tidlist, sizeof(tidlist[0]) * tididx)) {
|
||||
/*
|
||||
* On failure to copy to the user level, we need to undo
|
||||
* everything done so far so we don't leak resources.
|
||||
*/
|
||||
tinfo->tidlist = (unsigned long)&tidlist;
|
||||
hfi1_user_exp_rcv_clear(fd, tinfo);
|
||||
tinfo->tidlist = 0;
|
||||
ret = -EFAULT;
|
||||
goto bail;
|
||||
}
|
||||
/* unpin all pages not covered by a TID */
|
||||
unpin_rcv_pages(fd, tidbuf, NULL, mapped_pages, pinned - mapped_pages,
|
||||
false);
|
||||
|
||||
tinfo->tidcnt = tididx;
|
||||
tinfo->length = mapped_pages * PAGE_SIZE;
|
||||
|
||||
if (copy_to_user(u64_to_user_ptr(tinfo->tidlist),
|
||||
tidlist, sizeof(tidlist[0]) * tididx)) {
|
||||
ret = -EFAULT;
|
||||
goto fail_unprogram;
|
||||
}
|
||||
|
||||
/*
|
||||
* If not everything was mapped (due to insufficient RcvArray entries,
|
||||
* for example), unpin all unmapped pages so we can pin them nex time.
|
||||
*/
|
||||
if (mapped_pages != pinned)
|
||||
unpin_rcv_pages(fd, tidbuf, NULL, mapped_pages,
|
||||
(pinned - mapped_pages), false);
|
||||
bail:
|
||||
kfree(tidbuf->psets);
|
||||
kfree(tidlist);
|
||||
kfree(tidbuf->pages);
|
||||
kfree(tidbuf->psets);
|
||||
kfree(tidbuf);
|
||||
return ret > 0 ? 0 : ret;
|
||||
kfree(tidlist);
|
||||
return 0;
|
||||
|
||||
fail_unprogram:
|
||||
/* unprogram, unmap, and unpin all allocated TIDs */
|
||||
tinfo->tidlist = (unsigned long)tidlist;
|
||||
hfi1_user_exp_rcv_clear(fd, tinfo);
|
||||
tinfo->tidlist = 0;
|
||||
pinned = 0; /* nothing left to unpin */
|
||||
pageset_count = 0; /* nothing left reserved */
|
||||
fail_unreserve:
|
||||
spin_lock(&fd->tid_lock);
|
||||
fd->tid_used -= pageset_count;
|
||||
spin_unlock(&fd->tid_lock);
|
||||
fail_unpin:
|
||||
if (pinned > 0)
|
||||
unpin_rcv_pages(fd, tidbuf, NULL, 0, pinned, false);
|
||||
fail_release_mem:
|
||||
kfree(tidbuf->pages);
|
||||
kfree(tidbuf->psets);
|
||||
kfree(tidbuf);
|
||||
kfree(tidlist);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int hfi1_user_exp_rcv_clear(struct hfi1_filedata *fd,
|
||||
|
Loading…
Reference in New Issue
Block a user