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