From: Carsten Haitzler carsten.haitzler@arm.com
Vblank is a bit different because it produces events that then can copy pointers/capabilities back to userspace via a read on the DRM device as opposed to the ioctl. The compat handler called the normal native/purecap one and this happened to work for compat but not for purecap. Make the shared "native" purecap handling work for both and share that infra properly to avoid copy and paste.
Signed-off-by: Carsten Haitzler Carsten.Haitzler@arm.com --- drivers/gpu/drm/drm_atomic_uapi.c | 98 ++++++++++++++++++++++++------- drivers/gpu/drm/drm_vblank.c | 39 ++++++++---- include/drm/drm_vblank.h | 23 ++++++++ include/uapi/drm/drm.h | 6 +- include/uapi/drm/drm_mode.h | 12 ++-- tools/include/uapi/drm/drm.h | 2 +- 6 files changed, 137 insertions(+), 43 deletions(-)
diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index 3e4668a20157..11c5515039d4 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -940,7 +940,9 @@ int drm_atomic_get_property(struct drm_mode_object *obj, */
static struct drm_pending_vblank_event *create_vblank_event( - struct drm_crtc *crtc, uint64_t user_data) + struct drm_crtc *crtc, + __kernel_uintptr_t user_data, + bool compat) { struct drm_pending_vblank_event *e = NULL;
@@ -950,8 +952,14 @@ static struct drm_pending_vblank_event *create_vblank_event(
e->event.base.type = DRM_EVENT_FLIP_COMPLETE; e->event.base.length = sizeof(e->event); - e->event.vbl.crtc_id = crtc->base.id; - e->event.vbl.user_data = user_data; + e->compat = compat; + if (compat) { + e->event.vbl32.crtc_id = crtc->base.id; + e->event.vbl32.user_data = user_data; + } else { + e->event.vbl.crtc_id = crtc->base.id; + e->event.vbl.user_data = user_data; + }
return e; } @@ -1148,20 +1156,43 @@ static int setup_out_fence(struct drm_out_fence_state *fence_state, return 0; }
+struct drm_mode_atomic32 { + __u32 flags; + __u32 count_objs; + __u64 objs_ptr; + __u64 count_props_ptr; + __u64 props_ptr; + __u64 prop_values_ptr; + __u64 reserved; + __u64 user_data; +}; + static int prepare_signaling(struct drm_device *dev, struct drm_atomic_state *state, - struct drm_mode_atomic *arg, + void *arg_data, struct drm_file *file_priv, struct drm_out_fence_state **fence_state, unsigned int *num_fences) { + struct drm_mode_atomic32 *arg32 = arg_data; + struct drm_mode_atomic *arg = arg_data; struct drm_crtc *crtc; struct drm_crtc_state *crtc_state; struct drm_connector *conn; struct drm_connector_state *conn_state; int i, c = 0, ret; + __u32 flags; + __kernel_uintptr_t user_data; + + if (in_compat64_syscall()) { + flags = arg32->flags; + user_data = (__kernel_uintptr_t)compat_ptr(arg32->user_data); + } else { + flags = arg->flags; + user_data = arg->user_data; + }
- if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) + if (flags & DRM_MODE_ATOMIC_TEST_ONLY) return 0;
for_each_new_crtc_in_state(state, crtc, crtc_state, i) { @@ -1169,17 +1200,18 @@ static int prepare_signaling(struct drm_device *dev,
fence_ptr = get_out_fence_for_crtc(crtc_state->state, crtc);
- if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT || fence_ptr) { + if (flags & DRM_MODE_PAGE_FLIP_EVENT || fence_ptr) { struct drm_pending_vblank_event *e;
- e = create_vblank_event(crtc, arg->user_data); + e = create_vblank_event(crtc, user_data, + in_compat64_syscall()); if (!e) return -ENOMEM;
crtc_state->event = e; }
- if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) { + if (flags & DRM_MODE_PAGE_FLIP_EVENT) { struct drm_pending_vblank_event *e = crtc_state->event;
if (!file_priv) @@ -1265,7 +1297,7 @@ static int prepare_signaling(struct drm_device *dev, * Having this flag means user mode pends on event which will never * reach due to lack of at least one CRTC for signaling */ - if (c == 0 && (arg->flags & DRM_MODE_PAGE_FLIP_EVENT)) { + if (c == 0 && (flags & DRM_MODE_PAGE_FLIP_EVENT)) { drm_dbg_atomic(dev, "need at least one CRTC for DRM_MODE_PAGE_FLIP_EVENT"); return -EINVAL; } @@ -1326,17 +1358,21 @@ static void complete_signaling(struct drm_device *dev, int drm_mode_atomic_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { + struct drm_mode_atomic32 *arg32 = data; struct drm_mode_atomic *arg = data; - uint32_t __user *objs_ptr = uaddr_to_user_ptr(arg->objs_ptr); - uint32_t __user *count_props_ptr = uaddr_to_user_ptr(arg->count_props_ptr); - uint32_t __user *props_ptr = uaddr_to_user_ptr(arg->props_ptr); - uint64_t __user *prop_values_ptr = uaddr_to_user_ptr(arg->prop_values_ptr); unsigned int copied_objs, copied_props; struct drm_atomic_state *state; struct drm_modeset_acquire_ctx ctx; struct drm_out_fence_state *fence_state; int ret = 0; unsigned int i, j, num_fences; + __u32 flags; + __u32 count_objs; + uint32_t __user *objs_ptr; + uint32_t __user *count_props_ptr; + uint32_t __user *props_ptr; + uint64_t __user *prop_values_ptr; + void __user *reserved;
/* disallow for drivers not supporting atomic: */ if (!drm_core_check_feature(dev, DRIVER_ATOMIC)) @@ -1352,25 +1388,43 @@ int drm_mode_atomic_ioctl(struct drm_device *dev, return -EINVAL; }
- if (arg->flags & ~DRM_MODE_ATOMIC_FLAGS) { + if (in_compat64_syscall()) { + flags = arg32->flags; + count_objs = arg32->count_objs; + objs_ptr = compat_ptr(arg32->objs_ptr); + count_props_ptr = compat_ptr(arg32->count_props_ptr); + props_ptr = compat_ptr(arg32->props_ptr); + prop_values_ptr = compat_ptr(arg32->prop_values_ptr); + reserved = compat_ptr(arg32->reserved); + } else { + flags = arg->flags; + count_objs = arg->count_objs; + objs_ptr = (uint32_t __user *)arg->objs_ptr; + count_props_ptr = (uint32_t __user *)arg->count_props_ptr; + props_ptr = (uint32_t __user *)arg->props_ptr; + prop_values_ptr = (uint64_t __user *)arg->prop_values_ptr; + reserved = (void __user *)arg->reserved; + } + + if (flags & ~DRM_MODE_ATOMIC_FLAGS) { drm_dbg_atomic(dev, "commit failed: invalid flag\n"); return -EINVAL; }
- if (arg->reserved) { + if (reserved) { drm_dbg_atomic(dev, "commit failed: reserved field set\n"); return -EINVAL; }
- if (arg->flags & DRM_MODE_PAGE_FLIP_ASYNC) { + if (flags & DRM_MODE_PAGE_FLIP_ASYNC) { drm_dbg_atomic(dev, "commit failed: invalid flag DRM_MODE_PAGE_FLIP_ASYNC\n"); return -EINVAL; }
/* can't test and expect an event at the same time. */ - if ((arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) && - (arg->flags & DRM_MODE_PAGE_FLIP_EVENT)) { + if ((flags & DRM_MODE_ATOMIC_TEST_ONLY) && + (flags & DRM_MODE_PAGE_FLIP_EVENT)) { drm_dbg_atomic(dev, "commit failed: page-flip event requested with test-only commit\n"); return -EINVAL; @@ -1382,7 +1436,7 @@ int drm_mode_atomic_ioctl(struct drm_device *dev,
drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE); state->acquire_ctx = &ctx; - state->allow_modeset = !!(arg->flags & DRM_MODE_ATOMIC_ALLOW_MODESET); + state->allow_modeset = !!(flags & DRM_MODE_ATOMIC_ALLOW_MODESET);
retry: copied_objs = 0; @@ -1390,7 +1444,7 @@ int drm_mode_atomic_ioctl(struct drm_device *dev, fence_state = NULL; num_fences = 0;
- for (i = 0; i < arg->count_objs; i++) { + for (i = 0; i < count_objs; i++) { uint32_t obj_id, count_props; struct drm_mode_object *obj;
@@ -1468,9 +1522,9 @@ int drm_mode_atomic_ioctl(struct drm_device *dev, if (ret) goto out;
- if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) { + if (flags & DRM_MODE_ATOMIC_TEST_ONLY) { ret = drm_atomic_check_only(state); - } else if (arg->flags & DRM_MODE_ATOMIC_NONBLOCK) { + } else if (flags & DRM_MODE_ATOMIC_NONBLOCK) { ret = drm_atomic_nonblocking_commit(state); } else { ret = drm_atomic_commit(state); diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 877e2067534f..fbd60acc5eda 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -1031,14 +1031,20 @@ static void send_vblank_event(struct drm_device *dev, case DRM_EVENT_VBLANK: case DRM_EVENT_FLIP_COMPLETE: tv = ktime_to_timespec64(now); - e->event.vbl.sequence = seq; /* * e->event is a user space structure, with hardcoded unsigned * 32-bit seconds/microseconds. This is safe as we always use * monotonic timestamps since linux-4.15 */ - e->event.vbl.tv_sec = tv.tv_sec; - e->event.vbl.tv_usec = tv.tv_nsec / 1000; + if (e->compat) { + e->event.vbl32.sequence = seq; + e->event.vbl32.tv_sec = tv.tv_sec; + e->event.vbl32.tv_usec = tv.tv_nsec / 1000; + } else { + e->event.vbl.sequence = seq; + e->event.vbl.tv_sec = tv.tv_sec; + e->event.vbl.tv_usec = tv.tv_nsec / 1000; + } break; case DRM_EVENT_CRTC_SEQUENCE: if (seq) @@ -1659,10 +1665,13 @@ int drm_legacy_modeset_ctl_ioctl(struct drm_device *dev, void *data, static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, u64 req_seq, union drm_wait_vblank *vblwait, - struct drm_file *file_priv) + struct drm_file *file_priv, + bool compat + ) { struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; struct drm_pending_vblank_event *e; + struct drm_crtc *crtc = NULL; ktime_t now; u64 seq; int ret; @@ -1675,12 +1684,19 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe,
e->pipe = pipe; e->event.base.type = DRM_EVENT_VBLANK; - e->event.base.length = sizeof(e->event.vbl); - e->event.vbl.user_data = vblwait->request.signal; - e->event.vbl.crtc_id = 0; - if (drm_core_check_feature(dev, DRIVER_MODESET)) { - struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe); - + if (drm_core_check_feature(dev, DRIVER_MODESET)) + crtc = drm_crtc_from_index(dev, pipe); + e->compat = compat; + if (compat) { + e->event.base.length = sizeof(e->event.vbl32); + e->event.vbl32.user_data = vblwait->request.signal; + e->event.vbl32.crtc_id = 0; + if (crtc) + e->event.vbl32.crtc_id = crtc->base.id; + } else { + e->event.base.length = sizeof(e->event.vbl); + e->event.vbl.user_data = vblwait->request.signal; + e->event.vbl.crtc_id = 0; if (crtc) e->event.vbl.crtc_id = crtc->base.id; } @@ -1886,7 +1902,8 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data, /* must hold on to the vblank ref until the event fires * drm_vblank_put will be called asynchronously */ - return drm_queue_vblank_event(dev, pipe, req_seq, vblwait, file_priv); + return drm_queue_vblank_event(dev, pipe, req_seq, vblwait, + file_priv, in_compat64_syscall()); }
if (req_seq != seq) { diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h index 7f3957943dd1..ccbcea75ba13 100644 --- a/include/drm/drm_vblank.h +++ b/include/drm/drm_vblank.h @@ -36,6 +36,22 @@ struct drm_device; struct drm_crtc; struct drm_vblank_work;
+struct drm_event_vblank32 { + struct drm_event base; + __u64 user_data; + __u32 tv_sec; + __u32 tv_usec; + __u32 sequence; + __u32 crtc_id; /* 0 on older kernels that do not support this */ +}; + +struct drm_event_crtc_sequence32 { + struct drm_event base; + __u64 user_data; + __s64 time_ns; + __u64 sequence; +}; + /** * struct drm_pending_vblank_event - pending vblank event tracking */ @@ -52,6 +68,10 @@ struct drm_pending_vblank_event { * @sequence: frame event should be triggered at */ u64 sequence; + /** + * @compat: This is a compat event + */ + bool compat; /** * @event: Actual event which will be sent to userspace. */ @@ -75,6 +95,9 @@ struct drm_pending_vblank_event { * @event.seq: Event payload for the MODE_QUEUEU_SEQUENCE IOCTL. */ struct drm_event_crtc_sequence seq; + + struct drm_event_vblank32 vbl32; + struct drm_event_crtc_sequence32 seq32; } event; };
diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h index de723566c5ae..c5cc98abbffe 100644 --- a/include/uapi/drm/drm.h +++ b/include/uapi/drm/drm.h @@ -494,7 +494,7 @@ enum drm_vblank_seq_type { struct drm_wait_vblank_request { enum drm_vblank_seq_type type; unsigned int sequence; - unsigned long signal; + __kernel_uintptr_t signal; };
struct drm_wait_vblank_reply { @@ -971,7 +971,7 @@ struct drm_crtc_queue_sequence { __u32 crtc_id; __u32 flags; __u64 sequence; /* on input, target sequence. on output, actual sequence */ - __u64 user_data; /* user data passed to event */ + __kernel_uintptr_t user_data; /* user data passed to event */ };
#if defined(__cplusplus) @@ -1277,7 +1277,7 @@ struct drm_event {
struct drm_event_vblank { struct drm_event base; - __u64 user_data; + __kernel_uintptr_t user_data; __u32 tv_sec; __u32 tv_usec; __u32 sequence; diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 128d09138ceb..7f9aaa0bf03d 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -1130,12 +1130,12 @@ struct drm_mode_destroy_dumb { struct drm_mode_atomic { __u32 flags; __u32 count_objs; - __u64 objs_ptr; - __u64 count_props_ptr; - __u64 props_ptr; - __u64 prop_values_ptr; - __u64 reserved; - __u64 user_data; + __kernel_uintptr_t objs_ptr; + __kernel_uintptr_t count_props_ptr; + __kernel_uintptr_t props_ptr; + __kernel_uintptr_t prop_values_ptr; + __kernel_uintptr_t reserved; + __kernel_uintptr_t user_data; };
struct drm_format_modifier_blob { diff --git a/tools/include/uapi/drm/drm.h b/tools/include/uapi/drm/drm.h index de723566c5ae..82797c95efa6 100644 --- a/tools/include/uapi/drm/drm.h +++ b/tools/include/uapi/drm/drm.h @@ -1277,7 +1277,7 @@ struct drm_event {
struct drm_event_vblank { struct drm_event base; - __u64 user_data; + uintptr_t user_data; __u32 tv_sec; __u32 tv_usec; __u32 sequence;