.. | .. |
---|
1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
---|
2 | 2 | /* |
---|
3 | | - * Thunderbolt Cactus Ridge driver - path/tunnel functionality |
---|
| 3 | + * Thunderbolt driver - path/tunnel functionality |
---|
4 | 4 | * |
---|
5 | 5 | * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com> |
---|
| 6 | + * Copyright (C) 2019, Intel Corporation |
---|
6 | 7 | */ |
---|
7 | 8 | |
---|
8 | 9 | #include <linux/slab.h> |
---|
9 | 10 | #include <linux/errno.h> |
---|
| 11 | +#include <linux/delay.h> |
---|
| 12 | +#include <linux/ktime.h> |
---|
10 | 13 | |
---|
11 | 14 | #include "tb.h" |
---|
12 | 15 | |
---|
13 | | - |
---|
14 | | -static void tb_dump_hop(struct tb_port *port, struct tb_regs_hop *hop) |
---|
| 16 | +static void tb_dump_hop(const struct tb_path_hop *hop, const struct tb_regs_hop *regs) |
---|
15 | 17 | { |
---|
16 | | - tb_port_info(port, " Hop through port %d to hop %d (%s)\n", |
---|
17 | | - hop->out_port, hop->next_hop, |
---|
18 | | - hop->enable ? "enabled" : "disabled"); |
---|
19 | | - tb_port_info(port, " Weight: %d Priority: %d Credits: %d Drop: %d\n", |
---|
20 | | - hop->weight, hop->priority, |
---|
21 | | - hop->initial_credits, hop->drop_packages); |
---|
22 | | - tb_port_info(port, " Counter enabled: %d Counter index: %d\n", |
---|
23 | | - hop->counter_enable, hop->counter); |
---|
24 | | - tb_port_info(port, " Flow Control (In/Eg): %d/%d Shared Buffer (In/Eg): %d/%d\n", |
---|
25 | | - hop->ingress_fc, hop->egress_fc, |
---|
26 | | - hop->ingress_shared_buffer, hop->egress_shared_buffer); |
---|
27 | | - tb_port_info(port, " Unknown1: %#x Unknown2: %#x Unknown3: %#x\n", |
---|
28 | | - hop->unknown1, hop->unknown2, hop->unknown3); |
---|
| 18 | + const struct tb_port *port = hop->in_port; |
---|
| 19 | + |
---|
| 20 | + tb_port_dbg(port, " In HopID: %d => Out port: %d Out HopID: %d\n", |
---|
| 21 | + hop->in_hop_index, regs->out_port, regs->next_hop); |
---|
| 22 | + tb_port_dbg(port, " Weight: %d Priority: %d Credits: %d Drop: %d\n", |
---|
| 23 | + regs->weight, regs->priority, |
---|
| 24 | + regs->initial_credits, regs->drop_packages); |
---|
| 25 | + tb_port_dbg(port, " Counter enabled: %d Counter index: %d\n", |
---|
| 26 | + regs->counter_enable, regs->counter); |
---|
| 27 | + tb_port_dbg(port, " Flow Control (In/Eg): %d/%d Shared Buffer (In/Eg): %d/%d\n", |
---|
| 28 | + regs->ingress_fc, regs->egress_fc, |
---|
| 29 | + regs->ingress_shared_buffer, regs->egress_shared_buffer); |
---|
| 30 | + tb_port_dbg(port, " Unknown1: %#x Unknown2: %#x Unknown3: %#x\n", |
---|
| 31 | + regs->unknown1, regs->unknown2, regs->unknown3); |
---|
| 32 | +} |
---|
| 33 | + |
---|
| 34 | +static struct tb_port *tb_path_find_dst_port(struct tb_port *src, int src_hopid, |
---|
| 35 | + int dst_hopid) |
---|
| 36 | +{ |
---|
| 37 | + struct tb_port *port, *out_port = NULL; |
---|
| 38 | + struct tb_regs_hop hop; |
---|
| 39 | + struct tb_switch *sw; |
---|
| 40 | + int i, ret, hopid; |
---|
| 41 | + |
---|
| 42 | + hopid = src_hopid; |
---|
| 43 | + port = src; |
---|
| 44 | + |
---|
| 45 | + for (i = 0; port && i < TB_PATH_MAX_HOPS; i++) { |
---|
| 46 | + sw = port->sw; |
---|
| 47 | + |
---|
| 48 | + ret = tb_port_read(port, &hop, TB_CFG_HOPS, 2 * hopid, 2); |
---|
| 49 | + if (ret) { |
---|
| 50 | + tb_port_warn(port, "failed to read path at %d\n", hopid); |
---|
| 51 | + return NULL; |
---|
| 52 | + } |
---|
| 53 | + |
---|
| 54 | + if (!hop.enable) |
---|
| 55 | + return NULL; |
---|
| 56 | + |
---|
| 57 | + out_port = &sw->ports[hop.out_port]; |
---|
| 58 | + hopid = hop.next_hop; |
---|
| 59 | + port = out_port->remote; |
---|
| 60 | + } |
---|
| 61 | + |
---|
| 62 | + return out_port && hopid == dst_hopid ? out_port : NULL; |
---|
| 63 | +} |
---|
| 64 | + |
---|
| 65 | +static int tb_path_find_src_hopid(struct tb_port *src, |
---|
| 66 | + const struct tb_port *dst, int dst_hopid) |
---|
| 67 | +{ |
---|
| 68 | + struct tb_port *out; |
---|
| 69 | + int i; |
---|
| 70 | + |
---|
| 71 | + for (i = TB_PATH_MIN_HOPID; i <= src->config.max_in_hop_id; i++) { |
---|
| 72 | + out = tb_path_find_dst_port(src, i, dst_hopid); |
---|
| 73 | + if (out == dst) |
---|
| 74 | + return i; |
---|
| 75 | + } |
---|
| 76 | + |
---|
| 77 | + return 0; |
---|
29 | 78 | } |
---|
30 | 79 | |
---|
31 | 80 | /** |
---|
32 | | - * tb_path_alloc() - allocate a thunderbolt path |
---|
| 81 | + * tb_path_discover() - Discover a path |
---|
| 82 | + * @src: First input port of a path |
---|
| 83 | + * @src_hopid: Starting HopID of a path (%-1 if don't care) |
---|
| 84 | + * @dst: Expected destination port of the path (%NULL if don't care) |
---|
| 85 | + * @dst_hopid: HopID to the @dst (%-1 if don't care) |
---|
| 86 | + * @last: Last port is filled here if not %NULL |
---|
| 87 | + * @name: Name of the path |
---|
33 | 88 | * |
---|
34 | | - * Return: Returns a tb_path on success or NULL on failure. |
---|
| 89 | + * Follows a path starting from @src and @src_hopid to the last output |
---|
| 90 | + * port of the path. Allocates HopIDs for the visited ports. Call |
---|
| 91 | + * tb_path_free() to release the path and allocated HopIDs when the path |
---|
| 92 | + * is not needed anymore. |
---|
| 93 | + * |
---|
| 94 | + * Note function discovers also incomplete paths so caller should check |
---|
| 95 | + * that the @dst port is the expected one. If it is not, the path can be |
---|
| 96 | + * cleaned up by calling tb_path_deactivate() before tb_path_free(). |
---|
| 97 | + * |
---|
| 98 | + * Return: Discovered path on success, %NULL in case of failure |
---|
35 | 99 | */ |
---|
36 | | -struct tb_path *tb_path_alloc(struct tb *tb, int num_hops) |
---|
| 100 | +struct tb_path *tb_path_discover(struct tb_port *src, int src_hopid, |
---|
| 101 | + struct tb_port *dst, int dst_hopid, |
---|
| 102 | + struct tb_port **last, const char *name) |
---|
37 | 103 | { |
---|
38 | | - struct tb_path *path = kzalloc(sizeof(*path), GFP_KERNEL); |
---|
| 104 | + struct tb_port *out_port; |
---|
| 105 | + struct tb_regs_hop hop; |
---|
| 106 | + struct tb_path *path; |
---|
| 107 | + struct tb_switch *sw; |
---|
| 108 | + struct tb_port *p; |
---|
| 109 | + size_t num_hops; |
---|
| 110 | + int ret, i, h; |
---|
| 111 | + |
---|
| 112 | + if (src_hopid < 0 && dst) { |
---|
| 113 | + /* |
---|
| 114 | + * For incomplete paths the intermediate HopID can be |
---|
| 115 | + * different from the one used by the protocol adapter |
---|
| 116 | + * so in that case find a path that ends on @dst with |
---|
| 117 | + * matching @dst_hopid. That should give us the correct |
---|
| 118 | + * HopID for the @src. |
---|
| 119 | + */ |
---|
| 120 | + src_hopid = tb_path_find_src_hopid(src, dst, dst_hopid); |
---|
| 121 | + if (!src_hopid) |
---|
| 122 | + return NULL; |
---|
| 123 | + } |
---|
| 124 | + |
---|
| 125 | + p = src; |
---|
| 126 | + h = src_hopid; |
---|
| 127 | + num_hops = 0; |
---|
| 128 | + |
---|
| 129 | + for (i = 0; p && i < TB_PATH_MAX_HOPS; i++) { |
---|
| 130 | + sw = p->sw; |
---|
| 131 | + |
---|
| 132 | + ret = tb_port_read(p, &hop, TB_CFG_HOPS, 2 * h, 2); |
---|
| 133 | + if (ret) { |
---|
| 134 | + tb_port_warn(p, "failed to read path at %d\n", h); |
---|
| 135 | + return NULL; |
---|
| 136 | + } |
---|
| 137 | + |
---|
| 138 | + /* If the hop is not enabled we got an incomplete path */ |
---|
| 139 | + if (!hop.enable) |
---|
| 140 | + break; |
---|
| 141 | + |
---|
| 142 | + out_port = &sw->ports[hop.out_port]; |
---|
| 143 | + if (last) |
---|
| 144 | + *last = out_port; |
---|
| 145 | + |
---|
| 146 | + h = hop.next_hop; |
---|
| 147 | + p = out_port->remote; |
---|
| 148 | + num_hops++; |
---|
| 149 | + } |
---|
| 150 | + |
---|
| 151 | + path = kzalloc(sizeof(*path), GFP_KERNEL); |
---|
39 | 152 | if (!path) |
---|
40 | 153 | return NULL; |
---|
| 154 | + |
---|
| 155 | + path->name = name; |
---|
| 156 | + path->tb = src->sw->tb; |
---|
| 157 | + path->path_length = num_hops; |
---|
| 158 | + path->activated = true; |
---|
| 159 | + |
---|
41 | 160 | path->hops = kcalloc(num_hops, sizeof(*path->hops), GFP_KERNEL); |
---|
42 | 161 | if (!path->hops) { |
---|
43 | 162 | kfree(path); |
---|
44 | 163 | return NULL; |
---|
45 | 164 | } |
---|
46 | | - path->tb = tb; |
---|
47 | | - path->path_length = num_hops; |
---|
| 165 | + |
---|
| 166 | + p = src; |
---|
| 167 | + h = src_hopid; |
---|
| 168 | + |
---|
| 169 | + for (i = 0; i < num_hops; i++) { |
---|
| 170 | + int next_hop; |
---|
| 171 | + |
---|
| 172 | + sw = p->sw; |
---|
| 173 | + |
---|
| 174 | + ret = tb_port_read(p, &hop, TB_CFG_HOPS, 2 * h, 2); |
---|
| 175 | + if (ret) { |
---|
| 176 | + tb_port_warn(p, "failed to read path at %d\n", h); |
---|
| 177 | + goto err; |
---|
| 178 | + } |
---|
| 179 | + |
---|
| 180 | + if (tb_port_alloc_in_hopid(p, h, h) < 0) |
---|
| 181 | + goto err; |
---|
| 182 | + |
---|
| 183 | + out_port = &sw->ports[hop.out_port]; |
---|
| 184 | + next_hop = hop.next_hop; |
---|
| 185 | + |
---|
| 186 | + if (tb_port_alloc_out_hopid(out_port, next_hop, next_hop) < 0) { |
---|
| 187 | + tb_port_release_in_hopid(p, h); |
---|
| 188 | + goto err; |
---|
| 189 | + } |
---|
| 190 | + |
---|
| 191 | + path->hops[i].in_port = p; |
---|
| 192 | + path->hops[i].in_hop_index = h; |
---|
| 193 | + path->hops[i].in_counter_index = -1; |
---|
| 194 | + path->hops[i].out_port = out_port; |
---|
| 195 | + path->hops[i].next_hop_index = next_hop; |
---|
| 196 | + |
---|
| 197 | + h = next_hop; |
---|
| 198 | + p = out_port->remote; |
---|
| 199 | + } |
---|
| 200 | + |
---|
48 | 201 | return path; |
---|
| 202 | + |
---|
| 203 | +err: |
---|
| 204 | + tb_port_warn(src, "failed to discover path starting at HopID %d\n", |
---|
| 205 | + src_hopid); |
---|
| 206 | + tb_path_free(path); |
---|
| 207 | + return NULL; |
---|
49 | 208 | } |
---|
50 | 209 | |
---|
51 | 210 | /** |
---|
52 | | - * tb_path_free() - free a deactivated path |
---|
| 211 | + * tb_path_alloc() - allocate a thunderbolt path between two ports |
---|
| 212 | + * @tb: Domain pointer |
---|
| 213 | + * @src: Source port of the path |
---|
| 214 | + * @src_hopid: HopID used for the first ingress port in the path |
---|
| 215 | + * @dst: Destination port of the path |
---|
| 216 | + * @dst_hopid: HopID used for the last egress port in the path |
---|
| 217 | + * @link_nr: Preferred link if there are dual links on the path |
---|
| 218 | + * @name: Name of the path |
---|
| 219 | + * |
---|
| 220 | + * Creates path between two ports starting with given @src_hopid. Reserves |
---|
| 221 | + * HopIDs for each port (they can be different from @src_hopid depending on |
---|
| 222 | + * how many HopIDs each port already have reserved). If there are dual |
---|
| 223 | + * links on the path, prioritizes using @link_nr but takes into account |
---|
| 224 | + * that the lanes may be bonded. |
---|
| 225 | + * |
---|
| 226 | + * Return: Returns a tb_path on success or NULL on failure. |
---|
| 227 | + */ |
---|
| 228 | +struct tb_path *tb_path_alloc(struct tb *tb, struct tb_port *src, int src_hopid, |
---|
| 229 | + struct tb_port *dst, int dst_hopid, int link_nr, |
---|
| 230 | + const char *name) |
---|
| 231 | +{ |
---|
| 232 | + struct tb_port *in_port, *out_port, *first_port, *last_port; |
---|
| 233 | + int in_hopid, out_hopid; |
---|
| 234 | + struct tb_path *path; |
---|
| 235 | + size_t num_hops; |
---|
| 236 | + int i, ret; |
---|
| 237 | + |
---|
| 238 | + path = kzalloc(sizeof(*path), GFP_KERNEL); |
---|
| 239 | + if (!path) |
---|
| 240 | + return NULL; |
---|
| 241 | + |
---|
| 242 | + first_port = last_port = NULL; |
---|
| 243 | + i = 0; |
---|
| 244 | + tb_for_each_port_on_path(src, dst, in_port) { |
---|
| 245 | + if (!first_port) |
---|
| 246 | + first_port = in_port; |
---|
| 247 | + last_port = in_port; |
---|
| 248 | + i++; |
---|
| 249 | + } |
---|
| 250 | + |
---|
| 251 | + /* Check that src and dst are reachable */ |
---|
| 252 | + if (first_port != src || last_port != dst) { |
---|
| 253 | + kfree(path); |
---|
| 254 | + return NULL; |
---|
| 255 | + } |
---|
| 256 | + |
---|
| 257 | + /* Each hop takes two ports */ |
---|
| 258 | + num_hops = i / 2; |
---|
| 259 | + |
---|
| 260 | + path->hops = kcalloc(num_hops, sizeof(*path->hops), GFP_KERNEL); |
---|
| 261 | + if (!path->hops) { |
---|
| 262 | + kfree(path); |
---|
| 263 | + return NULL; |
---|
| 264 | + } |
---|
| 265 | + |
---|
| 266 | + in_hopid = src_hopid; |
---|
| 267 | + out_port = NULL; |
---|
| 268 | + |
---|
| 269 | + for (i = 0; i < num_hops; i++) { |
---|
| 270 | + in_port = tb_next_port_on_path(src, dst, out_port); |
---|
| 271 | + if (!in_port) |
---|
| 272 | + goto err; |
---|
| 273 | + |
---|
| 274 | + /* When lanes are bonded primary link must be used */ |
---|
| 275 | + if (!in_port->bonded && in_port->dual_link_port && |
---|
| 276 | + in_port->link_nr != link_nr) |
---|
| 277 | + in_port = in_port->dual_link_port; |
---|
| 278 | + |
---|
| 279 | + ret = tb_port_alloc_in_hopid(in_port, in_hopid, in_hopid); |
---|
| 280 | + if (ret < 0) |
---|
| 281 | + goto err; |
---|
| 282 | + in_hopid = ret; |
---|
| 283 | + |
---|
| 284 | + out_port = tb_next_port_on_path(src, dst, in_port); |
---|
| 285 | + if (!out_port) |
---|
| 286 | + goto err; |
---|
| 287 | + |
---|
| 288 | + /* |
---|
| 289 | + * Pick up right port when going from non-bonded to |
---|
| 290 | + * bonded or from bonded to non-bonded. |
---|
| 291 | + */ |
---|
| 292 | + if (out_port->dual_link_port) { |
---|
| 293 | + if (!in_port->bonded && out_port->bonded && |
---|
| 294 | + out_port->link_nr) { |
---|
| 295 | + /* |
---|
| 296 | + * Use primary link when going from |
---|
| 297 | + * non-bonded to bonded. |
---|
| 298 | + */ |
---|
| 299 | + out_port = out_port->dual_link_port; |
---|
| 300 | + } else if (!out_port->bonded && |
---|
| 301 | + out_port->link_nr != link_nr) { |
---|
| 302 | + /* |
---|
| 303 | + * If out port is not bonded follow |
---|
| 304 | + * link_nr. |
---|
| 305 | + */ |
---|
| 306 | + out_port = out_port->dual_link_port; |
---|
| 307 | + } |
---|
| 308 | + } |
---|
| 309 | + |
---|
| 310 | + if (i == num_hops - 1) |
---|
| 311 | + ret = tb_port_alloc_out_hopid(out_port, dst_hopid, |
---|
| 312 | + dst_hopid); |
---|
| 313 | + else |
---|
| 314 | + ret = tb_port_alloc_out_hopid(out_port, -1, -1); |
---|
| 315 | + |
---|
| 316 | + if (ret < 0) |
---|
| 317 | + goto err; |
---|
| 318 | + out_hopid = ret; |
---|
| 319 | + |
---|
| 320 | + path->hops[i].in_hop_index = in_hopid; |
---|
| 321 | + path->hops[i].in_port = in_port; |
---|
| 322 | + path->hops[i].in_counter_index = -1; |
---|
| 323 | + path->hops[i].out_port = out_port; |
---|
| 324 | + path->hops[i].next_hop_index = out_hopid; |
---|
| 325 | + |
---|
| 326 | + in_hopid = out_hopid; |
---|
| 327 | + } |
---|
| 328 | + |
---|
| 329 | + path->tb = tb; |
---|
| 330 | + path->path_length = num_hops; |
---|
| 331 | + path->name = name; |
---|
| 332 | + |
---|
| 333 | + return path; |
---|
| 334 | + |
---|
| 335 | +err: |
---|
| 336 | + tb_path_free(path); |
---|
| 337 | + return NULL; |
---|
| 338 | +} |
---|
| 339 | + |
---|
| 340 | +/** |
---|
| 341 | + * tb_path_free() - free a path |
---|
| 342 | + * @path: Path to free |
---|
| 343 | + * |
---|
| 344 | + * Frees a path. The path does not need to be deactivated. |
---|
53 | 345 | */ |
---|
54 | 346 | void tb_path_free(struct tb_path *path) |
---|
55 | 347 | { |
---|
56 | | - if (path->activated) { |
---|
57 | | - tb_WARN(path->tb, "trying to free an activated path\n") |
---|
58 | | - return; |
---|
| 348 | + int i; |
---|
| 349 | + |
---|
| 350 | + for (i = 0; i < path->path_length; i++) { |
---|
| 351 | + const struct tb_path_hop *hop = &path->hops[i]; |
---|
| 352 | + |
---|
| 353 | + if (hop->in_port) |
---|
| 354 | + tb_port_release_in_hopid(hop->in_port, |
---|
| 355 | + hop->in_hop_index); |
---|
| 356 | + if (hop->out_port) |
---|
| 357 | + tb_port_release_out_hopid(hop->out_port, |
---|
| 358 | + hop->next_hop_index); |
---|
59 | 359 | } |
---|
| 360 | + |
---|
60 | 361 | kfree(path->hops); |
---|
61 | 362 | kfree(path); |
---|
62 | 363 | } |
---|
.. | .. |
---|
74 | 375 | } |
---|
75 | 376 | } |
---|
76 | 377 | |
---|
| 378 | +static int __tb_path_deactivate_hop(struct tb_port *port, int hop_index, |
---|
| 379 | + bool clear_fc) |
---|
| 380 | +{ |
---|
| 381 | + struct tb_regs_hop hop; |
---|
| 382 | + ktime_t timeout; |
---|
| 383 | + int ret; |
---|
| 384 | + |
---|
| 385 | + /* Disable the path */ |
---|
| 386 | + ret = tb_port_read(port, &hop, TB_CFG_HOPS, 2 * hop_index, 2); |
---|
| 387 | + if (ret) |
---|
| 388 | + return ret; |
---|
| 389 | + |
---|
| 390 | + /* Already disabled */ |
---|
| 391 | + if (!hop.enable) |
---|
| 392 | + return 0; |
---|
| 393 | + |
---|
| 394 | + hop.enable = 0; |
---|
| 395 | + |
---|
| 396 | + ret = tb_port_write(port, &hop, TB_CFG_HOPS, 2 * hop_index, 2); |
---|
| 397 | + if (ret) |
---|
| 398 | + return ret; |
---|
| 399 | + |
---|
| 400 | + /* Wait until it is drained */ |
---|
| 401 | + timeout = ktime_add_ms(ktime_get(), 500); |
---|
| 402 | + do { |
---|
| 403 | + ret = tb_port_read(port, &hop, TB_CFG_HOPS, 2 * hop_index, 2); |
---|
| 404 | + if (ret) |
---|
| 405 | + return ret; |
---|
| 406 | + |
---|
| 407 | + if (!hop.pending) { |
---|
| 408 | + if (clear_fc) { |
---|
| 409 | + /* Clear flow control */ |
---|
| 410 | + hop.ingress_fc = 0; |
---|
| 411 | + hop.egress_fc = 0; |
---|
| 412 | + hop.ingress_shared_buffer = 0; |
---|
| 413 | + hop.egress_shared_buffer = 0; |
---|
| 414 | + |
---|
| 415 | + return tb_port_write(port, &hop, TB_CFG_HOPS, |
---|
| 416 | + 2 * hop_index, 2); |
---|
| 417 | + } |
---|
| 418 | + |
---|
| 419 | + return 0; |
---|
| 420 | + } |
---|
| 421 | + |
---|
| 422 | + usleep_range(10, 20); |
---|
| 423 | + } while (ktime_before(ktime_get(), timeout)); |
---|
| 424 | + |
---|
| 425 | + return -ETIMEDOUT; |
---|
| 426 | +} |
---|
| 427 | + |
---|
77 | 428 | static void __tb_path_deactivate_hops(struct tb_path *path, int first_hop) |
---|
78 | 429 | { |
---|
79 | 430 | int i, res; |
---|
80 | | - struct tb_regs_hop hop = { }; |
---|
| 431 | + |
---|
81 | 432 | for (i = first_hop; i < path->path_length; i++) { |
---|
82 | | - res = tb_port_write(path->hops[i].in_port, &hop, TB_CFG_HOPS, |
---|
83 | | - 2 * path->hops[i].in_hop_index, 2); |
---|
84 | | - if (res) |
---|
| 433 | + res = __tb_path_deactivate_hop(path->hops[i].in_port, |
---|
| 434 | + path->hops[i].in_hop_index, |
---|
| 435 | + path->clear_fc); |
---|
| 436 | + if (res && res != -ENODEV) |
---|
85 | 437 | tb_port_warn(path->hops[i].in_port, |
---|
86 | 438 | "hop deactivation failed for hop %d, index %d\n", |
---|
87 | 439 | i, path->hops[i].in_hop_index); |
---|
.. | .. |
---|
94 | 446 | tb_WARN(path->tb, "trying to deactivate an inactive path\n"); |
---|
95 | 447 | return; |
---|
96 | 448 | } |
---|
97 | | - tb_info(path->tb, |
---|
98 | | - "deactivating path from %llx:%x to %llx:%x\n", |
---|
99 | | - tb_route(path->hops[0].in_port->sw), |
---|
100 | | - path->hops[0].in_port->port, |
---|
101 | | - tb_route(path->hops[path->path_length - 1].out_port->sw), |
---|
102 | | - path->hops[path->path_length - 1].out_port->port); |
---|
| 449 | + tb_dbg(path->tb, |
---|
| 450 | + "deactivating %s path from %llx:%x to %llx:%x\n", |
---|
| 451 | + path->name, tb_route(path->hops[0].in_port->sw), |
---|
| 452 | + path->hops[0].in_port->port, |
---|
| 453 | + tb_route(path->hops[path->path_length - 1].out_port->sw), |
---|
| 454 | + path->hops[path->path_length - 1].out_port->port); |
---|
103 | 455 | __tb_path_deactivate_hops(path, 0); |
---|
104 | 456 | __tb_path_deallocate_nfc(path, 0); |
---|
105 | 457 | path->activated = false; |
---|
.. | .. |
---|
122 | 474 | return -EINVAL; |
---|
123 | 475 | } |
---|
124 | 476 | |
---|
125 | | - tb_info(path->tb, |
---|
126 | | - "activating path from %llx:%x to %llx:%x\n", |
---|
127 | | - tb_route(path->hops[0].in_port->sw), |
---|
128 | | - path->hops[0].in_port->port, |
---|
129 | | - tb_route(path->hops[path->path_length - 1].out_port->sw), |
---|
130 | | - path->hops[path->path_length - 1].out_port->port); |
---|
| 477 | + tb_dbg(path->tb, |
---|
| 478 | + "activating %s path from %llx:%x to %llx:%x\n", |
---|
| 479 | + path->name, tb_route(path->hops[0].in_port->sw), |
---|
| 480 | + path->hops[0].in_port->port, |
---|
| 481 | + tb_route(path->hops[path->path_length - 1].out_port->sw), |
---|
| 482 | + path->hops[path->path_length - 1].out_port->port); |
---|
131 | 483 | |
---|
132 | 484 | /* Clear counters. */ |
---|
133 | 485 | for (i = path->path_length - 1; i >= 0; i--) { |
---|
.. | .. |
---|
153 | 505 | for (i = path->path_length - 1; i >= 0; i--) { |
---|
154 | 506 | struct tb_regs_hop hop = { 0 }; |
---|
155 | 507 | |
---|
156 | | - /* |
---|
157 | | - * We do (currently) not tear down paths setup by the firmeware. |
---|
158 | | - * If a firmware device is unplugged and plugged in again then |
---|
159 | | - * it can happen that we reuse some of the hops from the (now |
---|
160 | | - * defunct) firmeware path. This causes the hotplug operation to |
---|
161 | | - * fail (the pci device does not show up). Clearing the hop |
---|
162 | | - * before overwriting it fixes the problem. |
---|
163 | | - * |
---|
164 | | - * Should be removed once we discover and tear down firmeware |
---|
165 | | - * paths. |
---|
166 | | - */ |
---|
167 | | - res = tb_port_write(path->hops[i].in_port, &hop, TB_CFG_HOPS, |
---|
168 | | - 2 * path->hops[i].in_hop_index, 2); |
---|
169 | | - if (res) { |
---|
170 | | - __tb_path_deactivate_hops(path, i); |
---|
171 | | - __tb_path_deallocate_nfc(path, 0); |
---|
172 | | - goto err; |
---|
173 | | - } |
---|
| 508 | + /* If it is left active deactivate it first */ |
---|
| 509 | + __tb_path_deactivate_hop(path->hops[i].in_port, |
---|
| 510 | + path->hops[i].in_hop_index, path->clear_fc); |
---|
174 | 511 | |
---|
175 | 512 | /* dword 0 */ |
---|
176 | 513 | hop.next_hop = path->hops[i].next_hop_index; |
---|
177 | 514 | hop.out_port = path->hops[i].out_port->port; |
---|
178 | | - /* TODO: figure out why these are good values */ |
---|
179 | | - hop.initial_credits = (i == path->path_length - 1) ? 16 : 7; |
---|
| 515 | + hop.initial_credits = path->hops[i].initial_credits; |
---|
180 | 516 | hop.unknown1 = 0; |
---|
181 | 517 | hop.enable = 1; |
---|
182 | 518 | |
---|
.. | .. |
---|
198 | 534 | & out_mask; |
---|
199 | 535 | hop.unknown3 = 0; |
---|
200 | 536 | |
---|
201 | | - tb_port_info(path->hops[i].in_port, "Writing hop %d, index %d", |
---|
202 | | - i, path->hops[i].in_hop_index); |
---|
203 | | - tb_dump_hop(path->hops[i].in_port, &hop); |
---|
| 537 | + tb_port_dbg(path->hops[i].in_port, "Writing hop %d\n", i); |
---|
| 538 | + tb_dump_hop(&path->hops[i], &hop); |
---|
204 | 539 | res = tb_port_write(path->hops[i].in_port, &hop, TB_CFG_HOPS, |
---|
205 | 540 | 2 * path->hops[i].in_hop_index, 2); |
---|
206 | 541 | if (res) { |
---|
.. | .. |
---|
210 | 545 | } |
---|
211 | 546 | } |
---|
212 | 547 | path->activated = true; |
---|
213 | | - tb_info(path->tb, "path activation complete\n"); |
---|
| 548 | + tb_dbg(path->tb, "path activation complete\n"); |
---|
214 | 549 | return 0; |
---|
215 | 550 | err: |
---|
216 | 551 | tb_WARN(path->tb, "path activation failed\n"); |
---|
.. | .. |
---|
233 | 568 | } |
---|
234 | 569 | return false; |
---|
235 | 570 | } |
---|
| 571 | + |
---|
| 572 | +/** |
---|
| 573 | + * tb_path_port_on_path() - Does the path go through certain port |
---|
| 574 | + * @path: Path to check |
---|
| 575 | + * @port: Switch to check |
---|
| 576 | + * |
---|
| 577 | + * Goes over all hops on path and checks if @port is any of them. |
---|
| 578 | + * Direction does not matter. |
---|
| 579 | + */ |
---|
| 580 | +bool tb_path_port_on_path(const struct tb_path *path, const struct tb_port *port) |
---|
| 581 | +{ |
---|
| 582 | + int i; |
---|
| 583 | + |
---|
| 584 | + for (i = 0; i < path->path_length; i++) { |
---|
| 585 | + if (path->hops[i].in_port == port || |
---|
| 586 | + path->hops[i].out_port == port) |
---|
| 587 | + return true; |
---|
| 588 | + } |
---|
| 589 | + |
---|
| 590 | + return false; |
---|
| 591 | +} |
---|