xref: /linux/drivers/infiniband/sw/rxe/rxe_odp.c (revision dd91b5e1d6448794c07378d1be12e3261c8769e7)
1b6017923SDaisuke Matsuda // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2b6017923SDaisuke Matsuda /*
3b6017923SDaisuke Matsuda  * Copyright (c) 2022-2023 Fujitsu Ltd. All rights reserved.
4b6017923SDaisuke Matsuda  */
5b6017923SDaisuke Matsuda 
6b6017923SDaisuke Matsuda #include <linux/hmm.h>
76703cb3dSDaisuke Matsuda #include <linux/libnvdimm.h>
8b6017923SDaisuke Matsuda 
9b6017923SDaisuke Matsuda #include <rdma/ib_umem_odp.h>
10b6017923SDaisuke Matsuda 
11b6017923SDaisuke Matsuda #include "rxe.h"
12b6017923SDaisuke Matsuda 
rxe_ib_invalidate_range(struct mmu_interval_notifier * mni,const struct mmu_notifier_range * range,unsigned long cur_seq)13b6017923SDaisuke Matsuda static bool rxe_ib_invalidate_range(struct mmu_interval_notifier *mni,
14b6017923SDaisuke Matsuda 				    const struct mmu_notifier_range *range,
15b6017923SDaisuke Matsuda 				    unsigned long cur_seq)
16b6017923SDaisuke Matsuda {
17b6017923SDaisuke Matsuda 	struct ib_umem_odp *umem_odp =
18b6017923SDaisuke Matsuda 		container_of(mni, struct ib_umem_odp, notifier);
19b6017923SDaisuke Matsuda 	unsigned long start, end;
20b6017923SDaisuke Matsuda 
21b6017923SDaisuke Matsuda 	if (!mmu_notifier_range_blockable(range))
22b6017923SDaisuke Matsuda 		return false;
23b6017923SDaisuke Matsuda 
24b6017923SDaisuke Matsuda 	mutex_lock(&umem_odp->umem_mutex);
25b6017923SDaisuke Matsuda 	mmu_interval_set_seq(mni, cur_seq);
26b6017923SDaisuke Matsuda 
27b6017923SDaisuke Matsuda 	start = max_t(u64, ib_umem_start(umem_odp), range->start);
28b6017923SDaisuke Matsuda 	end = min_t(u64, ib_umem_end(umem_odp), range->end);
29b6017923SDaisuke Matsuda 
30eedd5b12SLeon Romanovsky 	/* update umem_odp->map.pfn_list */
31b6017923SDaisuke Matsuda 	ib_umem_odp_unmap_dma_pages(umem_odp, start, end);
32b6017923SDaisuke Matsuda 
33b6017923SDaisuke Matsuda 	mutex_unlock(&umem_odp->umem_mutex);
34b6017923SDaisuke Matsuda 	return true;
35b6017923SDaisuke Matsuda }
36b6017923SDaisuke Matsuda 
37b6017923SDaisuke Matsuda const struct mmu_interval_notifier_ops rxe_mn_ops = {
38b6017923SDaisuke Matsuda 	.invalidate = rxe_ib_invalidate_range,
39b6017923SDaisuke Matsuda };
40d03fb5c6SDaisuke Matsuda 
410a924decSDaisuke Matsuda #define RXE_PAGEFAULT_DEFAULT 0
420a924decSDaisuke Matsuda #define RXE_PAGEFAULT_RDONLY BIT(0)
430a924decSDaisuke Matsuda #define RXE_PAGEFAULT_SNAPSHOT BIT(1)
rxe_odp_do_pagefault_and_lock(struct rxe_mr * mr,u64 user_va,int bcnt,u32 flags)44d03fb5c6SDaisuke Matsuda static int rxe_odp_do_pagefault_and_lock(struct rxe_mr *mr, u64 user_va, int bcnt, u32 flags)
45d03fb5c6SDaisuke Matsuda {
46d03fb5c6SDaisuke Matsuda 	struct ib_umem_odp *umem_odp = to_ib_umem_odp(mr->umem);
47d03fb5c6SDaisuke Matsuda 	bool fault = !(flags & RXE_PAGEFAULT_SNAPSHOT);
48eedd5b12SLeon Romanovsky 	u64 access_mask = 0;
49d03fb5c6SDaisuke Matsuda 	int np;
50d03fb5c6SDaisuke Matsuda 
51d03fb5c6SDaisuke Matsuda 	if (umem_odp->umem.writable && !(flags & RXE_PAGEFAULT_RDONLY))
52eedd5b12SLeon Romanovsky 		access_mask |= HMM_PFN_WRITE;
53d03fb5c6SDaisuke Matsuda 
54d03fb5c6SDaisuke Matsuda 	/*
55d03fb5c6SDaisuke Matsuda 	 * ib_umem_odp_map_dma_and_lock() locks umem_mutex on success.
56d03fb5c6SDaisuke Matsuda 	 * Callers must release the lock later to let invalidation handler
57d03fb5c6SDaisuke Matsuda 	 * do its work again.
58d03fb5c6SDaisuke Matsuda 	 */
59d03fb5c6SDaisuke Matsuda 	np = ib_umem_odp_map_dma_and_lock(umem_odp, user_va, bcnt,
60d03fb5c6SDaisuke Matsuda 					  access_mask, fault);
61d03fb5c6SDaisuke Matsuda 	return np;
62d03fb5c6SDaisuke Matsuda }
63d03fb5c6SDaisuke Matsuda 
rxe_odp_init_pages(struct rxe_mr * mr)64d03fb5c6SDaisuke Matsuda static int rxe_odp_init_pages(struct rxe_mr *mr)
65d03fb5c6SDaisuke Matsuda {
66d03fb5c6SDaisuke Matsuda 	struct ib_umem_odp *umem_odp = to_ib_umem_odp(mr->umem);
67d03fb5c6SDaisuke Matsuda 	int ret;
68d03fb5c6SDaisuke Matsuda 
69d03fb5c6SDaisuke Matsuda 	ret = rxe_odp_do_pagefault_and_lock(mr, mr->umem->address,
70d03fb5c6SDaisuke Matsuda 					    mr->umem->length,
71d03fb5c6SDaisuke Matsuda 					    RXE_PAGEFAULT_SNAPSHOT);
72d03fb5c6SDaisuke Matsuda 
73d03fb5c6SDaisuke Matsuda 	if (ret >= 0)
74d03fb5c6SDaisuke Matsuda 		mutex_unlock(&umem_odp->umem_mutex);
75d03fb5c6SDaisuke Matsuda 
76d03fb5c6SDaisuke Matsuda 	return ret >= 0 ? 0 : ret;
77d03fb5c6SDaisuke Matsuda }
78d03fb5c6SDaisuke Matsuda 
rxe_odp_mr_init_user(struct rxe_dev * rxe,u64 start,u64 length,u64 iova,int access_flags,struct rxe_mr * mr)79d03fb5c6SDaisuke Matsuda int rxe_odp_mr_init_user(struct rxe_dev *rxe, u64 start, u64 length,
80d03fb5c6SDaisuke Matsuda 			 u64 iova, int access_flags, struct rxe_mr *mr)
81d03fb5c6SDaisuke Matsuda {
82d03fb5c6SDaisuke Matsuda 	struct ib_umem_odp *umem_odp;
83d03fb5c6SDaisuke Matsuda 	int err;
84d03fb5c6SDaisuke Matsuda 
85d03fb5c6SDaisuke Matsuda 	if (!IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING))
86d03fb5c6SDaisuke Matsuda 		return -EOPNOTSUPP;
87d03fb5c6SDaisuke Matsuda 
88d03fb5c6SDaisuke Matsuda 	rxe_mr_init(access_flags, mr);
89d03fb5c6SDaisuke Matsuda 
90d03fb5c6SDaisuke Matsuda 	if (!start && length == U64_MAX) {
91d03fb5c6SDaisuke Matsuda 		if (iova != 0)
92d03fb5c6SDaisuke Matsuda 			return -EINVAL;
93d03fb5c6SDaisuke Matsuda 		if (!(rxe->attr.odp_caps.general_caps & IB_ODP_SUPPORT_IMPLICIT))
94d03fb5c6SDaisuke Matsuda 			return -EINVAL;
95d03fb5c6SDaisuke Matsuda 
96d03fb5c6SDaisuke Matsuda 		/* Never reach here, for implicit ODP is not implemented. */
97d03fb5c6SDaisuke Matsuda 	}
98d03fb5c6SDaisuke Matsuda 
99d03fb5c6SDaisuke Matsuda 	umem_odp = ib_umem_odp_get(&rxe->ib_dev, start, length, access_flags,
100d03fb5c6SDaisuke Matsuda 				   &rxe_mn_ops);
101d03fb5c6SDaisuke Matsuda 	if (IS_ERR(umem_odp)) {
102d03fb5c6SDaisuke Matsuda 		rxe_dbg_mr(mr, "Unable to create umem_odp err = %d\n",
103d03fb5c6SDaisuke Matsuda 			   (int)PTR_ERR(umem_odp));
104d03fb5c6SDaisuke Matsuda 		return PTR_ERR(umem_odp);
105d03fb5c6SDaisuke Matsuda 	}
106d03fb5c6SDaisuke Matsuda 
107d03fb5c6SDaisuke Matsuda 	umem_odp->private = mr;
108d03fb5c6SDaisuke Matsuda 
109d03fb5c6SDaisuke Matsuda 	mr->umem = &umem_odp->umem;
110d03fb5c6SDaisuke Matsuda 	mr->access = access_flags;
111d03fb5c6SDaisuke Matsuda 	mr->ibmr.length = length;
112d03fb5c6SDaisuke Matsuda 	mr->ibmr.iova = iova;
113d03fb5c6SDaisuke Matsuda 	mr->page_offset = ib_umem_offset(&umem_odp->umem);
114d03fb5c6SDaisuke Matsuda 
115d03fb5c6SDaisuke Matsuda 	err = rxe_odp_init_pages(mr);
116d03fb5c6SDaisuke Matsuda 	if (err) {
117d03fb5c6SDaisuke Matsuda 		ib_umem_odp_release(umem_odp);
118d03fb5c6SDaisuke Matsuda 		return err;
119d03fb5c6SDaisuke Matsuda 	}
120d03fb5c6SDaisuke Matsuda 
121d03fb5c6SDaisuke Matsuda 	mr->state = RXE_MR_STATE_VALID;
122d03fb5c6SDaisuke Matsuda 	mr->ibmr.type = IB_MR_TYPE_USER;
123d03fb5c6SDaisuke Matsuda 
124d03fb5c6SDaisuke Matsuda 	return err;
125d03fb5c6SDaisuke Matsuda }
1262fae67abSDaisuke Matsuda 
rxe_check_pagefault(struct ib_umem_odp * umem_odp,u64 iova,int length)127*0b261d7cSLeon Romanovsky static inline bool rxe_check_pagefault(struct ib_umem_odp *umem_odp, u64 iova,
128*0b261d7cSLeon Romanovsky 				       int length)
1292fae67abSDaisuke Matsuda {
1302fae67abSDaisuke Matsuda 	bool need_fault = false;
1312fae67abSDaisuke Matsuda 	u64 addr;
1322fae67abSDaisuke Matsuda 	int idx;
1332fae67abSDaisuke Matsuda 
1342fae67abSDaisuke Matsuda 	addr = iova & (~(BIT(umem_odp->page_shift) - 1));
1352fae67abSDaisuke Matsuda 
1362fae67abSDaisuke Matsuda 	/* Skim through all pages that are to be accessed. */
1372fae67abSDaisuke Matsuda 	while (addr < iova + length) {
1382fae67abSDaisuke Matsuda 		idx = (addr - ib_umem_start(umem_odp)) >> umem_odp->page_shift;
1392fae67abSDaisuke Matsuda 
140*0b261d7cSLeon Romanovsky 		if (!(umem_odp->map.pfn_list[idx] & HMM_PFN_VALID)) {
1412fae67abSDaisuke Matsuda 			need_fault = true;
1422fae67abSDaisuke Matsuda 			break;
1432fae67abSDaisuke Matsuda 		}
1442fae67abSDaisuke Matsuda 
1452fae67abSDaisuke Matsuda 		addr += BIT(umem_odp->page_shift);
1462fae67abSDaisuke Matsuda 	}
1472fae67abSDaisuke Matsuda 	return need_fault;
1482fae67abSDaisuke Matsuda }
1492fae67abSDaisuke Matsuda 
rxe_odp_iova_to_index(struct ib_umem_odp * umem_odp,u64 iova)1506703cb3dSDaisuke Matsuda static unsigned long rxe_odp_iova_to_index(struct ib_umem_odp *umem_odp, u64 iova)
1516703cb3dSDaisuke Matsuda {
1526703cb3dSDaisuke Matsuda 	return (iova - ib_umem_start(umem_odp)) >> umem_odp->page_shift;
1536703cb3dSDaisuke Matsuda }
1546703cb3dSDaisuke Matsuda 
rxe_odp_iova_to_page_offset(struct ib_umem_odp * umem_odp,u64 iova)1556703cb3dSDaisuke Matsuda static unsigned long rxe_odp_iova_to_page_offset(struct ib_umem_odp *umem_odp, u64 iova)
1566703cb3dSDaisuke Matsuda {
1576703cb3dSDaisuke Matsuda 	return iova & (BIT(umem_odp->page_shift) - 1);
1586703cb3dSDaisuke Matsuda }
1596703cb3dSDaisuke Matsuda 
rxe_odp_map_range_and_lock(struct rxe_mr * mr,u64 iova,int length,u32 flags)1602fae67abSDaisuke Matsuda static int rxe_odp_map_range_and_lock(struct rxe_mr *mr, u64 iova, int length, u32 flags)
1612fae67abSDaisuke Matsuda {
1622fae67abSDaisuke Matsuda 	struct ib_umem_odp *umem_odp = to_ib_umem_odp(mr->umem);
1632fae67abSDaisuke Matsuda 	bool need_fault;
1642fae67abSDaisuke Matsuda 	int err;
1652fae67abSDaisuke Matsuda 
1662fae67abSDaisuke Matsuda 	if (unlikely(length < 1))
1672fae67abSDaisuke Matsuda 		return -EINVAL;
1682fae67abSDaisuke Matsuda 
1692fae67abSDaisuke Matsuda 	mutex_lock(&umem_odp->umem_mutex);
1702fae67abSDaisuke Matsuda 
171*0b261d7cSLeon Romanovsky 	need_fault = rxe_check_pagefault(umem_odp, iova, length);
1722fae67abSDaisuke Matsuda 	if (need_fault) {
1732fae67abSDaisuke Matsuda 		mutex_unlock(&umem_odp->umem_mutex);
1742fae67abSDaisuke Matsuda 
1752fae67abSDaisuke Matsuda 		/* umem_mutex is locked on success. */
1762fae67abSDaisuke Matsuda 		err = rxe_odp_do_pagefault_and_lock(mr, iova, length,
1772fae67abSDaisuke Matsuda 						    flags);
1782fae67abSDaisuke Matsuda 		if (err < 0)
1792fae67abSDaisuke Matsuda 			return err;
1802fae67abSDaisuke Matsuda 
181*0b261d7cSLeon Romanovsky 		need_fault = rxe_check_pagefault(umem_odp, iova, length);
1822fae67abSDaisuke Matsuda 		if (need_fault)
1832fae67abSDaisuke Matsuda 			return -EFAULT;
1842fae67abSDaisuke Matsuda 	}
1852fae67abSDaisuke Matsuda 
1862fae67abSDaisuke Matsuda 	return 0;
1872fae67abSDaisuke Matsuda }
1882fae67abSDaisuke Matsuda 
__rxe_odp_mr_copy(struct rxe_mr * mr,u64 iova,void * addr,int length,enum rxe_mr_copy_dir dir)1892fae67abSDaisuke Matsuda static int __rxe_odp_mr_copy(struct rxe_mr *mr, u64 iova, void *addr,
1902fae67abSDaisuke Matsuda 			     int length, enum rxe_mr_copy_dir dir)
1912fae67abSDaisuke Matsuda {
1922fae67abSDaisuke Matsuda 	struct ib_umem_odp *umem_odp = to_ib_umem_odp(mr->umem);
1932fae67abSDaisuke Matsuda 	struct page *page;
1942fae67abSDaisuke Matsuda 	int idx, bytes;
1952fae67abSDaisuke Matsuda 	size_t offset;
1962fae67abSDaisuke Matsuda 	u8 *user_va;
1972fae67abSDaisuke Matsuda 
1986703cb3dSDaisuke Matsuda 	idx = rxe_odp_iova_to_index(umem_odp, iova);
1996703cb3dSDaisuke Matsuda 	offset = rxe_odp_iova_to_page_offset(umem_odp, iova);
2002fae67abSDaisuke Matsuda 
2012fae67abSDaisuke Matsuda 	while (length > 0) {
2022fae67abSDaisuke Matsuda 		u8 *src, *dest;
2032fae67abSDaisuke Matsuda 
2041efe8c06SLeon Romanovsky 		page = hmm_pfn_to_page(umem_odp->map.pfn_list[idx]);
2052fae67abSDaisuke Matsuda 		user_va = kmap_local_page(page);
2062fae67abSDaisuke Matsuda 		if (!user_va)
2072fae67abSDaisuke Matsuda 			return -EFAULT;
2082fae67abSDaisuke Matsuda 
2092fae67abSDaisuke Matsuda 		src = (dir == RXE_TO_MR_OBJ) ? addr : user_va;
2102fae67abSDaisuke Matsuda 		dest = (dir == RXE_TO_MR_OBJ) ? user_va : addr;
2112fae67abSDaisuke Matsuda 
2122fae67abSDaisuke Matsuda 		bytes = BIT(umem_odp->page_shift) - offset;
2132fae67abSDaisuke Matsuda 		if (bytes > length)
2142fae67abSDaisuke Matsuda 			bytes = length;
2152fae67abSDaisuke Matsuda 
2162fae67abSDaisuke Matsuda 		memcpy(dest, src, bytes);
2172fae67abSDaisuke Matsuda 		kunmap_local(user_va);
2182fae67abSDaisuke Matsuda 
2192fae67abSDaisuke Matsuda 		length  -= bytes;
2202fae67abSDaisuke Matsuda 		idx++;
2212fae67abSDaisuke Matsuda 		offset = 0;
2222fae67abSDaisuke Matsuda 	}
2232fae67abSDaisuke Matsuda 
2242fae67abSDaisuke Matsuda 	return 0;
2252fae67abSDaisuke Matsuda }
2262fae67abSDaisuke Matsuda 
rxe_odp_mr_copy(struct rxe_mr * mr,u64 iova,void * addr,int length,enum rxe_mr_copy_dir dir)2272fae67abSDaisuke Matsuda int rxe_odp_mr_copy(struct rxe_mr *mr, u64 iova, void *addr, int length,
2282fae67abSDaisuke Matsuda 		    enum rxe_mr_copy_dir dir)
2292fae67abSDaisuke Matsuda {
2302fae67abSDaisuke Matsuda 	struct ib_umem_odp *umem_odp = to_ib_umem_odp(mr->umem);
2310a924decSDaisuke Matsuda 	u32 flags = RXE_PAGEFAULT_DEFAULT;
2322fae67abSDaisuke Matsuda 	int err;
2332fae67abSDaisuke Matsuda 
2342fae67abSDaisuke Matsuda 	if (length == 0)
2352fae67abSDaisuke Matsuda 		return 0;
2362fae67abSDaisuke Matsuda 
2372fae67abSDaisuke Matsuda 	if (unlikely(!mr->umem->is_odp))
2382fae67abSDaisuke Matsuda 		return -EOPNOTSUPP;
2392fae67abSDaisuke Matsuda 
2402fae67abSDaisuke Matsuda 	switch (dir) {
2412fae67abSDaisuke Matsuda 	case RXE_TO_MR_OBJ:
2422fae67abSDaisuke Matsuda 		break;
2432fae67abSDaisuke Matsuda 
2442fae67abSDaisuke Matsuda 	case RXE_FROM_MR_OBJ:
2450a924decSDaisuke Matsuda 		flags |= RXE_PAGEFAULT_RDONLY;
2462fae67abSDaisuke Matsuda 		break;
2472fae67abSDaisuke Matsuda 
2482fae67abSDaisuke Matsuda 	default:
2492fae67abSDaisuke Matsuda 		return -EINVAL;
2502fae67abSDaisuke Matsuda 	}
2512fae67abSDaisuke Matsuda 
2522fae67abSDaisuke Matsuda 	err = rxe_odp_map_range_and_lock(mr, iova, length, flags);
2532fae67abSDaisuke Matsuda 	if (err)
2542fae67abSDaisuke Matsuda 		return err;
2552fae67abSDaisuke Matsuda 
2562fae67abSDaisuke Matsuda 	err =  __rxe_odp_mr_copy(mr, iova, addr, length, dir);
2572fae67abSDaisuke Matsuda 
2582fae67abSDaisuke Matsuda 	mutex_unlock(&umem_odp->umem_mutex);
2592fae67abSDaisuke Matsuda 
2602fae67abSDaisuke Matsuda 	return err;
2612fae67abSDaisuke Matsuda }
262b55e9d29SDaisuke Matsuda 
rxe_odp_do_atomic_op(struct rxe_mr * mr,u64 iova,int opcode,u64 compare,u64 swap_add,u64 * orig_val)26329610226SDaisuke Matsuda static enum resp_states rxe_odp_do_atomic_op(struct rxe_mr *mr, u64 iova,
26429610226SDaisuke Matsuda 					     int opcode, u64 compare,
26529610226SDaisuke Matsuda 					     u64 swap_add, u64 *orig_val)
266b55e9d29SDaisuke Matsuda {
267b55e9d29SDaisuke Matsuda 	struct ib_umem_odp *umem_odp = to_ib_umem_odp(mr->umem);
268b55e9d29SDaisuke Matsuda 	unsigned int page_offset;
269b55e9d29SDaisuke Matsuda 	struct page *page;
270b55e9d29SDaisuke Matsuda 	unsigned int idx;
271b55e9d29SDaisuke Matsuda 	u64 value;
272b55e9d29SDaisuke Matsuda 	u64 *va;
273b55e9d29SDaisuke Matsuda 	int err;
274b55e9d29SDaisuke Matsuda 
275b55e9d29SDaisuke Matsuda 	if (unlikely(mr->state != RXE_MR_STATE_VALID)) {
276b55e9d29SDaisuke Matsuda 		rxe_dbg_mr(mr, "mr not in valid state\n");
277b55e9d29SDaisuke Matsuda 		return RESPST_ERR_RKEY_VIOLATION;
278b55e9d29SDaisuke Matsuda 	}
279b55e9d29SDaisuke Matsuda 
280b55e9d29SDaisuke Matsuda 	err = mr_check_range(mr, iova, sizeof(value));
281b55e9d29SDaisuke Matsuda 	if (err) {
282b55e9d29SDaisuke Matsuda 		rxe_dbg_mr(mr, "iova out of range\n");
283b55e9d29SDaisuke Matsuda 		return RESPST_ERR_RKEY_VIOLATION;
284b55e9d29SDaisuke Matsuda 	}
285b55e9d29SDaisuke Matsuda 
2866703cb3dSDaisuke Matsuda 	idx = rxe_odp_iova_to_index(umem_odp, iova);
2876703cb3dSDaisuke Matsuda 	page_offset = rxe_odp_iova_to_page_offset(umem_odp, iova);
2881efe8c06SLeon Romanovsky 	page = hmm_pfn_to_page(umem_odp->map.pfn_list[idx]);
289b55e9d29SDaisuke Matsuda 	if (!page)
290b55e9d29SDaisuke Matsuda 		return RESPST_ERR_RKEY_VIOLATION;
291b55e9d29SDaisuke Matsuda 
292b55e9d29SDaisuke Matsuda 	if (unlikely(page_offset & 0x7)) {
293b55e9d29SDaisuke Matsuda 		rxe_dbg_mr(mr, "iova not aligned\n");
294b55e9d29SDaisuke Matsuda 		return RESPST_ERR_MISALIGNED_ATOMIC;
295b55e9d29SDaisuke Matsuda 	}
296b55e9d29SDaisuke Matsuda 
297b55e9d29SDaisuke Matsuda 	va = kmap_local_page(page);
298b55e9d29SDaisuke Matsuda 
299b55e9d29SDaisuke Matsuda 	spin_lock_bh(&atomic_ops_lock);
300b55e9d29SDaisuke Matsuda 	value = *orig_val = va[page_offset >> 3];
301b55e9d29SDaisuke Matsuda 
302b55e9d29SDaisuke Matsuda 	if (opcode == IB_OPCODE_RC_COMPARE_SWAP) {
303b55e9d29SDaisuke Matsuda 		if (value == compare)
304b55e9d29SDaisuke Matsuda 			va[page_offset >> 3] = swap_add;
305b55e9d29SDaisuke Matsuda 	} else {
306b55e9d29SDaisuke Matsuda 		value += swap_add;
307b55e9d29SDaisuke Matsuda 		va[page_offset >> 3] = value;
308b55e9d29SDaisuke Matsuda 	}
309b55e9d29SDaisuke Matsuda 	spin_unlock_bh(&atomic_ops_lock);
310b55e9d29SDaisuke Matsuda 
311b55e9d29SDaisuke Matsuda 	kunmap_local(va);
312b55e9d29SDaisuke Matsuda 
31329610226SDaisuke Matsuda 	return RESPST_NONE;
314b55e9d29SDaisuke Matsuda }
315b55e9d29SDaisuke Matsuda 
rxe_odp_atomic_op(struct rxe_mr * mr,u64 iova,int opcode,u64 compare,u64 swap_add,u64 * orig_val)31629610226SDaisuke Matsuda enum resp_states rxe_odp_atomic_op(struct rxe_mr *mr, u64 iova, int opcode,
317b55e9d29SDaisuke Matsuda 				   u64 compare, u64 swap_add, u64 *orig_val)
318b55e9d29SDaisuke Matsuda {
319b55e9d29SDaisuke Matsuda 	struct ib_umem_odp *umem_odp = to_ib_umem_odp(mr->umem);
320b55e9d29SDaisuke Matsuda 	int err;
321b55e9d29SDaisuke Matsuda 
3220a924decSDaisuke Matsuda 	err = rxe_odp_map_range_and_lock(mr, iova, sizeof(char),
3230a924decSDaisuke Matsuda 					 RXE_PAGEFAULT_DEFAULT);
324b55e9d29SDaisuke Matsuda 	if (err < 0)
32586ab0536SDaisuke Matsuda 		return RESPST_ERR_RKEY_VIOLATION;
326b55e9d29SDaisuke Matsuda 
327b55e9d29SDaisuke Matsuda 	err = rxe_odp_do_atomic_op(mr, iova, opcode, compare, swap_add,
328b55e9d29SDaisuke Matsuda 				   orig_val);
329b55e9d29SDaisuke Matsuda 	mutex_unlock(&umem_odp->umem_mutex);
330b55e9d29SDaisuke Matsuda 
331b55e9d29SDaisuke Matsuda 	return err;
332b55e9d29SDaisuke Matsuda }
3336703cb3dSDaisuke Matsuda 
rxe_odp_flush_pmem_iova(struct rxe_mr * mr,u64 iova,unsigned int length)3346703cb3dSDaisuke Matsuda int rxe_odp_flush_pmem_iova(struct rxe_mr *mr, u64 iova,
3356703cb3dSDaisuke Matsuda 			    unsigned int length)
3366703cb3dSDaisuke Matsuda {
3376703cb3dSDaisuke Matsuda 	struct ib_umem_odp *umem_odp = to_ib_umem_odp(mr->umem);
3386703cb3dSDaisuke Matsuda 	unsigned int page_offset;
3396703cb3dSDaisuke Matsuda 	unsigned long index;
3406703cb3dSDaisuke Matsuda 	struct page *page;
3416703cb3dSDaisuke Matsuda 	unsigned int bytes;
3426703cb3dSDaisuke Matsuda 	int err;
3436703cb3dSDaisuke Matsuda 	u8 *va;
3446703cb3dSDaisuke Matsuda 
3456703cb3dSDaisuke Matsuda 	err = rxe_odp_map_range_and_lock(mr, iova, length,
3466703cb3dSDaisuke Matsuda 					 RXE_PAGEFAULT_DEFAULT);
3476703cb3dSDaisuke Matsuda 	if (err)
3486703cb3dSDaisuke Matsuda 		return err;
3496703cb3dSDaisuke Matsuda 
3506703cb3dSDaisuke Matsuda 	while (length > 0) {
3516703cb3dSDaisuke Matsuda 		index = rxe_odp_iova_to_index(umem_odp, iova);
3526703cb3dSDaisuke Matsuda 		page_offset = rxe_odp_iova_to_page_offset(umem_odp, iova);
3536703cb3dSDaisuke Matsuda 
3541efe8c06SLeon Romanovsky 		page = hmm_pfn_to_page(umem_odp->map.pfn_list[index]);
3556703cb3dSDaisuke Matsuda 		if (!page) {
3566703cb3dSDaisuke Matsuda 			mutex_unlock(&umem_odp->umem_mutex);
3576703cb3dSDaisuke Matsuda 			return -EFAULT;
3586703cb3dSDaisuke Matsuda 		}
3596703cb3dSDaisuke Matsuda 
3606703cb3dSDaisuke Matsuda 		bytes = min_t(unsigned int, length,
3616703cb3dSDaisuke Matsuda 			      mr_page_size(mr) - page_offset);
3626703cb3dSDaisuke Matsuda 
3636703cb3dSDaisuke Matsuda 		va = kmap_local_page(page);
3646703cb3dSDaisuke Matsuda 		arch_wb_cache_pmem(va + page_offset, bytes);
3656703cb3dSDaisuke Matsuda 		kunmap_local(va);
3666703cb3dSDaisuke Matsuda 
3676703cb3dSDaisuke Matsuda 		length -= bytes;
3686703cb3dSDaisuke Matsuda 		iova += bytes;
3696703cb3dSDaisuke Matsuda 		page_offset = 0;
3706703cb3dSDaisuke Matsuda 	}
3716703cb3dSDaisuke Matsuda 
3726703cb3dSDaisuke Matsuda 	mutex_unlock(&umem_odp->umem_mutex);
3736703cb3dSDaisuke Matsuda 
3746703cb3dSDaisuke Matsuda 	return 0;
3756703cb3dSDaisuke Matsuda }
376b84001adSDaisuke Matsuda 
rxe_odp_do_atomic_write(struct rxe_mr * mr,u64 iova,u64 value)377b84001adSDaisuke Matsuda enum resp_states rxe_odp_do_atomic_write(struct rxe_mr *mr, u64 iova, u64 value)
378b84001adSDaisuke Matsuda {
379b84001adSDaisuke Matsuda 	struct ib_umem_odp *umem_odp = to_ib_umem_odp(mr->umem);
380b84001adSDaisuke Matsuda 	unsigned int page_offset;
381b84001adSDaisuke Matsuda 	unsigned long index;
382b84001adSDaisuke Matsuda 	struct page *page;
383b84001adSDaisuke Matsuda 	int err;
384b84001adSDaisuke Matsuda 	u64 *va;
385b84001adSDaisuke Matsuda 
386b84001adSDaisuke Matsuda 	/* See IBA oA19-28 */
387b84001adSDaisuke Matsuda 	err = mr_check_range(mr, iova, sizeof(value));
388b84001adSDaisuke Matsuda 	if (unlikely(err)) {
389b84001adSDaisuke Matsuda 		rxe_dbg_mr(mr, "iova out of range\n");
390b84001adSDaisuke Matsuda 		return RESPST_ERR_RKEY_VIOLATION;
391b84001adSDaisuke Matsuda 	}
392b84001adSDaisuke Matsuda 
393b84001adSDaisuke Matsuda 	err = rxe_odp_map_range_and_lock(mr, iova, sizeof(value),
394b84001adSDaisuke Matsuda 					 RXE_PAGEFAULT_DEFAULT);
395b84001adSDaisuke Matsuda 	if (err)
396b84001adSDaisuke Matsuda 		return RESPST_ERR_RKEY_VIOLATION;
397b84001adSDaisuke Matsuda 
398b84001adSDaisuke Matsuda 	page_offset = rxe_odp_iova_to_page_offset(umem_odp, iova);
399b84001adSDaisuke Matsuda 	index = rxe_odp_iova_to_index(umem_odp, iova);
4001efe8c06SLeon Romanovsky 	page = hmm_pfn_to_page(umem_odp->map.pfn_list[index]);
401b84001adSDaisuke Matsuda 	if (!page) {
402b84001adSDaisuke Matsuda 		mutex_unlock(&umem_odp->umem_mutex);
403b84001adSDaisuke Matsuda 		return RESPST_ERR_RKEY_VIOLATION;
404b84001adSDaisuke Matsuda 	}
405b84001adSDaisuke Matsuda 	/* See IBA A19.4.2 */
406b84001adSDaisuke Matsuda 	if (unlikely(page_offset & 0x7)) {
407b84001adSDaisuke Matsuda 		mutex_unlock(&umem_odp->umem_mutex);
408b84001adSDaisuke Matsuda 		rxe_dbg_mr(mr, "misaligned address\n");
409b84001adSDaisuke Matsuda 		return RESPST_ERR_MISALIGNED_ATOMIC;
410b84001adSDaisuke Matsuda 	}
411b84001adSDaisuke Matsuda 
412b84001adSDaisuke Matsuda 	va = kmap_local_page(page);
413b84001adSDaisuke Matsuda 	/* Do atomic write after all prior operations have completed */
414b84001adSDaisuke Matsuda 	smp_store_release(&va[page_offset >> 3], value);
415b84001adSDaisuke Matsuda 	kunmap_local(va);
416b84001adSDaisuke Matsuda 
417b84001adSDaisuke Matsuda 	mutex_unlock(&umem_odp->umem_mutex);
418b84001adSDaisuke Matsuda 
419b84001adSDaisuke Matsuda 	return RESPST_NONE;
420b84001adSDaisuke Matsuda }
421