On Fri, May 10, 2019 at 11:01:09AM +0100, Mayuresh Kulkarni wrote: > - The current driver increments the PM ref-count in its .open > API and decrements it in its .close API. > - Due to this, it is not possible for the usb_device to go into > suspend (L2) even if all of its interfaces report idle to usb-core. > - In order to allow the suspend, 2 new ioctls are added: > 1. USBDEVFS_SUSPEND: calls auto-suspend and indicates to usb/pm core > to attempt suspend, if appropriate. This is a non-blocking call. > 2. USBDEVFS_WAIT_RESUME: waits for resume. This is a blocking call. > The resume could happen due to: > host-wake (i.e.: any driver bound to interface(s) of device wants to > send/receive control/data) > OR > remote-wake (i.e.: when remote-wake enabled device generates a > remote-wake to host). > In both these conditions, this call will return. > - It is expected that: > 1. When user-space thinks the device is idle from its point-of-view, > it calls USBDEVFS_SUSPEND. > 2. After USBDEVFS_WAIT_RESUME call returns, > the callers queries the device/interface of its interest to see > what caused resume and take appropriate action on it. I'm going to make a bunch of random comments on this patch, some cosmetic... First off, the above is horrible to try to read, please format things in a sane way such that we can understand it well. > The link to relevant discussion about this patch on linux-usb is - > https://www.spinics.net/lists/linux-usb/msg173285.html You should not need to reference any external web site for a changelog entry, just put the needed information in the changelog itself. > Signed-off-by: Mayuresh Kulkarni <mkulkarni@xxxxxxxxxxxxxxxxxxxxx> > --- > drivers/usb/core/devio.c | 114 ++++++++++++++++++++++++++++++++++++-- > include/uapi/linux/usbdevice_fs.h | 2 + > 2 files changed, 112 insertions(+), 4 deletions(-) > > diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c > index fa783531..67dc326 100644 > --- a/drivers/usb/core/devio.c > +++ b/drivers/usb/core/devio.c > @@ -68,6 +68,9 @@ struct usb_dev_state { > u32 disabled_bulk_eps; > bool privileges_dropped; > unsigned long interface_allowed_mask; > + bool runtime_active; > + bool is_suspended; > + wait_queue_head_t wait_resume; That's some crazy alignment issues, please don't waste bytes for no good reason. And can you document these fields somewhere? > }; > > struct usb_memory { > @@ -444,6 +447,23 @@ static struct async *async_getpending(struct usb_dev_state *ps, > return NULL; > } > > +static int async_getpending_count(struct usb_dev_state *ps) > +{ > + struct async *as; > + int count; > + unsigned long flags; > + > + spin_lock_irqsave(&ps->lock, flags); > + if (list_empty(&ps->async_pending)) > + count = 0; > + else > + list_for_each_entry(as, &ps->async_pending, asynclist) > + count++; Doesn't list_for_each_entry() work just fine on an empty list? Just set count to 0 up above, right? > + spin_unlock_irqrestore(&ps->lock, flags); So, right after you call this function, and drop the lock, the entries can change, making your "count" invalid and stale. So you can not trust the value at all, right? > + > + return count; > +} > + > static void snoop_urb(struct usb_device *udev, > void __user *userurb, int pipe, unsigned length, > int timeout_or_status, enum snoop_when when, > @@ -699,16 +719,26 @@ static void driver_disconnect(struct usb_interface *intf) > destroy_async_on_interface(ps, ifnum); > } > > -/* The following routines are merely placeholders. There is no way > - * to inform a user task about suspend or resumes. > - */ > static int driver_suspend(struct usb_interface *intf, pm_message_t msg) > { > + struct usb_dev_state *ps = usb_get_intfdata(intf); > + > + ps->is_suspended = true; No locking needed? > + snoop(&ps->dev->dev, "driver-suspend\n"); Why? Does anyone use the snoop api anymore? > + > return 0; > } > > static int driver_resume(struct usb_interface *intf) > { > + struct usb_dev_state *ps = usb_get_intfdata(intf); > + > + ps->runtime_active = true; > + wake_up(&ps->wait_resume); > + > + snoop(&ps->dev->dev, "driver-resume: runtime-active = %d\n", > + ps->runtime_active); What does runtime_active give userspace here? Again, who is using snoop? And isn't runtime_active a bool? It's always going to be "true" here. Is this just debugging code left in? > + > return 0; > } > > @@ -718,6 +748,7 @@ struct usb_driver usbfs_driver = { > .disconnect = driver_disconnect, > .suspend = driver_suspend, > .resume = driver_resume, > + .supports_autosuspend = 1, > }; > > static int claimintf(struct usb_dev_state *ps, unsigned int ifnum) > @@ -963,6 +994,27 @@ static struct usb_device *usbdev_lookup_by_devt(dev_t devt) > return to_usb_device(dev); > } > > +/* must be called with usb-dev lock held */ > +static int usbdev_do_resume(struct usb_dev_state *ps) > +{ > + int ret = 0; > + > + if (!ps->runtime_active) { > + snoop(&ps->dev->dev, "suspended..resume now\n"); Again snoop? > + ps->is_suspended = false; > + if (usb_autoresume_device(ps->dev)) { > + ret = -EIO; > + goto _out; > + } > + snoop(&ps->dev->dev, "resume done..active now\n"); > + ps->runtime_active = true; > + wake_up(&ps->wait_resume); No locking at all? > + } > + > +_out: Why is this even needed, just return -EIO above and all is good. But again, is any locking needed here? > + return ret; > +} > + > /* > * file operations > */ > @@ -1008,6 +1060,9 @@ static int usbdev_open(struct inode *inode, struct file *file) > INIT_LIST_HEAD(&ps->async_completed); > INIT_LIST_HEAD(&ps->memory_list); > init_waitqueue_head(&ps->wait); > + init_waitqueue_head(&ps->wait_resume); > + ps->runtime_active = true; > + ps->is_suspended = false; > ps->disc_pid = get_pid(task_pid(current)); > ps->cred = get_current_cred(); > smp_wmb(); > @@ -1034,6 +1089,10 @@ static int usbdev_release(struct inode *inode, struct file *file) > struct async *as; > > usb_lock_device(dev); > + > + /* what can we can do if resume fails? */ You tell me! > + usbdev_do_resume(ps); > + > usb_hub_release_all_ports(dev, ps); > > list_del_init(&ps->list); > @@ -2355,6 +2414,18 @@ static int proc_drop_privileges(struct usb_dev_state *ps, void __user *arg) > return 0; > } > > +static int proc_wait_resume(struct usb_dev_state *ps) > +{ > + int ret; > + > + snoop(&ps->dev->dev, "wait-for-resume\n"); > + ret = wait_event_interruptible(ps->wait_resume, > + (ps->runtime_active == true)); > + snoop(&ps->dev->dev, "wait-for-resume...done\n"); This is just debugging code, right? Please remove. > + > + return ret; > +} > + > /* > * NOTE: All requests here that have interface numbers as parameters > * are assuming that somehow the configuration has been prevented from > @@ -2373,6 +2444,25 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, > > usb_lock_device(dev); > > + if (cmd != USBDEVFS_WAIT_RESUME) { > + ret = usbdev_do_resume(ps); do resume for all ioctl commands? are you sure? > + if (ret) > + goto done; > + } else { > + usb_unlock_device(dev); > + ret = proc_wait_resume(ps); > + > + /* Call auto-resume to balance auto-suspend of suspend-ioctl */ > + usb_lock_device(dev); > + if (ps->is_suspended) { > + ret = usb_autoresume_device(ps->dev); > + ps->is_suspended = false; > + } > + usb_unlock_device(dev); > + > + goto _done; What is the difference between _done and done? Please have descriptive labels if you are going to have any at all. Why isn't this part of the switch statement below? > + } > + > /* Reap operations are allowed even after disconnection */ > switch (cmd) { > case USBDEVFS_REAPURB: > @@ -2549,10 +2639,26 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, > case USBDEVFS_GET_SPEED: > ret = ps->dev->speed; > break; > + case USBDEVFS_SUSPEND: > + ret = 0; > + > + /* we cannot suspend when URB is pending */ > + if (async_getpending_count(ps)) { > + snoop(&ps->dev->dev, "ioctl-suspend but URB pending\n"); You do not know this, your value is stale :( And again, you are using snoop() calls for debugging, not for actual USB traffic, which is what it was designed for. These all need to be removed/rewritten. > + ret = -EINVAL; > + } else { > + if (ps->runtime_active) { > + snoop(&ps->dev->dev, "ioctl-suspend\n"); > + ps->runtime_active = false; > + usb_autosuspend_device(ps->dev); > + } > + } > + break; > } > > - done: > +done: > usb_unlock_device(dev); > +_done: See, horrid names :( > if (ret >= 0) > inode->i_atime = current_time(inode); > return ret; > diff --git a/include/uapi/linux/usbdevice_fs.h b/include/uapi/linux/usbdevice_fs.h > index 964e872..ae46beb 100644 > --- a/include/uapi/linux/usbdevice_fs.h > +++ b/include/uapi/linux/usbdevice_fs.h > @@ -197,5 +197,7 @@ struct usbdevfs_streams { > #define USBDEVFS_FREE_STREAMS _IOR('U', 29, struct usbdevfs_streams) > #define USBDEVFS_DROP_PRIVILEGES _IOW('U', 30, __u32) > #define USBDEVFS_GET_SPEED _IO('U', 31) > +#define USBDEVFS_SUSPEND _IO('U', 32) > +#define USBDEVFS_WAIT_RESUME _IO('U', 33) No documentation for what these do? thanks, greg k-h