Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Thunderbolt driver - path/tunnel functionality
0004  *
0005  * Copyright (c) 2014 Andreas Noever <andreas.noever@gmail.com>
0006  * Copyright (C) 2019, Intel Corporation
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  * tb_path_discover() - Discover a path
0082  * @src: First input port of a path
0083  * @src_hopid: Starting HopID of a path (%-1 if don't care)
0084  * @dst: Expected destination port of the path (%NULL if don't care)
0085  * @dst_hopid: HopID to the @dst (%-1 if don't care)
0086  * @last: Last port is filled here if not %NULL
0087  * @name: Name of the path
0088  * @alloc_hopid: Allocate HopIDs for the ports
0089  *
0090  * Follows a path starting from @src and @src_hopid to the last output
0091  * port of the path. Allocates HopIDs for the visited ports (if
0092  * @alloc_hopid is true). Call tb_path_free() to release the path and
0093  * allocated HopIDs when the path is not needed anymore.
0094  *
0095  * Note function discovers also incomplete paths so caller should check
0096  * that the @dst port is the expected one. If it is not, the path can be
0097  * cleaned up by calling tb_path_deactivate() before tb_path_free().
0098  *
0099  * Return: Discovered path on success, %NULL in case of failure
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          * For incomplete paths the intermediate HopID can be
0117          * different from the one used by the protocol adapter
0118          * so in that case find a path that ends on @dst with
0119          * matching @dst_hopid. That should give us the correct
0120          * HopID for the @src.
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         /* If the hop is not enabled we got an incomplete path */
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  * tb_path_alloc() - allocate a thunderbolt path between two ports
0222  * @tb: Domain pointer
0223  * @src: Source port of the path
0224  * @src_hopid: HopID used for the first ingress port in the path
0225  * @dst: Destination port of the path
0226  * @dst_hopid: HopID used for the last egress port in the path
0227  * @link_nr: Preferred link if there are dual links on the path
0228  * @name: Name of the path
0229  *
0230  * Creates path between two ports starting with given @src_hopid. Reserves
0231  * HopIDs for each port (they can be different from @src_hopid depending on
0232  * how many HopIDs each port already have reserved). If there are dual
0233  * links on the path, prioritizes using @link_nr but takes into account
0234  * that the lanes may be bonded.
0235  *
0236  * Return: Returns a tb_path on success or NULL on failure.
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     /* Check that src and dst are reachable */
0262     if (first_port != src || last_port != dst) {
0263         kfree(path);
0264         return NULL;
0265     }
0266 
0267     /* Each hop takes two ports */
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         /* When lanes are bonded primary link must be used */
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          * Pick up right port when going from non-bonded to
0302          * bonded or from bonded to non-bonded.
0303          */
0304         if (out_port->dual_link_port) {
0305             if (!in_port->bonded && out_port->bonded &&
0306                 out_port->link_nr) {
0307                 /*
0308                  * Use primary link when going from
0309                  * non-bonded to bonded.
0310                  */
0311                 out_port = out_port->dual_link_port;
0312             } else if (!out_port->bonded &&
0313                    out_port->link_nr != link_nr) {
0314                 /*
0315                  * If out port is not bonded follow
0316                  * link_nr.
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  * tb_path_free() - free a path
0354  * @path: Path to free
0355  *
0356  * Frees a path. The path does not need to be deactivated.
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     /* Disable the path */
0400     ret = tb_port_read(port, &hop, TB_CFG_HOPS, 2 * hop_index, 2);
0401     if (ret)
0402         return ret;
0403 
0404     /* Already disabled */
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     /* Wait until it is drained */
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                  * Clear flow control. Protocol adapters
0425                  * IFC and ISE bits are vendor defined
0426                  * in the USB4 spec so we clear them
0427                  * only for pre-USB4 adapters.
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  * tb_path_activate() - activate a path
0483  * @path: Path to activate
0484  *
0485  * Activate a path starting with the last hop and iterating backwards. The
0486  * caller must fill path->hops before calling tb_path_activate().
0487  *
0488  * Return: Returns 0 on success or an error code on failure.
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     /* Clear counters. */
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     /* Add non flow controlled credits. */
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     /* Activate hops. */
0527     for (i = path->path_length - 1; i >= 0; i--) {
0528         struct tb_regs_hop hop = { 0 };
0529 
0530         /* If it is left active deactivate it first */
0531         __tb_path_deactivate_hop(path->hops[i].in_port,
0532                 path->hops[i].in_hop_index, path->clear_fc);
0533 
0534         /* dword 0 */
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         /* dword 1 */
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  * tb_path_is_invalid() - check whether any ports on the path are invalid
0579  * @path: Path to check
0580  *
0581  * Return: Returns true if the path is invalid, false otherwise.
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  * tb_path_port_on_path() - Does the path go through certain port
0597  * @path: Path to check
0598  * @port: Switch to check
0599  *
0600  * Goes over all hops on path and checks if @port is any of them.
0601  * Direction does not matter.
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 }