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 (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( 160 page, 161 "Usage: echo [<source addr>@]<destination addr> > %s\n\n*addr ::= [ ip:<ipv4|ipv6> | gid:<gid> ]\n", 162 attr->attr.name); 163 } 164 165 static ssize_t add_path_store(struct device *dev, 166 struct device_attribute *attr, 167 const char *buf, size_t count) 168 { 169 struct sockaddr_storage srcaddr, dstaddr; 170 struct rtrs_addr addr = { 171 .src = &srcaddr, 172 .dst = &dstaddr 173 }; 174 struct rtrs_clt_sess *clt; 175 const char *nl; 176 size_t len; 177 int err; 178 179 clt = container_of(dev, struct rtrs_clt_sess, dev); 180 181 nl = strchr(buf, '\n'); 182 if (nl) 183 len = nl - buf; 184 else 185 len = count; 186 err = rtrs_addr_to_sockaddr(buf, len, clt->port, &addr); 187 if (err) 188 return -EINVAL; 189 190 err = rtrs_clt_create_path_from_sysfs(clt, &addr); 191 if (err) 192 return err; 193 194 return count; 195 } 196 197 static DEVICE_ATTR_RW(add_path); 198 199 static ssize_t rtrs_clt_state_show(struct kobject *kobj, 200 struct kobj_attribute *attr, char *page) 201 { 202 struct rtrs_clt_path *clt_path; 203 204 clt_path = container_of(kobj, struct rtrs_clt_path, kobj); 205 if (clt_path->state == RTRS_CLT_CONNECTED) 206 return sysfs_emit(page, "connected\n"); 207 208 return sysfs_emit(page, "disconnected\n"); 209 } 210 211 static struct kobj_attribute rtrs_clt_state_attr = 212 __ATTR(state, 0444, rtrs_clt_state_show, NULL); 213 214 static ssize_t rtrs_clt_reconnect_show(struct kobject *kobj, 215 struct kobj_attribute *attr, char *buf) 216 { 217 return sysfs_emit(buf, "Usage: echo 1 > %s\n", attr->attr.name); 218 } 219 220 static ssize_t rtrs_clt_reconnect_store(struct kobject *kobj, 221 struct kobj_attribute *attr, 222 const char *buf, size_t count) 223 { 224 struct rtrs_clt_path *clt_path; 225 int ret; 226 227 clt_path = container_of(kobj, struct rtrs_clt_path, kobj); 228 if (!sysfs_streq(buf, "1")) { 229 rtrs_err(clt_path->clt, "%s: unknown value: '%s'\n", 230 attr->attr.name, buf); 231 return -EINVAL; 232 } 233 ret = rtrs_clt_reconnect_from_sysfs(clt_path); 234 if (ret) 235 return ret; 236 237 return count; 238 } 239 240 static struct kobj_attribute rtrs_clt_reconnect_attr = 241 __ATTR(reconnect, 0644, rtrs_clt_reconnect_show, 242 rtrs_clt_reconnect_store); 243 244 static ssize_t rtrs_clt_disconnect_show(struct kobject *kobj, 245 struct kobj_attribute *attr, char *buf) 246 { 247 return sysfs_emit(buf, "Usage: echo 1 > %s\n", attr->attr.name); 248 } 249 250 static ssize_t rtrs_clt_disconnect_store(struct kobject *kobj, 251 struct kobj_attribute *attr, 252 const char *buf, size_t count) 253 { 254 struct rtrs_clt_path *clt_path; 255 256 clt_path = container_of(kobj, struct rtrs_clt_path, kobj); 257 if (!sysfs_streq(buf, "1")) { 258 rtrs_err(clt_path->clt, "%s: unknown value: '%s'\n", 259 attr->attr.name, buf); 260 return -EINVAL; 261 } 262 rtrs_clt_close_conns(clt_path, true); 263 264 return count; 265 } 266 267 static struct kobj_attribute rtrs_clt_disconnect_attr = 268 __ATTR(disconnect, 0644, rtrs_clt_disconnect_show, 269 rtrs_clt_disconnect_store); 270 271 static ssize_t rtrs_clt_remove_path_show(struct kobject *kobj, 272 struct kobj_attribute *attr, char *buf) 273 { 274 return sysfs_emit(buf, "Usage: echo 1 > %s\n", attr->attr.name); 275 } 276 277 static ssize_t rtrs_clt_remove_path_store(struct kobject *kobj, 278 struct kobj_attribute *attr, 279 const char *buf, size_t count) 280 { 281 struct rtrs_clt_path *clt_path; 282 int ret; 283 284 clt_path = container_of(kobj, struct rtrs_clt_path, kobj); 285 if (!sysfs_streq(buf, "1")) { 286 rtrs_err(clt_path->clt, "%s: unknown value: '%s'\n", 287 attr->attr.name, buf); 288 return -EINVAL; 289 } 290 ret = rtrs_clt_remove_path_from_sysfs(clt_path, &attr->attr); 291 if (ret) 292 return ret; 293 294 return count; 295 } 296 297 static struct kobj_attribute rtrs_clt_remove_path_attr = 298 __ATTR(remove_path, 0644, rtrs_clt_remove_path_show, 299 rtrs_clt_remove_path_store); 300 301 STAT_ATTR(struct rtrs_clt_stats, cpu_migration_from, 302 rtrs_clt_stats_migration_from_cnt_to_str, 303 rtrs_clt_reset_cpu_migr_stats); 304 305 STAT_ATTR(struct rtrs_clt_stats, cpu_migration_to, 306 rtrs_clt_stats_migration_to_cnt_to_str, 307 rtrs_clt_reset_cpu_migr_stats); 308 309 STAT_ATTR(struct rtrs_clt_stats, reconnects, 310 rtrs_clt_stats_reconnects_to_str, 311 rtrs_clt_reset_reconnects_stat); 312 313 STAT_ATTR(struct rtrs_clt_stats, rdma, 314 rtrs_clt_stats_rdma_to_str, 315 rtrs_clt_reset_rdma_stats); 316 317 STAT_ATTR(struct rtrs_clt_stats, reset_all, 318 rtrs_clt_reset_all_help, 319 rtrs_clt_reset_all_stats); 320 321 static struct attribute *rtrs_clt_stats_attrs[] = { 322 &cpu_migration_from_attr.attr, 323 &cpu_migration_to_attr.attr, 324 &reconnects_attr.attr, 325 &rdma_attr.attr, 326 &reset_all_attr.attr, 327 NULL 328 }; 329 330 static const struct attribute_group rtrs_clt_stats_attr_group = { 331 .attrs = rtrs_clt_stats_attrs, 332 }; 333 334 static ssize_t rtrs_clt_hca_port_show(struct kobject *kobj, 335 struct kobj_attribute *attr, 336 char *page) 337 { 338 struct rtrs_clt_path *clt_path; 339 340 clt_path = container_of(kobj, typeof(*clt_path), kobj); 341 342 return sysfs_emit(page, "%u\n", clt_path->hca_port); 343 } 344 345 static struct kobj_attribute rtrs_clt_hca_port_attr = 346 __ATTR(hca_port, 0444, rtrs_clt_hca_port_show, NULL); 347 348 static ssize_t rtrs_clt_hca_name_show(struct kobject *kobj, 349 struct kobj_attribute *attr, 350 char *page) 351 { 352 struct rtrs_clt_path *clt_path; 353 354 clt_path = container_of(kobj, struct rtrs_clt_path, kobj); 355 356 return sysfs_emit(page, "%s\n", clt_path->hca_name); 357 } 358 359 static struct kobj_attribute rtrs_clt_hca_name_attr = 360 __ATTR(hca_name, 0444, rtrs_clt_hca_name_show, NULL); 361 362 static ssize_t rtrs_clt_cur_latency_show(struct kobject *kobj, 363 struct kobj_attribute *attr, 364 char *page) 365 { 366 struct rtrs_clt_path *clt_path; 367 368 clt_path = container_of(kobj, struct rtrs_clt_path, kobj); 369 370 return sysfs_emit(page, "%lld ns\n", 371 ktime_to_ns(clt_path->s.hb_cur_latency)); 372 } 373 374 static struct kobj_attribute rtrs_clt_cur_latency_attr = 375 __ATTR(cur_latency, 0444, rtrs_clt_cur_latency_show, NULL); 376 377 static ssize_t rtrs_clt_src_addr_show(struct kobject *kobj, 378 struct kobj_attribute *attr, 379 char *page) 380 { 381 struct rtrs_clt_path *clt_path; 382 int len; 383 384 clt_path = container_of(kobj, struct rtrs_clt_path, kobj); 385 len = sockaddr_to_str((struct sockaddr *)&clt_path->s.src_addr, page, 386 PAGE_SIZE); 387 len += sysfs_emit_at(page, len, "\n"); 388 return len; 389 } 390 391 static struct kobj_attribute rtrs_clt_src_addr_attr = 392 __ATTR(src_addr, 0444, rtrs_clt_src_addr_show, NULL); 393 394 static ssize_t rtrs_clt_dst_addr_show(struct kobject *kobj, 395 struct kobj_attribute *attr, 396 char *page) 397 { 398 struct rtrs_clt_path *clt_path; 399 int len; 400 401 clt_path = container_of(kobj, struct rtrs_clt_path, kobj); 402 len = sockaddr_to_str((struct sockaddr *)&clt_path->s.dst_addr, page, 403 PAGE_SIZE); 404 len += sysfs_emit_at(page, len, "\n"); 405 return len; 406 } 407 408 static struct kobj_attribute rtrs_clt_dst_addr_attr = 409 __ATTR(dst_addr, 0444, rtrs_clt_dst_addr_show, NULL); 410 411 static struct attribute *rtrs_clt_path_attrs[] = { 412 &rtrs_clt_hca_name_attr.attr, 413 &rtrs_clt_hca_port_attr.attr, 414 &rtrs_clt_src_addr_attr.attr, 415 &rtrs_clt_dst_addr_attr.attr, 416 &rtrs_clt_state_attr.attr, 417 &rtrs_clt_reconnect_attr.attr, 418 &rtrs_clt_disconnect_attr.attr, 419 &rtrs_clt_remove_path_attr.attr, 420 &rtrs_clt_cur_latency_attr.attr, 421 NULL, 422 }; 423 424 static const struct attribute_group rtrs_clt_path_attr_group = { 425 .attrs = rtrs_clt_path_attrs, 426 }; 427 428 int rtrs_clt_create_path_files(struct rtrs_clt_path *clt_path) 429 { 430 struct rtrs_clt_sess *clt = clt_path->clt; 431 char str[NAME_MAX]; 432 int err; 433 struct rtrs_addr path = { 434 .src = &clt_path->s.src_addr, 435 .dst = &clt_path->s.dst_addr, 436 }; 437 438 rtrs_addr_to_str(&path, str, sizeof(str)); 439 err = kobject_init_and_add(&clt_path->kobj, &ktype_sess, 440 clt->kobj_paths, 441 "%s", str); 442 if (err) { 443 pr_err("kobject_init_and_add: %d\n", err); 444 kobject_put(&clt_path->kobj); 445 return err; 446 } 447 err = sysfs_create_group(&clt_path->kobj, &rtrs_clt_path_attr_group); 448 if (err) { 449 pr_err("sysfs_create_group(): %d\n", err); 450 goto put_kobj; 451 } 452 err = kobject_init_and_add(&clt_path->stats->kobj_stats, &ktype_stats, 453 &clt_path->kobj, "stats"); 454 if (err) { 455 pr_err("kobject_init_and_add: %d\n", err); 456 kobject_put(&clt_path->stats->kobj_stats); 457 goto remove_group; 458 } 459 460 err = sysfs_create_group(&clt_path->stats->kobj_stats, 461 &rtrs_clt_stats_attr_group); 462 if (err) { 463 pr_err("failed to create stats sysfs group, err: %d\n", err); 464 goto put_kobj_stats; 465 } 466 467 return 0; 468 469 put_kobj_stats: 470 kobject_del(&clt_path->stats->kobj_stats); 471 kobject_put(&clt_path->stats->kobj_stats); 472 remove_group: 473 sysfs_remove_group(&clt_path->kobj, &rtrs_clt_path_attr_group); 474 put_kobj: 475 kobject_del(&clt_path->kobj); 476 kobject_put(&clt_path->kobj); 477 478 return err; 479 } 480 481 void rtrs_clt_destroy_path_files(struct rtrs_clt_path *clt_path, 482 const struct attribute *sysfs_self) 483 { 484 kobject_del(&clt_path->stats->kobj_stats); 485 kobject_put(&clt_path->stats->kobj_stats); 486 if (sysfs_self) 487 sysfs_remove_file_self(&clt_path->kobj, sysfs_self); 488 kobject_del(&clt_path->kobj); 489 } 490 491 static struct attribute *rtrs_clt_attrs[] = { 492 &dev_attr_max_reconnect_attempts.attr, 493 &dev_attr_mpath_policy.attr, 494 &dev_attr_add_path.attr, 495 NULL, 496 }; 497 498 static const struct attribute_group rtrs_clt_attr_group = { 499 .attrs = rtrs_clt_attrs, 500 }; 501 502 int rtrs_clt_create_sysfs_root_files(struct rtrs_clt_sess *clt) 503 { 504 return sysfs_create_group(&clt->dev.kobj, &rtrs_clt_attr_group); 505 } 506 507 void rtrs_clt_destroy_sysfs_root(struct rtrs_clt_sess *clt) 508 { 509 sysfs_remove_group(&clt->dev.kobj, &rtrs_clt_attr_group); 510 511 if (clt->kobj_paths) { 512 kobject_del(clt->kobj_paths); 513 kobject_put(clt->kobj_paths); 514 } 515 } 516