Wed, 18 May 2016 19:08:29 +0200 Martin Pieuchot <[email protected]>
> On 18/05/16(Wed) 18:28, Patrick Wildt wrote:
> > Hi,
> > 
> > I had the pleasure of debugging a USB mass storage device that showed
> > interesting behaviour when used with our stack and in combination with
> > the blink(1) usb device.
> > 
> > The blink(1)-tool depends on libusb.  Libusb calls ioctl(USB_DEVICEINFO)
> > plenty of times to get a map of the USB tree.  Per ioctl, three USB
> > requests are created (to read: vendor, product, serial).  This kind of
> > floods all connected USB devices.
> > 
> > I have a hotplugd script that waits for usb mass storage devices, reads
> > the disklabel, fscks and tries to mount a partition.  Due to the ioctl,
> > one of those calls, which mostly only read from the storage device,
> > freeze until they later time out.  The flood of read requests caused
> > by the ioctl seem to make the mass storage halt its operation.
> > 
> > The same issue can be reproduced by calling usbdevs(8) in a loop.  It
> > uses the same ioctl and "breaks" this usb mass storage.
> >
> > An idea is to cache those three strings, so that the ioctl does not
> > create any requests.  NetBSD seems to have implemented a caching
> > mechanism to fix a similar issue.  Though they have not enabled the
> > caching for that specific ioctl.  
> 
> They did this caching because their stack call the function multiple
> times during the attachment of a device.
> 
> > The idea of this diff, adapted from NetBSD, is that we read out the
> > strings early on when the device is found, store a copy of those per
> > device.  Then whenever it's needed it can be used.
> > 
> > ok? Comments?  
> 
> ok mpi@
> 
> I'd also like to see if we can always use the cache.  Because it is
> unlikely that a vendor or product name will change.

Should not, but in reality.. especially some non-uniform sneaky USB
devices, are programmable and tinker accessible, often with intent.
Even if rare and/or non std usage, USB ID strings are not immutable.

> > diff --git sys/dev/usb/usb.c sys/dev/usb/usb.c
> > index 732ecfd..c198bf0 100644
> > --- sys/dev/usb/usb.c
> > +++ sys/dev/usb/usb.c
> > @@ -520,7 +520,7 @@ usb_fill_di_task(void *arg)
> >     if (dev == NULL)
> >             return;
> >  
> > -   usbd_fill_deviceinfo(dev, di, 1);
> > +   usbd_fill_deviceinfo(dev, di, 0);
> >  }
> >  
> >  void
> > diff --git sys/dev/usb/usb_subr.c sys/dev/usb/usb_subr.c
> > index a255793..fef6d52 100644
> > --- sys/dev/usb/usb_subr.c
> > +++ sys/dev/usb/usb_subr.c
> > @@ -63,6 +63,7 @@ usbd_status       usbd_set_config(struct usbd_device *, 
> > int);
> >  void               usbd_devinfo(struct usbd_device *, int, char *, size_t);
> >  void               usbd_devinfo_vp(struct usbd_device *, char *, size_t,
> >                 char *, size_t, int);
> > +void               usbd_get_device_string(struct usbd_device *, uByte, 
> > char **);
> >  char               *usbd_get_string(struct usbd_device *, int, char *, 
> > size_t);
> >  int                usbd_getnewaddr(struct usbd_bus *);
> >  int                usbd_print(void *, const char *);
> > @@ -212,6 +213,28 @@ usbd_trim_spaces(char *p)
> >  }
> >  
> >  void
> > +usbd_get_device_string(struct usbd_device *dev, uByte index, char **buf)
> > +{
> > +   char *b = malloc(USB_MAX_STRING_LEN, M_USB, M_NOWAIT);
> > +   if (b != NULL) {
> > +           usbd_get_string(dev, index, b, USB_MAX_STRING_LEN);
> > +           usbd_trim_spaces(b);
> > +   }
> > +   *buf = b;
> > +}
> > +
> > +void
> > +usbd_get_device_strings(struct usbd_device *dev)
> > +{
> > +   usbd_get_device_string(dev, dev->ddesc.iManufacturer,
> > +       &dev->vendor);
> > +   usbd_get_device_string(dev, dev->ddesc.iProduct,
> > +       &dev->product);
> > +   usbd_get_device_string(dev, dev->ddesc.iSerialNumber,
> > +       &dev->serial);
> > +}
> > +
> > +void
> >  usbd_devinfo_vp(struct usbd_device *dev, char *v, size_t vl,
> >      char *p, size_t pl, int usedev)
> >  {
> > @@ -232,6 +255,11 @@ usbd_devinfo_vp(struct usbd_device *dev, char *v, 
> > size_t vl,
> >             usbd_trim_spaces(vendor);
> >             product = usbd_get_string(dev, udd->iProduct, p, pl);
> >             usbd_trim_spaces(product);
> > +   } else {
> > +           if (dev->vendor != NULL)
> > +                   vendor = dev->vendor;
> > +           if (dev->product != NULL)
> > +                   product = dev->product;
> >     }
> >  #ifdef USBVERBOSE
> >     if (vendor == NULL || product == NULL) {
> > @@ -294,7 +322,7 @@ usbd_devinfo(struct usbd_device *dev, int showclass, 
> > char *base, size_t len)
> >     char *cp = base;
> >     int bcdDevice, bcdUSB;
> >  
> > -   usbd_devinfo_vp(dev, vendor, sizeof vendor, product, sizeof product, 1);
> > +   usbd_devinfo_vp(dev, vendor, sizeof vendor, product, sizeof product, 0);
> >     snprintf(cp, len, "\"%s %s\"", vendor, product);
> >     cp += strlen(cp);
> >     if (showclass) {
> > @@ -1188,6 +1216,8 @@ usbd_new_device(struct device *parent, struct 
> > usbd_bus *bus, int depth,
> >     DPRINTF(("usbd_new_device: new dev (addr %d), dev=%p, parent=%p\n",
> >              addr, dev, parent));
> >  
> > +   usbd_get_device_strings(dev);
> > +
> >     err = usbd_probe_and_attach(parent, dev, port, addr);
> >     if (err) {
> >             usb_free_device(dev);
> > @@ -1304,8 +1334,13 @@ usbd_fill_deviceinfo(struct usbd_device *dev, struct 
> > usb_device_info *di,
> >             di->udi_nports = 0;
> >  
> >     bzero(di->udi_serial, sizeof(di->udi_serial));
> > -   usbd_get_string(dev, dev->ddesc.iSerialNumber, di->udi_serial,
> > -       sizeof(di->udi_serial));
> > +   if (!usedev && dev->serial != NULL) {
> > +           strlcpy(di->udi_serial, dev->serial,
> > +               sizeof(di->udi_serial));
> > +   } else {
> > +           usbd_get_string(dev, dev->ddesc.iSerialNumber,
> > +               di->udi_serial, sizeof(di->udi_serial));
> > +   }
> >  }
> >  
> >  /* Retrieve a complete descriptor for a certain device and index. */
> > @@ -1368,6 +1403,13 @@ usb_free_device(struct usbd_device *dev)
> >             free(dev->subdevs, M_USB, 0);
> >     dev->bus->devices[dev->address] = NULL;
> >  
> > +   if (dev->vendor != NULL)
> > +           free(dev->vendor, M_USB, USB_MAX_STRING_LEN);
> > +   if (dev->product != NULL)
> > +           free(dev->product, M_USB, USB_MAX_STRING_LEN);
> > +   if (dev->serial != NULL)
> > +           free(dev->serial, M_USB, USB_MAX_STRING_LEN);
> > +
> >     free(dev, M_USB, 0);
> >  }
> >  
> > diff --git sys/dev/usb/usbdivar.h sys/dev/usb/usbdivar.h
> > index 3a7bac3..912234f 100644
> > --- sys/dev/usb/usbdivar.h
> > +++ sys/dev/usb/usbdivar.h
> > @@ -150,6 +150,10 @@ struct usbd_device {
> >     struct usbd_hub        *hub;           /* only if this is a hub */
> >     struct device         **subdevs;       /* sub-devices, 0 terminated */
> >     int                     ndevs;         /* # of subdevs */
> > +
> > +   char                   *serial;        /* serial number, can be NULL */
> > +   char                   *vendor;        /* vendor string, can be NULL */
> > +   char                   *product;       /* product string, can be NULL */
> >  };
> >  
> >  struct usbd_interface {
> > @@ -229,6 +233,7 @@ void usbd_dump_pipe(struct usbd_pipe *);
> >  
> >  /* Routines from usb_subr.c */
> >  int                usbctlprint(void *, const char *);
> > +void               usbd_get_device_strings(struct usbd_device *);
> >  void               usb_delay_ms(struct usbd_bus *, u_int);
> >  usbd_status        usbd_port_disown_to_1_1(struct usbd_device *, int);
> >  int                usbd_reset_port(struct usbd_device *, int);
> >   
> 
> 

Reply via email to