Skip to content

Commit b13cc8d

Browse files
Brian Starkeydliviu
authored andcommitted
drm: writeback: Add out-fences for writeback connectors
Add the WRITEBACK_OUT_FENCE_PTR property to writeback connectors, to enable userspace to get a fence which will signal once the writeback is complete. It is not allowed to request an out-fence without a framebuffer attached to the connector. A timeline is added to drm_writeback_connector for use by the writeback out-fences. In the case of a commit failure or DRM_MODE_ATOMIC_TEST_ONLY, the fence is set to -1. Changes from v2: - Rebase onto Gustavo Padovan's v9 explicit sync series - Change out_fence_ptr type to s32 __user * - Set *out_fence_ptr to -1 in drm_atomic_connector_set_property - Store fence in drm_writeback_job Gustavo Padovan: - Move out_fence_ptr out of connector_state - Signal fence from drm_writeback_signal_completion instead of in driver directly Changes from v3: - Rebase onto commit 7e9081c ("drm/fence: fix memory overwrite when setting out_fence fd") (change out_fence_ptr to s32 __user *, for real this time.) - Update documentation around WRITEBACK_OUT_FENCE_PTR Signed-off-by: Brian Starkey <[email protected]> [rebased and fixed conflicts] Signed-off-by: Mihail Atanassov <[email protected]> Signed-off-by: Liviu Dudau <[email protected]> Reviewed-by: Eric Anholt <[email protected]> Reviewed-by: Sean Paul <[email protected]> Link: https://patchwork.freedesktop.org/patch/229036/
1 parent 935774c commit b13cc8d

File tree

6 files changed

+257
-16
lines changed

6 files changed

+257
-16
lines changed

drivers/gpu/drm/drm_atomic.c

Lines changed: 90 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,35 @@ static s32 __user *get_out_fence_for_crtc(struct drm_atomic_state *state,
318318
return fence_ptr;
319319
}
320320

321+
static int set_out_fence_for_connector(struct drm_atomic_state *state,
322+
struct drm_connector *connector,
323+
s32 __user *fence_ptr)
324+
{
325+
unsigned int index = drm_connector_index(connector);
326+
327+
if (!fence_ptr)
328+
return 0;
329+
330+
if (put_user(-1, fence_ptr))
331+
return -EFAULT;
332+
333+
state->connectors[index].out_fence_ptr = fence_ptr;
334+
335+
return 0;
336+
}
337+
338+
static s32 __user *get_out_fence_for_connector(struct drm_atomic_state *state,
339+
struct drm_connector *connector)
340+
{
341+
unsigned int index = drm_connector_index(connector);
342+
s32 __user *fence_ptr;
343+
344+
fence_ptr = state->connectors[index].out_fence_ptr;
345+
state->connectors[index].out_fence_ptr = NULL;
346+
347+
return fence_ptr;
348+
}
349+
321350
/**
322351
* drm_atomic_set_mode_for_crtc - set mode for CRTC
323352
* @state: the CRTC whose incoming state to update
@@ -727,6 +756,12 @@ static int drm_atomic_connector_check(struct drm_connector *connector,
727756
return -EINVAL;
728757
}
729758

759+
if (writeback_job->out_fence && !writeback_job->fb) {
760+
DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] requesting out-fence without framebuffer\n",
761+
connector->base.id, connector->name);
762+
return -EINVAL;
763+
}
764+
730765
return 0;
731766
}
732767

@@ -1367,6 +1402,11 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector,
13671402
if (fb)
13681403
drm_framebuffer_put(fb);
13691404
return ret;
1405+
} else if (property == config->writeback_out_fence_ptr_property) {
1406+
s32 __user *fence_ptr = u64_to_user_ptr(val);
1407+
1408+
return set_out_fence_for_connector(state->state, connector,
1409+
fence_ptr);
13701410
} else if (connector->funcs->atomic_set_property) {
13711411
return connector->funcs->atomic_set_property(connector,
13721412
state, property, val);
@@ -1456,6 +1496,8 @@ drm_atomic_connector_get_property(struct drm_connector *connector,
14561496
} else if (property == config->writeback_fb_id_property) {
14571497
/* Writeback framebuffer is one-shot, write and forget */
14581498
*val = 0;
1499+
} else if (property == config->writeback_out_fence_ptr_property) {
1500+
*val = 0;
14591501
} else if (connector->funcs->atomic_get_property) {
14601502
return connector->funcs->atomic_get_property(connector,
14611503
state, property, val);
@@ -2292,7 +2334,7 @@ static int setup_out_fence(struct drm_out_fence_state *fence_state,
22922334
return 0;
22932335
}
22942336

2295-
static int prepare_crtc_signaling(struct drm_device *dev,
2337+
static int prepare_signaling(struct drm_device *dev,
22962338
struct drm_atomic_state *state,
22972339
struct drm_mode_atomic *arg,
22982340
struct drm_file *file_priv,
@@ -2301,6 +2343,8 @@ static int prepare_crtc_signaling(struct drm_device *dev,
23012343
{
23022344
struct drm_crtc *crtc;
23032345
struct drm_crtc_state *crtc_state;
2346+
struct drm_connector *conn;
2347+
struct drm_connector_state *conn_state;
23042348
int i, c = 0, ret;
23052349

23062350
if (arg->flags & DRM_MODE_ATOMIC_TEST_ONLY)
@@ -2366,6 +2410,43 @@ static int prepare_crtc_signaling(struct drm_device *dev,
23662410
c++;
23672411
}
23682412

2413+
for_each_new_connector_in_state(state, conn, conn_state, i) {
2414+
struct drm_writeback_job *job;
2415+
struct drm_out_fence_state *f;
2416+
struct dma_fence *fence;
2417+
s32 __user *fence_ptr;
2418+
2419+
fence_ptr = get_out_fence_for_connector(state, conn);
2420+
if (!fence_ptr)
2421+
continue;
2422+
2423+
job = drm_atomic_get_writeback_job(conn_state);
2424+
if (!job)
2425+
return -ENOMEM;
2426+
2427+
f = krealloc(*fence_state, sizeof(**fence_state) *
2428+
(*num_fences + 1), GFP_KERNEL);
2429+
if (!f)
2430+
return -ENOMEM;
2431+
2432+
memset(&f[*num_fences], 0, sizeof(*f));
2433+
2434+
f[*num_fences].out_fence_ptr = fence_ptr;
2435+
*fence_state = f;
2436+
2437+
fence = drm_writeback_get_out_fence((struct drm_writeback_connector *)conn);
2438+
if (!fence)
2439+
return -ENOMEM;
2440+
2441+
ret = setup_out_fence(&f[(*num_fences)++], fence);
2442+
if (ret) {
2443+
dma_fence_put(fence);
2444+
return ret;
2445+
}
2446+
2447+
job->out_fence = fence;
2448+
}
2449+
23692450
/*
23702451
* Having this flag means user mode pends on event which will never
23712452
* reach due to lack of at least one CRTC for signaling
@@ -2376,11 +2457,11 @@ static int prepare_crtc_signaling(struct drm_device *dev,
23762457
return 0;
23772458
}
23782459

2379-
static void complete_crtc_signaling(struct drm_device *dev,
2380-
struct drm_atomic_state *state,
2381-
struct drm_out_fence_state *fence_state,
2382-
unsigned int num_fences,
2383-
bool install_fds)
2460+
static void complete_signaling(struct drm_device *dev,
2461+
struct drm_atomic_state *state,
2462+
struct drm_out_fence_state *fence_state,
2463+
unsigned int num_fences,
2464+
bool install_fds)
23842465
{
23852466
struct drm_crtc *crtc;
23862467
struct drm_crtc_state *crtc_state;
@@ -2550,8 +2631,8 @@ int drm_mode_atomic_ioctl(struct drm_device *dev,
25502631
drm_mode_object_put(obj);
25512632
}
25522633

2553-
ret = prepare_crtc_signaling(dev, state, arg, file_priv, &fence_state,
2554-
&num_fences);
2634+
ret = prepare_signaling(dev, state, arg, file_priv, &fence_state,
2635+
&num_fences);
25552636
if (ret)
25562637
goto out;
25572638

@@ -2567,7 +2648,7 @@ int drm_mode_atomic_ioctl(struct drm_device *dev,
25672648
}
25682649

25692650
out:
2570-
complete_crtc_signaling(dev, state, fence_state, num_fences, !ret);
2651+
complete_signaling(dev, state, fence_state, num_fences, !ret);
25712652

25722653
if (ret == -EDEADLK) {
25732654
drm_atomic_state_clear(state);

drivers/gpu/drm/drm_writeback.c

Lines changed: 107 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <drm/drm_property.h>
1515
#include <drm/drm_writeback.h>
1616
#include <drm/drmP.h>
17+
#include <linux/dma-fence.h>
1718

1819
/**
1920
* DOC: overview
@@ -31,6 +32,16 @@
3132
* framebuffer applies only to a single commit (see below). A framebuffer may
3233
* not be attached while the CRTC is off.
3334
*
35+
* Unlike with planes, when a writeback framebuffer is removed by userspace DRM
36+
* makes no attempt to remove it from active use by the connector. This is
37+
* because no method is provided to abort a writeback operation, and in any
38+
* case making a new commit whilst a writeback is ongoing is undefined (see
39+
* WRITEBACK_OUT_FENCE_PTR below). As soon as the current writeback is finished,
40+
* the framebuffer will automatically no longer be in active use. As it will
41+
* also have already been removed from the framebuffer list, there will be no
42+
* way for any userspace application to retrieve a reference to it in the
43+
* intervening period.
44+
*
3445
* Writeback connectors have some additional properties, which userspace
3546
* can use to query and control them:
3647
*
@@ -47,8 +58,54 @@
4758
* data is an array of u32 DRM_FORMAT_* fourcc values.
4859
* Userspace can use this blob to find out what pixel formats are supported
4960
* by the connector's writeback engine.
61+
*
62+
* "WRITEBACK_OUT_FENCE_PTR":
63+
* Userspace can use this property to provide a pointer for the kernel to
64+
* fill with a sync_file file descriptor, which will signal once the
65+
* writeback is finished. The value should be the address of a 32-bit
66+
* signed integer, cast to a u64.
67+
* Userspace should wait for this fence to signal before making another
68+
* commit affecting any of the same CRTCs, Planes or Connectors.
69+
* **Failure to do so will result in undefined behaviour.**
70+
* For this reason it is strongly recommended that all userspace
71+
* applications making use of writeback connectors *always* retrieve an
72+
* out-fence for the commit and use it appropriately.
73+
* From userspace, this property will always read as zero.
5074
*/
5175

76+
#define fence_to_wb_connector(x) container_of(x->lock, \
77+
struct drm_writeback_connector, \
78+
fence_lock)
79+
80+
static const char *drm_writeback_fence_get_driver_name(struct dma_fence *fence)
81+
{
82+
struct drm_writeback_connector *wb_connector =
83+
fence_to_wb_connector(fence);
84+
85+
return wb_connector->base.dev->driver->name;
86+
}
87+
88+
static const char *
89+
drm_writeback_fence_get_timeline_name(struct dma_fence *fence)
90+
{
91+
struct drm_writeback_connector *wb_connector =
92+
fence_to_wb_connector(fence);
93+
94+
return wb_connector->timeline_name;
95+
}
96+
97+
static bool drm_writeback_fence_enable_signaling(struct dma_fence *fence)
98+
{
99+
return true;
100+
}
101+
102+
static const struct dma_fence_ops drm_writeback_fence_ops = {
103+
.get_driver_name = drm_writeback_fence_get_driver_name,
104+
.get_timeline_name = drm_writeback_fence_get_timeline_name,
105+
.enable_signaling = drm_writeback_fence_enable_signaling,
106+
.wait = dma_fence_default_wait,
107+
};
108+
52109
static int create_writeback_properties(struct drm_device *dev)
53110
{
54111
struct drm_property *prop;
@@ -72,6 +129,15 @@ static int create_writeback_properties(struct drm_device *dev)
72129
dev->mode_config.writeback_pixel_formats_property = prop;
73130
}
74131

132+
if (!dev->mode_config.writeback_out_fence_ptr_property) {
133+
prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
134+
"WRITEBACK_OUT_FENCE_PTR", 0,
135+
U64_MAX);
136+
if (!prop)
137+
return -ENOMEM;
138+
dev->mode_config.writeback_out_fence_ptr_property = prop;
139+
}
140+
75141
return 0;
76142
}
77143

@@ -141,6 +207,15 @@ int drm_writeback_connector_init(struct drm_device *dev,
141207
INIT_LIST_HEAD(&wb_connector->job_queue);
142208
spin_lock_init(&wb_connector->job_lock);
143209

210+
wb_connector->fence_context = dma_fence_context_alloc(1);
211+
spin_lock_init(&wb_connector->fence_lock);
212+
snprintf(wb_connector->timeline_name,
213+
sizeof(wb_connector->timeline_name),
214+
"CONNECTOR:%d-%s", connector->base.id, connector->name);
215+
216+
drm_object_attach_property(&connector->base,
217+
config->writeback_out_fence_ptr_property, 0);
218+
144219
drm_object_attach_property(&connector->base,
145220
config->writeback_fb_id_property, 0);
146221

@@ -210,6 +285,7 @@ static void cleanup_work(struct work_struct *work)
210285
/**
211286
* drm_writeback_signal_completion - Signal the completion of a writeback job
212287
* @wb_connector: The writeback connector whose job is complete
288+
* @status: Status code to set in the writeback out_fence (0 for success)
213289
*
214290
* Drivers should call this to signal the completion of a previously queued
215291
* writeback job. It should be called as soon as possible after the hardware
@@ -223,7 +299,8 @@ static void cleanup_work(struct work_struct *work)
223299
* See also: drm_writeback_queue_job()
224300
*/
225301
void
226-
drm_writeback_signal_completion(struct drm_writeback_connector *wb_connector)
302+
drm_writeback_signal_completion(struct drm_writeback_connector *wb_connector,
303+
int status)
227304
{
228305
unsigned long flags;
229306
struct drm_writeback_job *job;
@@ -232,8 +309,15 @@ drm_writeback_signal_completion(struct drm_writeback_connector *wb_connector)
232309
job = list_first_entry_or_null(&wb_connector->job_queue,
233310
struct drm_writeback_job,
234311
list_entry);
235-
if (job)
312+
if (job) {
236313
list_del(&job->list_entry);
314+
if (job->out_fence) {
315+
if (status)
316+
dma_fence_set_error(job->out_fence, status);
317+
dma_fence_signal(job->out_fence);
318+
dma_fence_put(job->out_fence);
319+
}
320+
}
237321
spin_unlock_irqrestore(&wb_connector->job_lock, flags);
238322

239323
if (WARN_ON(!job))
@@ -243,3 +327,24 @@ drm_writeback_signal_completion(struct drm_writeback_connector *wb_connector)
243327
queue_work(system_long_wq, &job->cleanup_work);
244328
}
245329
EXPORT_SYMBOL(drm_writeback_signal_completion);
330+
331+
struct dma_fence *
332+
drm_writeback_get_out_fence(struct drm_writeback_connector *wb_connector)
333+
{
334+
struct dma_fence *fence;
335+
336+
if (WARN_ON(wb_connector->base.connector_type !=
337+
DRM_MODE_CONNECTOR_WRITEBACK))
338+
return NULL;
339+
340+
fence = kzalloc(sizeof(*fence), GFP_KERNEL);
341+
if (!fence)
342+
return NULL;
343+
344+
dma_fence_init(fence, &drm_writeback_fence_ops,
345+
&wb_connector->fence_lock, wb_connector->fence_context,
346+
++wb_connector->fence_seqno);
347+
348+
return fence;
349+
}
350+
EXPORT_SYMBOL(drm_writeback_get_out_fence);

include/drm/drm_atomic.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,14 @@ struct __drm_crtcs_state {
160160
struct __drm_connnectors_state {
161161
struct drm_connector *ptr;
162162
struct drm_connector_state *state, *old_state, *new_state;
163+
/**
164+
* @out_fence_ptr:
165+
*
166+
* User-provided pointer which the kernel uses to return a sync_file
167+
* file descriptor. Used by writeback connectors to signal completion of
168+
* the writeback.
169+
*/
170+
s32 __user *out_fence_ptr;
163171
};
164172

165173
struct drm_private_obj;

include/drm/drm_connector.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -441,10 +441,10 @@ struct drm_connector_state {
441441
/**
442442
* @writeback_job: Writeback job for writeback connectors
443443
*
444-
* Holds the framebuffer for a writeback connector. As the writeback
445-
* completion may be asynchronous to the normal commit cycle, the
446-
* writeback job lifetime is managed separately from the normal atomic
447-
* state by this object.
444+
* Holds the framebuffer and out-fence for a writeback connector. As
445+
* the writeback completion may be asynchronous to the normal commit
446+
* cycle, the writeback job lifetime is managed separately from the
447+
* normal atomic state by this object.
448448
*
449449
* See also: drm_writeback_queue_job() and
450450
* drm_writeback_signal_completion()

include/drm/drm_mode_config.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,14 @@ struct drm_mode_config {
798798
* See also: drm_writeback_connector_init()
799799
*/
800800
struct drm_property *writeback_pixel_formats_property;
801+
/**
802+
* @writeback_out_fence_ptr_property: Property for writeback connectors,
803+
* fd pointer representing the outgoing fences for a writeback
804+
* connector. Userspace should provide a pointer to a value of type s32,
805+
* and then cast that pointer to u64.
806+
* See also: drm_writeback_connector_init()
807+
*/
808+
struct drm_property *writeback_out_fence_ptr_property;
801809

802810
/* dumb ioctl parameters */
803811
uint32_t preferred_depth, prefer_shadow;

0 commit comments

Comments
 (0)