Linux Documentation
 help / color / mirror / Atom feed
From: Mika Westerberg <mika.westerberg@linux.intel.com>
To: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
Cc: Andreas Noever <andreas.noever@gmail.com>,
	Mika Westerberg <westeri@kernel.org>,
	Yehezkel Bernat <YehezkelShB@gmail.com>,
	Jonathan Corbet <corbet@lwn.net>,
	Andrew Lunn <andrew+netdev@lunn.ch>,
	"David S . Miller" <davem@davemloft.net>,
	Eric Dumazet <edumazet@kernel.org>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	linux-usb@vger.kernel.org, linux-doc@vger.kernel.org,
	Mario Limonciello <Mario.Limonciello@amd.com>,
	Sanath S <Sanath.S@amd.com>
Subject: Re: [PATCH 1/3] thunderbolt: Allow tb_ring_start() to fail
Date: Tue, 6 Oct 2026 06:27:27 +0200	[thread overview]
Message-ID: <20261006042727.GN176164@black.igk.intel.com> (raw)
In-Reply-To: <13c3bccc590ec4a87bb718da0de40a3d041846d8.1790854235.git.Basavaraj.Natikar@amd.com>

Hi,

On Mon, Oct 05, 2026 at 07:13:39PM +0530, Basavaraj Natikar wrote:
> tb_ring_start() returns void, so its callers cannot tell when a ring fails
> to start and keep building an unusable tunnel. On some host interfaces a
> DMA HopID also cannot be reprogrammed until the host interface has been
> reset.
> 
> Hence, let tb_ring_start() return an error and unwind the callers on
> failure: stop an already started TX ring when its RX peer fails to start,
> and disable the DMA paths enabled before the rings were started.
> 
> A stream can also stay open after a failed resume. Therefore, free the
> partial allocations, clear the ring pointers, and let the subsequent I/O
> and close return without touching the freed rings. Check readiness under
> the device mutex and use a wake token so a wakeup is not lost across the
> unlocked sleep.
> 
> Co-developed-by: Sanath S <Sanath.S@amd.com>
> Signed-off-by: Sanath S <Sanath.S@amd.com>
> Signed-off-by: Basavaraj Natikar <Basavaraj.Natikar@amd.com>
> ---
> 
>  drivers/net/thunderbolt/main.c |  14 ++-
>  drivers/thunderbolt/ctl.c      |  23 ++++-
>  drivers/thunderbolt/dma_test.c |  32 +++++-
>  drivers/thunderbolt/nhi.c      |  12 ++-
>  drivers/thunderbolt/stream.c   | 175 +++++++++++++++++++++------------
>  include/linux/thunderbolt.h    |   2 +-
>  6 files changed, 181 insertions(+), 77 deletions(-)

This is pretty invasive change. How well did you test this?

I would have been hoping we can avoid touching the service drivers
completely.

> diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c
> index cf51b9c39f4e..93ccccc5cf8b 100644
> --- a/drivers/net/thunderbolt/main.c
> +++ b/drivers/net/thunderbolt/main.c
> @@ -669,8 +669,16 @@ static void tbnet_connected_work(struct work_struct *work)
>  	 * the Rx ring before any incoming packets are allowed to
>  	 * arrive.
>  	 */
> -	tb_ring_start(net->tx_ring.ring);
> -	tb_ring_start(net->rx_ring.ring);
> +	ret = tb_ring_start(net->tx_ring.ring);
> +	if (ret) {
> +		netdev_dbg(net->dev, "failed to start Tx ring, ret=%d\n", ret);

the ret= is not consistent wit the rest of the driver and should this be
_warn() instead? 

> +		goto err_release_hopid;
> +	}
> +	ret = tb_ring_start(net->rx_ring.ring);
> +	if (ret) {
> +		netdev_dbg(net->dev, "failed to start Rx ring, ret=%d\n", ret);
> +		goto err_stop_tx;
> +	}
> 
>  	ret = tbnet_alloc_rx_buffers(net, TBNET_RING_SIZE);
>  	if (ret)
> @@ -701,7 +709,9 @@ static void tbnet_connected_work(struct work_struct *work)
>  	tbnet_free_buffers(&net->rx_ring);
>  err_stop_rings:
>  	tb_ring_stop(net->rx_ring.ring);
> +err_stop_tx:
>  	tb_ring_stop(net->tx_ring.ring);
> +err_release_hopid:
>  	tb_xdomain_release_in_hopid(net->xd, net->remote_transmit_path);
>  	tbnet_connect_failed(net);
>  }
> diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c
> index 965988b18608..94b29430ffe4 100644
> --- a/drivers/thunderbolt/ctl.c
> +++ b/drivers/thunderbolt/ctl.c
> @@ -728,10 +728,27 @@ void tb_ctl_free(struct tb_ctl *ctl)
>   */
>  void tb_ctl_start(struct tb_ctl *ctl)
>  {
> -	int i;
> +	int i, ret;
>  	tb_ctl_dbg(ctl, "control channel starting...\n");
> -	tb_ring_start(ctl->tx); /* is used to ack hotplug packets, start first */
> -	tb_ring_start(ctl->rx);
> +
> +	/*
> +	 * TX is used to ack hotplug packets so start it first. -ENODEV
> +	 * means the host controller itself is already gone (expected on
> +	 * an unplug-during-suspend resume), so do not warn about that.

What?

> +	 */
> +	ret = tb_ring_start(ctl->tx);
> +	if (ret) {
> +		if (ret != -ENODEV)
> +			tb_ctl_WARN(ctl, "failed to start TX ring\n");
> +		return;
> +	}
> +	ret = tb_ring_start(ctl->rx);
> +	if (ret) {
> +		if (ret != -ENODEV)
> +			tb_ctl_WARN(ctl, "failed to start RX ring\n");
> +		tb_ring_stop(ctl->tx);
> +		return;
> +	}
>  	for (i = 0; i < TB_CTL_RX_PKG_COUNT; i++)
>  		tb_ctl_rx_submit(ctl->rx_packets[i]);
> 
> diff --git a/drivers/thunderbolt/dma_test.c b/drivers/thunderbolt/dma_test.c
> index bcecb0edcb81..e9c01bcfedf9 100644
> --- a/drivers/thunderbolt/dma_test.c
> +++ b/drivers/thunderbolt/dma_test.c
> @@ -203,12 +203,36 @@ static int dma_test_start_rings(struct dma_test *dt)
>  		return ret;
>  	}
> 
> -	if (dt->tx_ring)
> -		tb_ring_start(dt->tx_ring);
> -	if (dt->rx_ring)
> -		tb_ring_start(dt->rx_ring);
> +	if (dt->tx_ring) {
> +		ret = tb_ring_start(dt->tx_ring);
> +		if (ret)
> +			goto err_disable_paths;
> +	}
> +	if (dt->rx_ring) {
> +		ret = tb_ring_start(dt->rx_ring);
> +		if (ret)
> +			goto err_stop_tx;
> +	}
> 
>  	return 0;
> +
> +err_stop_tx:
> +	tb_xdomain_disable_paths(dt->xd, dt->tx_hopid,
> +				 dt->tx_ring ? dt->tx_ring->hop : -1,
> +				 dt->rx_hopid,
> +				 dt->rx_ring ? dt->rx_ring->hop : -1);
> +	if (dt->tx_ring)
> +		tb_ring_stop(dt->tx_ring);
> +	goto err_free;

This is way too fragile :-(

> +err_disable_paths:
> +	tb_xdomain_disable_paths(dt->xd, dt->tx_hopid,
> +				 dt->tx_ring ? dt->tx_ring->hop : -1,
> +				 dt->rx_hopid,
> +				 dt->rx_ring ? dt->rx_ring->hop : -1);
> +err_free:
> +	dma_test_free_rings(dt);
> +
> +	return ret;
>  }
> 
>  static void dma_test_stop_rings(struct dma_test *dt)
> diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c
> index e99a3fcc4a29..1960fa30e13a 100644
> --- a/drivers/thunderbolt/nhi.c
> +++ b/drivers/thunderbolt/nhi.c
> @@ -741,18 +741,24 @@ EXPORT_SYMBOL_GPL(tb_ring_alloc_rx);
>   * @ring: Ring to start
>   *
>   * Must not be invoked in parallel with tb_ring_stop().
> + *
> + * Returns %0 on success and negative errno in case of failure.
>   */
> -void tb_ring_start(struct tb_ring *ring)
> +int tb_ring_start(struct tb_ring *ring)
>  {
>  	u16 frame_size;
> +	int ret = 0;
>  	u32 flags;
> 
>  	spin_lock_irq(&ring->nhi->lock);
>  	spin_lock(&ring->lock);
> -	if (ring->nhi->going_away)
> +	if (ring->nhi->going_away) {
> +		ret = -ENODEV;
>  		goto err;
> +	}
>  	if (ring->running) {
>  		dev_WARN(ring->nhi->dev, "ring already started\n");
> +		ret = -EBUSY;
>  		goto err;
>  	}
>  	dev_dbg(ring->nhi->dev, "starting %s %d\n",
> @@ -810,6 +816,8 @@ void tb_ring_start(struct tb_ring *ring)
>  err:
>  	spin_unlock(&ring->lock);
>  	spin_unlock_irq(&ring->nhi->lock);
> +
> +	return ret;
>  }
>  EXPORT_SYMBOL_GPL(tb_ring_start);
> 
> diff --git a/drivers/thunderbolt/stream.c b/drivers/thunderbolt/stream.c
> index 4f9a57b77bfa..48b7e3f01312 100644
> --- a/drivers/thunderbolt/stream.c
> +++ b/drivers/thunderbolt/stream.c
> @@ -259,6 +259,9 @@ static void tbstream_ring_free(struct tbstream_ring *ring)
>  	enum dma_data_direction dir;
>  	int i;
> 
> +	if (!ring->frames)
> +		return;
> +
>  	if (ring->ring->is_tx)
>  		dir = DMA_TO_DEVICE;
>  	else
> @@ -279,6 +282,7 @@ static void tbstream_ring_free(struct tbstream_ring *ring)
>  	ring->prod = 0;
>  	ring->cons = 0;
>  	kfree(ring->frames);
> +	ring->frames = NULL;
>  }
> 
>  static inline bool tbstream_ring_available(const struct tbstream_ring *ring)
> @@ -575,6 +579,9 @@ static int tbstream_dev_send_close(struct tbstream_dev *sdev)
>  	struct tbstream_frame *sf;
>  	ktime_t timeout;
> 
> +	if (!sdev->tx_ring.ring)
> +		return -ESHUTDOWN;
> +
>  	/*
>  	 * Wait for the ring to have available slots before we send the
>  	 * CLOSE packet.
> @@ -645,7 +652,7 @@ static int tbstream_dev_start(struct tbstream_dev *sdev)
> 
>  	ret = tbstream_dev_alloc_tx_buffers(sdev);
>  	if (ret)
> -		goto err_free_tx;
> +		goto err_free_tx_buffers;
> 
>  	e2e_tx_hop = ring->hop;
>  	sof_mask = BIT(TBSTREAM_FRAME_START);
> @@ -672,8 +679,12 @@ static int tbstream_dev_start(struct tbstream_dev *sdev)
> 
>  	sdev->rx_pending = false;
> 
> -	tb_ring_start(sdev->tx_ring.ring);
> -	tb_ring_start(sdev->rx_ring.ring);
> +	ret = tb_ring_start(sdev->tx_ring.ring);
> +	if (ret)
> +		goto err_disable_paths;
> +	ret = tb_ring_start(sdev->rx_ring.ring);
> +	if (ret)
> +		goto err_stop_tx;
> 
>  	ret = tbstream_dev_alloc_rx_buffers(sdev);
>  	if (ret)
> @@ -682,13 +693,20 @@ static int tbstream_dev_start(struct tbstream_dev *sdev)
> 
>  err_stop:
>  	tb_ring_stop(sdev->rx_ring.ring);
> +	tbstream_ring_free(&sdev->rx_ring);
> +err_stop_tx:
>  	tb_ring_stop(sdev->tx_ring.ring);
> +err_disable_paths:
> +	tb_xdomain_disable_paths(xd, sdev->out_hopid, sdev->tx_ring.ring->hop,
> +				 sdev->in_hopid, sdev->rx_ring.ring->hop);
>  err_free_rx:
>  	tb_ring_free(sdev->rx_ring.ring);
> +	sdev->rx_ring.ring = NULL;
>  err_free_tx_buffers:
>  	tbstream_ring_free(&sdev->tx_ring);
> -err_free_tx:
>  	tb_ring_free(sdev->tx_ring.ring);
> +	sdev->tx_ring.ring = NULL;
> +	wake_up_interruptible(&sdev->wait);
> 
>  	return ret;
>  }
> @@ -708,6 +726,10 @@ static void tbstream_dev_stop(struct tbstream_dev *sdev)
>  {
>  	struct tb_xdomain *xd;
> 
> +	/* Starting may have failed and freed the rings already */
> +	if (!sdev->tx_ring.ring)
> +		return;
> +
>  	if (sdev->busy_poll) {
>  		/*
>  		 * When busy polling we must advance the ring ourselves
> @@ -742,6 +764,7 @@ static void tbstream_dev_stop(struct tbstream_dev *sdev)
>  	tbstream_ring_free(&sdev->tx_ring);
>  	tb_ring_free(sdev->tx_ring.ring);
>  	sdev->tx_ring.ring = NULL;
> +	wake_up_interruptible(&sdev->wait);
>  }
> 
>  /* Use only with read_iter/write_iter() to handle nowait */
> @@ -757,29 +780,6 @@ static int tbstream_dev_lock(struct tbstream_dev *sdev, bool nowait)
>  	return 0;
>  }
> 
> -/* Must not be called with @sdev->lock held */
> -static int tbstream_dev_busy_poll_wait(struct tbstream_dev *sdev,
> -				       struct tbstream_ring *ring)
> -{
> -	for (;;) {
> -		if (signal_pending(current))
> -			return -ERESTARTSYS;
> -		if (tb_ring_poll_pending(ring->ring))
> -			return 0;
> -		/*
> -		 * For TX ring we need to check the RX side too because
> -		 * it might have received CLOSE packet.
> -		 */
> -		if (ring == &sdev->tx_ring &&
> -		    tb_ring_poll_pending(sdev->rx_ring.ring))
> -			return 0;
> -		if (tbstream_dev_valid(sdev) != 0 ||
> -		    tbstream_dev_closed(sdev) || tbstream_dev_removed(sdev))
> -			return 0;
> -		cond_resched();
> -	}
> -}
> -
>  static bool
>  tbstream_dev_has_event(struct tbstream_dev *sdev, struct tbstream_ring *ring)
>  {
> @@ -796,6 +796,52 @@ tbstream_dev_has_event(struct tbstream_dev *sdev, struct tbstream_ring *ring)
>  	return tb_ring_poll_pending(sdev->rx_ring.ring);
>  }
> 
> +static bool tbstream_dev_ready(struct tbstream_dev *sdev,
> +			       struct tbstream_ring *ring)
> +{
> +	lockdep_assert_held(&sdev->lock);
> +
> +	/* Starting may have failed and freed the rings already */
> +	if (!sdev->tx_ring.ring)
> +		return true;
> +
> +	return tbstream_dev_has_event(sdev, ring) ||
> +	       tbstream_dev_close_received(sdev) ||
> +	       tb_ring_poll_pending(ring->ring);
> +}
> +
> +/* Must not be called with @sdev->lock held. */
> +static int tbstream_dev_wait(struct tbstream_dev *sdev,
> +			     struct tbstream_ring *ring)
> +{
> +	DEFINE_WAIT_FUNC(wait, woken_wake_function);

woken_wake_function?

> +	int ret = 0;
> +
> +	add_wait_queue(&sdev->wait, &wait);
> +	for (;;) {
> +		bool ready;
> +
> +		ret = mutex_lock_interruptible(&sdev->lock);
> +		if (ret)
> +			break;
> +		ready = tbstream_dev_ready(sdev, ring);
> +		mutex_unlock(&sdev->lock);
> +		if (ready)
> +			break;
> +		if (signal_pending(current)) {
> +			ret = -ERESTARTSYS;
> +			break;
> +		}
> +		if (sdev->busy_poll)
> +			cond_resched();
> +		else
> +			/* The wake token bridges the unlocked check-to-sleep gap. */
> +			wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
> +	}
> +	remove_wait_queue(&sdev->wait, &wait);

This is also really complex to just handle the error in tb_ring_start(). I
wonder what scenarios were actually tested?

> +	return ret;
> +}
> +
>  static ssize_t
>  tbstream_dev_fops_read_iter(struct kiocb *kiocb, struct iov_iter *to)
>  {
> @@ -814,6 +860,11 @@ tbstream_dev_fops_read_iter(struct kiocb *kiocb, struct iov_iter *to)
>  		return ret;
> 
>  	for (;;) {
> +		if (!sdev->tx_ring.ring) {
> +			mutex_unlock(&sdev->lock);
> +			return -ESHUTDOWN;

Now the ring went away behind the reader?

I don't think this is a good approach to be honest.

> +		}
> +
>  		/* Advance RX completions */
>  		tbstream_dev_advance_rx(sdev);
> 
> @@ -838,16 +889,9 @@ tbstream_dev_fops_read_iter(struct kiocb *kiocb, struct iov_iter *to)
>  		if (nowait)
>  			return -EAGAIN;
> 
> -		if (sdev->busy_poll) {
> -			ret = tbstream_dev_busy_poll_wait(sdev, &sdev->rx_ring);
> -			if (ret)
> -				return ret;
> -		} else {
> -			ret = wait_event_interruptible(sdev->wait,
> -				tbstream_dev_has_event(sdev, &sdev->rx_ring));
> -			if (ret)
> -				return ret;
> -		}
> +		ret = tbstream_dev_wait(sdev, &sdev->rx_ring);
> +		if (ret)
> +			return ret;
> 
>  		ret = tbstream_dev_lock(sdev, nowait);
>  		if (ret)
> @@ -957,6 +1001,11 @@ tbstream_dev_fops_write_iter(struct kiocb *kiocb, struct iov_iter *from)
>  		return ret;
> 
>  	for (;;) {
> +		if (!sdev->tx_ring.ring) {
> +			mutex_unlock(&sdev->lock);
> +			return -ESHUTDOWN;
> +		}
> +
>  		/* Advance TX (and RX) completions */
>  		tbstream_dev_advance_both(sdev);
> 
> @@ -984,17 +1033,9 @@ tbstream_dev_fops_write_iter(struct kiocb *kiocb, struct iov_iter *from)
>  		if (nowait)
>  			return -EAGAIN;
> 
> -		if (sdev->busy_poll) {
> -			ret = tbstream_dev_busy_poll_wait(sdev, &sdev->tx_ring);
> -			if (ret)
> -				return ret;
> -		} else {
> -			ret = wait_event_interruptible(sdev->wait,
> -				tbstream_dev_has_event(sdev, &sdev->tx_ring) ||
> -				tbstream_dev_close_received(sdev));
> -			if (ret)
> -				return ret;
> -		}
> +		ret = tbstream_dev_wait(sdev, &sdev->tx_ring);
> +		if (ret)
> +			return ret;
> 
>  		ret = tbstream_dev_lock(sdev, nowait);
>  		if (ret)
> @@ -1041,7 +1082,7 @@ tbstream_dev_fops_poll(struct file *file, struct poll_table_struct *wait)
> 
>  	poll_wait(file, &sdev->wait, wait);
>  	guard(mutex)(&sdev->lock);
> -	if (tbstream_dev_valid(sdev) != 0)
> +	if (tbstream_dev_valid(sdev) != 0 || !sdev->tx_ring.ring)
>  		return EPOLLHUP | EPOLLERR;
> 
>  	/*
> @@ -1105,6 +1146,11 @@ static int tbstream_dev_fops_open(struct inode *inode, struct file *file)
>  		}
>  	}
> 
> +	if (sdev->users && !sdev->tx_ring.ring) {
> +		ret = -ESHUTDOWN;
> +		goto err_unlock;
> +	}
> +
>  	/* Only on first open we allocate rings and enable paths */
>  	if (!sdev->users++) {
>  		ret = tbstream_dev_start(sdev);
> @@ -1136,7 +1182,7 @@ static int tbstream_dev_fops_release(struct inode *inode, struct file *file)
>  	struct tbstream_dev *sdev = to_tbstream_dev(file->private_data);
> 
>  	mutex_lock(&sdev->lock);
> -	if (--sdev->users == 0) {
> +	if (--sdev->users == 0 && sdev->tx_ring.ring) {
>  		/*
>  		 * Advance now in case there is CLOSE waiting in the RX
>  		 * ring.
> @@ -1884,14 +1930,14 @@ static int __maybe_unused tbstream_suspend(struct device *dev)
>  	if (!sg)
>  		return 0;
> 
> +	mutex_lock(&sg->lock);
>  	list_for_each_entry_reverse(sdev, &sg->dev_list, list) {
> -		tbstream_dev_get(sdev);
> -		/* Stop the stream (if it was open) */
> +		mutex_lock(&sdev->lock);
>  		if (sdev->users)
>  			tbstream_dev_stop(sdev);
> -		tbstream_dev_put(sdev);
> +		mutex_unlock(&sdev->lock);

What's this?

>  	}
> -
> +	mutex_unlock(&sg->lock);
>  	config_group_put(&sg->group);
>  	return 0;
>  }
> @@ -1902,28 +1948,27 @@ static int __maybe_unused tbstream_resume(struct device *dev)
>  	struct tbstream *stream = tb_service_get_drvdata(svc);
>  	struct tbstream_group *sg;
>  	struct tbstream_dev *sdev;
> +	int ret = 0;
> 
>  	sg = tbstream_group_find(stream);
>  	if (!sg)
>  		return 0;
> 
> +	mutex_lock(&sg->lock);
>  	list_for_each_entry(sdev, &sg->dev_list, list) {
> -		tbstream_dev_get(sdev);
> +		mutex_lock(&sdev->lock);
>  		if (sdev->users) {
> -			int ret;
> +			int err = tbstream_dev_start(sdev);
> 
> -			ret = tbstream_dev_start(sdev);
> -			if (ret) {
> -				tbstream_dev_put(sdev);
> -				config_group_put(&sg->group);
> -				return ret;
> -			}
> +			if (err && !ret)
> +				ret = err;
>  		}
> -		tbstream_dev_put(sdev);
> +		mutex_unlock(&sdev->lock);
> +		wake_up_interruptible(&sdev->wait);
>  	}
> -
> +	mutex_unlock(&sg->lock);
>  	config_group_put(&sg->group);
> -	return 0;
> +	return ret;
>  }
> 
>  static const struct dev_pm_ops tbstream_pm_ops = {
> diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h
> index 57502da29080..5fce1b67c376 100644
> --- a/include/linux/thunderbolt.h
> +++ b/include/linux/thunderbolt.h
> @@ -672,7 +672,7 @@ struct tb_ring *tb_ring_alloc_rx(struct tb_nhi *nhi, int hop, int size,
>  				 unsigned int flags, int e2e_tx_hop,
>  				 u16 sof_mask, u16 eof_mask,
>  				 void (*start_poll)(void *), void *poll_data);
> -void tb_ring_start(struct tb_ring *ring);
> +int tb_ring_start(struct tb_ring *ring);
>  bool tb_ring_flush(struct tb_ring *ring, unsigned int timeout_msec);
>  void tb_ring_stop(struct tb_ring *ring);
>  void tb_ring_free(struct tb_ring *ring);
> -- 
> 2.34.1

  reply	other threads:[~2026-10-06  4:27 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-05 13:43 [PATCH 0/3] thunderbolt: Reset affected AMD host interfaces before DMA HopID reuse Basavaraj Natikar
2026-10-05 13:43 ` [PATCH 1/3] thunderbolt: Allow tb_ring_start() to fail Basavaraj Natikar
2026-10-06  4:27   ` Mika Westerberg [this message]
2026-10-05 13:43 ` [PATCH 2/3] thunderbolt: Reset the host interface before reusing a DMA HopID Basavaraj Natikar
2026-10-05 14:35   ` Mika Westerberg
2026-10-05 15:12     ` Mario Limonciello
2026-10-05 16:50     ` Basavaraj Natikar
2026-10-06  4:33   ` Mika Westerberg
2026-10-06 14:47     ` Basavaraj Natikar
2026-10-05 13:43 ` [PATCH 3/3] thunderbolt: Add quirk to reset host interface for AMD USB4 routers Basavaraj Natikar

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261006042727.GN176164@black.igk.intel.com \
    --to=mika.westerberg@linux.intel.com \
    --cc=Basavaraj.Natikar@amd.com \
    --cc=Mario.Limonciello@amd.com \
    --cc=Sanath.S@amd.com \
    --cc=YehezkelShB@gmail.com \
    --cc=andreas.noever@gmail.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=corbet@lwn.net \
    --cc=davem@davemloft.net \
    --cc=edumazet@kernel.org \
    --cc=kuba@kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=westeri@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox