mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Johan Hovold <johan@kernel.org>
To: Crescent Hsieh <crescentcy.hsieh@moxa.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org,
	FangpingFP.Cheng@moxa.com, Epson.Chiang@moxa.com
Subject: Re: [PATCH v2 3/4] USB: serial: mxuport: handle SEND_NEXT transmit flow control
Date: Tue, 21 Jul 2026 17:51:58 +0200	[thread overview]
Message-ID: <al-VnkwqXYC7MoVG@hovoldconsulting.com> (raw)
In-Reply-To: <20260623080138.166398-5-crescentcy.hsieh@moxa.com>

On Tue, Jun 23, 2026 at 04:01:38PM +0800, Crescent Hsieh wrote:
> The device uses the SEND_NEXT event to pace host-to-device transmission.
> Without waiting for this event, continuous transmission on multiple
> ports can make the driver submit bulk-out URBs faster than the device
> can process them, which can result in data errors during burn-in
> testing.

Why does it matter if we're sending data for other ports? Isn't the
problem that we're sending more data than the per-port buffer can hold?

And is this an issue with all firmware versions, including the ones used
for the existing gen1 devices?
 
> Stop submitting further write URBs after requesting SEND_NEXT and resume
> transmission when the matching event is received.

How exactly does SEND_NEXT work? Is it signalled when the per-port
buffer drops below some threshold?

> This cannot be implemented by returning zero from
> prepare_write_buffer(), as the generic write implementation expects the
> callback to return a transfer length once data is available in the write
> FIFO. Add a mxuport-specific write path so that URB submission can be
> stopped and resumed explicitly.
> 
> This may reduce throughput, but avoids data errors under sustained
> transmit load.
> 
> Signed-off-by: Crescent Hsieh <crescentcy.hsieh@moxa.com>
> ---
>  drivers/usb/serial/mxuport.c | 165 +++++++++++++++++++++++++++++++++--
>  1 file changed, 156 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c
> index 067c8d9752e0..f3be2b3bd95b 100644
> --- a/drivers/usb/serial/mxuport.c
> +++ b/drivers/usb/serial/mxuport.c
> @@ -147,6 +147,8 @@ static const u16 mxuport_mux50u_fw_ver_offsets[] = {
>  #define UPORT_EVENT_LSR			4 /* Line status */
>  #define UPORT_EVENT_MCR			5 /* Modem control */
>  
> +#define UPORT_REQUEST_SEND_NEXT		0x80
> +
>  /* Definitions for serial event type */
>  #define SERIAL_EV_CTS			0x0008	/* CTS changed state */
>  #define SERIAL_EV_DSR			0x0010	/* DSR changed state */
> @@ -193,6 +195,8 @@ static const u16 mxuport_mux50u_fw_ver_offsets[] = {
>  
>  /* This structure holds all of the local port information */
>  struct mxuport_port {
> +	u32 sent_payload;
> +	u8 hold_reason;
>  	u8 mcr_state;		/* Last MCR state */
>  	u8 msr_state;		/* Last MSR state */
>  	struct mutex mutex;	/* Protects mcr_state */
> @@ -276,22 +280,148 @@ MODULE_DEVICE_TABLE(usb, mxuport_idtable);
>  static int mxuport_prepare_write_buffer(struct usb_serial_port *port,
>  					void *dest, size_t size)
>  {
> +	struct mxuport_port *mxport = usb_get_serial_port_data(port);
>  	u8 *buf = dest;
> +	unsigned long flags;
> +	bool request_send_next;
>  	int count;
>  
> -	count = kfifo_out_locked(&port->write_fifo, buf + HEADER_SIZE,
> -				 size - HEADER_SIZE,
> -				 &port->lock);
> +	spin_lock_irqsave(&port->lock, flags);
> +	count = kfifo_out(&port->write_fifo, buf + HEADER_SIZE,
> +			  size - HEADER_SIZE);
> +	mxport->sent_payload += count;
> +	request_send_next = mxport->sent_payload >= port->bulk_out_size;

How big are the per-port buffers?

> +	if (request_send_next)
> +		mxport->hold_reason |= MX_WAIT_FOR_SEND_NEXT;
> +	spin_unlock_irqrestore(&port->lock, flags);
>  
>  	put_unaligned_be16(port->port_number, buf);
>  	put_unaligned_be16(count, buf + 2);
>  
> +	if (request_send_next)
> +		buf[0] |= UPORT_REQUEST_SEND_NEXT;
> +
>  	dev_dbg(&port->dev, "%s - size %zd count %d\n", __func__,
>  		size, count);
>  
>  	return count + HEADER_SIZE;
>  }
>  
> +static int mxuport_write_start(struct usb_serial_port *port, gfp_t mem_flags)
> +{
> +	struct mxuport_port *mxport = usb_get_serial_port_data(port);
> +	struct urb *urb;
> +	unsigned long flags;
> +	int i;
> +	int count;
> +	int result;
> +
> +	if (test_and_set_bit_lock(USB_SERIAL_WRITE_BUSY, &port->flags))
> +		return 0;
> +retry:
> +	spin_lock_irqsave(&port->lock, flags);
> +	if ((mxport->hold_reason & MX_WAIT_FOR_SEND_NEXT) ||
> +	    !port->write_urbs_free || !kfifo_len(&port->write_fifo)) {
> +		clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags);
> +		spin_unlock_irqrestore(&port->lock, flags);
> +		return 0;
> +	}
> +
> +	i = (int)find_first_bit(&port->write_urbs_free,
> +				ARRAY_SIZE(port->write_urbs));
> +	spin_unlock_irqrestore(&port->lock, flags);
> +
> +	urb = port->write_urbs[i];
> +	count = mxuport_prepare_write_buffer(port, urb->transfer_buffer,
> +					     port->bulk_out_size);
> +	urb->transfer_buffer_length = count;
> +	usb_serial_debug_data(&port->dev, __func__, count,
> +			      urb->transfer_buffer);
> +
> +	spin_lock_irqsave(&port->lock, flags);
> +	port->tx_bytes += count;
> +	spin_unlock_irqrestore(&port->lock, flags);
> +
> +	clear_bit(i, &port->write_urbs_free);
> +	result = usb_submit_urb(urb, mem_flags);
> +	if (result) {
> +		dev_err_console(port, "%s - error submitting urb: %d\n",
> +				__func__, result);
> +		set_bit(i, &port->write_urbs_free);
> +		spin_lock_irqsave(&port->lock, flags);
> +		port->tx_bytes -= count;

> +		if (mxport->hold_reason & MX_WAIT_FOR_SEND_NEXT) {
> +			mxport->hold_reason &= ~MX_WAIT_FOR_SEND_NEXT;
> +			mxport->sent_payload = 0;
> +		}

Shouldn't you undo the effects of prepare_write_buffer() and subtract
count from sent_payload (and clear the flag) unconditionally?

> +		spin_unlock_irqrestore(&port->lock, flags);
> +
> +		clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags);
> +		return result;
> +	}
> +
> +	goto retry;
> +}

This is a more or less verbatim copy of the generic write
implementation. If we go this way you should at least mention that you
copied it in the commit message, but perhaps we should try to find a way
to generalise it instead.

Also, if you really need a custom implementation to throttle writes,
then shouldn't using one URB be enough? The other one is essentially
there to allow for higher throughput which we need to give up for
correctness here anyway.

> +
> +static int mxuport_write(struct tty_struct *tty, struct usb_serial_port *port,
> +			 const unsigned char *buf, int count)
> +{
> +	int result;
> +
> +	if (!port->bulk_out_size)
> +		return -ENODEV;

This is redundant in a custom implementation.

> +
> +	if (!count)
> +		return 0;
> +
> +	count = kfifo_in_locked(&port->write_fifo, buf, count, &port->lock);
> +	result = mxuport_write_start(port, GFP_ATOMIC);
> +	if (result)
> +		return result;
> +
> +	return count;
> +}

Johan

  reply	other threads:[~2026-07-21 15:52 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-23  8:01 [PATCH v2 0/4] USB: serial: mxuport: add MUX50U support and updates Crescent Hsieh
2026-06-23  8:01 ` [PATCH v2 1/4] USB: serial: mxuport: clean up firmware version handling Crescent Hsieh
2026-07-21 14:34   ` Johan Hovold
2026-07-27 10:27     ` Crescent Hsieh
2026-06-23  8:01 ` [PATCH v2 2/4] USB: serial: mxuport: add MUX50U-based device support Crescent Hsieh
2026-07-21 14:59   ` Johan Hovold
2026-07-27 10:27     ` Crescent Hsieh
2026-08-03  7:08       ` Johan Hovold
2026-06-23  8:01 ` [PATCH v2 3/4] USB: serial: mxuport: handle SEND_NEXT transmit flow control Crescent Hsieh
2026-07-21 15:51   ` Johan Hovold [this message]
2026-07-27 10:28     ` Crescent Hsieh
2026-08-03  8:35       ` Johan Hovold
2026-06-23  8:01 ` [PATCH v2 4/4] USB: serial: mxuport: support RS485 mode configuration Crescent Hsieh

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=al-VnkwqXYC7MoVG@hovoldconsulting.com \
    --to=johan@kernel.org \
    --cc=Epson.Chiang@moxa.com \
    --cc=FangpingFP.Cheng@moxa.com \
    --cc=crescentcy.hsieh@moxa.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.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

all inboxes | Powered by JetHome®