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