lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Mon, 20 Jun 2016 15:46:29 +0300
From:	Felipe Balbi <balbi@...nel.org>
To:	Roger Quadros <rogerq@...com>, peter.chen@...escale.com,
	yoshihiro.shimoda.uh@...esas.com
Cc:	tony@...mide.com, gregkh@...uxfoundation.org,
	dan.j.williams@...el.com, mathias.nyman@...ux.intel.com,
	Joao.Pinto@...opsys.com, sergei.shtylyov@...entembedded.com,
	jun.li@...escale.com, grygorii.strashko@...com, robh@...nel.org,
	nsekhar@...com, b-liu@...com, joe@...ches.com,
	linux-usb@...r.kernel.org, linux-omap@...r.kernel.org,
	linux-kernel@...r.kernel.org, devicetree@...r.kernel.org
Subject: Re: [PATCH v11 08/14] usb: otg: add OTG/dual-role core


Hi,

Roger Quadros <rogerq@...com> writes:
>>>>> diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
>>>>> index 8689dcb..ed596ec 100644
>>>>> --- a/drivers/usb/Kconfig
>>>>> +++ b/drivers/usb/Kconfig
>>>>> @@ -32,6 +32,23 @@ if USB_SUPPORT
>>>>>  config USB_COMMON
>>>>>  	tristate
>>>>>  
>>>>> +config USB_OTG_CORE
>>>>> +	tristate
>>>>
>>>> why tristate if you can never set it to 'M'?
>>>
>>> This gets internally set to M if either USB or GADGET is M.
>>> We select it in USB and GADGET.
>>> This was the only way I could get usb-otg.c to build as
>>>
>>> m if USB OR GADGET is m
>>> built-in if USB and GADGET are built in.
>> 
>> I could only see a "select USB_OTG_CORE", select will always set it 'y'
>> and disregard dependencies. Maybe I missed something else.
>
> Not always. See how USB_COMMON works.

USB_COMMON is always 'y'. That could be changes a bool as well.

Do you have any defconfig where USB_COMMON or USB_OTG_CORE gets set to
'm'?

>>>>> +static DEFINE_MUTEX(otg_list_mutex);
>>>>> +
>>>>> +static int usb_otg_hcd_is_primary_hcd(struct usb_hcd *hcd)
>>>>> +{
>>>>> +	if (!hcd->primary_hcd)
>>>>> +		return 1;
>>>>
>>>> these seems inverted. If hcd->primary is NULL (meaning, there's no
>>>> ->primary_hcd), then we tell caller that this _is_ a primary hcd? Care
>>>> to explain?
>>>
>>> hcd->primary_hcd is a link used by the shared hcd to point to the
>>> primary_hcd.  primary_hcd's have this link as NULL.
>> 
>> So the following check is unnecessary and should always evaluate to
>> false, right ?
>
> Actually primary_hcd's not having a shared HCD have hcd->primary_hcd as NULL
> and those having a shared HCD do have it pointing to the primary hcd.

But look at your check:

is_primary(struct usb_hcd *hcd)
{
	if (!hcd->primary_hcd)
        	return true;

	return hcd == hcd->primary_hcd;
}

if you're passing a primary hcd, you're gonna return on the first
branch. If you're passing a secondary hcd, then your equality will
always be false. 

IOW, this can be reduced to:

is_primary(struct usb_hcd *hcd)
{
	return !hcd->primary_hcd;
}

right?

>>>>> +int usb_otg_start_host(struct usb_otg *otg, int on)
>>>>> +{
>>>>> +	struct otg_hcd_ops *hcd_ops = otg->hcd_ops;
>>>>> +	int ret;
>>>>> +
>>>>> +	dev_dbg(otg->dev, "otg: %s %d\n", __func__, on);
>>>>> +	if (!otg->host) {
>>>>> +		WARN_ONCE(1, "otg: fsm running without host\n");
>>>>
>>>> if (WARN_ONCE(!otg->host, "otg: fsm running without host\n"))
>>>> 	return 0;
>>>>
>>>> but, frankly, if you require a 'host' and a 'gadget' don't start this
>>>> layer until you have both.
>>>
>>> We don't start the layer till we have both host and gadget. But
>>> this API is for external use and might be called at any time.
>> 
>> well, if callers call this at the wrong time, it's callers' fault. Let
>> them oops so we catch the error.
>
> So you suggest we allow a NULL pointer dereference here?

yes, it's a clear violation of the API contract. The only situation
where this would ever trigger, is if somebody is calling
usb_otg_start_host() without calling start_fsm() first. That shouldn't
be valid.

>>>>> +		return 0;
>>>>> +	}
>>>>> +
>>>>> +	if (on) {
>>>>> +		if (otg->flags & OTG_FLAG_HOST_RUNNING)
>>>>> +			return 0;
>>>>> +
>>>>> +		/* start host */
>>>>> +		ret = hcd_ops->add(otg->primary_hcd.hcd,
>>>>> +				   otg->primary_hcd.irqnum,
>>>>> +				   otg->primary_hcd.irqflags);
>>>>
>>>> this is usb_add_hcd(), is it not? Why add an indirection?
>>>
>>> I've introduced the host and gadget ops interface to get around the
>>> circular dependency issue we can't avoid.
>>> otg needs to call host/gadget functions and host/gadget also needs to
>>> call otg functions.
>> 
>> IMO, this shows a fragility of your design. You're, now, lying to
>> usb_hcd and usb_udc and making them register into a virtual layer that
>> doesn't exist. And that layer will end up calling the real registration
>> function when some magic event happens.
>> 
>> This is only really needed for quirky devices like dwc3 (but see more on
>> dwc3 below) where host and peripheral registers shadow each
>> other. Otherwise we would be able to always keep hcd and udc always
>> registered. They would get different interrupt statuses anyway and
>> nothing would ever break.
>
> Well I only had the opportunity to work with dwc3 so I had to ensure
> the design worked with it.

but this is exactly what I'm pointing you to. DWC3 does not need to go
through this because the HW maintains state machine for you.

>> However, when it comes to dwc3, we already have all the code necessary
>> to workaround this issue by destroying the XHCI pdev when OTG interrupt
>> says we should be peripheral (and vice-versa). DWC3 also keeps track of
>> the OTG states for those folks who really care about OTG (Hint: nobody
>> has cared for the past 10 years, why would they do so now?) and we don't
>> need a SW state machine when the HW handles that for us, right?
>
> Where is the code? I'd like to test dual-role on TI platforms.

Well, we just need an OTG IRQ handler to call dwc3_gadget_suspend() (or
that function renamed to match the usage) and something similar for the
host side.

It's all doable in a day or two.

>>> why? The kick could be triggered from an interrupt
>>> context. e.g. otg_irq.
>> 
>> We have threaded IRQ handlers in the kernel, right? Make use of that
>> and, with a little smart locking and IRQ masking, you can run the OTG
>> IRQ thread almost completely lockless ;-)
>
> Not a problem if we have the constraint that usb_otg_sync_inputs()
> needs to be called in thread context only.

that should be the case, right? If you're registering/unregistering
devices, you can't possibly call this from hardirq context.

>>>>> +	if (config->otg_work)	/* custom otg_work ? */
>>>>> +		INIT_WORK(&otg->work, config->otg_work);
>>>>> +	else
>>>>> +		INIT_WORK(&otg->work, usb_drd_work);
>>>>
>>>> why do you need to cope with custom work handlers?
>>>
>>> It was just a provision to provide your own state machine if the generic
>>> one does not meet your needs. But i'm OK to get rid of it as well.
>> 
>> If you allow for this, every time there is a limitation, people will
>> just provide a copy of the state machine with a small change here and
>> there instead of fixing the real issue.
>
> I agree with you here. I'll get rid of the custom_otg_work.

thanks

>>>>> +static void usb_otg_start_fsm(struct usb_otg *otg)
>>>>> +{
>>>>> +	struct otg_fsm *fsm = &otg->fsm;
>>>>> +
>>>>> +	if (fsm->running)
>>>>> +		goto kick_fsm;
>>>>> +
>>>>> +	if (!otg->host) {
>>>>> +		dev_info(otg->dev, "otg: can't start till host registers\n");
>>>>> +		return;
>>>>> +	}
>>>>> +
>>>>> +	if (!otg->gadget) {
>>>>> +		dev_info(otg->dev,
>>>>> +			 "otg: can't start till gadget UDC registers\n");
>>>>> +		return;
>>>>> +	}
>>>>
>>>> okay, so you never kick the FSM until host and gadget are
>>>> registered. Why do you need to test for the case where the FSM is
>>>> running without host/gadget?
>>>
>>> That message in the test was misleading. It could also be a
>>> used as a warning if users did something wrong.
>> 
>> this usb_otg_start_fsm() establishes a contract. That contract says that
>> the USB OTG FSM won't start until host and gadget are running and
>> registered, yada yada yada. Drivers trying to kicking the FSM without
>> calling usb_otg_start_fsm() first deserve to oops.
>
> I'm considering the worst case where OTG controller, host controller
> and gadget controller are 3 independent entities which can get probed
> in any order.

there is no such thing as OTG controller :-) Even in our wildest dreams,
the most we get is a multiplexer inside the SoC to mux signals to HCD or
UDC. DWC3, when configured as a dual-role-capable IP, has its own OTG
block. But that's all self-contained inside DWC3 itself :-)

> OTG controller driver doesn't really know when host and gadget
> register.  All it cares about is getting the hardware events and
> kicking the OTG machine.

Nothing should be kicking the OTG state machine anyways, until all parts
are ready, registered, running, etc.

> (NOTE: when I say OTG controller it might as well be just the
> dual-role bits that handle the ID and VBUS interrupts).

right

> usb_otg_start_fsm() is not public.
> usb_otg_sync_inputs() is the public function that the OTG driver will use.

the outcome is the same, right?

-- 
balbi

Download attachment "signature.asc" of type "application/pgp-signature" (819 bytes)

Powered by blists - more mailing lists