Brian Murphy <[EMAIL PROTECTED]> said:

> Alan Stern wrote:
> 
> >On Mon, 14 Mar 2005, Brian Murphy wrote:
> >
> >  
> >
> >>This was nice in theory and partially fixes the problem (no more crash) 
> >>unfortunately
> >>the module removal hangs if the data interface is attempted to be 
> >>removed first.
> >>
> >>This seems to be because usb_driver_claim_interface is not the same as 
> >>claiming
> >>the interface in the probe routine causing the call to 
> >>usb_driver_release_interface to
> >>not decrement some reference count somewhere.
> >>    
> >>
> >
> >That's bad.  usb_driver_claim_interface and returning 0 from probe are 
> >supposed to have the same overall effect.  Is there any way you can track 
> >down the missing reference count?
> >
> >Alan Stern
> >
> >
> >  
> >
> Yes. I'll look at it tomorrow. If you have any ideas about a good place 
> to look then
> that might save me some time.
> 
> /Brian
> 
> 

The problem is not reference counting the problem is some code in
driver/base/bus.c. First I will describe the scenario:

Insert the cdc_acm module and plug in a supported device then remove
the module with the device plugged in.
This leads to the modprobe process using 100% cpu and never returns.

The reason for this is that the removal process ends up busy waiting
in driver_detach. The process by which this happens is 

driver_detach is called with our cdc_acm driver as argument. This 
has two dependent devices which are the two interfaces. The code is 
thus:

static void driver_detach(struct device_driver * drv)
{
        struct list_head * entry, * next;
        list_for_each_safe(entry, next, &drv->devices) {
                struct device * dev = container_of(entry, struct device,
driver_list);
                device_release_driver(dev);
        }
}


so device_release_driver is called with the first interface as
argument it's code looks like this:

void device_release_driver(struct device * dev)
{
        struct device_driver * drv = dev->driver;
        if (drv) {
                sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj));
                sysfs_remove_link(&dev->kobj, "driver");
                list_del_init(&dev->driver_list);
                device_detach_shutdown(dev);
                if (drv->remove)
                        drv->remove(dev);
                dev->driver = NULL;
        }       
}

All is well and we call the driver remove function which calls 
acm_disconnect. acm_disconnect calls usb_driver_release_interface
with the other interface which re-enters device_release_driver
and removes its entry from the list. Unfortunately this call
detaches driver_detach's next pointer from the
list it is iterating over so when we return from the outside call 
of device_release_driver next->next is itself and will never be 
equal to the list head which is list_for_each_safe's loop end
condition. This is very bad.

I don't really understand why this is such an unusual situation
but it results from us freeing the interfaces in a different order
to that we claim them in - shouldn't the device driver interface
handle this - at least without an infinite loop?

As far as I can see device_release_driver should *never* be called
from somewhere which is called from device_release_driver with
the current implimentation and I really can't understand that this has
never happened before...

I will be less tired later and try to propose a solution. At least you
know what the problem is now.

/Brian


-------------------------------------------------------
SF email is sponsored by - The IT Product Guide
Read honest & candid reviews on hundreds of IT Products from real users.
Discover which products truly live up to the hype. Start reading now.
http://ads.osdn.com/?ad_id=6595&alloc_id=14396&op=click
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to