On 12/15/2016 09:28 AM, Hans Verkuil wrote:
> On 15/12/16 17:06, Shuah Khan wrote:
>> On 12/15/2016 08:26 AM, Hans Verkuil wrote:
>>> On 15/12/16 15:45, Shuah Khan wrote:
>>>> On 12/15/2016 07:03 AM, Hans Verkuil wrote:
>>>>> On 15/12/16 13:56, Laurent Pinchart wrote:
>>>>>> Hi Sakari,
>>>>>>
>>>>>> On Thursday 15 Dec 2016 13:30:41 Sakari Ailus wrote:
>>>>>>> On Tue, Dec 13, 2016 at 10:24:47AM -0200, Mauro Carvalho Chehab wrote:
>>>>>>>> Em Tue, 13 Dec 2016 12:53:05 +0200 Sakari Ailus escreveu:
>>>>>>>>> On Tue, Nov 29, 2016 at 09:13:05AM -0200, Mauro Carvalho Chehab wrote:
>>>>>>>>>> Hi Sakari,
>>>>>>>>>>
>>>>>>>>>> I answered you point to point below, but I suspect that you missed 
>>>>>>>>>> how
>>>>>>>>>> the current approach works. So, I decided to write a quick summary
>>>>>>>>>> here.
>>>>>>>>>>
>>>>>>>>>> The character devices /dev/media? are created via cdev, with relies 
>>>>>>>>>> on
>>>>>>>>>> a kobject per device, with has an embedded struct kref inside.
>>>>>>>>>>
>>>>>>>>>> Also, each kobj at /dev/media0, /dev/media1, ... is associated with a
>>>>>>>>>> struct device, when the code does:
>>>>>>>>>>   devnode->cdev.kobj.parent = &devnode->dev.kobj;
>>>>>>>>>>
>>>>>>>>>> before calling cdev_add().
>>>>>>>>>>
>>>>>>>>>> The current lifetime management is actually based on cdev's kobject's
>>>>>>>>>> refcount, provided by its embedded kref.
>>>>>>>>>>
>>>>>>>>>> The kref warrants that any data associated with /dev/media0 won't be
>>>>>>>>>> freed if there are any pending system call. In other words, when
>>>>>>>>>> cdev_del() is called, it will remove /dev/media0 from the filesystem,
>>>>>>>>>> and will call kobject_put().
>>>>>>>>>>
>>>>>>>>>> If the refcount is zero, it will call devnode->dev.release(). If the
>>>>>>>>>> kobject refcount is not zero, the data won't be freed.
>>>>>>>>>>
>>>>>>>>>> So, in the best case scenario, there's no opened file descriptors
>>>>>>>>>> by the time media device node is unregistered. So, it will free
>>>>>>>>>> everything.
>>>>>>>>>>
>>>>>>>>>> In the worse case scenario, e. g. when the driver is removed or
>>>>>>>>>> unbind while /dev/media0 has some opened file descriptor(s),
>>>>>>>>>> the cdev logic will do the proper lifetime management.
>>>>>>>>>>
>>>>>>>>>> On such case, /dev/media0 disappears from the file system, so another
>>>>>>>>>> open is not possible anymore. The data structures will remain
>>>>>>>>>> allocated until all associated file descriptors are not closed.
>>>>>>>>>>
>>>>>>>>>> When all file descriptors are closed, the data will be freed.
>>>>>>>>>>
>>>>>>>>>> On that time, it will call an optional dev.release() callback,
>>>>>>>>>> responsible to free any other data struct that the driver allocated.
>>>>>>>>>
>>>>>>>>> The patchset does not change this. It's not a question of the
>>>>>>>>> media_devnode struct either. That's not an issue.
>>>>>>>>>
>>>>>>>>> The issue is rather what else can be accessed through the media device
>>>>>>>>> and other interfaces. As IOCTLs are not serialised with device removal
>>>>>>>>> (which now releases much of the data structures)
>>>>>>>>
>>>>>>>> Huh? ioctls are serialized with struct device removal. The Driver core
>>>>>>>> warrants that.
>>>>>>>
>>>>>>> How?
>>>>>>>
>>>>>>> As far as I can tell, there's nothing in the way of an IOCTL being in
>>>>>>> progress on a character device which is registered by the driver for a
>>>>>>> hardware device which is being removed.
>>>>>>>
>>>>>>> vfs_ioctl() directly calls the unlocked_ioctl() file operation which 
>>>>>>> is, in
>>>>>>> case of MC, media_ioctl() in media-devnode.c. No mutexes (or other 
>>>>>>> locks)
>>>>>>> are taken during that path, which I believe is by design.
>>>>>
>>>>> chrdev_open in fs/char_dev.c increases the refcount on open() and 
>>>>> decreases it
>>>>> on release(). Thus ensuring that the cdev can never be removed while in an
>>>>> ioctl.
>>>>>
>>>>>>>
>>>>>>>>> there's a high chance of accessing
>>>>>>>>> released memory (or mutexes that have been already destroyed). An
>>>>>>>>> example of that is here, stopping a running pipeline after unbinding
>>>>>>>>> the device. What happens there is that the media device is released
>>>>>>>>> whilst it's in use through the video device.
>>>>>>>>>
>>>>>>>>> <URL:http://www.retiisi.org.uk/v4l2/tmp/media-ref-dmesg2.txt>
>>>>>>>>
>>>>>>>> It is not clear from the logs what the driver tried to do, but
>>>>>>>> that sounds like a driver's bug, with was not prepared to properly
>>>>>>>> handle unbinds.
>>>>>>>>
>>>>>>>> The problem here is that isp_video_release() is called by V4L2
>>>>>>>> release logic, and not by the MC one:
>>>>>>>>
>>>>>>>> static const struct v4l2_file_operations isp_video_fops = {
>>>>>>>>       .owner          = THIS_MODULE,
>>>>>>>>       .open           = isp_video_open,
>>>>>>>>       .release        = isp_video_release,
>>>>>>>>       .poll           = vb2_fop_poll,
>>>>>>>>       .unlocked_ioctl = video_ioctl2,
>>>>>>>>       .mmap           = vb2_fop_mmap,
>>>>>>>> };
>>>>>>>>
>>>>>>>> It seems that the driver's logic allows it to be called before or
>>>>>>>> after destroying the MC.
>>>>>>>>
>>>>>>>> Assuming that, if the OMAP3 driver is not used it works,
>>>>>>>> it means that, if the isp_video_release() is called
>>>>>>>> first, no errors will happen, but if MC is destroyed before
>>>>>>>> V4L2 call to its .release() callback, as there's no logic at the
>>>>>>>> driver that would detect it, isp_video_release() will be calling
>>>>>>>> isp_video_streamoff(), with depends on the MC to work.
>>>>>>>>
>>>>>>>> On a first glance, I can see two ways of fixing it:
>>>>>>>>
>>>>>>>> 1) to increment devnode's device kobject refcount at OMAP3 .probe(),
>>>>>>>> decrementing it only at isp_video_release(). That will ensure that
>>>>>>>> MC will only be removed after V4L2 removal.
>>>>>>
>>>>>> As soon as you have to dig deep in a structure to find a reference 
>>>>>> counter and
>>>>>> increment it, bypassing all the API layers, you can be entirely sure 
>>>>>> that the
>>>>>> solution is wrong.
>>>>>
>>>>> Indeed.
>>>>>
>>>>>>
>>>>>>>> 2) to call isp_video_streamoff() before removing the MC stuff, e. g.
>>>>>>>> inside the MC .release() callback.
>>>>>>>
>>>>>>> This is a fair suggestion, indeed. Let me see what could be done there.
>>>>>>> Albeit this is just *one* of the existing issues. It will not address 
>>>>>>> all
>>>>>>> problems fixed by the patchset.
>>>>>>
>>>>>> We need to stop the hardware at .remove() time. That should not be 
>>>>>> linked to a
>>>>>> videodev, v4l2_device or media_device .release() callback. When the 
>>>>>> .remove()
>>>>>> callback returns the driver is not allowed to touch the hardware 
>>>>>> anymore. In
>>>>>> particular, power domains might clocks or power supplies, leading to 
>>>>>> invalid
>>>>>> access faults if we try to access hardware registers.
>>>>>
>>>>> Correct.
>>>>>
>>>>>>
>>>>>> USB devices get help from the USB core that cancels all USB operations in
>>>>>> progress when they're disconnected. Platform devices don't have it as 
>>>>>> easy,
>>>>>> and need to implement everything themselves. We thus need to stop the
>>>>>> hardware, but I'm not sure it makes sense to fake a VIDIOC_STREAMOFF 
>>>>>> ioctl at
>>>>>> .remove() time.
>>>>>
>>>>> Please don't. This shouldn't be done automatically.
>>>>>
>>>>>> That could introduce other races between .remove() and the
>>>>>> userspace API. A better solution is to make sure the objects that are 
>>>>>> needed
>>>>>> at .release() time of the device node are all reference-counted and only
>>>>>> released when the last reference goes away.
>>>>>>
>>>>>> There's plenty of way to try and work around the problem in drivers, 
>>>>>> some more
>>>>>> racy than others, but if we require changes to all platform drivers to 
>>>>>> fix
>>>>>> this we need to ensure that we get it right, not as half-baked hacks 
>>>>>> spread
>>>>>> around the whole subsystem.
>>>>>
>>>>> Why on earth do we want this for the omap3 driver? It is not a 
>>>>> hot-pluggable
>>>>> device and I see no reason whatsoever to start modifying platform drivers 
>>>>> just
>>>>> because you can do an unbind. I know there are real hot-pluggable 
>>>>> devices, and
>>>>> getting this right for those is of course important.
>>>>
>>>> This was my first reaction when I saw this RFC series. None of the platform
>>>> drivers are designed to be unbound. Making core changes based on such as
>>>> driver would make the core very complex.
>>>>
>>>> We can't even reproduce the problem on other drivers.
>>>>
>>>>>
>>>>> If the omap3 is used as a testbed, then that's fine by me, but even then I
>>>>> probably wouldn't want the omap3 code that makes this possible in the 
>>>>> kernel.
>>>>> It's just additional code for no purpose.
>>>>
>>>> I agree with Hans. Why are we using the most complex case as a reference 
>>>> driver
>>>> and basing that driver to make core changes which will force changes to 
>>>> all the
>>>> driver that use mc-core?
>>>>
>>>>>
>>>>>>>> That could be done by overwriting the dev.release() callback at
>>>>>>>> omap3 driver, as I discussed on my past e-mails, and flagging the
>>>>>>>> driver that it should not accept streamon anymore, as the hardware
>>>>>>>> is being disconnecting.
>>>>>>>
>>>>>>> A mutex will be needed to serialise the this with starting streaming.
>>>>>>>
>>>>>>>> Btw, that explains a lot why Shuah can't reproduce the stuff you're
>>>>>>>> complaining on her USB hardware.
>>>>>>>>
>>>>>>>> The USB subsystem has a a .disconnect() callback that notifies
>>>>>>>> the drivers that a device was unbound (likely physically removed).
>>>>>>>> The way USB media drivers handle it is by returning -ENODEV to any
>>>>>>>> V4L2 call that would try to touch at the hardware after unbound.
>>>>>>
>>>>>
>>>>> In my view the main problem is that the media core is bound to a struct
>>>>> device set by the driver that creates the MC. But since the MC gives an
>>>>> overview of lots of other (sub)devices the refcount of the media device
>>>>> should be increased for any (sub)device that adds itself to the MC and
>>>>> decreased for any (sub)device that is removed. Only when the very last
>>>>> user goes away can the MC memory be released.
>>>>
>>>> Correct. Media Device Allocator API work I did allows creating media device
>>>> on parent USB device to allow media sound driver share the media device. It
>>>> does ref-counting on media device and media device is unregistered only 
>>>> when
>>>> the last driver unregisters it.
>>>>
>>>> There is another aspect to explore regarding media device and the graph.
>>>>
>>>> Should all the entities stick around until all references to media
>>>> device are gone? If an application has /dev/media open, does that
>>>> mean all entities should not be free'd until this app. exits? What
>>>> should happen if an app. is streaming? Should the graph stay intact
>>>> until the app. exits?
>>>
>>> Yes, everything must stay around until the last user has disappeared.
>>>
>>> In general unplugs can happen at any time. So applications can be in the 
>>> middle
>>> of an ioctl, and removing memory during that time is just impossible.
>>>
>>> On unplug you:
>>>
>>> 1) stop any HW DMA (highly device dependent)
>>> 2) wake up any filehandles that wait for an event
>>> 3) unregister any device nodes
>>>
>>> Then just sit back and wait for refcounts to go down as filehandles are 
>>> closed
>>> by the application.
>>>
>>> Note: the v4l2/media/cec/IR/whatever core is typically responsible for 
>>> rejecting
>>> any ioctls/mmap/etc. once the device node has been unregistered. The only 
>>> valid
>>> file operation is release().
>>>
>>>>
>>>>    If yes, this would pose problems when we have multiple drivers bound
>>>>    to the media device. When audio driver goes away for example, it should
>>>>    be allowed to delete its entities.
>>>
>>> Only if you can safely remove it from the topology data structures while
>>> being 100% certain that nobody can ever access it. I'm not sure if that is
>>> the case. Actually, looking at e.g. adv7604.c it does 
>>> media_entity_cleanup(&sd->entity);
>>> in remove() which is an empty function, so there doesn't appear any attempt
>>> to safely clean up an entity (i.e. make sure no running media ioctl can
>>> access it or call ops).
>>
>> Right. media_entity_cleanup() nothing at the moment. Also if it gets called
>> after media_device_unregister_entity(), it could pose problems. I wonder if
>> we have drivers that are calling media_entity_cleanup() after unregistering
>> the entity?
>>
>>>
>>> This probably will need to be serialized with the graph_mutex lock.
>>>
>>>>
>>>> The approach current mc-core takes is that the media_device and 
>>>> media_devnode
>>>> stick around, but entities can be added and removed during media_device
>>>> lifetime.
>>>
>>> Seems reasonable. But the removal needs to be done carefully, and that 
>>> doesn't
>>> seem to be the case now (unless adv7604.c is just buggy).
>>
>> Correct. It is possible media_device is embedded in this driver. When driver
>> that embeds is unbound, media_device goes away. I needed to make the media
>> device refcounted and sharable for audio work and that is what the Media 
>> Device
>> Allocator API does.
> 
> Basically all you need to do is to refcount the struct device in the 
> media_device:
> call get_device(mdev->dev) when you take a reference, and 
> put_device(mdev->dev)
> when you no longer need it. The mdev itself is freed when the mdev->dev 
> refcount
> goes to 0.
> 
> No need to add another kref.

Right. I do have an additional kref in Media Device Allocator API that serves
a different purpose. It is used for ref-counting drivers that are sharing the
media_device so it can't be unregistered until all those drivers unregister it.
I think we discussed this when this API was reviewed.

> 
> 
>>
>> Maybe we have more cases than this audio case that requires media_device 
>> refcounted.
>> If we have to keep entities that are in use until all the references go 
>> away, we
>> have to ref-count them as well.
>>
>>>
>>>>
>>>> If an app. is still running when media_device is unregistered, media_device
>>>> isn't released until the last reference goes away and ioctls can check if
>>>> media_device is registered or not.
>>>>
>>>> We have to decide on the larger lifetime question surrounding media_device
>>>> and graph as well.
>>>
>>> I don't think there is any choice but to keep it all alive until the last
>>> reference goes away.
>>
>> If you mean "all alive" entities as well, we have to ref-count them. Because
>> drivers can unregister entities during run-time now. I am looking at the
>> use-case where, a driver that has dvb and video and what should happen when
>> dvb is unbound for example. Should dvb entities go away or should they stay
>> until all the drivers are unbound?
> 
> That depends on the architecture. If these are completely independent devices
> then you want to allow this, if possible to do this safely. But for e.g.
> subdevices that depend on a parent device the unbind should just be 
> prohibited.
> There is no point whatsoever in allowing that.
> 
>>
>> v4l2-core registers and unregisters entities and so does dvb-core. So when a
>> driver unregisters video and dvb, these entities get deleted. So we have a
>> distributed mode of registering and unregistering entities. We also have
>> ioctls (video, dvb, and media) accessing these entities. So where do we make
>> changes to ensure entities stick around until all users exit?
>>
>> Ref-counting entities won't work if they are embedded - like in the case of
>> struct video_device which embeds the media entity. When struct video goes
>> away then entity will disappear. So we do have a complex lifetime model here
>> that we need to figure out how to fix.
> 
> That why I think the best approach would be to safely delete them from the
> MC graph: take the top-level lock (graph_mutex I think) and remove all 
> references
> before releasing the lock.

media_device_unregister_entity() entity does that now. It also removes links.
Do you believe something is missing there. It does hold graph_mutex.

> 
> I think this will work for interface entities, but for subdev entities this
> certainly won't work. Unbinding subdevs should be blocked (just set
> suppress_bind_attrs to true in all subdev drivers). Most top-level drivers
> have pointers to subdev data, so unbinding them will just fail horribly.
> 

Yes that is an option. I did something similar for au0828 and snd_usb_audio
case, so the module that registers the media_device can't unbound until the
other driver. If au0828 registers media_device, it becomes the owner and if
it gets unbound ioctls will start to see problems.

What this means though is that drivers can't be unbound easily. But that is
a small price to pay compared to the problems we will see if a driver is
unbound when its entities are still in use. Also, unsetting bind_attrs has
to be done as well, otherwise we can never unbind any driver.

thanks,
-- Shuah



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

Reply via email to