[PATCH v5 4/4] drm/fence: add out-fences support

From: Gustavo Padovan
Date: Thu Oct 20 2016 - 10:50:47 EST


From: Gustavo Padovan <gustavo.padovan@xxxxxxxxxxxxxxx>

Support DRM out-fences by creating a sync_file with a fence for each CRTC
that sets the OUT_FENCE_PTR property.

We use the out_fence pointer received in the OUT_FENCE_PTR prop to send
the sync_file fd back to userspace.

The sync_file and fd are allocated/created before commit, but the
fd_install operation only happens after we know that commit succeed.

In case of error userspace should receive a fd number of -1.

v2: Comment by Rob Clark:
- Squash commit that adds DRM_MODE_ATOMIC_OUT_FENCE flag here.

Comment by Daniel Vetter:
- Add clean up code for out_fences

v3: Comments by Daniel Vetter:
- create DRM_MODE_ATOMIC_EVENT_MASK
- userspace should fill out_fences_ptr with the crtc_ids for which
it wants fences back.

v4: Create OUT_FENCE_PTR properties and remove old approach.

Signed-off-by: Gustavo Padovan <gustavo.padovan@xxxxxxxxxxxxxxx>
---
drivers/gpu/drm/drm_atomic.c | 116 ++++++++++++++++++++++++++++++++++---------
drivers/gpu/drm/drm_crtc.c | 8 +++
include/drm/drm_crtc.h | 19 +++++++
3 files changed, 119 insertions(+), 24 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index b3284b2..07775fc 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -490,6 +490,8 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
&replaced);
state->color_mgmt_changed |= replaced;
return ret;
+ } else if (property == config->prop_out_fence_ptr) {
+ state->out_fence_ptr = u64_to_user_ptr(val);
} else if (crtc->funcs->atomic_set_property)
return crtc->funcs->atomic_set_property(crtc, state, property, val);
else
@@ -532,6 +534,8 @@ drm_atomic_crtc_get_property(struct drm_crtc *crtc,
*val = (state->ctm) ? state->ctm->base.id : 0;
else if (property == config->gamma_lut_property)
*val = (state->gamma_lut) ? state->gamma_lut->base.id : 0;
+ else if (property == config->prop_out_fence_ptr)
+ *val = 0;
else if (crtc->funcs->atomic_get_property)
return crtc->funcs->atomic_get_property(crtc, state, property, val);
else
@@ -1474,11 +1478,9 @@ EXPORT_SYMBOL(drm_atomic_nonblocking_commit);
*/

static struct drm_pending_vblank_event *create_vblank_event(
- struct drm_device *dev, struct drm_file *file_priv,
- struct fence *fence, uint64_t user_data)
+ struct drm_device *dev, uint64_t user_data)
{
struct drm_pending_vblank_event *e = NULL;
- int ret;

e = kzalloc(sizeof *e, GFP_KERNEL);
if (!e)
@@ -1488,17 +1490,6 @@ static struct drm_pending_vblank_event *create_vblank_event(
e->event.base.length = sizeof(e->event);
e->event.user_data = user_data;

- if (file_priv) {
- ret = drm_event_reserve_init(dev, file_priv, &e->base,
- &e->event.base);
- if (ret) {
- kfree(e);
- return NULL;
- }
- }
-
- e->base.fence = fence;
-
return e;
}

@@ -1603,6 +1594,40 @@ void drm_atomic_clean_old_fb(struct drm_device *dev,
}
EXPORT_SYMBOL(drm_atomic_clean_old_fb);

+static int crtc_setup_out_fence(struct drm_crtc *crtc,
+ struct drm_crtc_state *crtc_state,
+ struct drm_out_fence_state *fence_state)
+{
+ struct fence *fence;
+
+ fence_state->fd = get_unused_fd_flags(O_CLOEXEC);
+ if (fence_state->fd < 0) {
+ return fence_state->fd;
+ }
+
+ fence_state->out_fence_ptr = crtc_state->out_fence_ptr;
+ crtc_state->out_fence_ptr = NULL;
+
+ if (put_user(fence_state->fd, fence_state->out_fence_ptr))
+ return -EFAULT;
+
+ fence = kzalloc(sizeof(*fence), GFP_KERNEL);
+ if (!fence)
+ return -ENOMEM;
+
+ fence_init(fence, &drm_crtc_fence_ops, &crtc->fence_lock,
+ crtc->fence_context, ++crtc->fence_seqno);
+
+ fence_state->sync_file = sync_file_create(fence);
+ if(!fence_state->sync_file) {
+ fence_put(fence);
+ return -ENOMEM;
+ }
+
+ crtc_state->event->base.fence = fence_get(fence);
+ return 0;
+}
+
int drm_mode_atomic_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
@@ -1617,9 +1642,10 @@ int drm_mode_atomic_ioctl(struct drm_device *dev,
struct drm_plane *plane;
struct drm_crtc *crtc;
struct drm_crtc_state *crtc_state;
+ struct drm_out_fence_state *fence_state;
unsigned plane_mask;
int ret = 0;
- unsigned int i, j;
+ unsigned int i, j, fence_idx = 0;

/* disallow for drivers not supporting atomic: */
if (!drm_core_check_feature(dev, DRIVER_ATOMIC))
@@ -1734,12 +1760,19 @@ retry:
drm_mode_object_unreference(obj);
}

- if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) {
- for_each_crtc_in_state(state, crtc, crtc_state, i) {
+ fence_state = kcalloc(dev->mode_config.num_crtc, sizeof(*fence_state),
+ GFP_KERNEL);
+ if (!fence_state) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ for_each_crtc_in_state(state, crtc, crtc_state, i) {
+ if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT ||
+ crtc_state->out_fence_ptr) {
struct drm_pending_vblank_event *e;

- e = create_vblank_event(dev, file_priv, NULL,
- arg->user_data);
+ e = create_vblank_event(dev, arg->user_data);
if (!e) {
ret = -ENOMEM;
goto out;
@@ -1747,6 +1780,28 @@ retry:

crtc_state->event = e;
}
+
+ if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) {
+ struct drm_pending_vblank_event *e = crtc_state->event;
+
+ if (!file_priv)
+ continue;
+
+ ret = drm_event_reserve_init(dev, file_priv, &e->base,
+ &e->event.base);
+ if (ret) {
+ kfree(e);
+ crtc_state->event = NULL;
+ goto out;
+ }
+ }
+
+ if (crtc_state->out_fence_ptr) {
+ ret = crtc_setup_out_fence(crtc, crtc_state,
+ &fence_state[fence_idx++]);
+ if (ret)
+ goto out;
+ }
}

if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY) {
@@ -1761,24 +1816,37 @@ retry:
ret = drm_atomic_commit(state);
}

+ for (i = 0; i < fence_idx; i++)
+ fd_install(fence_state[i].fd, fence_state[i].sync_file->file);
+
out:
drm_atomic_clean_old_fb(dev, plane_mask, ret);

- if (ret && arg->flags & DRM_MODE_PAGE_FLIP_EVENT) {
+ for_each_crtc_in_state(state, crtc, crtc_state, i) {
/*
* TEST_ONLY and PAGE_FLIP_EVENT are mutually exclusive,
* if they weren't, this code should be called on success
* for TEST_ONLY too.
*/
+ if (ret && crtc_state->event)
+ drm_event_cancel_free(dev, &crtc_state->event->base);
+ }

- for_each_crtc_in_state(state, crtc, crtc_state, i) {
- if (!crtc_state->event)
- continue;
+ if (ret && fence_state) {
+ for (i = 0; i < fence_idx; i++) {
+ if (fence_state[i].fd >= 0)
+ put_unused_fd(fence_state[i].fd);
+ if (fence_state[i].sync_file)
+ fput(fence_state[i].sync_file->file);

- drm_event_cancel_free(dev, &crtc_state->event->base);
+ /* If this fails, there's not much we can do about it */
+ if (put_user(-1, fence_state->out_fence_ptr))
+ DRM_ERROR("Couldn't clear out_fence_ptr\n");
}
}

+ kfree(fence_state);
+
if (ret == -EDEADLK) {
drm_atomic_state_clear(state);
drm_modeset_backoff(&ctx);
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index b99090f..40bce97 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -274,6 +274,8 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {
drm_object_attach_property(&crtc->base, config->prop_active, 0);
drm_object_attach_property(&crtc->base, config->prop_mode_id, 0);
+ drm_object_attach_property(&crtc->base,
+ config->prop_out_fence_ptr, 0);
}

return 0;
@@ -434,6 +436,12 @@ static int drm_mode_create_standard_properties(struct drm_device *dev)
return -ENOMEM;
dev->mode_config.prop_in_fence_fd = prop;

+ prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
+ "OUT_FENCE_PTR", 0, U64_MAX);
+ if (!prop)
+ return -ENOMEM;
+ dev->mode_config.prop_out_fence_ptr = prop;
+
prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC,
"CRTC_ID", DRM_MODE_OBJECT_CRTC);
if (!prop)
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 657a33a..b898604 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -165,6 +165,8 @@ struct drm_crtc_state {
struct drm_property_blob *ctm;
struct drm_property_blob *gamma_lut;

+ u64 __user *out_fence_ptr;
+
/**
* @event:
*
@@ -748,6 +750,18 @@ static inline struct drm_crtc *fence_to_crtc(struct fence *fence)
}

/**
+ * struct drm_out_fence_state - store out_fence data for install and clean up
+ * @out_fence_ptr: user pointer to store the fence fd in.
+ * @sync_file: sync_file related to the out_fence
+ * @fd: file descriptor to installed on the sync_file.
+ */
+struct drm_out_fence_state {
+ u64 __user *out_fence_ptr;
+ struct sync_file *sync_file;
+ int fd;
+};
+
+/*
* struct drm_mode_set - new values for a CRTC config change
* @fb: framebuffer to use for new config
* @crtc: CRTC whose configuration we're about to change
@@ -1230,6 +1244,11 @@ struct drm_mode_config {
*/
struct drm_property *prop_in_fence_fd;
/**
+ * @prop_out_fence_ptr: Sync File fd pointer representing the
+ * outgoing fences for a CRTC.
+ */
+ struct drm_property *prop_out_fence_ptr;
+ /**
* @prop_crtc_id: Default atomic plane property to specify the
* &drm_crtc.
*/
--
2.5.5