Files
omarchy-pkgs/pkgbuilds/linux-omarchy/0605-thunderbolt-pcie-rescan-after-tunnel.patch
T

187 lines
4.9 KiB
Diff

diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c
--- a/drivers/thunderbolt/domain.c
+++ b/drivers/thunderbolt/domain.c
@@ -909,6 +909,10 @@ int tb_domain_init(void)
{
int ret;
+ ret = tb_pci_rescan_init();
+ if (ret)
+ return ret;
+
tb_configfs_init();
tb_debugfs_init();
tb_acpi_init();
@@ -927,6 +931,7 @@ err_xdomain:
err_acpi:
tb_acpi_exit();
tb_debugfs_exit();
+ tb_pci_rescan_exit();
return ret;
}
@@ -940,4 +945,5 @@ void tb_domain_exit(void)
tb_acpi_exit();
tb_debugfs_exit();
tb_configfs_exit();
+ tb_pci_rescan_exit();
}
diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c
--- a/drivers/thunderbolt/tb.c
+++ b/drivers/thunderbolt/tb.c
@@ -9,7 +9,9 @@
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/delay.h>
+#include <linux/kref.h>
#include <linux/pm_runtime.h>
+#include <linux/workqueue.h>
#include <linux/platform_data/x86/apple.h>
#include "tb.h"
@@ -18,6 +20,7 @@
#define TB_TIMEOUT 100 /* ms */
#define TB_RELEASE_BW_TIMEOUT 10000 /* ms */
+#define TB_PCI_RESCAN_DELAY 300 /* ms */
/*
* How many time bandwidth allocation request from graphics driver is
@@ -49,6 +52,13 @@ MODULE_PARM_DESC(asym_threshold,
"threshold (Mb/s) when to Gen 4 switch link symmetry. 0 disables. (default: "
__MODULE_STRING(TB_ASYM_THRESHOLD) ")");
+struct tb_pci_rescan {
+ struct delayed_work work;
+ struct kref kref;
+ struct pci_bus *bus;
+ bool stopped;
+};
+
/**
* struct tb_cm - Simple Thunderbolt connection manager
* @tunnel_list: List of active tunnels
@@ -67,8 +77,11 @@ struct tb_cm {
bool hotplug_active;
struct delayed_work remove_work;
struct tb_bandwidth_group groups[MAX_GROUPS];
+ struct tb_pci_rescan *pci_rescan;
};
+static struct workqueue_struct *tb_pci_rescan_wq;
+
static inline struct tb *tcm_to_tb(struct tb_cm *tcm)
{
return ((void *)tcm - sizeof(struct tb));
@@ -2282,6 +2295,82 @@ static int tb_disconnect_pci(struct tb *tb, struct tb_switch *sw)
return 0;
}
+static void tb_pci_rescan_release(struct kref *kref)
+{
+ struct tb_pci_rescan *rescan = container_of(kref, typeof(*rescan), kref);
+
+ put_device(&rescan->bus->dev);
+ kfree(rescan);
+}
+
+static void tb_pci_rescan_work_fn(struct work_struct *work)
+{
+ struct tb_pci_rescan *rescan =
+ container_of(to_delayed_work(work), typeof(*rescan), work);
+
+ pci_lock_rescan_remove();
+ if (!READ_ONCE(rescan->stopped))
+ pci_rescan_bus(rescan->bus);
+ pci_unlock_rescan_remove();
+ kref_put(&rescan->kref, tb_pci_rescan_release);
+}
+
+static void tb_pci_rescan_schedule(struct tb *tb)
+{
+ struct tb_cm *tcm = tb_priv(tb);
+ struct tb_pci_rescan *rescan = tcm->pci_rescan;
+
+ if (!rescan) {
+ struct pci_bus *bus;
+
+ if (!dev_is_pci(tb->nhi->dev))
+ return;
+ bus = to_pci_dev(tb->nhi->dev)->bus;
+ if (bus->parent)
+ bus = bus->parent;
+
+ rescan = kzalloc_obj(*rescan);
+ if (!rescan)
+ return;
+ INIT_DELAYED_WORK(&rescan->work, tb_pci_rescan_work_fn);
+ kref_init(&rescan->kref);
+ get_device(&bus->dev);
+ rescan->bus = bus;
+ tcm->pci_rescan = rescan;
+ }
+
+ kref_get(&rescan->kref);
+ if (mod_delayed_work(tb_pci_rescan_wq, &rescan->work,
+ msecs_to_jiffies(TB_PCI_RESCAN_DELAY)))
+ kref_put(&rescan->kref, tb_pci_rescan_release);
+}
+
+static void tb_pci_rescan_stop(struct tb_cm *tcm)
+{
+ struct tb_pci_rescan *rescan = tcm->pci_rescan;
+
+ if (!rescan)
+ return;
+
+ WRITE_ONCE(rescan->stopped, true);
+ if (cancel_delayed_work(&rescan->work))
+ kref_put(&rescan->kref, tb_pci_rescan_release);
+ kref_put(&rescan->kref, tb_pci_rescan_release);
+ tcm->pci_rescan = NULL;
+}
+
+int tb_pci_rescan_init(void)
+{
+ tb_pci_rescan_wq = alloc_workqueue("thunderbolt_pci_rescan",
+ WQ_FREEZABLE | WQ_UNBOUND, 0);
+ return tb_pci_rescan_wq ? 0 : -ENOMEM;
+}
+
+void tb_pci_rescan_exit(void)
+{
+ destroy_workqueue(tb_pci_rescan_wq);
+}
+
static int tb_tunnel_pci(struct tb *tb, struct tb_switch *sw)
{
struct tb_port *up, *down, *port;
@@ -2323,6 +2412,7 @@ static int tb_tunnel_pci(struct tb *tb, struct tb_switch *sw)
tb_sw_warn(sw, "failed to connect xHCI\n");
list_add_tail(&tunnel->list, &tcm->tunnel_list);
+ tb_pci_rescan_schedule(tb);
return 0;
}
@@ -2957,6 +3047,7 @@ static void tb_stop(struct tb *tb)
struct tb_tunnel *n;
cancel_delayed_work(&tcm->remove_work);
+ tb_pci_rescan_stop(tcm);
/* tunnels are only present after everything has been initialized */
list_for_each_entry_safe(tunnel, n, &tcm->tunnel_list, list) {
/*
diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h
--- a/drivers/thunderbolt/tb.h
+++ b/drivers/thunderbolt/tb.h
@@ -759,6 +759,8 @@ static inline int tb_port_write(struct tb_port *port, const void *buffer,
struct tb *icm_probe(struct tb_nhi *nhi);
struct tb *tb_probe(struct tb_nhi *nhi);
+int tb_pci_rescan_init(void);
+void tb_pci_rescan_exit(void);
extern const struct device_type tb_domain_type;
extern const struct device_type tb_retimer_type;