#include <linux/mm.h>
 #include <linux/sched.h>
 #include <linux/device.h>
+#include <linux/module.h>
 
 #include "hfi.h"
 
-int hfi1_acquire_user_pages(unsigned long vaddr, size_t npages, bool writable,
-                           struct page **pages)
+static unsigned long cache_size = 256;
+module_param(cache_size, ulong, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(cache_size, "Send and receive side cache size limit (in MB)");
+
+/*
+ * Determine whether the caller can pin pages.
+ *
+ * This function should be used in the implementation of buffer caches.
+ * The cache implementation should call this function prior to attempting
+ * to pin buffer pages in order to determine whether they should do so.
+ * The function computes cache limits based on the configured ulimit and
+ * cache size. Use of this function is especially important for caches
+ * which are not limited in any other way (e.g. by HW resources) and, thus,
+ * could keeping caching buffers.
+ *
+ */
+bool hfi1_can_pin_pages(struct hfi1_devdata *dd, u32 nlocked, u32 npages)
 {
-       unsigned long pinned, lock_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
+       unsigned long ulimit = rlimit(RLIMIT_MEMLOCK), pinned, cache_limit,
+               size = (cache_size * (1UL << 20)); /* convert to bytes */
+       unsigned usr_ctxts = dd->num_rcv_contexts - dd->first_user_ctxt;
        bool can_lock = capable(CAP_IPC_LOCK);
-       int ret;
+
+       /*
+        * Calculate per-cache size. The calculation below uses only a quarter
+        * of the available per-context limit. This leaves space for other
+        * pinning. Should we worry about shared ctxts?
+        */
+       cache_limit = (ulimit / usr_ctxts) / 4;
+
+       /* If ulimit isn't set to "unlimited" and is smaller than cache_size. */
+       if (ulimit != (-1UL) && size > cache_limit)
+               size = cache_limit;
+
+       /* Convert to number of pages */
+       size = DIV_ROUND_UP(size, PAGE_SIZE);
 
        down_read(¤t->mm->mmap_sem);
        pinned = current->mm->pinned_vm;
        up_read(¤t->mm->mmap_sem);
 
-       if (pinned + npages > lock_limit && !can_lock)
-               return -ENOMEM;
+       /* First, check the absolute limit against all pinned pages. */
+       if (pinned + npages >= ulimit && !can_lock)
+               return false;
+
+       return ((nlocked + npages) <= size) || can_lock;
+}
+
+int hfi1_acquire_user_pages(unsigned long vaddr, size_t npages, bool writable,
+                           struct page **pages)
+{
+       int ret;
 
        ret = get_user_pages_fast(vaddr, npages, writable, pages);
        if (ret < 0)