Re: [PATCH 08/10] usb: add usb port auto power off mechanism

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, 11 Dec 2012, Lan Tianyu wrote:

> >> @@ -108,11 +109,14 @@ MODULE_PARM_DESC(use_both_schemes,
> >>  DECLARE_RWSEM(ehci_cf_port_reset_rwsem);
> >>  EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
> >>
> >> +#define HUB_PORT_RECONNECT_TRIES	20
> > 
> > 20 is an awful lot.  Do you really need any more than one try?  The 
> > debounce routine already does its own looping.
> I tested a usb ssd which consumes about 1s to makes usb port status
> enter into connect status after powering off and powering on the port.
> So I set the tries 20 and the longest latency is larger than 2s.
> The debounce routine only guarantees port status stable rather than
> enter into connect status.

Then a better solution would be to first wait (up to 2 seconds) for a 
connect status and then call the debounce routine.

> >> @@ -718,13 +722,26 @@ int usb_hub_set_port_power(struct usb_device
> >> *hdev, int port1,
> >>  		bool set)
> >>  {
> >>  	int ret;
> >> +	struct usb_hub *hub = hdev_to_hub(hdev);
> >> +	struct usb_port *port_dev
> >> +		= hub->ports[port1 - 1];
> >> +
> >> +	if (set) {
> >> +		if (port_dev->power_is_on)
> >> +			return 0;
> >>
> >> -	if (set)
> >>  		ret = set_port_feature(hdev, port1,
> >>  				USB_PORT_FEAT_POWER);
> >> -	else
> >> +	} else {
> >> +		if (!port_dev->power_is_on)
> >> +			return 0;
> >> +
> >>  		ret = clear_port_feature(hdev, port1,
> >>  				USB_PORT_FEAT_POWER);
> >> +	}
> > 
> > Do we need these shortcuts?  How often will this routine be called when
> > the port is already in the right state
> When the PM Qos NO_POWER_OFF is changed,
> pm_runtime_get_sync/put(port_dev) will be invoked. This routine will be
> called during port resume and suspend. If one device was suspended and
> power off, the port's usage_count would be 0. After then, the port will
> be resumed and suspend every time pm qos NO_POWER_OFF flag being
> changed. So this depends on the user space.

You did not understand my question.  When usb_hub_set_port_power is 
called, won't the Set-Feature request almost always be necessary?

For example, how often will it happen that set and 
port_dev->power_is_on are both true?  Or both false?  It seems to me 
that this will almost never happen.  So why bother testing for it?

> >> @@ -2862,6 +2884,18 @@ int usb_port_suspend(struct usb_device *udev,
> >> pm_message_t msg)
> >>  		udev->port_is_suspended = 1;
> >>  		msleep(10);
> >>  	}
> >> +
> >> +	/*
> >> +	 * Check whether current status is meeting requirement of
> >> +	 * usb port power off mechanism
> >> +	 */
> >> +	if (!udev->do_remote_wakeup
> >> +			&& (dev_pm_qos_flags(&port_dev->dev,
> >> +			PM_QOS_FLAG_NO_POWER_OFF) != PM_QOS_FLAGS_ALL)
> >> +			&& udev->persist_enabled
> >> +			&& !status)
> >> +		pm_runtime_put_sync(&port_dev->dev);
> > 
> > You need to store somewhere the fact that you made this call, so that 
> > you will know whether or not to make the corresponding 
> > pm_runtime_get_sync call in usb_port_resume.
> You mean I should add a new flag to keep the
> pm_runtime_put_sync/put(port_dev) being called paired, right?
> How about "needs_resume"?

What you need isn't a resume; it's pm_runtime_get_sync.

> >> @@ -2982,10 +3035,39 @@ static int finish_port_resume(struct usb_device
> >> *udev)
> >>  int usb_port_resume(struct usb_device *udev, pm_message_t msg)
> >>  {
> >>  	struct usb_hub	*hub = hdev_to_hub(udev->parent);
> >> +	struct usb_port *port_dev = hub->ports[udev->portnum  - 1];
> >>  	int		port1 = udev->portnum;
> >>  	int		status;
> >>  	u16		portchange, portstatus;
> >>
> >> +
> >> +	set_bit(port1, hub->busy_bits);
> >> +
> >> +	if (!port_dev->power_is_on) {
> > 
> > This test is wrong.  It's possible that the port power is still on even 
> > though you called pm_runtime_put_sync.
> Above, we need to check the new flag, right?

Yes.

> >> +		status = pm_runtime_get_sync(&port_dev->dev);
> >> +		if (status < 0) {
> >> +			dev_dbg(&udev->dev, "can't resume usb port, status %d\n",
> >> +					status);
> >> +			clear_bit(port1, hub->busy_bits);
> >> +			return status;
> >> +		}
> > 
> > Don't you want to call usb_port_wait_for_connected right here?
> > 
> >> +	}
> >> +
> >> +
> >> +	/*
> >> +	 * Wait for usb hub port to be reconnected in order to make
> >> +	 * the resume procedure successful.
> >> +	 */
> >> +	if (port_dev->needs_debounce) {
> >> +		status = usb_port_wait_for_connected(hub, port1);
> > 
> > If you move this call earlier then you won't have to test
> > needs_debounce.
> The port may have been power on when device is resumed. One case, after
> device being suspended and power off, user may set the NO_POWER_OFF flag
> and port will be power on before device being resumed. For this case,
> port doesn't need to be resumed in the usb_port_resume() since port
> already power on and "wait for connected" is enough. So I divide resume
> port and wait for connected into two pieces.

You are confusing pm_runtime_get_sync with resume.  They are not the 
same thing.  You always need to call pm_runtime_get_sync here if 
pm_runtime_put_sync was called earlier, even if the port is already 
powered on.

>  But from your rely, I
> realize we should paired call pm_runtime_get_sync/put(port_dev). Now I
> think this should be put earlier.
> Something like this
> 
> if(port_dev->needs_resume) {
> 	status = pm_runtime_get_sync(&port_dev->dev);
> 	if (status < 0) {
> 		dev_dbg(&udev->dev, "can't resume usb port, status %d\n", status);
> 		clear_bit(port1, hub->busy_bits);
> 		return status;
> 	}
> 
> 	status = usb_port_wait_for_connected(hub, port1);
> 	if (status < 0) {
> 		dev_dbg(&udev->dev, "can't get reconnection after setting port  power
> on, status %d\n", status);
> 		clear_bit(port1, hub->busy_bits);
> 		return status;
> 	}
> 	
> }

Actually, it looks like you will want to call wait_for_connected in the 
port's runtime-resume routine.  After all, if the user changes the PM 
QOS flag while the port is powered off, you will need to start paying 
attention to the connect status.

> >> @@ -4175,6 +4256,11 @@ static void hub_port_connect_change(struct
> >> usb_hub *hub, int port1,
> >>  		}
> >>  	}
> >>
> >> +	if (!port_dev->power_is_on || port_dev->needs_debounce) {
> >> +		clear_bit(port1, hub->change_bits);
> >> +		return;
> >> +	}
> > 
> > Doesn't the busy_bits flag take care of this for you already?
> When port is power off or power on during PM Qos NO_POWER_OFF flag
> changing , there is also an connect change event and busy_bits is not
> set at that time.

This means you should set busy_bits in the port's runtime-resume
routine and keep it set until wait_for_connected is finished.

> > Also, what if the port is ganged, so even though you think you turned
> > off the power, it really is still on?  When that happens you probably
> > don't want to ignore port events.
> Even the power is still on but the PORT_POWER feature has been cleared.
> So there should be no event from port, right?

"should be" -- but buggy hardware might send an event anyway.  Also,
many root hubs don't pay attention to the power feature.  If this
happens, you probably should handle the connect change properly.  I 
don't see any point in ignoring it.

One other thing to watch out for: If the device is unplugged while it 
is suspended, you may need to call pm_runtime_get_sync from somewhere 
in the device-removal path.

Alan Stern

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux