162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright(c) 2015-2017 Intel Corporation.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/mm.h>
762306a36Sopenharmony_ci#include <linux/sched/signal.h>
862306a36Sopenharmony_ci#include <linux/device.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "hfi.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic unsigned long cache_size = 256;
1462306a36Sopenharmony_cimodule_param(cache_size, ulong, S_IRUGO | S_IWUSR);
1562306a36Sopenharmony_ciMODULE_PARM_DESC(cache_size, "Send and receive side cache size limit (in MB)");
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/*
1862306a36Sopenharmony_ci * Determine whether the caller can pin pages.
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * This function should be used in the implementation of buffer caches.
2162306a36Sopenharmony_ci * The cache implementation should call this function prior to attempting
2262306a36Sopenharmony_ci * to pin buffer pages in order to determine whether they should do so.
2362306a36Sopenharmony_ci * The function computes cache limits based on the configured ulimit and
2462306a36Sopenharmony_ci * cache size. Use of this function is especially important for caches
2562306a36Sopenharmony_ci * which are not limited in any other way (e.g. by HW resources) and, thus,
2662306a36Sopenharmony_ci * could keeping caching buffers.
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_cibool hfi1_can_pin_pages(struct hfi1_devdata *dd, struct mm_struct *mm,
3062306a36Sopenharmony_ci			u32 nlocked, u32 npages)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	unsigned long ulimit_pages;
3362306a36Sopenharmony_ci	unsigned long cache_limit_pages;
3462306a36Sopenharmony_ci	unsigned int usr_ctxts;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	/*
3762306a36Sopenharmony_ci	 * Perform RLIMIT_MEMLOCK based checks unless CAP_IPC_LOCK is present.
3862306a36Sopenharmony_ci	 */
3962306a36Sopenharmony_ci	if (!capable(CAP_IPC_LOCK)) {
4062306a36Sopenharmony_ci		ulimit_pages =
4162306a36Sopenharmony_ci			DIV_ROUND_DOWN_ULL(rlimit(RLIMIT_MEMLOCK), PAGE_SIZE);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci		/*
4462306a36Sopenharmony_ci		 * Pinning these pages would exceed this process's locked memory
4562306a36Sopenharmony_ci		 * limit.
4662306a36Sopenharmony_ci		 */
4762306a36Sopenharmony_ci		if (atomic64_read(&mm->pinned_vm) + npages > ulimit_pages)
4862306a36Sopenharmony_ci			return false;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci		/*
5162306a36Sopenharmony_ci		 * Only allow 1/4 of the user's RLIMIT_MEMLOCK to be used for HFI
5262306a36Sopenharmony_ci		 * caches.  This fraction is then equally distributed among all
5362306a36Sopenharmony_ci		 * existing user contexts.  Note that if RLIMIT_MEMLOCK is
5462306a36Sopenharmony_ci		 * 'unlimited' (-1), the value of this limit will be > 2^42 pages
5562306a36Sopenharmony_ci		 * (2^64 / 2^12 / 2^8 / 2^2).
5662306a36Sopenharmony_ci		 *
5762306a36Sopenharmony_ci		 * The effectiveness of this check may be reduced if I/O occurs on
5862306a36Sopenharmony_ci		 * some user contexts before all user contexts are created.  This
5962306a36Sopenharmony_ci		 * check assumes that this process is the only one using this
6062306a36Sopenharmony_ci		 * context (e.g., the corresponding fd was not passed to another
6162306a36Sopenharmony_ci		 * process for concurrent access) as there is no per-context,
6262306a36Sopenharmony_ci		 * per-process tracking of pinned pages.  It also assumes that each
6362306a36Sopenharmony_ci		 * user context has only one cache to limit.
6462306a36Sopenharmony_ci		 */
6562306a36Sopenharmony_ci		usr_ctxts = dd->num_rcv_contexts - dd->first_dyn_alloc_ctxt;
6662306a36Sopenharmony_ci		if (nlocked + npages > (ulimit_pages / usr_ctxts / 4))
6762306a36Sopenharmony_ci			return false;
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/*
7162306a36Sopenharmony_ci	 * Pinning these pages would exceed the size limit for this cache.
7262306a36Sopenharmony_ci	 */
7362306a36Sopenharmony_ci	cache_limit_pages = cache_size * (1024 * 1024) / PAGE_SIZE;
7462306a36Sopenharmony_ci	if (nlocked + npages > cache_limit_pages)
7562306a36Sopenharmony_ci		return false;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return true;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ciint hfi1_acquire_user_pages(struct mm_struct *mm, unsigned long vaddr, size_t npages,
8162306a36Sopenharmony_ci			    bool writable, struct page **pages)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	int ret;
8462306a36Sopenharmony_ci	unsigned int gup_flags = FOLL_LONGTERM | (writable ? FOLL_WRITE : 0);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	ret = pin_user_pages_fast(vaddr, npages, gup_flags, pages);
8762306a36Sopenharmony_ci	if (ret < 0)
8862306a36Sopenharmony_ci		return ret;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	atomic64_add(ret, &mm->pinned_vm);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	return ret;
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_civoid hfi1_release_user_pages(struct mm_struct *mm, struct page **p,
9662306a36Sopenharmony_ci			     size_t npages, bool dirty)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	unpin_user_pages_dirty_lock(p, npages, dirty);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	if (mm) { /* during close after signal, mm can be NULL */
10162306a36Sopenharmony_ci		atomic64_sub(npages, &mm->pinned_vm);
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci}
104