IB/hfi1: Fix expected receive setup error exit issues
authorDean Luick <dean.luick@cornelisnetworks.com>
Mon, 9 Jan 2023 17:31:21 +0000 (12:31 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 1 Feb 2023 07:27:05 +0000 (08:27 +0100)
[ 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: 7e7a436ecb6e ("staging/hfi1: Add TID entry program function body")
Fixes: 97736f36dbeb ("IB/hfi1: Validate page aligned for a given virtual addres")
Fixes: f404ca4c7ea8 ("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>
drivers/infiniband/hw/hfi1/user_exp_rcv.c

index 236cfc5..5b93a1a 100644 (file)
@@ -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->pages);
        kfree(tidbuf->psets);
+       kfree(tidbuf);
        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);
-       return ret > 0 ? 0 : ret;
+       kfree(tidlist);
+       return ret;
 }
 
 int hfi1_user_exp_rcv_clear(struct hfi1_filedata *fd,