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: Mon, 3 Aug 2026 10:35:12 +0200 [thread overview]
Message-ID: <anBSwNnnFrZLqOwO@hovoldconsulting.com> (raw)
In-Reply-To: <amcyubnG9N8ZI0ro@moxa-ThinkCentre-M90t>
On Mon, Jul 27, 2026 at 06:28:09PM +0800, Crescent Hsieh wrote:
> On Tue, Jul 21, 2026 at 05:51:58PM +0200, Johan Hovold wrote:
> > On Tue, Jun 23, 2026 at 04:01:38PM +0800, Crescent Hsieh wrote:
> > > @@ -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?
>
> The size depends on the device. The G2 and Platform UART firmware have
> a 4096-byte buffer per port, while the G1 per-port buffer ranges from
> 32 KiB to 256 KiB depending on the number of ports.
>
> The bulk_out_size threshold is not derived from the firmware buffer
> size. It is used as a simple and conservative pacing interval that
> works across the device families without requiring family-specific
> buffer sizes in the driver. This results in more frequent
> SEND_NEXT waits and may reduce throughput.
Thanks for the details. So there may be some room for optimisation later
here by taking the buffer sizes into account. Not sure if it's worth it
though (and draining 32k at low line speeds will take quite some time).
> > > +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.
>
> Agreed, the custom write path is largely based on the generic write
> implementation. In v1, I attempted to implement the throttling in
> prepare_write_buffer() to avoid changing the generic path, but
> returning zero caused the generic implementation to submit a
> zero-length URB. In v2, I therefore added a custom write path so that
> submission could be stopped before preparing another URB.
Yeah, we probably want a custom implementation for this. Making the
generic implementation handle drivers returning 0 from
prepare_write_buffer() is straightforward, but then you also need to
notify them if URB submission failed (e.g. to prevent stalled writes).
But then again, if that's all that's needed it doesn't sound too bad.
> I will investigate whether this can instead be generalised through a
> small driver callback that allows the generic write path to check
> whether another URB may be submitted. This would avoid duplicating the
> generic implementation while preserving the behaviour of other
> USB-to-serial drivers.
I think we may have other drivers that could benefit from this too.
> I will also describe the SEND_NEXT behaviour in more detail in the v3
> commit message.
Sounds good, thanks.
Johan
next prev parent reply other threads:[~2026-08-03 8:35 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
2026-07-27 10:28 ` Crescent Hsieh
2026-08-03 8:35 ` Johan Hovold [this message]
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=anBSwNnnFrZLqOwO@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