1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * RDMA Transport Layer
4  *
5  * Copyright (c) 2014 - 2018 ProfitBricks GmbH. All rights reserved.
6  * Copyright (c) 2018 - 2019 1&1 IONOS Cloud GmbH. All rights reserved.
7  * Copyright (c) 2019 - 2020 1&1 IONOS SE. All rights reserved.
8  */
9 #undef pr_fmt
10 #define pr_fmt(fmt) KBUILD_MODNAME " L" __stringify(__LINE__) ": " fmt
11 
12 #include "rtrs-pri.h"
13 #include "rtrs-clt.h"
14 #include "rtrs-log.h"
15 
16 #define MIN_MAX_RECONN_ATT -1
17 #define MAX_MAX_RECONN_ATT 9999
18 
19 static void rtrs_clt_sess_release(struct kobject *kobj)
20 {
21 	struct rtrs_clt_sess *sess;
22 
23 	sess = container_of(kobj, struct rtrs_clt_sess, kobj);
24 
25 	free_sess(sess);
26 }
27 
28 static struct kobj_type ktype_sess = {
29 	.sysfs_ops = &kobj_sysfs_ops,
30 	.release = rtrs_clt_sess_release
31 };
32 
33 static void rtrs_clt_sess_stats_release(struct kobject *kobj)
34 {
35 	struct rtrs_clt_stats *stats;
36 
37 	stats = container_of(kobj, struct rtrs_clt_stats, kobj_stats);
38 
39 	free_percpu(stats->pcpu_stats);
40 
41 	kfree(stats);
42 }
43 
44 static struct kobj_type ktype_stats = {
45 	.sysfs_ops = &kobj_sysfs_ops,
46 	.release = rtrs_clt_sess_stats_release,
47 };
48 
49 static ssize_t max_reconnect_attempts_show(struct device *dev,
50 					   struct device_attribute *attr,
51 					   char *page)
52 {
53 	struct rtrs_clt *clt = container_of(dev, struct rtrs_clt, dev);
54 
55 	return sprintf(page, "%d\n", rtrs_clt_get_max_reconnect_attempts(clt));
56 }
57 
58 static ssize_t max_reconnect_attempts_store(struct device *dev,
59 					    struct device_attribute *attr,
60 					    const char *buf,
61 					    size_t count)
62 {
63 	int value;
64 	int ret;
65 	struct rtrs_clt *clt  = container_of(dev, struct rtrs_clt, dev);
66 
67 	ret = kstrtoint(buf, 10, &value);
68 	if (ret) {
69 		rtrs_err(clt, "%s: failed to convert string '%s' to int\n",
70 			  attr->attr.name, buf);
71 		return ret;
72 	}
73 	if (value > MAX_MAX_RECONN_ATT ||
74 		     value < MIN_MAX_RECONN_ATT) {
75 		rtrs_err(clt,
76 			  "%s: invalid range (provided: '%s', accepted: min: %d, max: %d)\n",
77 			  attr->attr.name, buf, MIN_MAX_RECONN_ATT,
78 			  MAX_MAX_RECONN_ATT);
79 		return -EINVAL;
80 	}
81 	rtrs_clt_set_max_reconnect_attempts(clt, value);
82 
83 	return count;
84 }
85 
86 static DEVICE_ATTR_RW(max_reconnect_attempts);
87 
88 static ssize_t mpath_policy_show(struct device *dev,
89 				 struct device_attribute *attr,
90 				 char *page)
91 {
92 	struct rtrs_clt *clt;
93 
94 	clt = container_of(dev, struct rtrs_clt, dev);
95 
96 	switch (clt->mp_policy) {
97 	case MP_POLICY_RR:
98 		return sprintf(page, "round-robin (RR: %d)\n", clt->mp_policy);
99 	case MP_POLICY_MIN_INFLIGHT:
100 		return sprintf(page, "min-inflight (MI: %d)\n", clt->mp_policy);
101 	default:
102 		return sprintf(page, "Unknown (%d)\n", clt->mp_policy);
103 	}
104 }
105 
106 static ssize_t mpath_policy_store(struct device *dev,
107 				  struct device_attribute *attr,
108 				  const char *buf,
109 				  size_t count)
110 {
111 	struct rtrs_clt *clt;
112 	int value;
113 	int ret;
114 
115 	clt = container_of(dev, struct rtrs_clt, dev);
116 
117 	ret = kstrtoint(buf, 10, &value);
118 	if (!ret && (value == MP_POLICY_RR ||
119 		     value == MP_POLICY_MIN_INFLIGHT)) {
120 		clt->mp_policy = value;
121 		return count;
122 	}
123 
124 	if (!strncasecmp(buf, "round-robin", 11) ||
125 	    !strncasecmp(buf, "rr", 2))
126 		clt->mp_policy = MP_POLICY_RR;
127 	else if (!strncasecmp(buf, "min-inflight", 12) ||
128 		 !strncasecmp(buf, "mi", 2))
129 		clt->mp_policy = MP_POLICY_MIN_INFLIGHT;
130 	else
131 		return -EINVAL;
132 
133 	return count;
134 }
135 
136 static DEVICE_ATTR_RW(mpath_policy);
137 
138 static ssize_t add_path_show(struct device *dev,
139 			     struct device_attribute *attr, char *page)
140 {
141 	return scnprintf(page, PAGE_SIZE,
142 			 "Usage: echo [<source addr>@]<destination addr> > %s\n\n*addr ::= [ ip:<ipv4|ipv6> | gid:<gid> ]\n",
143 			 attr->attr.name);
144 }
145 
146 static ssize_t add_path_store(struct device *dev,
147 			      struct device_attribute *attr,
148 			      const char *buf, size_t count)
149 {
150 	struct sockaddr_storage srcaddr, dstaddr;
151 	struct rtrs_addr addr = {
152 		.src = &srcaddr,
153 		.dst = &dstaddr
154 	};
155 	struct rtrs_clt *clt;
156 	const char *nl;
157 	size_t len;
158 	int err;
159 
160 	clt = container_of(dev, struct rtrs_clt, dev);
161 
162 	nl = strchr(buf, '\n');
163 	if (nl)
164 		len = nl - buf;
165 	else
166 		len = count;
167 	err = rtrs_addr_to_sockaddr(buf, len, clt->port, &addr);
168 	if (err)
169 		return -EINVAL;
170 
171 	err = rtrs_clt_create_path_from_sysfs(clt, &addr);
172 	if (err)
173 		return err;
174 
175 	return count;
176 }
177 
178 static DEVICE_ATTR_RW(add_path);
179 
180 static ssize_t rtrs_clt_state_show(struct kobject *kobj,
181 				    struct kobj_attribute *attr, char *page)
182 {
183 	struct rtrs_clt_sess *sess;
184 
185 	sess = container_of(kobj, struct rtrs_clt_sess, kobj);
186 	if (sess->state == RTRS_CLT_CONNECTED)
187 		return sprintf(page, "connected\n");
188 
189 	return sprintf(page, "disconnected\n");
190 }
191 
192 static struct kobj_attribute rtrs_clt_state_attr =
193 	__ATTR(state, 0444, rtrs_clt_state_show, NULL);
194 
195 static ssize_t rtrs_clt_reconnect_show(struct kobject *kobj,
196 					struct kobj_attribute *attr,
197 					char *page)
198 {
199 	return scnprintf(page, PAGE_SIZE, "Usage: echo 1 > %s\n",
200 			 attr->attr.name);
201 }
202 
203 static ssize_t rtrs_clt_reconnect_store(struct kobject *kobj,
204 					 struct kobj_attribute *attr,
205 					 const char *buf, size_t count)
206 {
207 	struct rtrs_clt_sess *sess;
208 	int ret;
209 
210 	sess = container_of(kobj, struct rtrs_clt_sess, kobj);
211 	if (!sysfs_streq(buf, "1")) {
212 		rtrs_err(sess->clt, "%s: unknown value: '%s'\n",
213 			  attr->attr.name, buf);
214 		return -EINVAL;
215 	}
216 	ret = rtrs_clt_reconnect_from_sysfs(sess);
217 	if (ret)
218 		return ret;
219 
220 	return count;
221 }
222 
223 static struct kobj_attribute rtrs_clt_reconnect_attr =
224 	__ATTR(reconnect, 0644, rtrs_clt_reconnect_show,
225 	       rtrs_clt_reconnect_store);
226 
227 static ssize_t rtrs_clt_disconnect_show(struct kobject *kobj,
228 					 struct kobj_attribute *attr,
229 					 char *page)
230 {
231 	return scnprintf(page, PAGE_SIZE, "Usage: echo 1 > %s\n",
232 			 attr->attr.name);
233 }
234 
235 static ssize_t rtrs_clt_disconnect_store(struct kobject *kobj,
236 					  struct kobj_attribute *attr,
237 					  const char *buf, size_t count)
238 {
239 	struct rtrs_clt_sess *sess;
240 	int ret;
241 
242 	sess = container_of(kobj, struct rtrs_clt_sess, kobj);
243 	if (!sysfs_streq(buf, "1")) {
244 		rtrs_err(sess->clt, "%s: unknown value: '%s'\n",
245 			  attr->attr.name, buf);
246 		return -EINVAL;
247 	}
248 	ret = rtrs_clt_disconnect_from_sysfs(sess);
249 	if (ret)
250 		return ret;
251 
252 	return count;
253 }
254 
255 static struct kobj_attribute rtrs_clt_disconnect_attr =
256 	__ATTR(disconnect, 0644, rtrs_clt_disconnect_show,
257 	       rtrs_clt_disconnect_store);
258 
259 static ssize_t rtrs_clt_remove_path_show(struct kobject *kobj,
260 					  struct kobj_attribute *attr,
261 					  char *page)
262 {
263 	return scnprintf(page, PAGE_SIZE, "Usage: echo 1 > %s\n",
264 			 attr->attr.name);
265 }
266 
267 static ssize_t rtrs_clt_remove_path_store(struct kobject *kobj,
268 					   struct kobj_attribute *attr,
269 					   const char *buf, size_t count)
270 {
271 	struct rtrs_clt_sess *sess;
272 	int ret;
273 
274 	sess = container_of(kobj, struct rtrs_clt_sess, kobj);
275 	if (!sysfs_streq(buf, "1")) {
276 		rtrs_err(sess->clt, "%s: unknown value: '%s'\n",
277 			  attr->attr.name, buf);
278 		return -EINVAL;
279 	}
280 	ret = rtrs_clt_remove_path_from_sysfs(sess, &attr->attr);
281 	if (ret)
282 		return ret;
283 
284 	return count;
285 }
286 
287 static struct kobj_attribute rtrs_clt_remove_path_attr =
288 	__ATTR(remove_path, 0644, rtrs_clt_remove_path_show,
289 	       rtrs_clt_remove_path_store);
290 
291 STAT_ATTR(struct rtrs_clt_stats, cpu_migration,
292 	  rtrs_clt_stats_migration_cnt_to_str,
293 	  rtrs_clt_reset_cpu_migr_stats);
294 
295 STAT_ATTR(struct rtrs_clt_stats, reconnects,
296 	  rtrs_clt_stats_reconnects_to_str,
297 	  rtrs_clt_reset_reconnects_stat);
298 
299 STAT_ATTR(struct rtrs_clt_stats, rdma,
300 	  rtrs_clt_stats_rdma_to_str,
301 	  rtrs_clt_reset_rdma_stats);
302 
303 STAT_ATTR(struct rtrs_clt_stats, reset_all,
304 	  rtrs_clt_reset_all_help,
305 	  rtrs_clt_reset_all_stats);
306 
307 static struct attribute *rtrs_clt_stats_attrs[] = {
308 	&cpu_migration_attr.attr,
309 	&reconnects_attr.attr,
310 	&rdma_attr.attr,
311 	&reset_all_attr.attr,
312 	NULL
313 };
314 
315 static struct attribute_group rtrs_clt_stats_attr_group = {
316 	.attrs = rtrs_clt_stats_attrs,
317 };
318 
319 static ssize_t rtrs_clt_hca_port_show(struct kobject *kobj,
320 				       struct kobj_attribute *attr,
321 				       char *page)
322 {
323 	struct rtrs_clt_sess *sess;
324 
325 	sess = container_of(kobj, typeof(*sess), kobj);
326 
327 	return scnprintf(page, PAGE_SIZE, "%u\n", sess->hca_port);
328 }
329 
330 static struct kobj_attribute rtrs_clt_hca_port_attr =
331 	__ATTR(hca_port, 0444, rtrs_clt_hca_port_show, NULL);
332 
333 static ssize_t rtrs_clt_hca_name_show(struct kobject *kobj,
334 				       struct kobj_attribute *attr,
335 				       char *page)
336 {
337 	struct rtrs_clt_sess *sess;
338 
339 	sess = container_of(kobj, struct rtrs_clt_sess, kobj);
340 
341 	return scnprintf(page, PAGE_SIZE, "%s\n", sess->hca_name);
342 }
343 
344 static struct kobj_attribute rtrs_clt_hca_name_attr =
345 	__ATTR(hca_name, 0444, rtrs_clt_hca_name_show, NULL);
346 
347 static ssize_t rtrs_clt_src_addr_show(struct kobject *kobj,
348 				       struct kobj_attribute *attr,
349 				       char *page)
350 {
351 	struct rtrs_clt_sess *sess;
352 	int cnt;
353 
354 	sess = container_of(kobj, struct rtrs_clt_sess, kobj);
355 	cnt = sockaddr_to_str((struct sockaddr *)&sess->s.src_addr,
356 			      page, PAGE_SIZE);
357 	return cnt + scnprintf(page + cnt, PAGE_SIZE - cnt, "\n");
358 }
359 
360 static struct kobj_attribute rtrs_clt_src_addr_attr =
361 	__ATTR(src_addr, 0444, rtrs_clt_src_addr_show, NULL);
362 
363 static ssize_t rtrs_clt_dst_addr_show(struct kobject *kobj,
364 				       struct kobj_attribute *attr,
365 				       char *page)
366 {
367 	struct rtrs_clt_sess *sess;
368 	int cnt;
369 
370 	sess = container_of(kobj, struct rtrs_clt_sess, kobj);
371 	cnt = sockaddr_to_str((struct sockaddr *)&sess->s.dst_addr,
372 			      page, PAGE_SIZE);
373 	return cnt + scnprintf(page + cnt, PAGE_SIZE - cnt, "\n");
374 }
375 
376 static struct kobj_attribute rtrs_clt_dst_addr_attr =
377 	__ATTR(dst_addr, 0444, rtrs_clt_dst_addr_show, NULL);
378 
379 static struct attribute *rtrs_clt_sess_attrs[] = {
380 	&rtrs_clt_hca_name_attr.attr,
381 	&rtrs_clt_hca_port_attr.attr,
382 	&rtrs_clt_src_addr_attr.attr,
383 	&rtrs_clt_dst_addr_attr.attr,
384 	&rtrs_clt_state_attr.attr,
385 	&rtrs_clt_reconnect_attr.attr,
386 	&rtrs_clt_disconnect_attr.attr,
387 	&rtrs_clt_remove_path_attr.attr,
388 	NULL,
389 };
390 
391 static struct attribute_group rtrs_clt_sess_attr_group = {
392 	.attrs = rtrs_clt_sess_attrs,
393 };
394 
395 int rtrs_clt_create_sess_files(struct rtrs_clt_sess *sess)
396 {
397 	struct rtrs_clt *clt = sess->clt;
398 	char str[NAME_MAX];
399 	int err, cnt;
400 
401 	cnt = sockaddr_to_str((struct sockaddr *)&sess->s.src_addr,
402 			      str, sizeof(str));
403 	cnt += scnprintf(str + cnt, sizeof(str) - cnt, "@");
404 	sockaddr_to_str((struct sockaddr *)&sess->s.dst_addr,
405 			str + cnt, sizeof(str) - cnt);
406 
407 	err = kobject_init_and_add(&sess->kobj, &ktype_sess, clt->kobj_paths,
408 				   "%s", str);
409 	if (err) {
410 		pr_err("kobject_init_and_add: %d\n", err);
411 		return err;
412 	}
413 	err = sysfs_create_group(&sess->kobj, &rtrs_clt_sess_attr_group);
414 	if (err) {
415 		pr_err("sysfs_create_group(): %d\n", err);
416 		goto put_kobj;
417 	}
418 	err = kobject_init_and_add(&sess->stats->kobj_stats, &ktype_stats,
419 				   &sess->kobj, "stats");
420 	if (err) {
421 		pr_err("kobject_init_and_add: %d\n", err);
422 		goto remove_group;
423 	}
424 
425 	err = sysfs_create_group(&sess->stats->kobj_stats,
426 				 &rtrs_clt_stats_attr_group);
427 	if (err) {
428 		pr_err("failed to create stats sysfs group, err: %d\n", err);
429 		goto put_kobj_stats;
430 	}
431 
432 	return 0;
433 
434 put_kobj_stats:
435 	kobject_del(&sess->stats->kobj_stats);
436 	kobject_put(&sess->stats->kobj_stats);
437 remove_group:
438 	sysfs_remove_group(&sess->kobj, &rtrs_clt_sess_attr_group);
439 put_kobj:
440 	kobject_del(&sess->kobj);
441 	kobject_put(&sess->kobj);
442 
443 	return err;
444 }
445 
446 void rtrs_clt_destroy_sess_files(struct rtrs_clt_sess *sess,
447 				  const struct attribute *sysfs_self)
448 {
449 	kobject_del(&sess->stats->kobj_stats);
450 	kobject_put(&sess->stats->kobj_stats);
451 	if (sysfs_self)
452 		sysfs_remove_file_self(&sess->kobj, sysfs_self);
453 	kobject_del(&sess->kobj);
454 }
455 
456 static struct attribute *rtrs_clt_attrs[] = {
457 	&dev_attr_max_reconnect_attempts.attr,
458 	&dev_attr_mpath_policy.attr,
459 	&dev_attr_add_path.attr,
460 	NULL,
461 };
462 
463 static struct attribute_group rtrs_clt_attr_group = {
464 	.attrs = rtrs_clt_attrs,
465 };
466 
467 int rtrs_clt_create_sysfs_root_files(struct rtrs_clt *clt)
468 {
469 	return sysfs_create_group(&clt->dev.kobj, &rtrs_clt_attr_group);
470 }
471 
472 void rtrs_clt_destroy_sysfs_root_folders(struct rtrs_clt *clt)
473 {
474 	if (clt->kobj_paths) {
475 		kobject_del(clt->kobj_paths);
476 		kobject_put(clt->kobj_paths);
477 	}
478 }
479 
480 void rtrs_clt_destroy_sysfs_root_files(struct rtrs_clt *clt)
481 {
482 	sysfs_remove_group(&clt->dev.kobj, &rtrs_clt_attr_group);
483 }
484