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