static int program_rcvarray(struct file *, unsigned long, struct tid_group *,
                            struct tid_pageset *, unsigned, u16, struct page **,
                            u32 *, unsigned *, unsigned *) __maybe_unused;
-static int unprogram_rcvarray(struct file *, u32,
-                             struct tid_group **) __maybe_unused;
-static void clear_tid_node(struct hfi1_filedata *, u16,
-                          struct mmu_rb_node *) __maybe_unused;
+static int unprogram_rcvarray(struct file *, u32, struct tid_group **);
+static void clear_tid_node(struct hfi1_filedata *, u16, struct mmu_rb_node *);
 
 static inline u32 rcventry2tidinfo(u32 rcventry)
 {
         * Make sure that we set the tid counts only after successful
         * init.
         */
+       spin_lock(&fd->tid_lock);
        if (uctxt->subctxt_cnt && !HFI1_CAP_IS_USET(TID_UNMAP)) {
                u16 remainder;
 
        } else {
                fd->tid_limit = uctxt->expected_count;
        }
+       spin_unlock(&fd->tid_lock);
 done:
        return ret;
 }
 
 int hfi1_user_exp_rcv_clear(struct file *fp, struct hfi1_tid_info *tinfo)
 {
-       return -EINVAL;
+       int ret = 0;
+       struct hfi1_filedata *fd = fp->private_data;
+       struct hfi1_ctxtdata *uctxt = fd->uctxt;
+       u32 *tidinfo;
+       unsigned tididx;
+
+       tidinfo = kcalloc(tinfo->tidcnt, sizeof(*tidinfo), GFP_KERNEL);
+       if (!tidinfo)
+               return -ENOMEM;
+
+       if (copy_from_user(tidinfo, (void __user *)(unsigned long)
+                          tinfo->tidlist, sizeof(tidinfo[0]) *
+                          tinfo->tidcnt)) {
+               ret = -EFAULT;
+               goto done;
+       }
+
+       mutex_lock(&uctxt->exp_lock);
+       for (tididx = 0; tididx < tinfo->tidcnt; tididx++) {
+               ret = unprogram_rcvarray(fp, tidinfo[tididx], NULL);
+               if (ret) {
+                       hfi1_cdbg(TID, "Failed to unprogram rcv array %d",
+                                 ret);
+                       break;
+               }
+       }
+       spin_lock(&fd->tid_lock);
+       fd->tid_used -= tididx;
+       spin_unlock(&fd->tid_lock);
+       tinfo->tidcnt = tididx;
+       mutex_unlock(&uctxt->exp_lock);
+done:
+       kfree(tidinfo);
+       return ret;
 }
 
 int hfi1_user_exp_rcv_invalid(struct file *fp, struct hfi1_tid_info *tinfo)
 {
-       return -EINVAL;
+       struct hfi1_filedata *fd = fp->private_data;
+       struct hfi1_ctxtdata *uctxt = fd->uctxt;
+       unsigned long *ev = uctxt->dd->events +
+               (((uctxt->ctxt - uctxt->dd->first_user_ctxt) *
+                 HFI1_MAX_SHARED_CTXTS) + fd->subctxt);
+       u32 *array;
+       int ret = 0;
+
+       if (!fd->invalid_tids)
+               return -EINVAL;
+
+       /*
+        * copy_to_user() can sleep, which will leave the invalid_lock
+        * locked and cause the MMU notifier to be blocked on the lock
+        * for a long time.
+        * Copy the data to a local buffer so we can release the lock.
+        */
+       array = kcalloc(uctxt->expected_count, sizeof(*array), GFP_KERNEL);
+       if (!array)
+               return -EFAULT;
+
+       spin_lock(&fd->invalid_lock);
+       if (fd->invalid_tid_idx) {
+               memcpy(array, fd->invalid_tids, sizeof(*array) *
+                      fd->invalid_tid_idx);
+               memset(fd->invalid_tids, 0, sizeof(*fd->invalid_tids) *
+                      fd->invalid_tid_idx);
+               tinfo->tidcnt = fd->invalid_tid_idx;
+               fd->invalid_tid_idx = 0;
+               /*
+                * Reset the user flag while still holding the lock.
+                * Otherwise, PSM can miss events.
+                */
+               clear_bit(_HFI1_EVENT_TID_MMU_NOTIFY_BIT, ev);
+       } else {
+               tinfo->tidcnt = 0;
+       }
+       spin_unlock(&fd->invalid_lock);
+
+       if (tinfo->tidcnt) {
+               if (copy_to_user((void __user *)tinfo->tidlist,
+                                array, sizeof(*array) * tinfo->tidcnt))
+                       ret = -EFAULT;
+       }
+       kfree(array);
+
+       return ret;
 }
 
 static u32 find_phys_blocks(struct page **pages, unsigned npages,