On Fri, Dec 24, 2010 at 12:59:38PM +0100, Laurent Pinchart wrote: > Hi Greg, > > Thank you for the review. > > On Thursday 23 December 2010 04:32:53 Greg KH wrote: > > On Mon, Dec 20, 2010 at 12:36:24PM +0100, Laurent Pinchart wrote: > > > The media_devnode structure provides support for registering and > > > unregistering character devices using a dynamic major number. Reference > > > counting is handled internally, making device drivers easier to write > > > without having to solve the open/disconnect race condition issue over > > > and over again. > > > > What race condition are you referring to? > > In a nutshell, the race between device disconnection, which results in the > device instance being delete (if not in use of course), and open() calls from > userspace. The problem has been solved in V4L a couple of releases ago after > suffering from this race for a too long time. As V4L devices (and now media > devices) need to create both a struct device and a struct cdev instance, > careful locking is needed. Ok, that's not really nice, but I guess it is needed. > > > +config MEDIA_CONTROLLER > > > + bool "Media Controller API (EXPERIMENTAL)" > > > + depends on EXPERIMENTAL > > > + ---help--- > > > + Enable the media controller API used to query media devices internal > > > + topology and configure it dynamically. > > > + > > > + This API is mostly used by camera interfaces in embedded platforms. > > > > That's nice, but why should I enable this? Or will drivers enable it > > automatically? > > Drivers depending on the media controller API will enable this, yes. The > option will probably removed later when the API won't be deemed as > experimental anymore. > > > > +#define MEDIA_NUM_DEVICES 256 > > > > Why this limit? > > Because I'm using a bitmap to store the used minor numbers, and I thus need a > limit. I could get rid of it of it by using a linked list, but that will not > be efficient (you could argue that the list will hold a few entries only most > of the time, but in that case a limit of 256 minors wouldn't be a problem > :-)). As it's only needed to be looked up at open() time, why not just make it dynamic? > > > +#define MEDIA_NAME "media" > > > > Are you sure this is a good name for a camera? > > It's not just camera. Media devices are... well, media devices. Basically > anything that can handle audio and/or video streams. The media controller API > can be used by plain audio devices. Ok. > > > +static dev_t media_dev_t; > > > > Only one major number? Is it always dynamic? > > Yes, one major and (for now) 256 minors. Is there a problem with it being > dynamic ? No, just curious. > > > + > > > +/* > > > + * Active devices > > > + */ > > > +static DEFINE_MUTEX(media_devnode_lock); > > > +static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES); > > > + > > > +/* Called when the last user of the media device exits. */ > > > +static void media_devnode_release(struct device *cd) > > > +{ > > > + struct media_devnode *mdev = to_media_devnode(cd); > > > + > > > + mutex_lock(&media_devnode_lock); > > > + > > > + /* Delete the cdev on this minor as well */ > > > + cdev_del(&mdev->cdev); > > > + > > > + /* Mark device node number as free */ > > > + clear_bit(mdev->minor, media_devnode_nums); > > > + > > > + mutex_unlock(&media_devnode_lock); > > > + > > > + /* Release media_devnode and perform other cleanups as needed. */ > > > + if (mdev->release) > > > + mdev->release(mdev); > > > +} > > > > You forgot to free the device structure here as well, right? > > That will be done by the release callback. The media_devnode structure is > embedded in the media_device structure, which will be embedded in driver- > specific structures. Ok. > > > +static ssize_t media_read(struct file *filp, char __user *buf, > > > + size_t sz, loff_t *off) > > > +{ > > > + struct media_devnode *mdev = media_devnode_data(filp); > > > + > > > + if (!mdev->fops->read) > > > + return -EINVAL; > > > + if (!media_devnode_is_registered(mdev)) > > > + return -EIO; > > > > How could this happen? > > This can happen when a USB device is disconnected for instance. But what's to keep that from happening on the next line as well? That doesn't seem like a check you can ever be sure about, so I wouldn't do it at all. > > And are you sure -EIO is correct? > > -ENXIO is probably better (I always confuse that with -ENODEV). > > > > + return mdev->fops->read(filp, buf, sz, off); > > > +} > > > + > > > +static ssize_t media_write(struct file *filp, const char __user *buf, > > > + size_t sz, loff_t *off) > > > +{ > > > + struct media_devnode *mdev = media_devnode_data(filp); > > > + > > > + if (!mdev->fops->write) > > > + return -EINVAL; > > > + if (!media_devnode_is_registered(mdev)) > > > + return -EIO; > > > > Same as above, and same comment in other places (poll, ioctl.) > > OK. > > > > +/* Override for the open function */ > > > +static int media_open(struct inode *inode, struct file *filp) > > > +{ > > > + struct media_devnode *mdev; > > > + int ret; > > > + > > > + /* Check if the media device is available. This needs to be done with > > > + * the media_devnode_lock held to prevent an open/unregister race: > > > + * without the lock, the device could be unregistered and freed between > > > + * the media_devnode_is_registered() and get_device() calls, leading to > > > + * a crash. > > > + */ > > > + mutex_lock(&media_devnode_lock); > > > + mdev = container_of(inode->i_cdev, struct media_devnode, cdev); > > > > By virtue of having the reference to the module held by the vfs, this > > shouldn't ever go away, even if the lock is not held. > > inode->i_cdev is set to NULL by cdev_default_release() which can be called > from media_devnode_unregister(). I could move to container_of outside the > lock, but in that case I would have to check for mdev == NULL || > !mdev_devnode_is_registered(mdev) (or move the NULL check inside > mdev_devnode_is_registered). Is that what you would like ? As container_of _ALWAYS_ returns a valid pointer, you can't check it for NULL. I don't know, it just doesn't seem correct here, but if you are sure it's working properly, I'll not push the issue. > > > + /* return ENXIO if the media device has been removed > > > + already or if it is not registered anymore. */ > > > + if (!media_devnode_is_registered(mdev)) { > > > + mutex_unlock(&media_devnode_lock); > > > + return -ENXIO; > > > + } > > > > So you can unregister a device at any time, even if the device is open, > > or about to be opened? > > That's correct. That way drivers don't need to care about unregister/open > races, media_devnode will handle it for them. Ok. > > Then that's fine, but you can put the lock after the container_of(), right? > > If I add a NULL check (as explained above), yes. Again, you can't check for NULL as the result of container_of() that does not work (hint, container_of() is just pointer math, without ever looking at the original pointer value.) > > > + /* and increase the device refcount */ > > > + get_device(&mdev->dev); > > > > How is that holding anything into memory? > > That will prevent the device instance from being freed until the device is > closed, thereby holding both the device instance and the cdev instance in > memory. Tricky :) > > Don't you want to keep the module that the fops pointer in the device in > > memory, not necessarily the device itself? > > The cdev owner pointer is set to the fops owner. Unless I'm mistaken it will > keep the module in memory. I need to keep the device in memory (or rather the > media_devnode structure that embeds it) to handle file operations on a device > that gets unregistered after it has been opened. Ok. > > > + mutex_unlock(&media_devnode_lock); > > > + > > > + filp->private_data = mdev; > > > + > > > + if (mdev->fops->open) { > > > + ret = mdev->fops->open(filp); > > > + if (ret) { > > > + put_device(&mdev->dev); > > > + return ret; > > > + } > > > + } > > > + > > > + return 0; > > > +} > > > > No reference counting for the fops? Why not? > > Because cdev already increments the owner refcount on open(). Ok. > > Anyway, it looks like what you really want is an "easier" way to handle > > a cdev and a struct device that will export the proper information to > > userspace, right? > > > > Why not do this generically, fixing up the cdev interface (which really > > needs it) and not tie it to media devices at all, making it possible for > > _everyone_ to use this type of infrastructure? > > > > That seems like the better thing to do here. > > Sounds like a good idea. You're a better cdev expert than me, so could you > give me a few pointers ? Do you want me to create a new object that will hold > a struct cdev and a struct device together, or to embed the device structure > into the existing cdev structure ? I don't really know, all I know is that cdev is a difficult thing to handle at times, but not everyone who uses it needs a struct device. But some people do (as this code shows), so I guess it needs to be a whole new structure/interface that binds the two together like you just did. I think that would be good for a lot more places other than just the media subsystem, so it should go into the core kernel instead. thanks, greg k-h -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html