xref: /linux-6.15/net/smc/smc_loopback.c (revision 45783ee8)
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_ism.h"
19 #include "smc_loopback.h"
20 
21 #define SMC_LO_V2_CAPABLE	0x1 /* loopback-ism acts as ISMv2 */
22 
23 static const char smc_lo_dev_name[] = "loopback-ism";
24 static struct smc_lo_dev *lo_dev;
25 
26 static void smc_lo_generate_ids(struct smc_lo_dev *ldev)
27 {
28 	struct smcd_gid *lgid = &ldev->local_gid;
29 	uuid_t uuid;
30 
31 	uuid_gen(&uuid);
32 	memcpy(&lgid->gid, &uuid, sizeof(lgid->gid));
33 	memcpy(&lgid->gid_ext, (u8 *)&uuid + sizeof(lgid->gid),
34 	       sizeof(lgid->gid_ext));
35 
36 	ldev->chid = SMC_LO_RESERVED_CHID;
37 }
38 
39 static int smc_lo_query_rgid(struct smcd_dev *smcd, struct smcd_gid *rgid,
40 			     u32 vid_valid, u32 vid)
41 {
42 	struct smc_lo_dev *ldev = smcd->priv;
43 
44 	/* rgid should be the same as lgid */
45 	if (!ldev || rgid->gid != ldev->local_gid.gid ||
46 	    rgid->gid_ext != ldev->local_gid.gid_ext)
47 		return -ENETUNREACH;
48 	return 0;
49 }
50 
51 static int smc_lo_supports_v2(void)
52 {
53 	return SMC_LO_V2_CAPABLE;
54 }
55 
56 static void smc_lo_get_local_gid(struct smcd_dev *smcd,
57 				 struct smcd_gid *smcd_gid)
58 {
59 	struct smc_lo_dev *ldev = smcd->priv;
60 
61 	smcd_gid->gid = ldev->local_gid.gid;
62 	smcd_gid->gid_ext = ldev->local_gid.gid_ext;
63 }
64 
65 static u16 smc_lo_get_chid(struct smcd_dev *smcd)
66 {
67 	return ((struct smc_lo_dev *)smcd->priv)->chid;
68 }
69 
70 static struct device *smc_lo_get_dev(struct smcd_dev *smcd)
71 {
72 	return &((struct smc_lo_dev *)smcd->priv)->dev;
73 }
74 
75 static const struct smcd_ops lo_ops = {
76 	.query_remote_gid = smc_lo_query_rgid,
77 	.register_dmb		= NULL,
78 	.unregister_dmb		= NULL,
79 	.add_vlan_id		= NULL,
80 	.del_vlan_id		= NULL,
81 	.set_vlan_required	= NULL,
82 	.reset_vlan_required	= NULL,
83 	.signal_event		= NULL,
84 	.move_data		= NULL,
85 	.supports_v2 = smc_lo_supports_v2,
86 	.get_local_gid = smc_lo_get_local_gid,
87 	.get_chid = smc_lo_get_chid,
88 	.get_dev = smc_lo_get_dev,
89 };
90 
91 static struct smcd_dev *smcd_lo_alloc_dev(const struct smcd_ops *ops,
92 					  int max_dmbs)
93 {
94 	struct smcd_dev *smcd;
95 
96 	smcd = kzalloc(sizeof(*smcd), GFP_KERNEL);
97 	if (!smcd)
98 		return NULL;
99 
100 	smcd->conn = kcalloc(max_dmbs, sizeof(struct smc_connection *),
101 			     GFP_KERNEL);
102 	if (!smcd->conn)
103 		goto out_smcd;
104 
105 	smcd->ops = ops;
106 
107 	spin_lock_init(&smcd->lock);
108 	spin_lock_init(&smcd->lgr_lock);
109 	INIT_LIST_HEAD(&smcd->vlan);
110 	INIT_LIST_HEAD(&smcd->lgr_list);
111 	init_waitqueue_head(&smcd->lgrs_deleted);
112 	return smcd;
113 
114 out_smcd:
115 	kfree(smcd);
116 	return NULL;
117 }
118 
119 static int smcd_lo_register_dev(struct smc_lo_dev *ldev)
120 {
121 	struct smcd_dev *smcd;
122 
123 	smcd = smcd_lo_alloc_dev(&lo_ops, SMC_LO_MAX_DMBS);
124 	if (!smcd)
125 		return -ENOMEM;
126 	ldev->smcd = smcd;
127 	smcd->priv = ldev;
128 
129 	/* TODO:
130 	 * register loopback-ism to smcd_dev list.
131 	 */
132 	return 0;
133 }
134 
135 static void smcd_lo_unregister_dev(struct smc_lo_dev *ldev)
136 {
137 	struct smcd_dev *smcd = ldev->smcd;
138 
139 	/* TODO:
140 	 * unregister loopback-ism from smcd_dev list.
141 	 */
142 	kfree(smcd->conn);
143 	kfree(smcd);
144 }
145 
146 static int smc_lo_dev_init(struct smc_lo_dev *ldev)
147 {
148 	smc_lo_generate_ids(ldev);
149 	return smcd_lo_register_dev(ldev);
150 }
151 
152 static void smc_lo_dev_exit(struct smc_lo_dev *ldev)
153 {
154 	smcd_lo_unregister_dev(ldev);
155 }
156 
157 static void smc_lo_dev_release(struct device *dev)
158 {
159 	struct smc_lo_dev *ldev =
160 		container_of(dev, struct smc_lo_dev, dev);
161 
162 	kfree(ldev);
163 }
164 
165 static int smc_lo_dev_probe(void)
166 {
167 	struct smc_lo_dev *ldev;
168 	int ret;
169 
170 	ldev = kzalloc(sizeof(*ldev), GFP_KERNEL);
171 	if (!ldev)
172 		return -ENOMEM;
173 
174 	ldev->dev.parent = NULL;
175 	ldev->dev.release = smc_lo_dev_release;
176 	device_initialize(&ldev->dev);
177 	dev_set_name(&ldev->dev, smc_lo_dev_name);
178 
179 	ret = smc_lo_dev_init(ldev);
180 	if (ret)
181 		goto free_dev;
182 
183 	lo_dev = ldev; /* global loopback device */
184 	return 0;
185 
186 free_dev:
187 	put_device(&ldev->dev);
188 	return ret;
189 }
190 
191 static void smc_lo_dev_remove(void)
192 {
193 	if (!lo_dev)
194 		return;
195 
196 	smc_lo_dev_exit(lo_dev);
197 	put_device(&lo_dev->dev); /* device_initialize in smc_lo_dev_probe */
198 }
199 
200 int smc_loopback_init(void)
201 {
202 	return smc_lo_dev_probe();
203 }
204 
205 void smc_loopback_exit(void)
206 {
207 	smc_lo_dev_remove();
208 }
209