IB/hfi1: Fix expected receive setup error exit issues
[ Upstream commit e0c4a422f5246abefbf7c178ef99a1f2dc3c5f62 ] 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> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:

committed by
Greg Kroah-Hartman

parent
ee474dd66e
commit
6dd8136fd1
@@ -310,15 +310,14 @@ int hfi1_user_exp_rcv_setup(struct hfi1_filedata *fd,
|
|||||||
tidbuf->psets = kcalloc(uctxt->expected_count, sizeof(*tidbuf->psets),
|
tidbuf->psets = kcalloc(uctxt->expected_count, sizeof(*tidbuf->psets),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!tidbuf->psets) {
|
if (!tidbuf->psets) {
|
||||||
kfree(tidbuf);
|
ret = -ENOMEM;
|
||||||
return -ENOMEM;
|
goto fail_release_mem;
|
||||||
}
|
}
|
||||||
|
|
||||||
pinned = pin_rcv_pages(fd, tidbuf);
|
pinned = pin_rcv_pages(fd, tidbuf);
|
||||||
if (pinned <= 0) {
|
if (pinned <= 0) {
|
||||||
kfree(tidbuf->psets);
|
ret = (pinned < 0) ? pinned : -ENOSPC;
|
||||||
kfree(tidbuf);
|
goto fail_unpin;
|
||||||
return pinned;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find sets of physically contiguous pages */
|
/* Find sets of physically contiguous pages */
|
||||||
@@ -333,14 +332,16 @@ int hfi1_user_exp_rcv_setup(struct hfi1_filedata *fd,
|
|||||||
fd->tid_used += pageset_count;
|
fd->tid_used += pageset_count;
|
||||||
spin_unlock(&fd->tid_lock);
|
spin_unlock(&fd->tid_lock);
|
||||||
|
|
||||||
if (!pageset_count)
|
if (!pageset_count) {
|
||||||
goto bail;
|
ret = -ENOSPC;
|
||||||
|
goto fail_unreserve;
|
||||||
|
}
|
||||||
|
|
||||||
ngroups = pageset_count / dd->rcv_entries.group_size;
|
ngroups = pageset_count / dd->rcv_entries.group_size;
|
||||||
tidlist = kcalloc(pageset_count, sizeof(*tidlist), GFP_KERNEL);
|
tidlist = kcalloc(pageset_count, sizeof(*tidlist), GFP_KERNEL);
|
||||||
if (!tidlist) {
|
if (!tidlist) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto nomem;
|
goto fail_unreserve;
|
||||||
}
|
}
|
||||||
|
|
||||||
tididx = 0;
|
tididx = 0;
|
||||||
@@ -436,44 +437,60 @@ int hfi1_user_exp_rcv_setup(struct hfi1_filedata *fd,
|
|||||||
}
|
}
|
||||||
unlock:
|
unlock:
|
||||||
mutex_unlock(&uctxt->exp_mutex);
|
mutex_unlock(&uctxt->exp_mutex);
|
||||||
nomem:
|
|
||||||
hfi1_cdbg(TID, "total mapped: tidpairs:%u pages:%u (%d)", tididx,
|
hfi1_cdbg(TID, "total mapped: tidpairs:%u pages:%u (%d)", tididx,
|
||||||
mapped_pages, ret);
|
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 */
|
/* adjust reserved tid_used to actual count */
|
||||||
spin_lock(&fd->tid_lock);
|
spin_lock(&fd->tid_lock);
|
||||||
fd->tid_used -= pageset_count - tididx;
|
fd->tid_used -= pageset_count - tididx;
|
||||||
spin_unlock(&fd->tid_lock);
|
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),
|
/* unpin all pages not covered by a TID */
|
||||||
tidlist, sizeof(tidlist[0]) * tididx)) {
|
unpin_rcv_pages(fd, tidbuf, NULL, mapped_pages, pinned - mapped_pages,
|
||||||
/*
|
false);
|
||||||
* On failure to copy to the user level, we need to undo
|
|
||||||
* everything done so far so we don't leak resources.
|
tinfo->tidcnt = tididx;
|
||||||
*/
|
tinfo->length = mapped_pages * PAGE_SIZE;
|
||||||
tinfo->tidlist = (unsigned long)&tidlist;
|
|
||||||
hfi1_user_exp_rcv_clear(fd, tinfo);
|
if (copy_to_user(u64_to_user_ptr(tinfo->tidlist),
|
||||||
tinfo->tidlist = 0;
|
tidlist, sizeof(tidlist[0]) * tididx)) {
|
||||||
ret = -EFAULT;
|
ret = -EFAULT;
|
||||||
goto bail;
|
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->pages);
|
||||||
|
kfree(tidbuf->psets);
|
||||||
kfree(tidbuf);
|
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,
|
int hfi1_user_exp_rcv_clear(struct hfi1_filedata *fd,
|
||||||
|
Reference in New Issue
Block a user