515 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * RDMA Transport Layer4 *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_fmt10#define pr_fmt(fmt) KBUILD_MODNAME " L" __stringify(__LINE__) ": " fmt11 12#include "rtrs-pri.h"13#include "rtrs-clt.h"14#include "rtrs-log.h"15 16#define MIN_MAX_RECONN_ATT -117#define MAX_MAX_RECONN_ATT 999918 19static 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 28static struct kobj_type ktype_sess = {29 .sysfs_ops = &kobj_sysfs_ops,30 .release = rtrs_clt_path_release31};32 33static 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 44static struct kobj_type ktype_stats = {45 .sysfs_ops = &kobj_sysfs_ops,46 .release = rtrs_clt_path_stats_release,47};48 49static 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 60static 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 89static DEVICE_ATTR_RW(max_reconnect_attempts);90 91static 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 114static 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 else149 return -EINVAL;150 151 return count;152}153 154static DEVICE_ATTR_RW(mpath_policy);155 156static 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 164static 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 = &dstaddr172 };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 else184 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 196static DEVICE_ATTR_RW(add_path);197 198static 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 210static struct kobj_attribute rtrs_clt_state_attr =211 __ATTR(state, 0444, rtrs_clt_state_show, NULL);212 213static 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 219static 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 239static struct kobj_attribute rtrs_clt_reconnect_attr =240 __ATTR(reconnect, 0644, rtrs_clt_reconnect_show,241 rtrs_clt_reconnect_store);242 243static 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 249static 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 266static struct kobj_attribute rtrs_clt_disconnect_attr =267 __ATTR(disconnect, 0644, rtrs_clt_disconnect_show,268 rtrs_clt_disconnect_store);269 270static 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 276static 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 296static 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 300STAT_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 304STAT_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 308STAT_ATTR(struct rtrs_clt_stats, reconnects,309 rtrs_clt_stats_reconnects_to_str,310 rtrs_clt_reset_reconnects_stat);311 312STAT_ATTR(struct rtrs_clt_stats, rdma,313 rtrs_clt_stats_rdma_to_str,314 rtrs_clt_reset_rdma_stats);315 316STAT_ATTR(struct rtrs_clt_stats, reset_all,317 rtrs_clt_reset_all_help,318 rtrs_clt_reset_all_stats);319 320static 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 NULL327};328 329static const struct attribute_group rtrs_clt_stats_attr_group = {330 .attrs = rtrs_clt_stats_attrs,331};332 333static 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 344static struct kobj_attribute rtrs_clt_hca_port_attr =345 __ATTR(hca_port, 0444, rtrs_clt_hca_port_show, NULL);346 347static 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 358static struct kobj_attribute rtrs_clt_hca_name_attr =359 __ATTR(hca_name, 0444, rtrs_clt_hca_name_show, NULL);360 361static 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 373static struct kobj_attribute rtrs_clt_cur_latency_attr =374 __ATTR(cur_latency, 0444, rtrs_clt_cur_latency_show, NULL);375 376static 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 390static struct kobj_attribute rtrs_clt_src_addr_attr =391 __ATTR(src_addr, 0444, rtrs_clt_src_addr_show, NULL);392 393static 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 407static struct kobj_attribute rtrs_clt_dst_addr_attr =408 __ATTR(dst_addr, 0444, rtrs_clt_dst_addr_show, NULL);409 410static 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 423static const struct attribute_group rtrs_clt_path_attr_group = {424 .attrs = rtrs_clt_path_attrs,425};426 427int 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 468put_kobj_stats:469 kobject_del(&clt_path->stats->kobj_stats);470 kobject_put(&clt_path->stats->kobj_stats);471remove_group:472 sysfs_remove_group(&clt_path->kobj, &rtrs_clt_path_attr_group);473put_kobj:474 kobject_del(&clt_path->kobj);475 kobject_put(&clt_path->kobj);476 477 return err;478}479 480void 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 490static 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 497static const struct attribute_group rtrs_clt_attr_group = {498 .attrs = rtrs_clt_attrs,499};500 501int 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 506void 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