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 default: 105 return sysfs_emit(page, "Unknown (%d)\n", clt->mp_policy); 106 } 107 } 108 109 static ssize_t mpath_policy_store(struct device *dev, 110 struct device_attribute *attr, 111 const char *buf, 112 size_t count) 113 { 114 struct rtrs_clt *clt; 115 int value; 116 int ret; 117 118 clt = container_of(dev, struct rtrs_clt, dev); 119 120 ret = kstrtoint(buf, 10, &value); 121 if (!ret && (value == MP_POLICY_RR || 122 value == MP_POLICY_MIN_INFLIGHT)) { 123 clt->mp_policy = value; 124 return count; 125 } 126 127 if (!strncasecmp(buf, "round-robin", 11) || 128 !strncasecmp(buf, "rr", 2)) 129 clt->mp_policy = MP_POLICY_RR; 130 else if (!strncasecmp(buf, "min-inflight", 12) || 131 !strncasecmp(buf, "mi", 2)) 132 clt->mp_policy = MP_POLICY_MIN_INFLIGHT; 133 else 134 return -EINVAL; 135 136 return count; 137 } 138 139 static DEVICE_ATTR_RW(mpath_policy); 140 141 static ssize_t add_path_show(struct device *dev, 142 struct device_attribute *attr, char *page) 143 { 144 return sysfs_emit( 145 page, 146 "Usage: echo [<source addr>@]<destination addr> > %s\n\n*addr ::= [ ip:<ipv4|ipv6> | gid:<gid> ]\n", 147 attr->attr.name); 148 } 149 150 static ssize_t add_path_store(struct device *dev, 151 struct device_attribute *attr, 152 const char *buf, size_t count) 153 { 154 struct sockaddr_storage srcaddr, dstaddr; 155 struct rtrs_addr addr = { 156 .src = &srcaddr, 157 .dst = &dstaddr 158 }; 159 struct rtrs_clt *clt; 160 const char *nl; 161 size_t len; 162 int err; 163 164 clt = container_of(dev, struct rtrs_clt, dev); 165 166 nl = strchr(buf, '\n'); 167 if (nl) 168 len = nl - buf; 169 else 170 len = count; 171 err = rtrs_addr_to_sockaddr(buf, len, clt->port, &addr); 172 if (err) 173 return -EINVAL; 174 175 err = rtrs_clt_create_path_from_sysfs(clt, &addr); 176 if (err) 177 return err; 178 179 return count; 180 } 181 182 static DEVICE_ATTR_RW(add_path); 183 184 static ssize_t rtrs_clt_state_show(struct kobject *kobj, 185 struct kobj_attribute *attr, char *page) 186 { 187 struct rtrs_clt_sess *sess; 188 189 sess = container_of(kobj, struct rtrs_clt_sess, kobj); 190 if (sess->state == RTRS_CLT_CONNECTED) 191 return sysfs_emit(page, "connected\n"); 192 193 return sysfs_emit(page, "disconnected\n"); 194 } 195 196 static struct kobj_attribute rtrs_clt_state_attr = 197 __ATTR(state, 0444, rtrs_clt_state_show, NULL); 198 199 static ssize_t rtrs_clt_reconnect_show(struct kobject *kobj, 200 struct kobj_attribute *attr, char *buf) 201 { 202 return sysfs_emit(buf, "Usage: echo 1 > %s\n", attr->attr.name); 203 } 204 205 static ssize_t rtrs_clt_reconnect_store(struct kobject *kobj, 206 struct kobj_attribute *attr, 207 const char *buf, size_t count) 208 { 209 struct rtrs_clt_sess *sess; 210 int ret; 211 212 sess = container_of(kobj, struct rtrs_clt_sess, kobj); 213 if (!sysfs_streq(buf, "1")) { 214 rtrs_err(sess->clt, "%s: unknown value: '%s'\n", 215 attr->attr.name, buf); 216 return -EINVAL; 217 } 218 ret = rtrs_clt_reconnect_from_sysfs(sess); 219 if (ret) 220 return ret; 221 222 return count; 223 } 224 225 static struct kobj_attribute rtrs_clt_reconnect_attr = 226 __ATTR(reconnect, 0644, rtrs_clt_reconnect_show, 227 rtrs_clt_reconnect_store); 228 229 static ssize_t rtrs_clt_disconnect_show(struct kobject *kobj, 230 struct kobj_attribute *attr, char *buf) 231 { 232 return sysfs_emit(buf, "Usage: echo 1 > %s\n", 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, char *buf) 261 { 262 return sysfs_emit(buf, "Usage: echo 1 > %s\n", attr->attr.name); 263 } 264 265 static ssize_t rtrs_clt_remove_path_store(struct kobject *kobj, 266 struct kobj_attribute *attr, 267 const char *buf, size_t count) 268 { 269 struct rtrs_clt_sess *sess; 270 int ret; 271 272 sess = container_of(kobj, struct rtrs_clt_sess, kobj); 273 if (!sysfs_streq(buf, "1")) { 274 rtrs_err(sess->clt, "%s: unknown value: '%s'\n", 275 attr->attr.name, buf); 276 return -EINVAL; 277 } 278 ret = rtrs_clt_remove_path_from_sysfs(sess, &attr->attr); 279 if (ret) 280 return ret; 281 282 return count; 283 } 284 285 static struct kobj_attribute rtrs_clt_remove_path_attr = 286 __ATTR(remove_path, 0644, rtrs_clt_remove_path_show, 287 rtrs_clt_remove_path_store); 288 289 STAT_ATTR(struct rtrs_clt_stats, cpu_migration, 290 rtrs_clt_stats_migration_cnt_to_str, 291 rtrs_clt_reset_cpu_migr_stats); 292 293 STAT_ATTR(struct rtrs_clt_stats, reconnects, 294 rtrs_clt_stats_reconnects_to_str, 295 rtrs_clt_reset_reconnects_stat); 296 297 STAT_ATTR(struct rtrs_clt_stats, rdma, 298 rtrs_clt_stats_rdma_to_str, 299 rtrs_clt_reset_rdma_stats); 300 301 STAT_ATTR(struct rtrs_clt_stats, reset_all, 302 rtrs_clt_reset_all_help, 303 rtrs_clt_reset_all_stats); 304 305 static struct attribute *rtrs_clt_stats_attrs[] = { 306 &cpu_migration_attr.attr, 307 &reconnects_attr.attr, 308 &rdma_attr.attr, 309 &reset_all_attr.attr, 310 NULL 311 }; 312 313 static const struct attribute_group rtrs_clt_stats_attr_group = { 314 .attrs = rtrs_clt_stats_attrs, 315 }; 316 317 static ssize_t rtrs_clt_hca_port_show(struct kobject *kobj, 318 struct kobj_attribute *attr, 319 char *page) 320 { 321 struct rtrs_clt_sess *sess; 322 323 sess = container_of(kobj, typeof(*sess), kobj); 324 325 return sysfs_emit(page, "%u\n", sess->hca_port); 326 } 327 328 static struct kobj_attribute rtrs_clt_hca_port_attr = 329 __ATTR(hca_port, 0444, rtrs_clt_hca_port_show, NULL); 330 331 static ssize_t rtrs_clt_hca_name_show(struct kobject *kobj, 332 struct kobj_attribute *attr, 333 char *page) 334 { 335 struct rtrs_clt_sess *sess; 336 337 sess = container_of(kobj, struct rtrs_clt_sess, kobj); 338 339 return sysfs_emit(page, "%s\n", sess->hca_name); 340 } 341 342 static struct kobj_attribute rtrs_clt_hca_name_attr = 343 __ATTR(hca_name, 0444, rtrs_clt_hca_name_show, NULL); 344 345 static ssize_t rtrs_clt_src_addr_show(struct kobject *kobj, 346 struct kobj_attribute *attr, 347 char *page) 348 { 349 struct rtrs_clt_sess *sess; 350 int len; 351 352 sess = container_of(kobj, struct rtrs_clt_sess, kobj); 353 len = sockaddr_to_str((struct sockaddr *)&sess->s.src_addr, page, 354 PAGE_SIZE); 355 len += sysfs_emit_at(page, len, "\n"); 356 return len; 357 } 358 359 static struct kobj_attribute rtrs_clt_src_addr_attr = 360 __ATTR(src_addr, 0444, rtrs_clt_src_addr_show, NULL); 361 362 static ssize_t rtrs_clt_dst_addr_show(struct kobject *kobj, 363 struct kobj_attribute *attr, 364 char *page) 365 { 366 struct rtrs_clt_sess *sess; 367 int len; 368 369 sess = container_of(kobj, struct rtrs_clt_sess, kobj); 370 len = sockaddr_to_str((struct sockaddr *)&sess->s.dst_addr, page, 371 PAGE_SIZE); 372 len += sysfs_emit_at(page, len, "\n"); 373 return len; 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 const 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 const 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