From patchwork Thu Sep 17 21:19:36 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Arnd Bergmann X-Patchwork-Id: 7210731 Return-Path: X-Original-To: patchwork-linux-media@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 8B7CABEEC1 for ; Thu, 17 Sep 2015 21:21:50 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 340862062B for ; Thu, 17 Sep 2015 21:21:49 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 7624020690 for ; Thu, 17 Sep 2015 21:21:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752689AbbIQVV0 (ORCPT ); Thu, 17 Sep 2015 17:21:26 -0400 Received: from mout.kundenserver.de ([212.227.17.13]:61161 "EHLO mout.kundenserver.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751478AbbIQVU0 (ORCPT ); Thu, 17 Sep 2015 17:20:26 -0400 Received: from wuerfel.lan. ([149.172.15.242]) by mrelayeu.kundenserver.de (mreue102) with ESMTPSA (Nemesis) id 0MLy2e-1ZXBtD1GrE-007j5t; Thu, 17 Sep 2015 23:20:15 +0200 From: Arnd Bergmann To: linux-media@vger.kernel.org Cc: linux-kernel@vger.kernel.org, y2038@lists.linaro.org, Mauro Carvalho Chehab , linux-api@vger.kernel.org, linux-samsung-soc@vger.kernel.org, Hans Verkuil , Arnd Bergmann Subject: [PATCH v2 5/9] [media] make VIDIOC_DQEVENT work with 64-bit time_t Date: Thu, 17 Sep 2015 23:19:36 +0200 Message-Id: <1442524780-781677-6-git-send-email-arnd@arndb.de> X-Mailer: git-send-email 2.1.0.rc2 In-Reply-To: <1442524780-781677-1-git-send-email-arnd@arndb.de> References: <1442524780-781677-1-git-send-email-arnd@arndb.de> X-Provags-ID: V03:K0:BJZ8d8VAV0TCXpVN2elD7ZPrEPMgOd+oUP4AyAAYCsE0br/E4gM 8ubWhU+wP6UI1TvqkBcbwK/Mn3W8+SJrckaq6bdv5wb8eBCwDyNim4tCRCJuBDgX6uEBcX5 jpr6jvYTTyLrlCr/CnISwJi9re0IWlFlpMGM1i/jDgvaG6+Zxc3b8hjAz1ZYMu8CN/IvfKT z6rFmbr0UkGE7U0MGPg3Q== X-UI-Out-Filterresults: notjunk:1; V01:K0:cx7nXtKrz8U=:qlaF2mkZ5Eayi19oG4xW10 dfwa5wofONy0zLNF+a7HX024Cx32WBs3tdSRmysKWsYjXJ7FnHlDvOF97TaAkJg3yMV7R/RcL a8UwstGJZy6YsTTNbnVDwcbYzYOK+AOllL5kz1vi6Ew4hmVs+Ke3VchJx+9VBCsFYZDcoX47W uFeifrrTHh/FJb5xEL7G3EWYSxuWNyNPsP8c8zawxh4gj5+3EUSP7Ho3jyjFVUikihLu5F861 b6wPWcf4Sg1zgtj5oVc/xoZ80ao6HE4D/t4p/ssAOfzQXOVOqYcs0VcQeBrYogMZARzuxJBHW K3W01wpeUUckNzQAY8/WKST4TwkqSB0oru2ZC/M/0UplMAL/mzCp5EIz0mKEAyx2ZFF/js1ON zUCsROIvr4bUZyzoX7vkJLCswvpMtZDNRAtETJoeGpqYYYCX+aCgbZbcUtnrtmVkyyfxTASFN yJ7y37rQu1u7JOgMG23XAn73uC6bFg3JX+SBeUPlg9Vfixu+yQ+IJkqi2PI/GcxPXvhGbhnHm 9F7C0HH5kGybKOJH2nCm+aoxi59iBbkKgw7DaJJ2abcVnYqO5b31r7ohEsagZ81KZEVKQwfRj QF8jxuXp+Wi516kW+Qw6h/Ue2jPFURG4NhBXyUFJuQR8K7ej5vgaFpb1Ned8WP4/Q7cr31gen zfWVyo3rrH/BL7ZVSClENMeP/tMnILozooNfpYQt1wvcrJPEpNRr6KNaDb7764wrGRl4= Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The v4l2 event queue uses a 'struct timespec' to pass monotonic timestamps. This is not a problem by itself, but it breaks when user space redefines timespec to use 'long long' on 32-bit systems. This is the second approach on fixing the problem, by changing the kernel to internally use 64-bit members for the timestamps in struct v4l2_event, and letting user space use either version. Unfortunately, we cannot use timespec64 here, because that does not have a well-defined ABI yet, and differs from the 64-bit version of 'timespec'. Using a pair of __s64 members makes sure the structure is well-defined and contains no padding that might leak kernel stack data. We now need the handling for VIDIOC_DQEVENT32 on both 32-bit and 64-bit architectures, so the handling for that is moved from v4l2-compat-ioctl32.c to v4l2-ioctl.c and v4l2-subdev.c. Signed-off-by: Arnd Bergmann --- drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 35 ------------------ drivers/media/v4l2-core/v4l2-event.c | 35 +++++++++++++++--- drivers/media/v4l2-core/v4l2-ioctl.c | 53 ++++++++++++++++++++------- drivers/media/v4l2-core/v4l2-subdev.c | 6 +++ include/media/v4l2-event.h | 2 + include/uapi/linux/videodev2.h | 31 ++++++++++++++++ 6 files changed, 107 insertions(+), 55 deletions(-) diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index af635430524e..9ffe7302206e 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -735,32 +735,6 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext return 0; } -struct v4l2_event32 { - __u32 type; - union { - __u8 data[64]; - } u; - __u32 pending; - __u32 sequence; - struct compat_timespec timestamp; - __u32 id; - __u32 reserved[8]; -}; - -static int put_v4l2_event32(struct v4l2_event *kp, struct v4l2_event32 __user *up) -{ - if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_event32)) || - put_user(kp->type, &up->type) || - copy_to_user(&up->u, &kp->u, sizeof(kp->u)) || - put_user(kp->pending, &up->pending) || - put_user(kp->sequence, &up->sequence) || - compat_put_timespec(&kp->timestamp, &up->timestamp) || - put_user(kp->id, &up->id) || - copy_to_user(up->reserved, kp->reserved, 8 * sizeof(__u32))) - return -EFAULT; - return 0; -} - struct v4l2_edid32 { __u32 pad; __u32 start_block; @@ -814,7 +788,6 @@ static int put_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up) #define VIDIOC_G_EXT_CTRLS32 _IOWR('V', 71, struct v4l2_ext_controls32) #define VIDIOC_S_EXT_CTRLS32 _IOWR('V', 72, struct v4l2_ext_controls32) #define VIDIOC_TRY_EXT_CTRLS32 _IOWR('V', 73, struct v4l2_ext_controls32) -#define VIDIOC_DQEVENT32 _IOR ('V', 89, struct v4l2_event32) #define VIDIOC_CREATE_BUFS32 _IOWR('V', 92, struct v4l2_create_buffers32) #define VIDIOC_PREPARE_BUF32 _IOWR('V', 93, struct v4l2_buffer32) @@ -860,7 +833,6 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar case VIDIOC_G_EXT_CTRLS32: cmd = VIDIOC_G_EXT_CTRLS; break; case VIDIOC_S_EXT_CTRLS32: cmd = VIDIOC_S_EXT_CTRLS; break; case VIDIOC_TRY_EXT_CTRLS32: cmd = VIDIOC_TRY_EXT_CTRLS; break; - case VIDIOC_DQEVENT32: cmd = VIDIOC_DQEVENT; break; case VIDIOC_OVERLAY32: cmd = VIDIOC_OVERLAY; break; case VIDIOC_STREAMON32: cmd = VIDIOC_STREAMON; break; case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break; @@ -940,9 +912,6 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar err = get_v4l2_ext_controls32(&karg.v2ecs, up); compatible_arg = 0; break; - case VIDIOC_DQEVENT: - compatible_arg = 0; - break; } if (err) return err; @@ -983,10 +952,6 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar err = put_v4l2_framebuffer32(&karg.v2fb, up); break; - case VIDIOC_DQEVENT: - err = put_v4l2_event32(&karg.v2ev, up); - break; - case VIDIOC_G_EDID: case VIDIOC_S_EDID: err = put_v4l2_edid32(&karg.v2edid, up); diff --git a/drivers/media/v4l2-core/v4l2-event.c b/drivers/media/v4l2-core/v4l2-event.c index 8d3171c6bee8..149342490e91 100644 --- a/drivers/media/v4l2-core/v4l2-event.c +++ b/drivers/media/v4l2-core/v4l2-event.c @@ -92,6 +92,28 @@ int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event, } EXPORT_SYMBOL_GPL(v4l2_event_dequeue); +int v4l2_event_dequeue32(struct v4l2_fh *fh, struct v4l2_event32 *event32, + int nonblocking) +{ + struct v4l2_event event64; + int ret; + + ret = v4l2_event_dequeue(fh, &event64, nonblocking); + if (ret) + return ret; + + /* only the timestamp differs, so use memcpy to copy everything else */ + memcpy(event32, &event64, offsetof(struct v4l2_event32, timestamp)); + event32->timestamp.tv_sec = (long)event64.timestamp.tv_sec; + event32->timestamp.tv_nsec = (long)event64.timestamp.tv_nsec; + memcpy(&event32->id, &event64.id, + sizeof(event32->id) + sizeof(event32->reserved)); + + return ret; +} +EXPORT_SYMBOL_GPL(v4l2_event_dequeue32); + + /* Caller must hold fh->vdev->fh_lock! */ static struct v4l2_subscribed_event *v4l2_event_subscribed( struct v4l2_fh *fh, u32 type, u32 id) @@ -108,7 +130,7 @@ static struct v4l2_subscribed_event *v4l2_event_subscribed( } static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev, - const struct timespec *ts) + const struct timespec64 *ts) { struct v4l2_subscribed_event *sev; struct v4l2_kevent *kev; @@ -156,7 +178,8 @@ static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *e if (copy_payload) kev->event.u = ev->u; kev->event.id = ev->id; - kev->event.timestamp = *ts; + kev->event.timestamp.tv_sec = ts->tv_sec; + kev->event.timestamp.tv_nsec = ts->tv_nsec; kev->event.sequence = fh->sequence; sev->in_use++; list_add_tail(&kev->list, &fh->available); @@ -170,12 +193,12 @@ void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev) { struct v4l2_fh *fh; unsigned long flags; - struct timespec timestamp; + struct timespec64 timestamp; if (vdev == NULL) return; - ktime_get_ts(×tamp); + ktime_get_ts64(×tamp); spin_lock_irqsave(&vdev->fh_lock, flags); @@ -189,9 +212,9 @@ EXPORT_SYMBOL_GPL(v4l2_event_queue); void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev) { unsigned long flags; - struct timespec timestamp; + struct timespec64 timestamp; - ktime_get_ts(×tamp); + ktime_get_ts64(×tamp); spin_lock_irqsave(&fh->vdev->fh_lock, flags); __v4l2_event_queue_fh(fh, ev, ×tamp); diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 4a384fc765b8..7aab18dd2ca5 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -797,22 +797,18 @@ static void v4l_print_frmivalenum(const void *arg, bool write_only) } } -static void v4l_print_event(const void *arg, bool write_only) +static void v4l_print_event_data(const void *arg, bool write_only, u32 type) { - const struct v4l2_event *p = arg; - const struct v4l2_event_ctrl *c; + const struct v4l2_event_vsync *vsync = arg; + const struct v4l2_event_ctrl *c = arg; + const struct v4l2_event_frame_sync *frame_sync = arg; - pr_cont("type=0x%x, pending=%u, sequence=%u, id=%u, " - "timestamp=%lu.%9.9lu\n", - p->type, p->pending, p->sequence, p->id, - p->timestamp.tv_sec, p->timestamp.tv_nsec); - switch (p->type) { + switch (type) { case V4L2_EVENT_VSYNC: printk(KERN_DEBUG "field=%s\n", - prt_names(p->u.vsync.field, v4l2_field_names)); + prt_names(vsync->field, v4l2_field_names)); break; case V4L2_EVENT_CTRL: - c = &p->u.ctrl; printk(KERN_DEBUG "changes=0x%x, type=%u, ", c->changes, c->type); if (c->type == V4L2_CTRL_TYPE_INTEGER64) @@ -825,12 +821,34 @@ static void v4l_print_event(const void *arg, bool write_only) c->step, c->default_value); break; case V4L2_EVENT_FRAME_SYNC: - pr_cont("frame_sequence=%u\n", - p->u.frame_sync.frame_sequence); + pr_cont("frame_sequence=%u\n", frame_sync->frame_sequence); break; } } +static void v4l_print_event32(const void *arg, bool write_only) +{ + const struct v4l2_event32 *p = arg; + pr_cont("type=0x%x, pending=%u, sequence=%u, id=%u, " + "timestamp=%d.%9.9u\n", + p->type, p->pending, p->sequence, p->id, + p->timestamp.tv_sec, p->timestamp.tv_nsec); + + return v4l_print_event_data(&p->u, write_only, p->type); +} + +static void v4l_print_event64(const void *arg, bool write_only) +{ + const struct v4l2_event *p = arg; + + pr_cont("type=0x%x, pending=%u, sequence=%u, id=%u, " + "timestamp=%lld.%9.9llu\n", + p->type, p->pending, p->sequence, p->id, + p->timestamp.tv_sec, p->timestamp.tv_nsec); + + return v4l_print_event_data(&p->u, write_only, p->type); +} + static void v4l_print_event_subscription(const void *arg, bool write_only) { const struct v4l2_event_subscription *p = arg; @@ -2235,12 +2253,18 @@ static int v4l_dbg_g_chip_info(const struct v4l2_ioctl_ops *ops, #endif } -static int v4l_dqevent(const struct v4l2_ioctl_ops *ops, +static int v4l_dqevent64(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { return v4l2_event_dequeue(fh, arg, file->f_flags & O_NONBLOCK); } +static int v4l_dqevent32(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + return v4l2_event_dequeue32(fh, arg, file->f_flags & O_NONBLOCK); +} + static int v4l_subscribe_event(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { @@ -2449,7 +2473,8 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_FNC(VIDIOC_S_HW_FREQ_SEEK, v4l_s_hw_freq_seek, v4l_print_hw_freq_seek, INFO_FL_PRIO), IOCTL_INFO_STD(VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings, v4l_print_dv_timings, INFO_FL_PRIO), IOCTL_INFO_STD(VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings, v4l_print_dv_timings, 0), - IOCTL_INFO_FNC(VIDIOC_DQEVENT, v4l_dqevent, v4l_print_event, 0), + IOCTL_INFO_FNC(VIDIOC_DQEVENT, v4l_dqevent64, v4l_print_event64, 0), + IOCTL_INFO_FNC(VIDIOC_DQEVENT32, v4l_dqevent32, v4l_print_event32, 0), IOCTL_INFO_FNC(VIDIOC_SUBSCRIBE_EVENT, v4l_subscribe_event, v4l_print_event_subscription, 0), IOCTL_INFO_FNC(VIDIOC_UNSUBSCRIBE_EVENT, v4l_unsubscribe_event, v4l_print_event_subscription, 0), IOCTL_INFO_FNC(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE), diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 83615b8fb46a..9b0cde143c6c 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -211,6 +211,12 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_TRY_EXT_CTRLS: return v4l2_try_ext_ctrls(vfh->ctrl_handler, arg); + case VIDIOC_DQEVENT32: + if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS)) + return -ENOIOCTLCMD; + + return v4l2_event_dequeue32(vfh, arg, file->f_flags & O_NONBLOCK); + case VIDIOC_DQEVENT: if (!(sd->flags & V4L2_SUBDEV_FL_HAS_EVENTS)) return -ENOIOCTLCMD; diff --git a/include/media/v4l2-event.h b/include/media/v4l2-event.h index 9792f906423b..5e8ce27a0671 100644 --- a/include/media/v4l2-event.h +++ b/include/media/v4l2-event.h @@ -124,6 +124,8 @@ struct v4l2_subscribed_event { int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event, int nonblocking); +int v4l2_event_dequeue32(struct v4l2_fh *fh, struct v4l2_event32 *event, + int nonblocking); void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev); void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev); int v4l2_event_pending(struct v4l2_fh *fh); diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 3228fbebcd63..3e2c497c31fd 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -2082,10 +2082,40 @@ struct v4l2_event { } u; __u32 pending; __u32 sequence; +#ifdef __KERNEL__ + struct { + __s64 tv_sec; + __s64 tv_nsec; + } timestamp; +#else struct timespec timestamp; +#endif + __u32 id; + __u32 reserved[8]; +}; + +#ifdef __KERNEL__ +/* + * User space will see either 64-bit or 32-bit time_t, which changes + * the v4l2_event layout. Both are y2038 safe because the timestamps + * are in monotonic time, but the kernel has to handle both cases. + */ +struct v4l2_event32 { + __u32 type; + union { + __u8 data[64]; + } u; + __u32 pending; + __u32 sequence; + struct { + __s32 tv_sec; + __s32 tv_nsec; + } timestamp; + __u32 id; __u32 reserved[8]; }; +#endif #define V4L2_EVENT_SUB_FL_SEND_INITIAL (1 << 0) #define V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK (1 << 1) @@ -2237,6 +2267,7 @@ struct v4l2_create_buffers { #define VIDIOC_S_DV_TIMINGS _IOWR('V', 87, struct v4l2_dv_timings) #define VIDIOC_G_DV_TIMINGS _IOWR('V', 88, struct v4l2_dv_timings) #define VIDIOC_DQEVENT _IOR('V', 89, struct v4l2_event) +#define VIDIOC_DQEVENT32 _IOR('V', 89, struct v4l2_event32) #define VIDIOC_SUBSCRIBE_EVENT _IOW('V', 90, struct v4l2_event_subscription) #define VIDIOC_UNSUBSCRIBE_EVENT _IOW('V', 91, struct v4l2_event_subscription)