Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm: Add DRM_MODESET_LOCK_BEGIN/END helpers

This patch adds a couple of helpers to remove the boilerplate involved
in grabbing all of the modeset locks.

I've also converted the obvious cases in drm core to use the helpers.

The only remaining instance of drm_modeset_lock_all_ctx() is in
drm_framebuffer. It's complicated by the state clear that occurs on
deadlock. ATM, there's no way to inject code in the deadlock path with
the helpers, so it's unfit for conversion.

Changes in v2:
- Relocate ret argument to the end of the list (Daniel)
- Incorporate Daniel's doc suggestions (Daniel)

Suggested-by: Daniel Vetter <daniel@ffwll.ch>
Reviewed-by: Daniel Vetter <daniel@ffwll.ch>
Signed-off-by: Sean Paul <seanpaul@chromium.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20181129150423.239081-4-sean@poorly.run

+83 -78
+10 -41
drivers/gpu/drm/drm_atomic_helper.c
··· 3128 3128 struct drm_modeset_acquire_ctx ctx; 3129 3129 int ret; 3130 3130 3131 - drm_modeset_acquire_init(&ctx, 0); 3132 - while (1) { 3133 - ret = drm_modeset_lock_all_ctx(dev, &ctx); 3134 - if (!ret) 3135 - ret = __drm_atomic_helper_disable_all(dev, &ctx, true); 3131 + DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, ret); 3136 3132 3137 - if (ret != -EDEADLK) 3138 - break; 3139 - 3140 - drm_modeset_backoff(&ctx); 3141 - } 3142 - 3133 + ret = __drm_atomic_helper_disable_all(dev, &ctx, true); 3143 3134 if (ret) 3144 3135 DRM_ERROR("Disabling all crtc's during unload failed with %i\n", ret); 3145 3136 3146 - drm_modeset_drop_locks(&ctx); 3147 - drm_modeset_acquire_fini(&ctx); 3137 + DRM_MODESET_LOCK_ALL_END(ctx, ret); 3148 3138 } 3149 3139 EXPORT_SYMBOL(drm_atomic_helper_shutdown); 3150 3140 ··· 3169 3179 struct drm_atomic_state *state; 3170 3180 int err; 3171 3181 3172 - drm_modeset_acquire_init(&ctx, 0); 3173 - 3174 - retry: 3175 - err = drm_modeset_lock_all_ctx(dev, &ctx); 3176 - if (err < 0) { 3177 - state = ERR_PTR(err); 3178 - goto unlock; 3179 - } 3182 + DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, err); 3180 3183 3181 3184 state = drm_atomic_helper_duplicate_state(dev, &ctx); 3182 3185 if (IS_ERR(state)) ··· 3183 3200 } 3184 3201 3185 3202 unlock: 3186 - if (PTR_ERR(state) == -EDEADLK) { 3187 - drm_modeset_backoff(&ctx); 3188 - goto retry; 3189 - } 3203 + DRM_MODESET_LOCK_ALL_END(ctx, err); 3204 + if (err) 3205 + return ERR_PTR(err); 3190 3206 3191 - drm_modeset_drop_locks(&ctx); 3192 - drm_modeset_acquire_fini(&ctx); 3193 3207 return state; 3194 3208 } 3195 3209 EXPORT_SYMBOL(drm_atomic_helper_suspend); ··· 3260 3280 3261 3281 drm_mode_config_reset(dev); 3262 3282 3263 - drm_modeset_acquire_init(&ctx, 0); 3264 - while (1) { 3265 - err = drm_modeset_lock_all_ctx(dev, &ctx); 3266 - if (err) 3267 - goto out; 3283 + DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, err); 3268 3284 3269 - err = drm_atomic_helper_commit_duplicated_state(state, &ctx); 3270 - out: 3271 - if (err != -EDEADLK) 3272 - break; 3285 + err = drm_atomic_helper_commit_duplicated_state(state, &ctx); 3273 3286 3274 - drm_modeset_backoff(&ctx); 3275 - } 3276 - 3277 - drm_modeset_drop_locks(&ctx); 3278 - drm_modeset_acquire_fini(&ctx); 3287 + DRM_MODESET_LOCK_ALL_END(ctx, err); 3279 3288 drm_atomic_state_put(state); 3280 3289 3281 3290 return err;
+2 -12
drivers/gpu/drm/drm_color_mgmt.c
··· 255 255 if (crtc_lut->gamma_size != crtc->gamma_size) 256 256 return -EINVAL; 257 257 258 - drm_modeset_acquire_init(&ctx, 0); 259 - retry: 260 - ret = drm_modeset_lock_all_ctx(dev, &ctx); 261 - if (ret) 262 - goto out; 258 + DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, ret); 263 259 264 260 size = crtc_lut->gamma_size * (sizeof(uint16_t)); 265 261 r_base = crtc->gamma_store; ··· 280 284 crtc->gamma_size, &ctx); 281 285 282 286 out: 283 - if (ret == -EDEADLK) { 284 - drm_modeset_backoff(&ctx); 285 - goto retry; 286 - } 287 - drm_modeset_drop_locks(&ctx); 288 - drm_modeset_acquire_fini(&ctx); 289 - 287 + DRM_MODESET_LOCK_ALL_END(ctx, ret); 290 288 return ret; 291 289 292 290 }
+3 -12
drivers/gpu/drm/drm_crtc.c
··· 599 599 plane = crtc->primary; 600 600 601 601 mutex_lock(&crtc->dev->mode_config.mutex); 602 - drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE); 603 - retry: 604 - ret = drm_modeset_lock_all_ctx(crtc->dev, &ctx); 605 - if (ret) 606 - goto out; 602 + DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 603 + DRM_MODESET_ACQUIRE_INTERRUPTIBLE, ret); 607 604 608 605 if (crtc_req->mode_valid) { 609 606 /* If we have a mode we need a framebuffer. */ ··· 765 768 fb = NULL; 766 769 mode = NULL; 767 770 768 - if (ret == -EDEADLK) { 769 - ret = drm_modeset_backoff(&ctx); 770 - if (!ret) 771 - goto retry; 772 - } 773 - drm_modeset_drop_locks(&ctx); 774 - drm_modeset_acquire_fini(&ctx); 771 + DRM_MODESET_LOCK_ALL_END(ctx, ret); 775 772 mutex_unlock(&crtc->dev->mode_config.mutex); 776 773 777 774 return ret;
+6
drivers/gpu/drm/drm_modeset_lock.c
··· 56 56 * drm_modeset_drop_locks(ctx); 57 57 * drm_modeset_acquire_fini(ctx); 58 58 * 59 + * For convenience this control flow is implemented in 60 + * DRM_MODESET_LOCK_ALL_BEGIN() and DRM_MODESET_LOCK_ALL_END() for the case 61 + * where all modeset locks need to be taken through drm_modeset_lock_all_ctx(). 62 + * 59 63 * If all that is needed is a single modeset lock, then the &struct 60 64 * drm_modeset_acquire_ctx is not needed and the locking can be simplified 61 65 * by passing a NULL instead of ctx in the drm_modeset_lock() call or ··· 386 382 * 387 383 * Locks acquired with this function should be released by calling the 388 384 * drm_modeset_drop_locks() function on @ctx. 385 + * 386 + * See also: DRM_MODESET_LOCK_ALL_BEGIN() and DRM_MODESET_LOCK_ALL_END() 389 387 * 390 388 * Returns: 0 on success or a negative error-code on failure. 391 389 */
+3 -13
drivers/gpu/drm/drm_plane.c
··· 767 767 struct drm_modeset_acquire_ctx ctx; 768 768 int ret; 769 769 770 - drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE); 771 - retry: 772 - ret = drm_modeset_lock_all_ctx(plane->dev, &ctx); 773 - if (ret) 774 - goto fail; 770 + DRM_MODESET_LOCK_ALL_BEGIN(plane->dev, ctx, 771 + DRM_MODESET_ACQUIRE_INTERRUPTIBLE, ret); 775 772 776 773 if (drm_drv_uses_atomic_modeset(plane->dev)) 777 774 ret = __setplane_atomic(plane, crtc, fb, ··· 779 782 crtc_x, crtc_y, crtc_w, crtc_h, 780 783 src_x, src_y, src_w, src_h, &ctx); 781 784 782 - fail: 783 - if (ret == -EDEADLK) { 784 - ret = drm_modeset_backoff(&ctx); 785 - if (!ret) 786 - goto retry; 787 - } 788 - drm_modeset_drop_locks(&ctx); 789 - drm_modeset_acquire_fini(&ctx); 785 + DRM_MODESET_LOCK_ALL_END(ctx, ret); 790 786 791 787 return ret; 792 788 }
+59
include/drm/drm_modeset_lock.h
··· 130 130 int drm_modeset_lock_all_ctx(struct drm_device *dev, 131 131 struct drm_modeset_acquire_ctx *ctx); 132 132 133 + /** 134 + * DRM_MODESET_LOCK_ALL_BEGIN - Helper to acquire modeset locks 135 + * @dev: drm device 136 + * @ctx: local modeset acquire context, will be dereferenced 137 + * @flags: DRM_MODESET_ACQUIRE_* flags to pass to drm_modeset_acquire_init() 138 + * @ret: local ret/err/etc variable to track error status 139 + * 140 + * Use these macros to simplify grabbing all modeset locks using a local 141 + * context. This has the advantage of reducing boilerplate, but also properly 142 + * checking return values where appropriate. 143 + * 144 + * Any code run between BEGIN and END will be holding the modeset locks. 145 + * 146 + * This must be paired with DRM_MODESET_LOCK_ALL_END(). We will jump back and 147 + * forth between the labels on deadlock and error conditions. 148 + * 149 + * Drivers can acquire additional modeset locks. If any lock acquisition 150 + * fails, the control flow needs to jump to DRM_MODESET_LOCK_ALL_END() with 151 + * the @ret parameter containing the return value of drm_modeset_lock(). 152 + * 153 + * Returns: 154 + * The only possible value of ret immediately after DRM_MODESET_LOCK_ALL_BEGIN() 155 + * is 0, so no error checking is necessary 156 + */ 157 + #define DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, flags, ret) \ 158 + drm_modeset_acquire_init(&ctx, flags); \ 159 + modeset_lock_retry: \ 160 + ret = drm_modeset_lock_all_ctx(dev, &ctx); \ 161 + if (ret) \ 162 + goto modeset_lock_fail; 163 + 164 + /** 165 + * DRM_MODESET_LOCK_ALL_END - Helper to release and cleanup modeset locks 166 + * @ctx: local modeset acquire context, will be dereferenced 167 + * @ret: local ret/err/etc variable to track error status 168 + * 169 + * The other side of DRM_MODESET_LOCK_ALL_BEGIN(). It will bounce back to BEGIN 170 + * if ret is -EDEADLK. 171 + * 172 + * It's important that you use the same ret variable for begin and end so 173 + * deadlock conditions are properly handled. 174 + * 175 + * Returns: 176 + * ret will be untouched unless it is -EDEADLK on entry. That means that if you 177 + * successfully acquire the locks, ret will be whatever your code sets it to. If 178 + * there is a deadlock or other failure with acquire or backoff, ret will be set 179 + * to that failure. In both of these cases the code between BEGIN/END will not 180 + * be run, so the failure will reflect the inability to grab the locks. 181 + */ 182 + #define DRM_MODESET_LOCK_ALL_END(ctx, ret) \ 183 + modeset_lock_fail: \ 184 + if (ret == -EDEADLK) { \ 185 + ret = drm_modeset_backoff(&ctx); \ 186 + if (!ret) \ 187 + goto modeset_lock_retry; \ 188 + } \ 189 + drm_modeset_drop_locks(&ctx); \ 190 + drm_modeset_acquire_fini(&ctx); 191 + 133 192 #endif /* DRM_MODESET_LOCK_H_ */