Skip to content

Commit b3deec2

Browse files
DeanLuickrleon
authored andcommitted
IB/hfi1: Remove user expected buffer invalidate race
During setup, there is a possible race between a page invalidate and hardware programming. Add a covering invalidate over the user target range during setup. If anything within that range is invalidated during setup, fail the setup. Once set up, each TID will have its own invalidate callback and invalidate. Fixes: 3889551 ("RDMA/hfi1: Use mmu_interval_notifier_insert for user_exp_rcv") Signed-off-by: Dean Luick <[email protected]> Signed-off-by: Dennis Dalessandro <[email protected]> Link: https://lore.kernel.org/r/167328549178.1472310.9867497376936699488.stgit@awfm-02.cornelisnetworks.com Signed-off-by: Leon Romanovsky <[email protected]>
1 parent 1c7edde commit b3deec2

File tree

2 files changed

+55
-5
lines changed

2 files changed

+55
-5
lines changed

drivers/infiniband/hw/hfi1/user_exp_rcv.c

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ static void cacheless_tid_rb_remove(struct hfi1_filedata *fdata,
2323
static bool tid_rb_invalidate(struct mmu_interval_notifier *mni,
2424
const struct mmu_notifier_range *range,
2525
unsigned long cur_seq);
26+
static bool tid_cover_invalidate(struct mmu_interval_notifier *mni,
27+
const struct mmu_notifier_range *range,
28+
unsigned long cur_seq);
2629
static int program_rcvarray(struct hfi1_filedata *fd, struct tid_user_buf *,
2730
struct tid_group *grp,
2831
unsigned int start, u16 count,
@@ -36,6 +39,9 @@ static void clear_tid_node(struct hfi1_filedata *fd, struct tid_rb_node *node);
3639
static const struct mmu_interval_notifier_ops tid_mn_ops = {
3740
.invalidate = tid_rb_invalidate,
3841
};
42+
static const struct mmu_interval_notifier_ops tid_cover_ops = {
43+
.invalidate = tid_cover_invalidate,
44+
};
3945

4046
/*
4147
* Initialize context and file private data needed for Expected
@@ -254,6 +260,7 @@ int hfi1_user_exp_rcv_setup(struct hfi1_filedata *fd,
254260
tididx = 0, mapped, mapped_pages = 0;
255261
u32 *tidlist = NULL;
256262
struct tid_user_buf *tidbuf;
263+
unsigned long mmu_seq = 0;
257264

258265
if (!PAGE_ALIGNED(tinfo->vaddr))
259266
return -EINVAL;
@@ -264,6 +271,7 @@ int hfi1_user_exp_rcv_setup(struct hfi1_filedata *fd,
264271
if (!tidbuf)
265272
return -ENOMEM;
266273

274+
mutex_init(&tidbuf->cover_mutex);
267275
tidbuf->vaddr = tinfo->vaddr;
268276
tidbuf->length = tinfo->length;
269277
tidbuf->psets = kcalloc(uctxt->expected_count, sizeof(*tidbuf->psets),
@@ -273,6 +281,16 @@ int hfi1_user_exp_rcv_setup(struct hfi1_filedata *fd,
273281
goto fail_release_mem;
274282
}
275283

284+
if (fd->use_mn) {
285+
ret = mmu_interval_notifier_insert(
286+
&tidbuf->notifier, current->mm,
287+
tidbuf->vaddr, tidbuf->npages * PAGE_SIZE,
288+
&tid_cover_ops);
289+
if (ret)
290+
goto fail_release_mem;
291+
mmu_seq = mmu_interval_read_begin(&tidbuf->notifier);
292+
}
293+
276294
pinned = pin_rcv_pages(fd, tidbuf);
277295
if (pinned <= 0) {
278296
ret = (pinned < 0) ? pinned : -ENOSPC;
@@ -415,6 +433,20 @@ int hfi1_user_exp_rcv_setup(struct hfi1_filedata *fd,
415433
unpin_rcv_pages(fd, tidbuf, NULL, mapped_pages, pinned - mapped_pages,
416434
false);
417435

436+
if (fd->use_mn) {
437+
/* check for an invalidate during setup */
438+
bool fail = false;
439+
440+
mutex_lock(&tidbuf->cover_mutex);
441+
fail = mmu_interval_read_retry(&tidbuf->notifier, mmu_seq);
442+
mutex_unlock(&tidbuf->cover_mutex);
443+
444+
if (fail) {
445+
ret = -EBUSY;
446+
goto fail_unprogram;
447+
}
448+
}
449+
418450
tinfo->tidcnt = tididx;
419451
tinfo->length = mapped_pages * PAGE_SIZE;
420452

@@ -424,6 +456,8 @@ int hfi1_user_exp_rcv_setup(struct hfi1_filedata *fd,
424456
goto fail_unprogram;
425457
}
426458

459+
if (fd->use_mn)
460+
mmu_interval_notifier_remove(&tidbuf->notifier);
427461
kfree(tidbuf->pages);
428462
kfree(tidbuf->psets);
429463
kfree(tidbuf);
@@ -442,6 +476,8 @@ int hfi1_user_exp_rcv_setup(struct hfi1_filedata *fd,
442476
fd->tid_used -= pageset_count;
443477
spin_unlock(&fd->tid_lock);
444478
fail_unpin:
479+
if (fd->use_mn)
480+
mmu_interval_notifier_remove(&tidbuf->notifier);
445481
if (pinned > 0)
446482
unpin_rcv_pages(fd, tidbuf, NULL, 0, pinned, false);
447483
fail_release_mem:
@@ -740,11 +776,6 @@ static int set_rcvarray_entry(struct hfi1_filedata *fd,
740776
&tid_mn_ops);
741777
if (ret)
742778
goto out_unmap;
743-
/*
744-
* FIXME: This is in the wrong order, the notifier should be
745-
* established before the pages are pinned by pin_rcv_pages.
746-
*/
747-
mmu_interval_read_begin(&node->notifier);
748779
}
749780
fd->entry_to_rb[node->rcventry - uctxt->expected_base] = node;
750781

@@ -919,6 +950,23 @@ static bool tid_rb_invalidate(struct mmu_interval_notifier *mni,
919950
return true;
920951
}
921952

953+
static bool tid_cover_invalidate(struct mmu_interval_notifier *mni,
954+
const struct mmu_notifier_range *range,
955+
unsigned long cur_seq)
956+
{
957+
struct tid_user_buf *tidbuf =
958+
container_of(mni, struct tid_user_buf, notifier);
959+
960+
/* take action only if unmapping */
961+
if (range->event == MMU_NOTIFY_UNMAP) {
962+
mutex_lock(&tidbuf->cover_mutex);
963+
mmu_interval_set_seq(mni, cur_seq);
964+
mutex_unlock(&tidbuf->cover_mutex);
965+
}
966+
967+
return true;
968+
}
969+
922970
static void cacheless_tid_rb_remove(struct hfi1_filedata *fdata,
923971
struct tid_rb_node *tnode)
924972
{

drivers/infiniband/hw/hfi1/user_exp_rcv.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ struct tid_pageset {
1616
};
1717

1818
struct tid_user_buf {
19+
struct mmu_interval_notifier notifier;
20+
struct mutex cover_mutex;
1921
unsigned long vaddr;
2022
unsigned long length;
2123
unsigned int npages;

0 commit comments

Comments
 (0)