0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/slab.h>
0010 #include <linux/errno.h>
0011 #include <linux/delay.h>
0012 #include <linux/ktime.h>
0013
0014 #include "tb.h"
0015
0016 static void tb_dump_hop(const struct tb_path_hop *hop, const struct tb_regs_hop *regs)
0017 {
0018 const struct tb_port *port = hop->in_port;
0019
0020 tb_port_dbg(port, " In HopID: %d => Out port: %d Out HopID: %d\n",
0021 hop->in_hop_index, regs->out_port, regs->next_hop);
0022 tb_port_dbg(port, " Weight: %d Priority: %d Credits: %d Drop: %d\n",
0023 regs->weight, regs->priority,
0024 regs->initial_credits, regs->drop_packages);
0025 tb_port_dbg(port, " Counter enabled: %d Counter index: %d\n",
0026 regs->counter_enable, regs->counter);
0027 tb_port_dbg(port, " Flow Control (In/Eg): %d/%d Shared Buffer (In/Eg): %d/%d\n",
0028 regs->ingress_fc, regs->egress_fc,
0029 regs->ingress_shared_buffer, regs->egress_shared_buffer);
0030 tb_port_dbg(port, " Unknown1: %#x Unknown2: %#x Unknown3: %#x\n",
0031 regs->unknown1, regs->unknown2, regs->unknown3);
0032 }
0033
0034 static struct tb_port *tb_path_find_dst_port(struct tb_port *src, int src_hopid,
0035 int dst_hopid)
0036 {
0037 struct tb_port *port, *out_port = NULL;
0038 struct tb_regs_hop hop;
0039 struct tb_switch *sw;
0040 int i, ret, hopid;
0041
0042 hopid = src_hopid;
0043 port = src;
0044
0045 for (i = 0; port && i < TB_PATH_MAX_HOPS; i++) {
0046 sw = port->sw;
0047
0048 ret = tb_port_read(port, &hop, TB_CFG_HOPS, 2 * hopid, 2);
0049 if (ret) {
0050 tb_port_warn(port, "failed to read path at %d\n", hopid);
0051 return NULL;
0052 }
0053
0054 if (!hop.enable)
0055 return NULL;
0056
0057 out_port = &sw->ports[hop.out_port];
0058 hopid = hop.next_hop;
0059 port = out_port->remote;
0060 }
0061
0062 return out_port && hopid == dst_hopid ? out_port : NULL;
0063 }
0064
0065 static int tb_path_find_src_hopid(struct tb_port *src,
0066 const struct tb_port *dst, int dst_hopid)
0067 {
0068 struct tb_port *out;
0069 int i;
0070
0071 for (i = TB_PATH_MIN_HOPID; i <= src->config.max_in_hop_id; i++) {
0072 out = tb_path_find_dst_port(src, i, dst_hopid);
0073 if (out == dst)
0074 return i;
0075 }
0076
0077 return 0;
0078 }
0079
0080
0081
0082
0083
0084
0085
0086
0087
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101 struct tb_path *tb_path_discover(struct tb_port *src, int src_hopid,
0102 struct tb_port *dst, int dst_hopid,
0103 struct tb_port **last, const char *name,
0104 bool alloc_hopid)
0105 {
0106 struct tb_port *out_port;
0107 struct tb_regs_hop hop;
0108 struct tb_path *path;
0109 struct tb_switch *sw;
0110 struct tb_port *p;
0111 size_t num_hops;
0112 int ret, i, h;
0113
0114 if (src_hopid < 0 && dst) {
0115
0116
0117
0118
0119
0120
0121
0122 src_hopid = tb_path_find_src_hopid(src, dst, dst_hopid);
0123 if (!src_hopid)
0124 return NULL;
0125 }
0126
0127 p = src;
0128 h = src_hopid;
0129 num_hops = 0;
0130
0131 for (i = 0; p && i < TB_PATH_MAX_HOPS; i++) {
0132 sw = p->sw;
0133
0134 ret = tb_port_read(p, &hop, TB_CFG_HOPS, 2 * h, 2);
0135 if (ret) {
0136 tb_port_warn(p, "failed to read path at %d\n", h);
0137 return NULL;
0138 }
0139
0140
0141 if (!hop.enable)
0142 break;
0143
0144 out_port = &sw->ports[hop.out_port];
0145 if (last)
0146 *last = out_port;
0147
0148 h = hop.next_hop;
0149 p = out_port->remote;
0150 num_hops++;
0151 }
0152
0153 path = kzalloc(sizeof(*path), GFP_KERNEL);
0154 if (!path)
0155 return NULL;
0156
0157 path->name = name;
0158 path->tb = src->sw->tb;
0159 path->path_length = num_hops;
0160 path->activated = true;
0161 path->alloc_hopid = alloc_hopid;
0162
0163 path->hops = kcalloc(num_hops, sizeof(*path->hops), GFP_KERNEL);
0164 if (!path->hops) {
0165 kfree(path);
0166 return NULL;
0167 }
0168
0169 tb_dbg(path->tb, "discovering %s path starting from %llx:%u\n",
0170 path->name, tb_route(src->sw), src->port);
0171
0172 p = src;
0173 h = src_hopid;
0174
0175 for (i = 0; i < num_hops; i++) {
0176 int next_hop;
0177
0178 sw = p->sw;
0179
0180 ret = tb_port_read(p, &hop, TB_CFG_HOPS, 2 * h, 2);
0181 if (ret) {
0182 tb_port_warn(p, "failed to read path at %d\n", h);
0183 goto err;
0184 }
0185
0186 if (alloc_hopid && tb_port_alloc_in_hopid(p, h, h) < 0)
0187 goto err;
0188
0189 out_port = &sw->ports[hop.out_port];
0190 next_hop = hop.next_hop;
0191
0192 if (alloc_hopid &&
0193 tb_port_alloc_out_hopid(out_port, next_hop, next_hop) < 0) {
0194 tb_port_release_in_hopid(p, h);
0195 goto err;
0196 }
0197
0198 path->hops[i].in_port = p;
0199 path->hops[i].in_hop_index = h;
0200 path->hops[i].in_counter_index = -1;
0201 path->hops[i].out_port = out_port;
0202 path->hops[i].next_hop_index = next_hop;
0203
0204 tb_dump_hop(&path->hops[i], &hop);
0205
0206 h = next_hop;
0207 p = out_port->remote;
0208 }
0209
0210 tb_dbg(path->tb, "path discovery complete\n");
0211 return path;
0212
0213 err:
0214 tb_port_warn(src, "failed to discover path starting at HopID %d\n",
0215 src_hopid);
0216 tb_path_free(path);
0217 return NULL;
0218 }
0219
0220
0221
0222
0223
0224
0225
0226
0227
0228
0229
0230
0231
0232
0233
0234
0235
0236
0237
0238 struct tb_path *tb_path_alloc(struct tb *tb, struct tb_port *src, int src_hopid,
0239 struct tb_port *dst, int dst_hopid, int link_nr,
0240 const char *name)
0241 {
0242 struct tb_port *in_port, *out_port, *first_port, *last_port;
0243 int in_hopid, out_hopid;
0244 struct tb_path *path;
0245 size_t num_hops;
0246 int i, ret;
0247
0248 path = kzalloc(sizeof(*path), GFP_KERNEL);
0249 if (!path)
0250 return NULL;
0251
0252 first_port = last_port = NULL;
0253 i = 0;
0254 tb_for_each_port_on_path(src, dst, in_port) {
0255 if (!first_port)
0256 first_port = in_port;
0257 last_port = in_port;
0258 i++;
0259 }
0260
0261
0262 if (first_port != src || last_port != dst) {
0263 kfree(path);
0264 return NULL;
0265 }
0266
0267
0268 num_hops = i / 2;
0269
0270 path->hops = kcalloc(num_hops, sizeof(*path->hops), GFP_KERNEL);
0271 if (!path->hops) {
0272 kfree(path);
0273 return NULL;
0274 }
0275
0276 path->alloc_hopid = true;
0277
0278 in_hopid = src_hopid;
0279 out_port = NULL;
0280
0281 for (i = 0; i < num_hops; i++) {
0282 in_port = tb_next_port_on_path(src, dst, out_port);
0283 if (!in_port)
0284 goto err;
0285
0286
0287 if (!in_port->bonded && in_port->dual_link_port &&
0288 in_port->link_nr != link_nr)
0289 in_port = in_port->dual_link_port;
0290
0291 ret = tb_port_alloc_in_hopid(in_port, in_hopid, in_hopid);
0292 if (ret < 0)
0293 goto err;
0294 in_hopid = ret;
0295
0296 out_port = tb_next_port_on_path(src, dst, in_port);
0297 if (!out_port)
0298 goto err;
0299
0300
0301
0302
0303
0304 if (out_port->dual_link_port) {
0305 if (!in_port->bonded && out_port->bonded &&
0306 out_port->link_nr) {
0307
0308
0309
0310
0311 out_port = out_port->dual_link_port;
0312 } else if (!out_port->bonded &&
0313 out_port->link_nr != link_nr) {
0314
0315
0316
0317
0318 out_port = out_port->dual_link_port;
0319 }
0320 }
0321
0322 if (i == num_hops - 1)
0323 ret = tb_port_alloc_out_hopid(out_port, dst_hopid,
0324 dst_hopid);
0325 else
0326 ret = tb_port_alloc_out_hopid(out_port, -1, -1);
0327
0328 if (ret < 0)
0329 goto err;
0330 out_hopid = ret;
0331
0332 path->hops[i].in_hop_index = in_hopid;
0333 path->hops[i].in_port = in_port;
0334 path->hops[i].in_counter_index = -1;
0335 path->hops[i].out_port = out_port;
0336 path->hops[i].next_hop_index = out_hopid;
0337
0338 in_hopid = out_hopid;
0339 }
0340
0341 path->tb = tb;
0342 path->path_length = num_hops;
0343 path->name = name;
0344
0345 return path;
0346
0347 err:
0348 tb_path_free(path);
0349 return NULL;
0350 }
0351
0352
0353
0354
0355
0356
0357
0358 void tb_path_free(struct tb_path *path)
0359 {
0360 if (path->alloc_hopid) {
0361 int i;
0362
0363 for (i = 0; i < path->path_length; i++) {
0364 const struct tb_path_hop *hop = &path->hops[i];
0365
0366 if (hop->in_port)
0367 tb_port_release_in_hopid(hop->in_port,
0368 hop->in_hop_index);
0369 if (hop->out_port)
0370 tb_port_release_out_hopid(hop->out_port,
0371 hop->next_hop_index);
0372 }
0373 }
0374
0375 kfree(path->hops);
0376 kfree(path);
0377 }
0378
0379 static void __tb_path_deallocate_nfc(struct tb_path *path, int first_hop)
0380 {
0381 int i, res;
0382 for (i = first_hop; i < path->path_length; i++) {
0383 res = tb_port_add_nfc_credits(path->hops[i].in_port,
0384 -path->hops[i].nfc_credits);
0385 if (res)
0386 tb_port_warn(path->hops[i].in_port,
0387 "nfc credits deallocation failed for hop %d\n",
0388 i);
0389 }
0390 }
0391
0392 static int __tb_path_deactivate_hop(struct tb_port *port, int hop_index,
0393 bool clear_fc)
0394 {
0395 struct tb_regs_hop hop;
0396 ktime_t timeout;
0397 int ret;
0398
0399
0400 ret = tb_port_read(port, &hop, TB_CFG_HOPS, 2 * hop_index, 2);
0401 if (ret)
0402 return ret;
0403
0404
0405 if (!hop.enable)
0406 return 0;
0407
0408 hop.enable = 0;
0409
0410 ret = tb_port_write(port, &hop, TB_CFG_HOPS, 2 * hop_index, 2);
0411 if (ret)
0412 return ret;
0413
0414
0415 timeout = ktime_add_ms(ktime_get(), 500);
0416 do {
0417 ret = tb_port_read(port, &hop, TB_CFG_HOPS, 2 * hop_index, 2);
0418 if (ret)
0419 return ret;
0420
0421 if (!hop.pending) {
0422 if (clear_fc) {
0423
0424
0425
0426
0427
0428
0429 if (!tb_switch_is_usb4(port->sw)) {
0430 hop.ingress_fc = 0;
0431 hop.ingress_shared_buffer = 0;
0432 }
0433 hop.egress_fc = 0;
0434 hop.egress_shared_buffer = 0;
0435
0436 return tb_port_write(port, &hop, TB_CFG_HOPS,
0437 2 * hop_index, 2);
0438 }
0439
0440 return 0;
0441 }
0442
0443 usleep_range(10, 20);
0444 } while (ktime_before(ktime_get(), timeout));
0445
0446 return -ETIMEDOUT;
0447 }
0448
0449 static void __tb_path_deactivate_hops(struct tb_path *path, int first_hop)
0450 {
0451 int i, res;
0452
0453 for (i = first_hop; i < path->path_length; i++) {
0454 res = __tb_path_deactivate_hop(path->hops[i].in_port,
0455 path->hops[i].in_hop_index,
0456 path->clear_fc);
0457 if (res && res != -ENODEV)
0458 tb_port_warn(path->hops[i].in_port,
0459 "hop deactivation failed for hop %d, index %d\n",
0460 i, path->hops[i].in_hop_index);
0461 }
0462 }
0463
0464 void tb_path_deactivate(struct tb_path *path)
0465 {
0466 if (!path->activated) {
0467 tb_WARN(path->tb, "trying to deactivate an inactive path\n");
0468 return;
0469 }
0470 tb_dbg(path->tb,
0471 "deactivating %s path from %llx:%u to %llx:%u\n",
0472 path->name, tb_route(path->hops[0].in_port->sw),
0473 path->hops[0].in_port->port,
0474 tb_route(path->hops[path->path_length - 1].out_port->sw),
0475 path->hops[path->path_length - 1].out_port->port);
0476 __tb_path_deactivate_hops(path, 0);
0477 __tb_path_deallocate_nfc(path, 0);
0478 path->activated = false;
0479 }
0480
0481
0482
0483
0484
0485
0486
0487
0488
0489
0490 int tb_path_activate(struct tb_path *path)
0491 {
0492 int i, res;
0493 enum tb_path_port out_mask, in_mask;
0494 if (path->activated) {
0495 tb_WARN(path->tb, "trying to activate already activated path\n");
0496 return -EINVAL;
0497 }
0498
0499 tb_dbg(path->tb,
0500 "activating %s path from %llx:%u to %llx:%u\n",
0501 path->name, tb_route(path->hops[0].in_port->sw),
0502 path->hops[0].in_port->port,
0503 tb_route(path->hops[path->path_length - 1].out_port->sw),
0504 path->hops[path->path_length - 1].out_port->port);
0505
0506
0507 for (i = path->path_length - 1; i >= 0; i--) {
0508 if (path->hops[i].in_counter_index == -1)
0509 continue;
0510 res = tb_port_clear_counter(path->hops[i].in_port,
0511 path->hops[i].in_counter_index);
0512 if (res)
0513 goto err;
0514 }
0515
0516
0517 for (i = path->path_length - 1; i >= 0; i--) {
0518 res = tb_port_add_nfc_credits(path->hops[i].in_port,
0519 path->hops[i].nfc_credits);
0520 if (res) {
0521 __tb_path_deallocate_nfc(path, i);
0522 goto err;
0523 }
0524 }
0525
0526
0527 for (i = path->path_length - 1; i >= 0; i--) {
0528 struct tb_regs_hop hop = { 0 };
0529
0530
0531 __tb_path_deactivate_hop(path->hops[i].in_port,
0532 path->hops[i].in_hop_index, path->clear_fc);
0533
0534
0535 hop.next_hop = path->hops[i].next_hop_index;
0536 hop.out_port = path->hops[i].out_port->port;
0537 hop.initial_credits = path->hops[i].initial_credits;
0538 hop.unknown1 = 0;
0539 hop.enable = 1;
0540
0541
0542 out_mask = (i == path->path_length - 1) ?
0543 TB_PATH_DESTINATION : TB_PATH_INTERNAL;
0544 in_mask = (i == 0) ? TB_PATH_SOURCE : TB_PATH_INTERNAL;
0545 hop.weight = path->weight;
0546 hop.unknown2 = 0;
0547 hop.priority = path->priority;
0548 hop.drop_packages = path->drop_packages;
0549 hop.counter = path->hops[i].in_counter_index;
0550 hop.counter_enable = path->hops[i].in_counter_index != -1;
0551 hop.ingress_fc = path->ingress_fc_enable & in_mask;
0552 hop.egress_fc = path->egress_fc_enable & out_mask;
0553 hop.ingress_shared_buffer = path->ingress_shared_buffer
0554 & in_mask;
0555 hop.egress_shared_buffer = path->egress_shared_buffer
0556 & out_mask;
0557 hop.unknown3 = 0;
0558
0559 tb_port_dbg(path->hops[i].in_port, "Writing hop %d\n", i);
0560 tb_dump_hop(&path->hops[i], &hop);
0561 res = tb_port_write(path->hops[i].in_port, &hop, TB_CFG_HOPS,
0562 2 * path->hops[i].in_hop_index, 2);
0563 if (res) {
0564 __tb_path_deactivate_hops(path, i);
0565 __tb_path_deallocate_nfc(path, 0);
0566 goto err;
0567 }
0568 }
0569 path->activated = true;
0570 tb_dbg(path->tb, "path activation complete\n");
0571 return 0;
0572 err:
0573 tb_WARN(path->tb, "path activation failed\n");
0574 return res;
0575 }
0576
0577
0578
0579
0580
0581
0582
0583 bool tb_path_is_invalid(struct tb_path *path)
0584 {
0585 int i = 0;
0586 for (i = 0; i < path->path_length; i++) {
0587 if (path->hops[i].in_port->sw->is_unplugged)
0588 return true;
0589 if (path->hops[i].out_port->sw->is_unplugged)
0590 return true;
0591 }
0592 return false;
0593 }
0594
0595
0596
0597
0598
0599
0600
0601
0602
0603 bool tb_path_port_on_path(const struct tb_path *path, const struct tb_port *port)
0604 {
0605 int i;
0606
0607 for (i = 0; i < path->path_length; i++) {
0608 if (path->hops[i].in_port == port ||
0609 path->hops[i].out_port == port)
0610 return true;
0611 }
0612
0613 return false;
0614 }