Hi Hans,
Thanks for the review.
On Saturday 24 July 2010 13:59:11 Hans Verkuil wrote:
> On Wednesday 21 July 2010 16:35:26 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.
> >
> > The code is based on video/v4l2-dev.c.
[snip]
> > diff --git a/drivers/media/media-devnode.c
> > b/drivers/media/media-devnode.c new file mode 100644
> > index 0000000..fa300f8
> > --- /dev/null
> > +++ b/drivers/media/media-devnode.c
> > @@ -0,0 +1,339 @@
[snip]
> > +/*
> > + * Active devices
> > + */
> > +static DEFINE_MUTEX(media_devnode_lock);
>
> I don't think this lock is actually needed. The bit operations are already
> atomic, so we can do without this lock.
The mutex is needed to avoid an open/unregister race. Without it the device
could be unregistered during media_open, after the media_devnode_is_registered
check but before the call to get_device.
> > +static DECLARE_BITMAP(media_devnode_nums, MEDIA_NUM_DEVICES);
> > +
> > +static inline void media_get(struct media_devnode *mdev)
> > +{
> > + get_device(&mdev->dev);
> > +}
> > +
> > +static inline void media_put(struct media_devnode *mdev)
> > +{
> > + put_device(&mdev->dev);
> > +}
>
> These functions are used quite rarely. I'd remove these inlines and just
> call get/put_device directly.
Agreed. I was thinking of removing them and thought you would like it better
if I kept them :-)
[snip]
> > +static long media_ioctl(struct file *filp, unsigned int cmd, unsigned
> > long arg) +{
> > + struct media_devnode *mdev = media_devnode_data(filp);
> > +
> > + if (!media_devnode_is_registered(mdev))
> > + return -EIO;
> > +
> > + if (!mdev->fops->unlocked_ioctl)
> > + return -ENOTTY;
>
> These two if's should be swapper (just like in media_read and write).
I'm not sure why, but OK.
> > +
> > + return mdev->fops->unlocked_ioctl(filp, cmd, arg);
> > +}
> > +
> > +/* 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 */
> > + mutex_lock(&media_devnode_lock);
> > + mdev = container_of(inode->i_cdev, struct media_devnode, cdev);
> > + /* return ENODEV if the media device has been removed
> > + already or if it is not registered anymore. */
> > + if (mdev == NULL || !media_devnode_is_registered(mdev)) {
>
> mdev can never be NULL.
Indeed, thanks.
> > + mutex_unlock(&media_devnode_lock);
> > + return -ENODEV;
> > + }
> > + /* and increase the device refcount */
> > + media_get(mdev);
> > + mutex_unlock(&media_devnode_lock);
> > + if (mdev->fops->open) {
> > + ret = mdev->fops->open(filp);
> > + if (ret) {
> > + media_put(mdev);
> > + return ret;
> > + }
> > + }
> > +
> > + filp->private_data = mdev;
> > + return 0;
> > +}
[snip]
> > +/**
> > + * media_devnode_register - register a media device node
> > + * @mdev: media device node structure we want to register
> > + *
> > + * The registration code assigns minor numbers and registers the new
> > device node
> > + * with the kernel. An error is returned if no free minor number can be
> > found,
> > + * or if the registration of the device node fails.
> > + *
> > + * Zero is returned on success.
> > + *
> > + * Note that if the media_devnode_register call fails, the release()
> > callback of
> > + * the media_devnode structure is *not* called, so the caller is
> > responsible for
> > + * freeing any data.
> > + */
> > +int __must_check media_devnode_register(struct media_devnode *mdev)
> > +{
> > + void *priv;
> > + int minor;
> > + int ret;
> > +
> > + /* Part 1: find a free minor number */
> > + mutex_lock(&media_devnode_lock);
> > + minor = find_next_zero_bit(media_devnode_nums, 0, MEDIA_NUM_DEVICES);
>
> When media_devnode_lock is removed we can't use find_next_zero_bit anymore.
> Instead, we have to loop over all bits and call test_and_set_bit() to
> ensure atomicity.
I think the lock is still needed (see above), so find_next_zero_bit can be
kept.
> > + if (minor == MEDIA_NUM_DEVICES) {
> > + printk(KERN_ERR "could not get a free minor\n");
> > + mutex_unlock(&media_devnode_lock);
> > + return -ENFILE;
> > + }
> > +
> > + set_bit(mdev->minor, media_devnode_nums);
> > + mdev->minor = minor;
> > +
> > + mutex_unlock(&media_devnode_lock);
> > +
> > + /* Part 2: Initialize and register the character device */
> > + cdev_init(&mdev->cdev, &media_devnode_fops);
> > + mdev->cdev.owner = mdev->fops->owner;
> > +
> > + ret = cdev_add(&mdev->cdev, MKDEV(MAJOR(media_dev_t), mdev->minor), 1);
> > + if (ret < 0) {
> > + printk(KERN_ERR "%s: cdev_add failed\n", __func__);
> > + goto error;
> > + }
> > +
> > + /* Part 3: register the media device
> > + *
> > + * Zeroing struct device will clear the device's drvdata, so make a
> > + * copy and put it back.
> > + */
> > + priv = dev_get_drvdata(&mdev->dev);
> > + memset(&mdev->dev, 0, sizeof(mdev->dev));
> > + dev_set_drvdata(&mdev->dev, priv);
>
> Don't zero mdev->dev. We should assume that mdev has been zeroed by the
> caller. This construct is actually a bug in v4l2-dev.c. I've seen patches
> that fixed it, but I don't know the status of those patches. The problem
> is that dev_set_drvdata these days allocates memory. So if dev_set_drvdata
> was called before this function, then this construct will leak a bit of
> memory.
>
> I would just remove this and require that mdev was initialized correctly
> when it was allocated.
OK.
> > + mdev->dev.class = &media_class;
> > + mdev->dev.devt = MKDEV(MAJOR(media_dev_t), mdev->minor);
> > + mdev->dev.release = media_devnode_release;
> > + if (mdev->parent)
> > + mdev->dev.parent = mdev->parent;
> > + dev_set_name(&mdev->dev, "media%d", mdev->minor);
>
> Wouldn't mediactlX be a better name? Just plain 'media' is awfully general.
Good question.
> > + ret = device_register(&mdev->dev);
> > + if (ret < 0) {
> > + printk(KERN_ERR "%s: device_register failed\n", __func__);
> > + goto error;
> > + }
> > +
> > + /* Part 4: Activate this minor. The char device can now be used. */
> > + set_bit(MEDIA_FLAG_REGISTERED, &mdev->flags);
> > +
> > + return 0;
> > +
> > +error:
> > + cdev_del(&mdev->cdev);
> > + mutex_lock(&media_devnode_lock);
> > + clear_bit(mdev->minor, media_devnode_nums);
> > + mutex_unlock(&media_devnode_lock);
> > + return ret;
> > +}
[snip]
> > diff --git a/include/media/media-devnode.h
> > b/include/media/media-devnode.h new file mode 100644
> > index 0000000..5c8f682
> > --- /dev/null
> > +++ b/include/media/media-devnode.h
> > @@ -0,0 +1,91 @@
[snip]
> > +struct media_file_operations {
> > + struct module *owner;
> > + ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
> > + ssize_t (*write) (struct file *, const char __user *, size_t, loff_t
> > *); + unsigned int (*poll) (struct file *, struct poll_table_struct
> > *);
> > + long (*ioctl) (struct file *, unsigned int, unsigned long);
>
> I think you forgot to remove the ioctl, mmap and get_unmapped_area ops from
> this struct.
Yes, thanks.
> > + long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
> > + unsigned long (*get_unmapped_area) (struct file *, unsigned long,
> > + unsigned long, unsigned long, unsigned long);
> > + int (*mmap) (struct file *, struct vm_area_struct *);
> > + int (*open) (struct file *);
> > + int (*release) (struct file *);
> > +};
> > +
> > +/**
> > + * struct media_devnode - Media device node
> > + * @parent: parent device
> > + * @name: media device node name
> > + * @minor: device node minor number
> > + * @flags: flags, combination of the MEDIA_FLAG_* constants
> > + *
> > + * This structure represents a media-related device node.
> > + *
> > + * The @parent is a physical device. It must be set by core or device
> > drivers
> > + * before registering the node.
> > + *
> > + * @name is a descriptive name exported through sysfs. It doesn't have to
> > be
> > + * unique.
> > + *
> > + * The device node number @num is used to create the kobject name and
> > thus
> > + * serves as a hint to udev when creating the device node.
>
> 'num' no longer exists.
Oops :-)
--
Regards,
Laurent Pinchart
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html