Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 03965f8a authored by Dean Luick's avatar Dean Luick Committed by Greg Kroah-Hartman
Browse files

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: 7e7a436e ("staging/hfi1: Add TID entry program function body")
Fixes: 97736f36 ("IB/hfi1: Validate page aligned for a given virtual addres")
Fixes: f404ca4c ("IB/hfi1: Refactor hfi_user_exp_rcv_setup() IOCTL")
Signed-off-by: default avatarDean Luick <dean.luick@cornelisnetworks.com>
Signed-off-by: default avatarDennis Dalessandro <dennis.dalessandro@cornelisnetworks.com>
Link: https://lore.kernel.org/r/167328548150.1472310.1492305874804187634.stgit@awfm-02.cornelisnetworks.com


Signed-off-by: default avatarLeon Romanovsky <leon@kernel.org>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent f5c918dc
Loading
Loading
Loading
Loading
+50 −33
Original line number Diff line number Diff line
@@ -337,15 +337,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 */
@@ -360,14 +359,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;
@@ -463,44 +464,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) {

	/* 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)) {
			/*
			 * 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;
		}
		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,