any further thoughts on this?

On Sun, Dec 12, 2010 at 01:02:41PM +0000, Jacob Meuser wrote:
> On Sat, Dec 11, 2010 at 08:56:38PM +0000, Jacob Meuser wrote:
> > On Sat, Dec 11, 2010 at 08:35:00PM +0000, Jacob Meuser wrote:
> > > On Sat, Dec 11, 2010 at 08:14:24PM +0100, Mark Kettenis wrote:
> > > > > Date: Wed, 8 Dec 2010 06:07:30 +0000
> > > > > From: Jacob Meuser <jake...@sdf.lonestar.org>
> > > > > 
> > > > > I recently got a hp officejet 4500.  it's a 3-in-1 
> > > > > printer/scanner/fax.
> > > > > printing works great with the hplip packages.  scanning doesn't work 
> > > > > at
> > > > > all.
> > > > > 
> > > > > I tracked the problem to read() failing in usb_bulk_read() in libusb.
> > > > > errno was EINTR.  so I made this function just continue when read() 
> > > > > got
> > > > > EINTR.  I could then scan, but not very reliably.
> > > > > 
> > > > > so I went into the kernel, and tracked it to usbdi_util.c:
> > > > > 
> > > > > @ usbd_bulk_transfer(usbd_xfer_handle xfer, usbd_pipe_ha
> > > > > 
> > > > >       error = tsleep((caddr_t)xfer, PZERO | PCATCH, lbl, 0);
> > > > >       splx(s);
> > > > >       if (error) {
> > > > >               DPRINTF(("usbd_bulk_transfer: tsleep=%d\n", error));
> > > > >               usbd_abort_pipe(pipe);
> > > > >               return (USBD_INTERRUPTED);
> > > > >       }
> > > > > 
> > > > > on a hunch, I changed 'PZERO | PCATCH' to 'PWAIT'.  then scanning 
> > > > > became
> > > > > 100% reliable.  even full-page 1200dpi (~525MB of data) worked
> > > > > perfectly.
> > > > > 
> > > > > great.  but, what if we need to interrupt the transfer.  we don't want
> > > > > to hang here.
> > > > > 
> > > > > well, this function takes a timeout.  so, it's possible to make it
> > > > > return, even if the transfer stalls.  but is this used?  I looked
> > > > > at the ports that use libusb.  setting 0/infinite timeout for bulk
> > > > > transfers is extremely rare, only set in code marked experimental
> > > > > or problematic.  in the kernel, there are however some callers that
> > > > > use USBD_NO_TIMEOUT.
> > > > > 
> > > > > so, I offer the following.  if there's no timeout, behave like we do
> > > > > now and catch signals, and if there's a timeout, ignore signals.
> > > > > 
> > > > > I did the same for interrupt transfers because the situation seems
> > > > > to be the same.
> > > > > 
> > > > > thoughts?
> > > > 
> > > > Sorry, but this very much feels like you're hacking around a bug in
> > > > the application you're using.  If it installs signal handler without
> > > > specifying the SA_RESTART flag, it has to deal with read(2) failing
> > > > with EINTR.
> > > 
> > > that application would be libusb itself.  is a library supposed
> > > to be messing with signals?
> > 
> > to be clear, the actual read() is in usb_bulk_read() in libusb.
> > so, the application doesn't really know that there's a read().
> > 
> > I tried making libusb continue, but the problem is that when
> > usbd_bulk_transfer catches the signal, it aborts everything in
> > the pipe.  restarting from there correctly is complicated,
> > probably not even possible in a sane way.
> 
> I looked at all users of libusb.  none deal with usb_{bulk,interrupt}_read
> (the libusb "frontend") quitting with EINTR.  none set SA_RESTART.
> 
> usbd_{bulk,intr}_transfer() (in the usb(4) stack) do not obey
> SA_RESTART.  neither do the ugen(4) ends of those functions.
> 
> usbd_transfer() does not catch signals.  in fact.  the only other
> thing in the usb stack that does catch signals is usbread(), which
> is an interface to read messages from the usb event queue.  the
> event queue is stored in software.  it doesn't handle tsleep()
> returning ERESTART either, btw.
> 
> figuring out an absolute max timeout seems fairly straight-forward.
> timeouts are specified in miliseconds.  the usb frame rate is 1000 Hz.
> an empty frame signals the end of a transfer.  timeout = bytes,
> basically.
> 
> now, usbd_intr_transfer() may be a different story.  that will block
> (if not set or non-blocking io) until there is data to read.  most
> users of this in libusb do set a timeout though.  some also restart
> the read if they get ETIMEDOUT.
> 
> I think, catching signals is wrong.  at least, it's not expected by
> most of it's users and not even handled correctly in the kernel.
> 
> further, by not setting a timeout, it leaves the user to figure out
> that something is wrong.
> 
> 
> 
> > 
> > 
> > > 
> > > 
> > > > > Index: usbdi_util.c
> > > > > ===================================================================
> > > > > RCS file: /cvs/src/sys/dev/usb/usbdi_util.c,v
> > > > > retrieving revision 1.25
> > > > > diff -u -p usbdi_util.c
> > > > > --- usbdi_util.c      26 Jun 2008 05:42:19 -0000      1.25
> > > > > +++ usbdi_util.c      8 Dec 2010 04:58:49 -0000
> > > > > @@ -426,7 +426,7 @@ usbd_bulk_transfer(usbd_xfer_handle xfer, 
> > > > > usbd_pipe_ha
> > > > >      u_int16_t flags, u_int32_t timeout, void *buf, u_int32_t *size, 
> > > > > char *lbl)
> > > > >  {
> > > > >       usbd_status err;
> > > > > -     int s, error;
> > > > > +     int s, error, pri;
> > > > >  
> > > > >       usbd_setup_xfer(xfer, pipe, 0, buf, *size, flags, timeout,
> > > > >           usbd_bulk_transfer_cb);
> > > > > @@ -437,7 +437,8 @@ usbd_bulk_transfer(usbd_xfer_handle xfer, 
> > > > > usbd_pipe_ha
> > > > >               splx(s);
> > > > >               return (err);
> > > > >       }
> > > > > -     error = tsleep((caddr_t)xfer, PZERO | PCATCH, lbl, 0);
> > > > > +     pri = timeout == USBD_NO_TIMEOUT ? (PZERO | PCATCH) : PWAIT;
> > > > > +     error = tsleep((caddr_t)xfer, pri, lbl, 0);
> > > > >       splx(s);
> > > > >       if (error) {
> > > > >               DPRINTF(("usbd_bulk_transfer: tsleep=%d\n", error));
> > > > > @@ -467,7 +468,7 @@ usbd_intr_transfer(usbd_xfer_handle xfer, 
> > > > > usbd_pipe_ha
> > > > >      u_int16_t flags, u_int32_t timeout, void *buf, u_int32_t *size, 
> > > > > char *lbl)
> > > > >  {
> > > > >       usbd_status err;
> > > > > -     int s, error;
> > > > > +     int s, error, pri;
> > > > >  
> > > > >       usbd_setup_xfer(xfer, pipe, 0, buf, *size, flags, timeout,
> > > > >           usbd_intr_transfer_cb);
> > > > > @@ -478,7 +479,8 @@ usbd_intr_transfer(usbd_xfer_handle xfer, 
> > > > > usbd_pipe_ha
> > > > >               splx(s);
> > > > >               return (err);
> > > > >       }
> > > > > -     error = tsleep(xfer, PZERO | PCATCH, lbl, 0);
> > > > > +     pri = timeout == USBD_NO_TIMEOUT ? (PZERO | PCATCH) : PWAIT;
> > > > > +     error = tsleep(xfer, pri, lbl, 0);
> > > > >       splx(s);
> > > > >       if (error) {
> > > > >               DPRINTF(("usbd_intr_transfer: tsleep=%d\n", error));
> > > 
> > > -- 
> > > jake...@sdf.lonestar.org
> > > SDF Public Access UNIX System - http://sdf.lonestar.org
> > 
> > -- 
> > jake...@sdf.lonestar.org
> > SDF Public Access UNIX System - http://sdf.lonestar.org
> 
> -- 
> jake...@sdf.lonestar.org
> SDF Public Access UNIX System - http://sdf.lonestar.org

-- 
jake...@sdf.lonestar.org
SDF Public Access UNIX System - http://sdf.lonestar.org

Reply via email to