xref: /linux/drivers/infiniband/ulp/rtrs/rtrs-clt-sysfs.c (revision 4b660dbd9ee2059850fd30e0df420ca7a38a1856)
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_path_release(struct kobject *kobj)
20 {
21 	struct rtrs_clt_path *clt_path;
22 
23 	clt_path = container_of(kobj, struct rtrs_clt_path, kobj);
24 
25 	free_path(clt_path);
26 }
27 
28 static struct kobj_type ktype_sess = {
29 	.sysfs_ops = &kobj_sysfs_ops,
30 	.release = rtrs_clt_path_release
31 };
32 
33 static void rtrs_clt_path_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_path_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_sess *clt = container_of(dev, struct rtrs_clt_sess,
54 						 dev);
55 
56 	return sysfs_emit(page, "%d\n",
57 			  rtrs_clt_get_max_reconnect_attempts(clt));
58 }
59 
60 static ssize_t max_reconnect_attempts_store(struct device *dev,
61 					    struct device_attribute *attr,
62 					    const char *buf,
63 					    size_t count)
64 {
65 	int value;
66 	int ret;
67 	struct rtrs_clt_sess *clt  = container_of(dev, struct rtrs_clt_sess,
68 						  dev);
69 
70 	ret = kstrtoint(buf, 10, &value);
71 	if (ret) {
72 		rtrs_err(clt, "%s: failed to convert string '%s' to int\n",
73 			  attr->attr.name, buf);
74 		return ret;
75 	}
76 	if (value > MAX_MAX_RECONN_ATT ||
77 		     value < MIN_MAX_RECONN_ATT) {
78 		rtrs_err(clt,
79 			  "%s: invalid range (provided: '%s', accepted: min: %d, max: %d)\n",
80 			  attr->attr.name, buf, MIN_MAX_RECONN_ATT,
81 			  MAX_MAX_RECONN_ATT);
82 		return -EINVAL;
83 	}
84 	rtrs_clt_set_max_reconnect_attempts(clt, value);
85 
86 	return count;
87 }
88 
89 static DEVICE_ATTR_RW(max_reconnect_attempts);
90 
91 static ssize_t mpath_policy_show(struct device *dev,
92 				 struct device_attribute *attr,
93 				 char *page)
94 {
95 	struct rtrs_clt_sess *clt;
96 
97 	clt = container_of(dev, struct rtrs_clt_sess, dev);
98 
99 	switch (clt->mp_policy) {
100 	case MP_POLICY_RR:
101 		return sysfs_emit(page, "round-robin (RR: %d)\n",
102 				  clt->mp_policy);
103 	case MP_POLICY_MIN_INFLIGHT:
104 		return sysfs_emit(page, "min-inflight (MI: %d)\n",
105 				  clt->mp_policy);
106 	case MP_POLICY_MIN_LATENCY:
107 		return sysfs_emit(page, "min-latency (ML: %d)\n",
108 				  clt->mp_policy);
109 	default:
110 		return sysfs_emit(page, "Unknown (%d)\n", clt->mp_policy);
111 	}
112 }
113 
114 static ssize_t mpath_policy_store(struct device *dev,
115 				  struct device_attribute *attr,
116 				  const char *buf,
117 				  size_t count)
118 {
119 	struct rtrs_clt_sess *clt;
120 	int value;
121 	int ret;
122 	size_t len = 0;
123 
124 	clt = container_of(dev, struct rtrs_clt_sess, dev);
125 
126 	ret = kstrtoint(buf, 10, &value);
127 	if (!ret && (value == MP_POLICY_RR ||
128 		     value == MP_POLICY_MIN_INFLIGHT ||
129 		     value == MP_POLICY_MIN_LATENCY)) {
130 		clt->mp_policy = value;
131 		return count;
132 	}
133 
134 	/* distinguish "mi" and "min-latency" with length */
135 	len = strnlen(buf, NAME_MAX);
136 	if (len && buf[len - 1] == '\n')
137 		len--;
138 
139 	if (!strncasecmp(buf, "round-robin", 11) ||
140 	    (len == 2 && !strncasecmp(buf, "rr", 2)))
141 		clt->mp_policy = MP_POLICY_RR;
142 	else if (!strncasecmp(buf, "min-inflight", 12) ||
143 		 (len == 2 && !strncasecmp(buf, "mi", 2)))
144 		clt->mp_policy = MP_POLICY_MIN_INFLIGHT;
145 	else if (!strncasecmp(buf, "min-latency", 11) ||
146 		 (len == 2 && !strncasecmp(buf, "ml", 2)))
147 		clt->mp_policy = MP_POLICY_MIN_LATENCY;
148 	else
149 		return -EINVAL;
150 
151 	return count;
152 }
153 
154 static DEVICE_ATTR_RW(mpath_policy);
155 
156 static ssize_t add_path_show(struct device *dev,
157 			     struct device_attribute *attr, char *page)
158 {
159 	return sysfs_emit(page,
160 		"Usage: echo [<source addr>@]<destination addr> > %s\n\n*addr ::= [ ip:<ipv4|ipv6> | gid:<gid> ]\n",
161 		attr->attr.name);
162 }
163 
164 static ssize_t add_path_store(struct device *dev,
165 			      struct device_attribute *attr,
166 			      const char *buf, size_t count)
167 {
168 	struct sockaddr_storage srcaddr, dstaddr;
169 	struct rtrs_addr addr = {
170 		.src = &srcaddr,
171 		.dst = &dstaddr
172 	};
173 	struct rtrs_clt_sess *clt;
174 	const char *nl;
175 	size_t len;
176 	int err;
177 
178 	clt = container_of(dev, struct rtrs_clt_sess, dev);
179 
180 	nl = strchr(buf, '\n');
181 	if (nl)
182 		len = nl - buf;
183 	else
184 		len = count;
185 	err = rtrs_addr_to_sockaddr(buf, len, clt->port, &addr);
186 	if (err)
187 		return -EINVAL;
188 
189 	err = rtrs_clt_create_path_from_sysfs(clt, &addr);
190 	if (err)
191 		return err;
192 
193 	return count;
194 }
195 
196 static DEVICE_ATTR_RW(add_path);
197 
198 static ssize_t rtrs_clt_state_show(struct kobject *kobj,
199 				    struct kobj_attribute *attr, char *page)
200 {
201 	struct rtrs_clt_path *clt_path;
202 
203 	clt_path = container_of(kobj, struct rtrs_clt_path, kobj);
204 	if (clt_path->state == RTRS_CLT_CONNECTED)
205 		return sysfs_emit(page, "connected\n");
206 
207 	return sysfs_emit(page, "disconnected\n");
208 }
209 
210 static struct kobj_attribute rtrs_clt_state_attr =
211 	__ATTR(state, 0444, rtrs_clt_state_show, NULL);
212 
213 static ssize_t rtrs_clt_reconnect_show(struct kobject *kobj,
214 				       struct kobj_attribute *attr, char *buf)
215 {
216 	return sysfs_emit(buf, "Usage: echo 1 > %s\n", attr->attr.name);
217 }
218 
219 static ssize_t rtrs_clt_reconnect_store(struct kobject *kobj,
220 					 struct kobj_attribute *attr,
221 					 const char *buf, size_t count)
222 {
223 	struct rtrs_clt_path *clt_path;
224 	int ret;
225 
226 	clt_path = container_of(kobj, struct rtrs_clt_path, kobj);
227 	if (!sysfs_streq(buf, "1")) {
228 		rtrs_err(clt_path->clt, "%s: unknown value: '%s'\n",
229 			  attr->attr.name, buf);
230 		return -EINVAL;
231 	}
232 	ret = rtrs_clt_reconnect_from_sysfs(clt_path);
233 	if (ret)
234 		return ret;
235 
236 	return count;
237 }
238 
239 static struct kobj_attribute rtrs_clt_reconnect_attr =
240 	__ATTR(reconnect, 0644, rtrs_clt_reconnect_show,
241 	       rtrs_clt_reconnect_store);
242 
243 static ssize_t rtrs_clt_disconnect_show(struct kobject *kobj,
244 					struct kobj_attribute *attr, char *buf)
245 {
246 	return sysfs_emit(buf, "Usage: echo 1 > %s\n", attr->attr.name);
247 }
248 
249 static ssize_t rtrs_clt_disconnect_store(struct kobject *kobj,
250 					  struct kobj_attribute *attr,
251 					  const char *buf, size_t count)
252 {
253 	struct rtrs_clt_path *clt_path;
254 
255 	clt_path = container_of(kobj, struct rtrs_clt_path, kobj);
256 	if (!sysfs_streq(buf, "1")) {
257 		rtrs_err(clt_path->clt, "%s: unknown value: '%s'\n",
258 			  attr->attr.name, buf);
259 		return -EINVAL;
260 	}
261 	rtrs_clt_close_conns(clt_path, true);
262 
263 	return count;
264 }
265 
266 static struct kobj_attribute rtrs_clt_disconnect_attr =
267 	__ATTR(disconnect, 0644, rtrs_clt_disconnect_show,
268 	       rtrs_clt_disconnect_store);
269 
270 static ssize_t rtrs_clt_remove_path_show(struct kobject *kobj,
271 					 struct kobj_attribute *attr, char *buf)
272 {
273 	return sysfs_emit(buf, "Usage: echo 1 > %s\n", attr->attr.name);
274 }
275 
276 static ssize_t rtrs_clt_remove_path_store(struct kobject *kobj,
277 					   struct kobj_attribute *attr,
278 					   const char *buf, size_t count)
279 {
280 	struct rtrs_clt_path *clt_path;
281 	int ret;
282 
283 	clt_path = container_of(kobj, struct rtrs_clt_path, kobj);
284 	if (!sysfs_streq(buf, "1")) {
285 		rtrs_err(clt_path->clt, "%s: unknown value: '%s'\n",
286 			  attr->attr.name, buf);
287 		return -EINVAL;
288 	}
289 	ret = rtrs_clt_remove_path_from_sysfs(clt_path, &attr->attr);
290 	if (ret)
291 		return ret;
292 
293 	return count;
294 }
295 
296 static struct kobj_attribute rtrs_clt_remove_path_attr =
297 	__ATTR(remove_path, 0644, rtrs_clt_remove_path_show,
298 	       rtrs_clt_remove_path_store);
299 
300 STAT_ATTR(struct rtrs_clt_stats, cpu_migration_from,
301 	  rtrs_clt_stats_migration_from_cnt_to_str,
302 	  rtrs_clt_reset_cpu_migr_stats);
303 
304 STAT_ATTR(struct rtrs_clt_stats, cpu_migration_to,
305 	  rtrs_clt_stats_migration_to_cnt_to_str,
306 	  rtrs_clt_reset_cpu_migr_stats);
307 
308 STAT_ATTR(struct rtrs_clt_stats, reconnects,
309 	  rtrs_clt_stats_reconnects_to_str,
310 	  rtrs_clt_reset_reconnects_stat);
311 
312 STAT_ATTR(struct rtrs_clt_stats, rdma,
313 	  rtrs_clt_stats_rdma_to_str,
314 	  rtrs_clt_reset_rdma_stats);
315 
316 STAT_ATTR(struct rtrs_clt_stats, reset_all,
317 	  rtrs_clt_reset_all_help,
318 	  rtrs_clt_reset_all_stats);
319 
320 static struct attribute *rtrs_clt_stats_attrs[] = {
321 	&cpu_migration_from_attr.attr,
322 	&cpu_migration_to_attr.attr,
323 	&reconnects_attr.attr,
324 	&rdma_attr.attr,
325 	&reset_all_attr.attr,
326 	NULL
327 };
328 
329 static const struct attribute_group rtrs_clt_stats_attr_group = {
330 	.attrs = rtrs_clt_stats_attrs,
331 };
332 
333 static ssize_t rtrs_clt_hca_port_show(struct kobject *kobj,
334 				       struct kobj_attribute *attr,
335 				       char *page)
336 {
337 	struct rtrs_clt_path *clt_path;
338 
339 	clt_path = container_of(kobj, typeof(*clt_path), kobj);
340 
341 	return sysfs_emit(page, "%u\n", clt_path->hca_port);
342 }
343 
344 static struct kobj_attribute rtrs_clt_hca_port_attr =
345 	__ATTR(hca_port, 0444, rtrs_clt_hca_port_show, NULL);
346 
347 static ssize_t rtrs_clt_hca_name_show(struct kobject *kobj,
348 				       struct kobj_attribute *attr,
349 				       char *page)
350 {
351 	struct rtrs_clt_path *clt_path;
352 
353 	clt_path = container_of(kobj, struct rtrs_clt_path, kobj);
354 
355 	return sysfs_emit(page, "%s\n", clt_path->hca_name);
356 }
357 
358 static struct kobj_attribute rtrs_clt_hca_name_attr =
359 	__ATTR(hca_name, 0444, rtrs_clt_hca_name_show, NULL);
360 
361 static ssize_t rtrs_clt_cur_latency_show(struct kobject *kobj,
362 				    struct kobj_attribute *attr,
363 				    char *page)
364 {
365 	struct rtrs_clt_path *clt_path;
366 
367 	clt_path = container_of(kobj, struct rtrs_clt_path, kobj);
368 
369 	return sysfs_emit(page, "%lld ns\n",
370 			  ktime_to_ns(clt_path->s.hb_cur_latency));
371 }
372 
373 static struct kobj_attribute rtrs_clt_cur_latency_attr =
374 	__ATTR(cur_latency, 0444, rtrs_clt_cur_latency_show, NULL);
375 
376 static ssize_t rtrs_clt_src_addr_show(struct kobject *kobj,
377 				       struct kobj_attribute *attr,
378 				       char *page)
379 {
380 	struct rtrs_clt_path *clt_path;
381 	int len;
382 
383 	clt_path = container_of(kobj, struct rtrs_clt_path, kobj);
384 	len = sockaddr_to_str((struct sockaddr *)&clt_path->s.src_addr, page,
385 			      PAGE_SIZE);
386 	len += sysfs_emit_at(page, len, "\n");
387 	return len;
388 }
389 
390 static struct kobj_attribute rtrs_clt_src_addr_attr =
391 	__ATTR(src_addr, 0444, rtrs_clt_src_addr_show, NULL);
392 
393 static ssize_t rtrs_clt_dst_addr_show(struct kobject *kobj,
394 				       struct kobj_attribute *attr,
395 				       char *page)
396 {
397 	struct rtrs_clt_path *clt_path;
398 	int len;
399 
400 	clt_path = container_of(kobj, struct rtrs_clt_path, kobj);
401 	len = sockaddr_to_str((struct sockaddr *)&clt_path->s.dst_addr, page,
402 			      PAGE_SIZE);
403 	len += sysfs_emit_at(page, len, "\n");
404 	return len;
405 }
406 
407 static struct kobj_attribute rtrs_clt_dst_addr_attr =
408 	__ATTR(dst_addr, 0444, rtrs_clt_dst_addr_show, NULL);
409 
410 static struct attribute *rtrs_clt_path_attrs[] = {
411 	&rtrs_clt_hca_name_attr.attr,
412 	&rtrs_clt_hca_port_attr.attr,
413 	&rtrs_clt_src_addr_attr.attr,
414 	&rtrs_clt_dst_addr_attr.attr,
415 	&rtrs_clt_state_attr.attr,
416 	&rtrs_clt_reconnect_attr.attr,
417 	&rtrs_clt_disconnect_attr.attr,
418 	&rtrs_clt_remove_path_attr.attr,
419 	&rtrs_clt_cur_latency_attr.attr,
420 	NULL,
421 };
422 
423 static const struct attribute_group rtrs_clt_path_attr_group = {
424 	.attrs = rtrs_clt_path_attrs,
425 };
426 
427 int rtrs_clt_create_path_files(struct rtrs_clt_path *clt_path)
428 {
429 	struct rtrs_clt_sess *clt = clt_path->clt;
430 	char str[NAME_MAX];
431 	int err;
432 	struct rtrs_addr path = {
433 		.src = &clt_path->s.src_addr,
434 		.dst = &clt_path->s.dst_addr,
435 	};
436 
437 	rtrs_addr_to_str(&path, str, sizeof(str));
438 	err = kobject_init_and_add(&clt_path->kobj, &ktype_sess,
439 				   clt->kobj_paths,
440 				   "%s", str);
441 	if (err) {
442 		pr_err("kobject_init_and_add: %d\n", err);
443 		kobject_put(&clt_path->kobj);
444 		return err;
445 	}
446 	err = sysfs_create_group(&clt_path->kobj, &rtrs_clt_path_attr_group);
447 	if (err) {
448 		pr_err("sysfs_create_group(): %d\n", err);
449 		goto put_kobj;
450 	}
451 	err = kobject_init_and_add(&clt_path->stats->kobj_stats, &ktype_stats,
452 				   &clt_path->kobj, "stats");
453 	if (err) {
454 		pr_err("kobject_init_and_add: %d\n", err);
455 		kobject_put(&clt_path->stats->kobj_stats);
456 		goto remove_group;
457 	}
458 
459 	err = sysfs_create_group(&clt_path->stats->kobj_stats,
460 				 &rtrs_clt_stats_attr_group);
461 	if (err) {
462 		pr_err("failed to create stats sysfs group, err: %d\n", err);
463 		goto put_kobj_stats;
464 	}
465 
466 	return 0;
467 
468 put_kobj_stats:
469 	kobject_del(&clt_path->stats->kobj_stats);
470 	kobject_put(&clt_path->stats->kobj_stats);
471 remove_group:
472 	sysfs_remove_group(&clt_path->kobj, &rtrs_clt_path_attr_group);
473 put_kobj:
474 	kobject_del(&clt_path->kobj);
475 	kobject_put(&clt_path->kobj);
476 
477 	return err;
478 }
479 
480 void rtrs_clt_destroy_path_files(struct rtrs_clt_path *clt_path,
481 				  const struct attribute *sysfs_self)
482 {
483 	kobject_del(&clt_path->stats->kobj_stats);
484 	kobject_put(&clt_path->stats->kobj_stats);
485 	if (sysfs_self)
486 		sysfs_remove_file_self(&clt_path->kobj, sysfs_self);
487 	kobject_del(&clt_path->kobj);
488 }
489 
490 static struct attribute *rtrs_clt_attrs[] = {
491 	&dev_attr_max_reconnect_attempts.attr,
492 	&dev_attr_mpath_policy.attr,
493 	&dev_attr_add_path.attr,
494 	NULL,
495 };
496 
497 static const struct attribute_group rtrs_clt_attr_group = {
498 	.attrs = rtrs_clt_attrs,
499 };
500 
501 int rtrs_clt_create_sysfs_root_files(struct rtrs_clt_sess *clt)
502 {
503 	return sysfs_create_group(&clt->dev.kobj, &rtrs_clt_attr_group);
504 }
505 
506 void rtrs_clt_destroy_sysfs_root(struct rtrs_clt_sess *clt)
507 {
508 	sysfs_remove_group(&clt->dev.kobj, &rtrs_clt_attr_group);
509 
510 	if (clt->kobj_paths) {
511 		kobject_del(clt->kobj_paths);
512 		kobject_put(clt->kobj_paths);
513 	}
514 }
515