1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2/*
3 * Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved
4 */
5
6#include "rdma_core.h"
7#include "uverbs.h"
8#include <rdma/uverbs_std_types.h>
9#include "restrack.h"
10
11static int uverbs_free_dmah(struct ib_uobject *uobject,
12 enum rdma_remove_reason why,
13 struct uverbs_attr_bundle *attrs)
14{
15 struct ib_dmah *dmah = uobject->object;
16 int ret;
17
18 if (atomic_read(v: &dmah->usecnt))
19 return -EBUSY;
20
21 ret = dmah->device->ops.dealloc_dmah(dmah, attrs);
22 if (ret)
23 return ret;
24
25 rdma_restrack_del(res: &dmah->res);
26 kfree(objp: dmah);
27 return 0;
28}
29
30static int UVERBS_HANDLER(UVERBS_METHOD_DMAH_ALLOC)(
31 struct uverbs_attr_bundle *attrs)
32{
33 struct ib_uobject *uobj =
34 uverbs_attr_get(attrs_bundle: attrs, idx: UVERBS_ATTR_ALLOC_DMAH_HANDLE)
35 ->obj_attr.uobject;
36 struct ib_device *ib_dev = attrs->context->device;
37 struct ib_dmah *dmah;
38 int ret;
39
40 dmah = rdma_zalloc_drv_obj(ib_dev, ib_dmah);
41 if (!dmah)
42 return -ENOMEM;
43
44 if (uverbs_attr_is_valid(attrs_bundle: attrs, idx: UVERBS_ATTR_ALLOC_DMAH_CPU_ID)) {
45 ret = uverbs_copy_from(&dmah->cpu_id, attrs,
46 UVERBS_ATTR_ALLOC_DMAH_CPU_ID);
47 if (ret)
48 goto err;
49
50 if (!cpumask_test_cpu(cpu: dmah->cpu_id, current->cpus_ptr)) {
51 ret = -EPERM;
52 goto err;
53 }
54
55 dmah->valid_fields |= BIT(IB_DMAH_CPU_ID_EXISTS);
56 }
57
58 if (uverbs_attr_is_valid(attrs_bundle: attrs, idx: UVERBS_ATTR_ALLOC_DMAH_TPH_MEM_TYPE)) {
59 dmah->mem_type = uverbs_attr_get_enum_id(attrs_bundle: attrs,
60 idx: UVERBS_ATTR_ALLOC_DMAH_TPH_MEM_TYPE);
61 dmah->valid_fields |= BIT(IB_DMAH_MEM_TYPE_EXISTS);
62 }
63
64 if (uverbs_attr_is_valid(attrs_bundle: attrs, idx: UVERBS_ATTR_ALLOC_DMAH_PH)) {
65 ret = uverbs_copy_from(&dmah->ph, attrs,
66 UVERBS_ATTR_ALLOC_DMAH_PH);
67 if (ret)
68 goto err;
69
70 /* Per PCIe spec 6.2-1.0, only the lowest two bits are applicable */
71 if (dmah->ph & 0xFC) {
72 ret = -EINVAL;
73 goto err;
74 }
75
76 dmah->valid_fields |= BIT(IB_DMAH_PH_EXISTS);
77 }
78
79 dmah->device = ib_dev;
80 dmah->uobject = uobj;
81 atomic_set(v: &dmah->usecnt, i: 0);
82
83 rdma_restrack_new(res: &dmah->res, type: RDMA_RESTRACK_DMAH);
84 rdma_restrack_set_name(res: &dmah->res, NULL);
85
86 ret = ib_dev->ops.alloc_dmah(dmah, attrs);
87 if (ret) {
88 rdma_restrack_put(res: &dmah->res);
89 goto err;
90 }
91
92 uobj->object = dmah;
93 rdma_restrack_add(res: &dmah->res);
94 uverbs_finalize_uobj_create(attrs_bundle: attrs, idx: UVERBS_ATTR_ALLOC_DMAH_HANDLE);
95 return 0;
96err:
97 kfree(objp: dmah);
98 return ret;
99}
100
101static const struct uverbs_attr_spec uverbs_dmah_mem_type[] = {
102 [TPH_MEM_TYPE_VM] = {
103 .type = UVERBS_ATTR_TYPE_PTR_IN,
104 UVERBS_ATTR_NO_DATA(),
105 },
106 [TPH_MEM_TYPE_PM] = {
107 .type = UVERBS_ATTR_TYPE_PTR_IN,
108 UVERBS_ATTR_NO_DATA(),
109 },
110};
111
112DECLARE_UVERBS_NAMED_METHOD(
113 UVERBS_METHOD_DMAH_ALLOC,
114 UVERBS_ATTR_IDR(UVERBS_ATTR_ALLOC_DMAH_HANDLE,
115 UVERBS_OBJECT_DMAH,
116 UVERBS_ACCESS_NEW,
117 UA_MANDATORY),
118 UVERBS_ATTR_PTR_IN(UVERBS_ATTR_ALLOC_DMAH_CPU_ID,
119 UVERBS_ATTR_TYPE(u32),
120 UA_OPTIONAL),
121 UVERBS_ATTR_ENUM_IN(UVERBS_ATTR_ALLOC_DMAH_TPH_MEM_TYPE,
122 uverbs_dmah_mem_type,
123 UA_OPTIONAL),
124 UVERBS_ATTR_PTR_IN(UVERBS_ATTR_ALLOC_DMAH_PH,
125 UVERBS_ATTR_TYPE(u8),
126 UA_OPTIONAL));
127
128DECLARE_UVERBS_NAMED_METHOD_DESTROY(
129 UVERBS_METHOD_DMAH_FREE,
130 UVERBS_ATTR_IDR(UVERBS_ATTR_FREE_DMA_HANDLE,
131 UVERBS_OBJECT_DMAH,
132 UVERBS_ACCESS_DESTROY,
133 UA_MANDATORY));
134
135DECLARE_UVERBS_NAMED_OBJECT(UVERBS_OBJECT_DMAH,
136 UVERBS_TYPE_ALLOC_IDR(uverbs_free_dmah),
137 &UVERBS_METHOD(UVERBS_METHOD_DMAH_ALLOC),
138 &UVERBS_METHOD(UVERBS_METHOD_DMAH_FREE));
139
140const struct uapi_definition uverbs_def_obj_dmah[] = {
141 UAPI_DEF_CHAIN_OBJ_TREE_NAMED(UVERBS_OBJECT_DMAH,
142 UAPI_DEF_OBJ_NEEDS_FN(dealloc_dmah),
143 UAPI_DEF_OBJ_NEEDS_FN(alloc_dmah)),
144 {}
145};
146

source code of linux/drivers/infiniband/core/uverbs_std_types_dmah.c