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