On 6/1/19 7:57 PM, Ezequiel Garcia wrote:
> On Mon, 2019-03-11 at 12:36 +0100, Hans Verkuil wrote:
>> On 2/27/19 6:07 PM, Ezequiel Garcia wrote:
>>> Currently, the v4l2 control code is a bit silent on errors.
>>> Now that we have a debug parameter, it's possible to enable
>>> debugging messages here.
>>>
>>> Add debug messages on (hopefully) most of the error paths.
>>> Since it's really hard to associate all these errors
>>> to video device instance, we are forced to use the global
>>> debug parameter only.
>>>
>>> Add a warning in case the user enables control debugging
>>> at the per-device dev_debug level.
>>>
>>> Signed-off-by: Ezequiel Garcia <[email protected]>
>>> ---
>>>  drivers/media/v4l2-core/v4l2-ctrls.c  | 93 +++++++++++++++++++++------
>>>  drivers/media/v4l2-core/v4l2-dev.c    |  2 +
>>>  drivers/media/v4l2-core/v4l2-ioctl.c  |  8 +--
>>>  drivers/media/v4l2-core/v4l2-subdev.c |  4 +-
>>>  include/media/v4l2-ctrls.h            |  9 ++-
>>>  include/media/v4l2-ioctl.h            |  2 +
>>>  6 files changed, 91 insertions(+), 27 deletions(-)
>>>
>>> diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c 
>>> b/drivers/media/v4l2-core/v4l2-ctrls.c
>>> index b79d3bbd8350..af8ad83d1e08 100644
>>> --- a/drivers/media/v4l2-core/v4l2-ctrls.c
>>> +++ b/drivers/media/v4l2-core/v4l2-ctrls.c
>>> @@ -18,6 +18,8 @@
>>>      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  
>>> USA
>>>   */
>>>  
>>> +#define pr_fmt(fmt) "v4l2-ctrls: " fmt
>>> +
>>>  #include <linux/ctype.h>
>>>  #include <linux/mm.h>
>>>  #include <linux/slab.h>
>>> @@ -28,6 +30,14 @@
>>>  #include <media/v4l2-event.h>
>>>  #include <media/v4l2-dev.h>
>>>  
>>> +extern unsigned int videodev_debug;
>>> +
>>> +#define dprintk(fmt, arg...) do {                                  \
>>> +   if (videodev_debug & V4L2_DEV_DEBUG_CTRL)                       \
>>> +           printk(KERN_DEBUG pr_fmt("%s: " fmt),                   \
>>> +                  __func__, ##arg);                                \
>>> +} while (0)
>>> +
>>>  #define has_op(master, op) \
>>>     (master->ops && master->ops->op)
>>>  #define call_op(master, op) \
>>> @@ -1952,8 +1962,11 @@ static int validate_new(const struct v4l2_ctrl 
>>> *ctrl, union v4l2_ctrl_ptr p_new)
>>>     unsigned idx;
>>>     int err = 0;
>>>  
>>> -   for (idx = 0; !err && idx < ctrl->elems; idx++)
>>> +   for (idx = 0; !err && idx < ctrl->elems; idx++) {
>>>             err = ctrl->type_ops->validate(ctrl, idx, p_new);
>>> +           if (err)
>>> +                   dprintk("failed to validate control id 0x%x (%d)\n", 
>>> ctrl->id, err);
>>> +   }
>>>     return err;
>>>  }
>>>  
>>> @@ -3136,20 +3149,28 @@ static int prepare_ext_ctrls(struct 
>>> v4l2_ctrl_handler *hdl,
>>>             if (cs->which &&
>>>                 cs->which != V4L2_CTRL_WHICH_DEF_VAL &&
>>>                 cs->which != V4L2_CTRL_WHICH_REQUEST_VAL &&
>>> -               V4L2_CTRL_ID2WHICH(id) != cs->which)
>>> +               V4L2_CTRL_ID2WHICH(id) != cs->which) {
>>> +                   dprintk("invalid which 0x%x or control id 0x%x\n", 
>>> cs->which, id);
>>>                     return -EINVAL;
>>> +           }
>>>  
>>>             /* Old-style private controls are not allowed for
>>>                extended controls */
>>> -           if (id >= V4L2_CID_PRIVATE_BASE)
>>> +           if (id >= V4L2_CID_PRIVATE_BASE) {
>>> +                   dprintk("old-style private controls not allowed for 
>>> extended controls\n");
>>>                     return -EINVAL;
>>> +           }
>>>             ref = find_ref_lock(hdl, id);
>>> -           if (ref == NULL)
>>> +           if (ref == NULL) {
>>> +                   dprintk("cannot find control id 0x%x\n", id);
>>>                     return -EINVAL;
>>> +           }
>>>             h->ref = ref;
>>>             ctrl = ref->ctrl;
>>> -           if (ctrl->flags & V4L2_CTRL_FLAG_DISABLED)
>>> +           if (ctrl->flags & V4L2_CTRL_FLAG_DISABLED) {
>>> +                   dprintk("control id 0x%x is disabled\n", id);
>>>                     return -EINVAL;
>>> +           }
>>>  
>>>             if (ctrl->cluster[0]->ncontrols > 1)
>>>                     have_clusters = true;
>>> @@ -3159,10 +3180,16 @@ static int prepare_ext_ctrls(struct 
>>> v4l2_ctrl_handler *hdl,
>>>                     unsigned tot_size = ctrl->elems * ctrl->elem_size;
>>>  
>>>                     if (c->size < tot_size) {
>>> +                           /*
>>> +                            * In the get case the application first queries
>>> +                            * to obtain the size of the control.
>>> +                            */
>>>                             if (get) {
>>>                                     c->size = tot_size;
>>>                                     return -ENOSPC;
>>>                             }
>>> +                           dprintk("pointer control id 0x%x size too 
>>> small, %d bytes but %d bytes needed\n",
>>> +                                   id, c->size, tot_size);
>>>                             return -EFAULT;
>>>                     }
>>>                     c->size = tot_size;
>>> @@ -3534,16 +3561,20 @@ static int validate_ctrls(struct v4l2_ext_controls 
>>> *cs,
>>>  
>>>             cs->error_idx = i;
>>>  
>>> -           if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY)
>>> +           if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY) {
>>> +                   dprintk("control id 0x%x is read-only\n", ctrl->id);
>>>                     return -EACCES;
>>> +           }
>>>             /* This test is also done in try_set_control_cluster() which
>>>                is called in atomic context, so that has the final say,
>>>                but it makes sense to do an up-front check as well. Once
>>>                an error occurs in try_set_control_cluster() some other
>>>                controls may have been set already and we want to do a
>>>                best-effort to avoid that. */
>>> -           if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED))
>>> +           if (set && (ctrl->flags & V4L2_CTRL_FLAG_GRABBED)) {
>>> +                   dprintk("control id 0x%x is grabbed, cannot set\n", 
>>> ctrl->id);
>>>                     return -EBUSY;
>>> +           }
>>>             /*
>>>              * Skip validation for now if the payload needs to be copied
>>>              * from userspace into kernelspace. We'll validate those later.
>>> @@ -3576,7 +3607,8 @@ static void update_from_auto_cluster(struct v4l2_ctrl 
>>> *master)
>>>  }
>>>  
>>>  /* Try or try-and-set controls */
>>> -static int try_set_ext_ctrls_common(struct v4l2_fh *fh,
>>> +static int try_set_ext_ctrls_common(struct video_device *vdev,
>>> +                               struct v4l2_fh *fh,
>>>                                 struct v4l2_ctrl_handler *hdl,
>>>                                 struct v4l2_ext_controls *cs, bool set)
>>>  {
>>> @@ -3588,13 +3620,17 @@ static int try_set_ext_ctrls_common(struct v4l2_fh 
>>> *fh,
>>>     cs->error_idx = cs->count;
>>>  
>>>     /* Default value cannot be changed */
>>> -   if (cs->which == V4L2_CTRL_WHICH_DEF_VAL)
>>> +   if (cs->which == V4L2_CTRL_WHICH_DEF_VAL) {
>>> +           dprintk("%s: cannot change default value\n", 
>>> video_device_node_name(vdev));
>>>             return -EINVAL;
>>> +   }
>>>  
>>>     cs->which = V4L2_CTRL_ID2WHICH(cs->which);
>>>  
>>> -   if (hdl == NULL)
>>> +   if (hdl == NULL) {
>>> +           dprintk("%s: invalid null control handler\n", 
>>> video_device_node_name(vdev));
>>>             return -EINVAL;
>>> +   }
>>>  
>>>     if (cs->count == 0)
>>>             return class_check(hdl, cs->which);
>>> @@ -3691,7 +3727,8 @@ static int try_set_ext_ctrls_common(struct v4l2_fh 
>>> *fh,
>>>     return ret;
>>>  }
>>>  
>>> -static int try_set_ext_ctrls(struct v4l2_fh *fh,
>>> +static int try_set_ext_ctrls(struct video_device *vdev,
>>> +                        struct v4l2_fh *fh,
>>>                          struct v4l2_ctrl_handler *hdl, struct media_device 
>>> *mdev,
>>>                          struct v4l2_ext_controls *cs, bool set)
>>>  {
>>> @@ -3700,21 +3737,32 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh,
>>>     int ret;
>>>  
>>>     if (cs->which == V4L2_CTRL_WHICH_REQUEST_VAL) {
>>> -           if (!mdev || cs->request_fd < 0)
>>> +           if (!mdev) {
>>> +                   dprintk("%s: missing media device\n", 
>>> video_device_node_name(vdev));
>>> +                   return -EINVAL;
>>> +           }
>>> +
>>> +           if (cs->request_fd < 0) {
>>> +                   dprintk("%s: invalid request fd %d\n", 
>>> video_device_node_name(vdev), cs->request_fd);
>>>                     return -EINVAL;
>>> +           }
>>>  
>>>             req = media_request_get_by_fd(mdev, cs->request_fd);
>>> -           if (IS_ERR(req))
>>> +           if (IS_ERR(req)) {
>>> +                   dprintk("%s: cannot find request fd %d\n", 
>>> video_device_node_name(vdev), cs->request_fd);
>>>                     return PTR_ERR(req);
>>> +           }
>>>  
>>>             ret = media_request_lock_for_update(req);
>>>             if (ret) {
>>> +                   dprintk("%s: cannot lock request fd %d\n", 
>>> video_device_node_name(vdev), cs->request_fd);
>>>                     media_request_put(req);
>>>                     return ret;
>>>             }
>>>  
>>>             obj = v4l2_ctrls_find_req_obj(hdl, req, set);
>>>             if (IS_ERR(obj)) {
>>> +                   dprintk("%s: cannot find request object for request fd 
>>> %d\n", video_device_node_name(vdev), cs->request_fd);
>>
>> These lines are way too long. Just add a newline after the first comma.
>>
>> Same elsewhere.
>>
>>>                     media_request_unlock_for_update(req);
>>>                     media_request_put(req);
>>>                     return PTR_ERR(obj);
>>> @@ -3723,7 +3771,9 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh,
>>>                                req_obj);
>>>     }
>>>  
>>> -   ret = try_set_ext_ctrls_common(fh, hdl, cs, set);
>>> +   ret = try_set_ext_ctrls_common(vdev, fh, hdl, cs, set);
>>> +   if (ret)
>>> +           dprintk("%s: try_set_ext_ctrls_common failed (%d)\n", 
>>> video_device_node_name(vdev), ret);
>>>  
>>>     if (obj) {
>>>             media_request_unlock_for_update(req);
>>> @@ -3734,17 +3784,22 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh,
>>>     return ret;
>>>  }
>>>  
>>> -int v4l2_try_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct media_device 
>>> *mdev,
>>> +int v4l2_try_ext_ctrls(struct video_device *vdev,
>>> +                  struct v4l2_ctrl_handler *hdl,
>>> +                  struct media_device *mdev,
>>>                    struct v4l2_ext_controls *cs)
>>>  {
>>> -   return try_set_ext_ctrls(NULL, hdl, mdev, cs, false);
>>> +   return try_set_ext_ctrls(vdev, NULL, hdl, mdev, cs, false);
>>>  }
>>>  EXPORT_SYMBOL(v4l2_try_ext_ctrls);
>>>  
>>> -int v4l2_s_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl,
>>> -                struct media_device *mdev, struct v4l2_ext_controls *cs)
>>> +int v4l2_s_ext_ctrls(struct video_device *vdev,
>>> +                struct v4l2_fh *fh,
>>> +                struct v4l2_ctrl_handler *hdl,
>>> +                struct media_device *mdev,
>>> +                struct v4l2_ext_controls *cs)
>>>  {
>>> -   return try_set_ext_ctrls(fh, hdl, mdev, cs, true);
>>> +   return try_set_ext_ctrls(vdev, fh, hdl, mdev, cs, true);
>>>  }
>>>  EXPORT_SYMBOL(v4l2_s_ext_ctrls);
>>>  
>>> diff --git a/drivers/media/v4l2-core/v4l2-dev.c 
>>> b/drivers/media/v4l2-core/v4l2-dev.c
>>> index 39d22bfbe420..c6bcc9ea1122 100644
>>> --- a/drivers/media/v4l2-core/v4l2-dev.c
>>> +++ b/drivers/media/v4l2-core/v4l2-dev.c
>>> @@ -83,6 +83,8 @@ static ssize_t dev_debug_store(struct device *cd, struct 
>>> device_attribute *attr,
>>>     if (res)
>>>             return res;
>>>  
>>> +   if (value & V4L2_DEV_DEBUG_CTRL)
>>> +           pr_warn_once("Warning: V4L2_DEV_DEBUG_CTRL cannot be enabled 
>>> via the dev_debug attribute.\n");

BTW, you should clear the V4L2_DEV_DEBUG_CTRL bit before setting 
vdev->dev_debug.

>>
>> Actually, you can for those functions that have the vdev pointer.
>> And I think you can pass vdev on to more functions. Certainly 
>> validate_ctrls()
>> and possibly all of them.
>>
> 
> Before sending this patch, I tried different options,
> but failed to find a proper way of associating all error paths
> with a struct video_device.
> 
> For instance, __v4l2_ctrl_s_ctrl eventually calls validate_new,
> and it seems really nasty to change its prototype, as it's called
> by so many drivers.
> 
> I think it's a too invasive change, and not worth it just to
> add one debug print.
> 
> So one option would be to drop the validate_new print.

Yeah, that's probably best.

> 
> Another option would be have a slightly inconsistent behavior between
> setting the module debug parameter and the per-device debug attribute.
> 
> I think for debugging, consistency is very important, and that's
> why I prefered keeping the debug parameter and produce this warning.

OK, post a v3 and I'll take it.

Regards,

        Hans

> 
> Thanks,
> Ezequiel
> 

Reply via email to