xref: /linux-6.15/net/smc/smc_loopback.c (revision f7a22071)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  *  Shared Memory Communications Direct over loopback-ism device.
4  *
5  *  Functions for loopback-ism device.
6  *
7  *  Copyright (c) 2024, Alibaba Inc.
8  *
9  *  Author: Wen Gu <[email protected]>
10  *          Tony Lu <[email protected]>
11  *
12  */
13 
14 #include <linux/device.h>
15 #include <linux/types.h>
16 #include <net/smc.h>
17 
18 #include "smc_cdc.h"
19 #include "smc_ism.h"
20 #include "smc_loopback.h"
21 
22 #define SMC_LO_V2_CAPABLE	0x1 /* loopback-ism acts as ISMv2 */
23 #define SMC_DMA_ADDR_INVALID	(~(dma_addr_t)0)
24 
25 static const char smc_lo_dev_name[] = "loopback-ism";
26 static struct smc_lo_dev *lo_dev;
27 
28 static void smc_lo_generate_ids(struct smc_lo_dev *ldev)
29 {
30 	struct smcd_gid *lgid = &ldev->local_gid;
31 	uuid_t uuid;
32 
33 	uuid_gen(&uuid);
34 	memcpy(&lgid->gid, &uuid, sizeof(lgid->gid));
35 	memcpy(&lgid->gid_ext, (u8 *)&uuid + sizeof(lgid->gid),
36 	       sizeof(lgid->gid_ext));
37 
38 	ldev->chid = SMC_LO_RESERVED_CHID;
39 }
40 
41 static int smc_lo_query_rgid(struct smcd_dev *smcd, struct smcd_gid *rgid,
42 			     u32 vid_valid, u32 vid)
43 {
44 	struct smc_lo_dev *ldev = smcd->priv;
45 
46 	/* rgid should be the same as lgid */
47 	if (!ldev || rgid->gid != ldev->local_gid.gid ||
48 	    rgid->gid_ext != ldev->local_gid.gid_ext)
49 		return -ENETUNREACH;
50 	return 0;
51 }
52 
53 static int smc_lo_register_dmb(struct smcd_dev *smcd, struct smcd_dmb *dmb,
54 			       void *client_priv)
55 {
56 	struct smc_lo_dmb_node *dmb_node, *tmp_node;
57 	struct smc_lo_dev *ldev = smcd->priv;
58 	int sba_idx, rc;
59 
60 	/* check space for new dmb */
61 	for_each_clear_bit(sba_idx, ldev->sba_idx_mask, SMC_LO_MAX_DMBS) {
62 		if (!test_and_set_bit(sba_idx, ldev->sba_idx_mask))
63 			break;
64 	}
65 	if (sba_idx == SMC_LO_MAX_DMBS)
66 		return -ENOSPC;
67 
68 	dmb_node = kzalloc(sizeof(*dmb_node), GFP_KERNEL);
69 	if (!dmb_node) {
70 		rc = -ENOMEM;
71 		goto err_bit;
72 	}
73 
74 	dmb_node->sba_idx = sba_idx;
75 	dmb_node->len = dmb->dmb_len;
76 	dmb_node->cpu_addr = kzalloc(dmb_node->len, GFP_KERNEL |
77 				     __GFP_NOWARN | __GFP_NORETRY |
78 				     __GFP_NOMEMALLOC);
79 	if (!dmb_node->cpu_addr) {
80 		rc = -ENOMEM;
81 		goto err_node;
82 	}
83 	dmb_node->dma_addr = SMC_DMA_ADDR_INVALID;
84 
85 again:
86 	/* add new dmb into hash table */
87 	get_random_bytes(&dmb_node->token, sizeof(dmb_node->token));
88 	write_lock_bh(&ldev->dmb_ht_lock);
89 	hash_for_each_possible(ldev->dmb_ht, tmp_node, list, dmb_node->token) {
90 		if (tmp_node->token == dmb_node->token) {
91 			write_unlock_bh(&ldev->dmb_ht_lock);
92 			goto again;
93 		}
94 	}
95 	hash_add(ldev->dmb_ht, &dmb_node->list, dmb_node->token);
96 	write_unlock_bh(&ldev->dmb_ht_lock);
97 
98 	dmb->sba_idx = dmb_node->sba_idx;
99 	dmb->dmb_tok = dmb_node->token;
100 	dmb->cpu_addr = dmb_node->cpu_addr;
101 	dmb->dma_addr = dmb_node->dma_addr;
102 	dmb->dmb_len = dmb_node->len;
103 
104 	return 0;
105 
106 err_node:
107 	kfree(dmb_node);
108 err_bit:
109 	clear_bit(sba_idx, ldev->sba_idx_mask);
110 	return rc;
111 }
112 
113 static int smc_lo_unregister_dmb(struct smcd_dev *smcd, struct smcd_dmb *dmb)
114 {
115 	struct smc_lo_dmb_node *dmb_node = NULL, *tmp_node;
116 	struct smc_lo_dev *ldev = smcd->priv;
117 
118 	/* remove dmb from hash table */
119 	write_lock_bh(&ldev->dmb_ht_lock);
120 	hash_for_each_possible(ldev->dmb_ht, tmp_node, list, dmb->dmb_tok) {
121 		if (tmp_node->token == dmb->dmb_tok) {
122 			dmb_node = tmp_node;
123 			break;
124 		}
125 	}
126 	if (!dmb_node) {
127 		write_unlock_bh(&ldev->dmb_ht_lock);
128 		return -EINVAL;
129 	}
130 	hash_del(&dmb_node->list);
131 	write_unlock_bh(&ldev->dmb_ht_lock);
132 
133 	clear_bit(dmb_node->sba_idx, ldev->sba_idx_mask);
134 	kfree(dmb_node->cpu_addr);
135 	kfree(dmb_node);
136 
137 	return 0;
138 }
139 
140 static int smc_lo_move_data(struct smcd_dev *smcd, u64 dmb_tok,
141 			    unsigned int idx, bool sf, unsigned int offset,
142 			    void *data, unsigned int size)
143 {
144 	struct smc_lo_dmb_node *rmb_node = NULL, *tmp_node;
145 	struct smc_lo_dev *ldev = smcd->priv;
146 	struct smc_connection *conn;
147 
148 	read_lock_bh(&ldev->dmb_ht_lock);
149 	hash_for_each_possible(ldev->dmb_ht, tmp_node, list, dmb_tok) {
150 		if (tmp_node->token == dmb_tok) {
151 			rmb_node = tmp_node;
152 			break;
153 		}
154 	}
155 	if (!rmb_node) {
156 		read_unlock_bh(&ldev->dmb_ht_lock);
157 		return -EINVAL;
158 	}
159 	memcpy((char *)rmb_node->cpu_addr + offset, data, size);
160 	read_unlock_bh(&ldev->dmb_ht_lock);
161 
162 	if (sf) {
163 		conn = smcd->conn[rmb_node->sba_idx];
164 		if (conn && !conn->killed)
165 			tasklet_schedule(&conn->rx_tsklet);
166 		else
167 			return -EPIPE;
168 	}
169 	return 0;
170 }
171 
172 static int smc_lo_supports_v2(void)
173 {
174 	return SMC_LO_V2_CAPABLE;
175 }
176 
177 static void smc_lo_get_local_gid(struct smcd_dev *smcd,
178 				 struct smcd_gid *smcd_gid)
179 {
180 	struct smc_lo_dev *ldev = smcd->priv;
181 
182 	smcd_gid->gid = ldev->local_gid.gid;
183 	smcd_gid->gid_ext = ldev->local_gid.gid_ext;
184 }
185 
186 static u16 smc_lo_get_chid(struct smcd_dev *smcd)
187 {
188 	return ((struct smc_lo_dev *)smcd->priv)->chid;
189 }
190 
191 static struct device *smc_lo_get_dev(struct smcd_dev *smcd)
192 {
193 	return &((struct smc_lo_dev *)smcd->priv)->dev;
194 }
195 
196 static const struct smcd_ops lo_ops = {
197 	.query_remote_gid = smc_lo_query_rgid,
198 	.register_dmb = smc_lo_register_dmb,
199 	.unregister_dmb = smc_lo_unregister_dmb,
200 	.add_vlan_id		= NULL,
201 	.del_vlan_id		= NULL,
202 	.set_vlan_required	= NULL,
203 	.reset_vlan_required	= NULL,
204 	.signal_event		= NULL,
205 	.move_data = smc_lo_move_data,
206 	.supports_v2 = smc_lo_supports_v2,
207 	.get_local_gid = smc_lo_get_local_gid,
208 	.get_chid = smc_lo_get_chid,
209 	.get_dev = smc_lo_get_dev,
210 };
211 
212 static struct smcd_dev *smcd_lo_alloc_dev(const struct smcd_ops *ops,
213 					  int max_dmbs)
214 {
215 	struct smcd_dev *smcd;
216 
217 	smcd = kzalloc(sizeof(*smcd), GFP_KERNEL);
218 	if (!smcd)
219 		return NULL;
220 
221 	smcd->conn = kcalloc(max_dmbs, sizeof(struct smc_connection *),
222 			     GFP_KERNEL);
223 	if (!smcd->conn)
224 		goto out_smcd;
225 
226 	smcd->ops = ops;
227 
228 	spin_lock_init(&smcd->lock);
229 	spin_lock_init(&smcd->lgr_lock);
230 	INIT_LIST_HEAD(&smcd->vlan);
231 	INIT_LIST_HEAD(&smcd->lgr_list);
232 	init_waitqueue_head(&smcd->lgrs_deleted);
233 	return smcd;
234 
235 out_smcd:
236 	kfree(smcd);
237 	return NULL;
238 }
239 
240 static int smcd_lo_register_dev(struct smc_lo_dev *ldev)
241 {
242 	struct smcd_dev *smcd;
243 
244 	smcd = smcd_lo_alloc_dev(&lo_ops, SMC_LO_MAX_DMBS);
245 	if (!smcd)
246 		return -ENOMEM;
247 	ldev->smcd = smcd;
248 	smcd->priv = ldev;
249 
250 	/* TODO:
251 	 * register loopback-ism to smcd_dev list.
252 	 */
253 	return 0;
254 }
255 
256 static void smcd_lo_unregister_dev(struct smc_lo_dev *ldev)
257 {
258 	struct smcd_dev *smcd = ldev->smcd;
259 
260 	/* TODO:
261 	 * unregister loopback-ism from smcd_dev list.
262 	 */
263 	kfree(smcd->conn);
264 	kfree(smcd);
265 }
266 
267 static int smc_lo_dev_init(struct smc_lo_dev *ldev)
268 {
269 	smc_lo_generate_ids(ldev);
270 	rwlock_init(&ldev->dmb_ht_lock);
271 	hash_init(ldev->dmb_ht);
272 	return smcd_lo_register_dev(ldev);
273 }
274 
275 static void smc_lo_dev_exit(struct smc_lo_dev *ldev)
276 {
277 	smcd_lo_unregister_dev(ldev);
278 }
279 
280 static void smc_lo_dev_release(struct device *dev)
281 {
282 	struct smc_lo_dev *ldev =
283 		container_of(dev, struct smc_lo_dev, dev);
284 
285 	kfree(ldev);
286 }
287 
288 static int smc_lo_dev_probe(void)
289 {
290 	struct smc_lo_dev *ldev;
291 	int ret;
292 
293 	ldev = kzalloc(sizeof(*ldev), GFP_KERNEL);
294 	if (!ldev)
295 		return -ENOMEM;
296 
297 	ldev->dev.parent = NULL;
298 	ldev->dev.release = smc_lo_dev_release;
299 	device_initialize(&ldev->dev);
300 	dev_set_name(&ldev->dev, smc_lo_dev_name);
301 
302 	ret = smc_lo_dev_init(ldev);
303 	if (ret)
304 		goto free_dev;
305 
306 	lo_dev = ldev; /* global loopback device */
307 	return 0;
308 
309 free_dev:
310 	put_device(&ldev->dev);
311 	return ret;
312 }
313 
314 static void smc_lo_dev_remove(void)
315 {
316 	if (!lo_dev)
317 		return;
318 
319 	smc_lo_dev_exit(lo_dev);
320 	put_device(&lo_dev->dev); /* device_initialize in smc_lo_dev_probe */
321 }
322 
323 int smc_loopback_init(void)
324 {
325 	return smc_lo_dev_probe();
326 }
327 
328 void smc_loopback_exit(void)
329 {
330 	smc_lo_dev_remove();
331 }
332