162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright(c) 2016 Intel Corporation.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/slab.h>
762306a36Sopenharmony_ci#include <linux/vmalloc.h>
862306a36Sopenharmony_ci#include <linux/mm.h>
962306a36Sopenharmony_ci#include <rdma/uverbs_ioctl.h>
1062306a36Sopenharmony_ci#include "mmap.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci/**
1362306a36Sopenharmony_ci * rvt_mmap_init - init link list and lock for mem map
1462306a36Sopenharmony_ci * @rdi: rvt dev struct
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_civoid rvt_mmap_init(struct rvt_dev_info *rdi)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	INIT_LIST_HEAD(&rdi->pending_mmaps);
1962306a36Sopenharmony_ci	spin_lock_init(&rdi->pending_lock);
2062306a36Sopenharmony_ci	rdi->mmap_offset = PAGE_SIZE;
2162306a36Sopenharmony_ci	spin_lock_init(&rdi->mmap_offset_lock);
2262306a36Sopenharmony_ci}
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci/**
2562306a36Sopenharmony_ci * rvt_release_mmap_info - free mmap info structure
2662306a36Sopenharmony_ci * @ref: a pointer to the kref within struct rvt_mmap_info
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_civoid rvt_release_mmap_info(struct kref *ref)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	struct rvt_mmap_info *ip =
3162306a36Sopenharmony_ci		container_of(ref, struct rvt_mmap_info, ref);
3262306a36Sopenharmony_ci	struct rvt_dev_info *rdi = ib_to_rvt(ip->context->device);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	spin_lock_irq(&rdi->pending_lock);
3562306a36Sopenharmony_ci	list_del(&ip->pending_mmaps);
3662306a36Sopenharmony_ci	spin_unlock_irq(&rdi->pending_lock);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	vfree(ip->obj);
3962306a36Sopenharmony_ci	kfree(ip);
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic void rvt_vma_open(struct vm_area_struct *vma)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	struct rvt_mmap_info *ip = vma->vm_private_data;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	kref_get(&ip->ref);
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic void rvt_vma_close(struct vm_area_struct *vma)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	struct rvt_mmap_info *ip = vma->vm_private_data;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	kref_put(&ip->ref, rvt_release_mmap_info);
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic const struct vm_operations_struct rvt_vm_ops = {
5762306a36Sopenharmony_ci	.open = rvt_vma_open,
5862306a36Sopenharmony_ci	.close = rvt_vma_close,
5962306a36Sopenharmony_ci};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/**
6262306a36Sopenharmony_ci * rvt_mmap - create a new mmap region
6362306a36Sopenharmony_ci * @context: the IB user context of the process making the mmap() call
6462306a36Sopenharmony_ci * @vma: the VMA to be initialized
6562306a36Sopenharmony_ci *
6662306a36Sopenharmony_ci * Return: zero if the mmap is OK. Otherwise, return an errno.
6762306a36Sopenharmony_ci */
6862306a36Sopenharmony_ciint rvt_mmap(struct ib_ucontext *context, struct vm_area_struct *vma)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	struct rvt_dev_info *rdi = ib_to_rvt(context->device);
7162306a36Sopenharmony_ci	unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
7262306a36Sopenharmony_ci	unsigned long size = vma->vm_end - vma->vm_start;
7362306a36Sopenharmony_ci	struct rvt_mmap_info *ip, *pp;
7462306a36Sopenharmony_ci	int ret = -EINVAL;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	/*
7762306a36Sopenharmony_ci	 * Search the device's list of objects waiting for a mmap call.
7862306a36Sopenharmony_ci	 * Normally, this list is very short since a call to create a
7962306a36Sopenharmony_ci	 * CQ, QP, or SRQ is soon followed by a call to mmap().
8062306a36Sopenharmony_ci	 */
8162306a36Sopenharmony_ci	spin_lock_irq(&rdi->pending_lock);
8262306a36Sopenharmony_ci	list_for_each_entry_safe(ip, pp, &rdi->pending_mmaps,
8362306a36Sopenharmony_ci				 pending_mmaps) {
8462306a36Sopenharmony_ci		/* Only the creator is allowed to mmap the object */
8562306a36Sopenharmony_ci		if (context != ip->context || (__u64)offset != ip->offset)
8662306a36Sopenharmony_ci			continue;
8762306a36Sopenharmony_ci		/* Don't allow a mmap larger than the object. */
8862306a36Sopenharmony_ci		if (size > ip->size)
8962306a36Sopenharmony_ci			break;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci		list_del_init(&ip->pending_mmaps);
9262306a36Sopenharmony_ci		spin_unlock_irq(&rdi->pending_lock);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci		ret = remap_vmalloc_range(vma, ip->obj, 0);
9562306a36Sopenharmony_ci		if (ret)
9662306a36Sopenharmony_ci			goto done;
9762306a36Sopenharmony_ci		vma->vm_ops = &rvt_vm_ops;
9862306a36Sopenharmony_ci		vma->vm_private_data = ip;
9962306a36Sopenharmony_ci		rvt_vma_open(vma);
10062306a36Sopenharmony_ci		goto done;
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci	spin_unlock_irq(&rdi->pending_lock);
10362306a36Sopenharmony_cidone:
10462306a36Sopenharmony_ci	return ret;
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/**
10862306a36Sopenharmony_ci * rvt_create_mmap_info - allocate information for hfi1_mmap
10962306a36Sopenharmony_ci * @rdi: rvt dev struct
11062306a36Sopenharmony_ci * @size: size in bytes to map
11162306a36Sopenharmony_ci * @udata: user data (must be valid!)
11262306a36Sopenharmony_ci * @obj: opaque pointer to a cq, wq etc
11362306a36Sopenharmony_ci *
11462306a36Sopenharmony_ci * Return: rvt_mmap struct on success, ERR_PTR on failure
11562306a36Sopenharmony_ci */
11662306a36Sopenharmony_cistruct rvt_mmap_info *rvt_create_mmap_info(struct rvt_dev_info *rdi, u32 size,
11762306a36Sopenharmony_ci					   struct ib_udata *udata, void *obj)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	struct rvt_mmap_info *ip;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (!udata)
12262306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	ip = kmalloc_node(sizeof(*ip), GFP_KERNEL, rdi->dparms.node);
12562306a36Sopenharmony_ci	if (!ip)
12662306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	size = PAGE_ALIGN(size);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	spin_lock_irq(&rdi->mmap_offset_lock);
13162306a36Sopenharmony_ci	if (rdi->mmap_offset == 0)
13262306a36Sopenharmony_ci		rdi->mmap_offset = ALIGN(PAGE_SIZE, SHMLBA);
13362306a36Sopenharmony_ci	ip->offset = rdi->mmap_offset;
13462306a36Sopenharmony_ci	rdi->mmap_offset += ALIGN(size, SHMLBA);
13562306a36Sopenharmony_ci	spin_unlock_irq(&rdi->mmap_offset_lock);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	INIT_LIST_HEAD(&ip->pending_mmaps);
13862306a36Sopenharmony_ci	ip->size = size;
13962306a36Sopenharmony_ci	ip->context =
14062306a36Sopenharmony_ci		container_of(udata, struct uverbs_attr_bundle, driver_udata)
14162306a36Sopenharmony_ci			->context;
14262306a36Sopenharmony_ci	ip->obj = obj;
14362306a36Sopenharmony_ci	kref_init(&ip->ref);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	return ip;
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci/**
14962306a36Sopenharmony_ci * rvt_update_mmap_info - update a mem map
15062306a36Sopenharmony_ci * @rdi: rvt dev struct
15162306a36Sopenharmony_ci * @ip: mmap info pointer
15262306a36Sopenharmony_ci * @size: size to grow by
15362306a36Sopenharmony_ci * @obj: opaque pointer to cq, wq, etc.
15462306a36Sopenharmony_ci */
15562306a36Sopenharmony_civoid rvt_update_mmap_info(struct rvt_dev_info *rdi, struct rvt_mmap_info *ip,
15662306a36Sopenharmony_ci			  u32 size, void *obj)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	size = PAGE_ALIGN(size);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	spin_lock_irq(&rdi->mmap_offset_lock);
16162306a36Sopenharmony_ci	if (rdi->mmap_offset == 0)
16262306a36Sopenharmony_ci		rdi->mmap_offset = PAGE_SIZE;
16362306a36Sopenharmony_ci	ip->offset = rdi->mmap_offset;
16462306a36Sopenharmony_ci	rdi->mmap_offset += size;
16562306a36Sopenharmony_ci	spin_unlock_irq(&rdi->mmap_offset_lock);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	ip->size = size;
16862306a36Sopenharmony_ci	ip->obj = obj;
16962306a36Sopenharmony_ci}
170