| @@ -262,15 +262,21 @@ again: · 261 unmodified lines |
| 262 | 262 | |
| 263 | 263 | mutex_lock(&dev->mode_config.idr_mutex); |
| 264 | 264 | ret = idr_get_new_above(&dev->mode_config.crtc_idr, obj, 1, &new_id); |
| 265 | + |
| 266 | + if (!ret) { |
| 267 | + /* |
| 268 | + * Set up the object linking under the protection of the idr |
| 269 | + * lock so that other users can't see inconsistent state. |
| 270 | + */ |
| 271 | + obj->id = new_id; |
| 272 | + obj->type = obj_type; |
| 273 | + } |
| 265 | 274 | mutex_unlock(&dev->mode_config.idr_mutex); |
| 275 | + |
| 266 | 276 | if (ret == -EAGAIN) |
| 267 | 277 | goto again; |
| 268 | | − else if (ret) |
| 269 | | − return ret; |
| 270 | 278 | |
| 271 | | − obj->id = new_id; |
| 272 | | − obj->type = obj_type; |
| 273 | | − return 0; |
| 279 | + return ret; |
| 274 | 280 | } |
| 275 | 281 | |
| 276 | 282 | /** |
| @@ -312,6 +318,12 @@ EXPORT_SYMBOL(drm_mode_object_find); · 35 unmodified lines |
| 312 | 318 | * Allocates an ID for the framebuffer's parent mode object, sets its mode |
| 313 | 319 | * functions & device file and adds it to the master fd list. |
| 314 | 320 | * |
| 321 | + * IMPORTANT: |
| 322 | + * This functions publishes the fb and makes it available for concurrent access |
| 323 | + * by other users. Which means by this point the fb _must_ be fully set up - |
| 324 | + * since all the fb attributes are invariant over its lifetime, no further |
| 325 | + * locking but only correct reference counting is required. |
| 326 | + * |
| 315 | 327 | * RETURNS: |
| 316 | 328 | * Zero on success, error code on failure. |
| 317 | 329 | */ |
| @@ -320,16 +332,20 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, · 2 unmodified lines |
| 320 | 332 | { |
| 321 | 333 | int ret; |
| 322 | 334 | |
| 335 | + mutex_lock(&dev->mode_config.fb_lock); |
| 323 | 336 | kref_init(&fb->refcount); |
| 337 | + INIT_LIST_HEAD(&fb->filp_head); |
| 338 | + fb->dev = dev; |
| 339 | + fb->funcs = funcs; |
| 324 | 340 | |
| 325 | 341 | ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB); |
| 326 | 342 | if (ret) |
| 327 | | − return ret; |
| 343 | + goto out; |
| 328 | 344 | |
| 329 | | − fb->dev = dev; |
| 330 | | − fb->funcs = funcs; |
| 331 | 345 | dev->mode_config.num_fb++; |
| 332 | 346 | list_add(&fb->head, &dev->mode_config.fb_list); |
| 347 | +out: |
| 348 | + mutex_unlock(&dev->mode_config.fb_lock); |
| 333 | 349 | |
| 334 | 350 | return 0; |
| 335 | 351 | } |
| @@ -385,8 +401,10 @@ void drm_framebuffer_cleanup(struct drm_framebuffer *fb) · 49 unmodified lines |
| 385 | 401 | * this.) |
| 386 | 402 | */ |
| 387 | 403 | drm_mode_object_put(dev, &fb->base); |
| 404 | + mutex_lock(&dev->mode_config.fb_lock); |
| 388 | 405 | list_del(&fb->head); |
| 389 | 406 | dev->mode_config.num_fb--; |
| 407 | + mutex_unlock(&dev->mode_config.fb_lock); |
| 390 | 408 | } |
| 391 | 409 | EXPORT_SYMBOL(drm_framebuffer_cleanup); |
| 392 | 410 | |
| @@ -406,6 +424,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) · 13 unmodified lines |
| 406 | 424 | int ret; |
| 407 | 425 | |
| 408 | 426 | WARN_ON(!drm_modeset_is_locked(dev)); |
| 427 | + WARN_ON(!list_empty(&fb->filp_head)); |
| 409 | 428 | |
| 410 | 429 | /* remove from any CRTC */ |
| 411 | 430 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
| @@ -432,8 +451,6 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) · 20 unmodified lines |
| 432 | 451 | } |
| 433 | 452 | } |
| 434 | 453 | |
| 435 | | − list_del(&fb->filp_head); |
| 436 | | − |
| 437 | 454 | drm_framebuffer_unreference(fb); |
| 438 | 455 | } |
| 439 | 456 | EXPORT_SYMBOL(drm_framebuffer_remove); |
| @@ -989,6 +1006,7 @@ void drm_mode_config_init(struct drm_device *dev) · 549 unmodified lines |
| 989 | 1006 | { |
| 990 | 1007 | mutex_init(&dev->mode_config.mutex); |
| 991 | 1008 | mutex_init(&dev->mode_config.idr_mutex); |
| 1009 | + mutex_init(&dev->mode_config.fb_lock); |
| 992 | 1010 | INIT_LIST_HEAD(&dev->mode_config.fb_list); |
| 993 | 1011 | INIT_LIST_HEAD(&dev->mode_config.crtc_list); |
| 994 | 1012 | INIT_LIST_HEAD(&dev->mode_config.connector_list); |
| @@ -1091,6 +1109,9 @@ void drm_mode_config_cleanup(struct drm_device *dev) · 96 unmodified lines |
| 1091 | 1109 | drm_property_destroy(dev, property); |
| 1092 | 1110 | } |
| 1093 | 1111 | |
| 1112 | + /* Single-threaded teardown context, so it's not requied to grab the |
| 1113 | + * fb_lock to protect against concurrent fb_list access. Contrary, it |
| 1114 | + * would actually deadlock with the drm_framebuffer_cleanup function. */ |
| 1094 | 1115 | list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { |
| 1095 | 1116 | drm_framebuffer_remove(fb); |
| 1096 | 1117 | } |
| @@ -1220,8 +1241,8 @@ int drm_mode_getresources(struct drm_device *dev, void *data, · 123 unmodified lines |
| 1220 | 1241 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
| 1221 | 1242 | return -EINVAL; |
| 1222 | 1243 | |
| 1223 | | − drm_modeset_lock_all(dev); |
| 1224 | 1244 | |
| 1245 | + mutex_lock(&file_priv->fbs_lock); |
| 1225 | 1246 | /* |
| 1226 | 1247 | * For the non-control nodes we need to limit the list of resources |
| 1227 | 1248 | * by IDs in the group list for this node |
| @@ -1229,6 +1250,23 @@ int drm_mode_getresources(struct drm_device *dev, void *data, · 1 unmodified line |
| 1229 | 1250 | list_for_each(lh, &file_priv->fbs) |
| 1230 | 1251 | fb_count++; |
| 1231 | 1252 | |
| 1253 | + /* handle this in 4 parts */ |
| 1254 | + /* FBs */ |
| 1255 | + if (card_res->count_fbs >= fb_count) { |
| 1256 | + copied = 0; |
| 1257 | + fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr; |
| 1258 | + list_for_each_entry(fb, &file_priv->fbs, filp_head) { |
| 1259 | + if (put_user(fb->base.id, fb_id + copied)) { |
| 1260 | + mutex_unlock(&file_priv->fbs_lock); |
| 1261 | + return -EFAULT; |
| 1262 | + } |
| 1263 | + copied++; |
| 1264 | + } |
| 1265 | + } |
| 1266 | + card_res->count_fbs = fb_count; |
| 1267 | + mutex_unlock(&file_priv->fbs_lock); |
| 1268 | + |
| 1269 | + drm_modeset_lock_all(dev); |
| 1232 | 1270 | mode_group = &file_priv->master->minor->mode_group; |
| 1233 | 1271 | if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { |
| 1234 | 1272 | |
| @@ -1252,21 +1290,6 @@ int drm_mode_getresources(struct drm_device *dev, void *data, · 17 unmodified lines |
| 1252 | 1290 | card_res->max_width = dev->mode_config.max_width; |
| 1253 | 1291 | card_res->min_width = dev->mode_config.min_width; |
| 1254 | 1292 | |
| 1255 | | − /* handle this in 4 parts */ |
| 1256 | | − /* FBs */ |
| 1257 | | − if (card_res->count_fbs >= fb_count) { |
| 1258 | | − copied = 0; |
| 1259 | | − fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr; |
| 1260 | | − list_for_each_entry(fb, &file_priv->fbs, filp_head) { |
| 1261 | | − if (put_user(fb->base.id, fb_id + copied)) { |
| 1262 | | − ret = -EFAULT; |
| 1263 | | − goto out; |
| 1264 | | − } |
| 1265 | | − copied++; |
| 1266 | | − } |
| 1267 | | − } |
| 1268 | | − card_res->count_fbs = fb_count; |
| 1269 | | − |
| 1270 | 1293 | /* CRTCs */ |
| 1271 | 1294 | if (card_res->count_crtcs >= crtc_count) { |
| 1272 | 1295 | copied = 0; |
| @@ -1765,8 +1788,10 @@ int drm_mode_setplane(struct drm_device *dev, void *data, · 492 unmodified lines |
| 1765 | 1788 | } |
| 1766 | 1789 | crtc = obj_to_crtc(obj); |
| 1767 | 1790 | |
| 1791 | + mutex_lock(&dev->mode_config.fb_lock); |
| 1768 | 1792 | obj = drm_mode_object_find(dev, plane_req->fb_id, |
| 1769 | 1793 | DRM_MODE_OBJECT_FB); |
| 1794 | + mutex_unlock(&dev->mode_config.fb_lock); |
| 1770 | 1795 | if (!obj) { |
| 1771 | 1796 | DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", |
| 1772 | 1797 | plane_req->fb_id); |
| @@ -1908,8 +1933,10 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, · 135 unmodified lines |
| 1908 | 1933 | } |
| 1909 | 1934 | fb = crtc->fb; |
| 1910 | 1935 | } else { |
| 1936 | + mutex_lock(&dev->mode_config.fb_lock); |
| 1911 | 1937 | obj = drm_mode_object_find(dev, crtc_req->fb_id, |
| 1912 | 1938 | DRM_MODE_OBJECT_FB); |
| 1939 | + mutex_unlock(&dev->mode_config.fb_lock); |
| 1913 | 1940 | if (!obj) { |
| 1914 | 1941 | DRM_DEBUG_KMS("Unknown FB ID%d\n", |
| 1915 | 1942 | crtc_req->fb_id); |
| @@ -2151,16 +2178,17 @@ int drm_mode_addfb(struct drm_device *dev, · 235 unmodified lines |
| 2151 | 2178 | fb = dev->mode_config.funcs->fb_create(dev, file_priv, &r); |
| 2152 | 2179 | if (IS_ERR(fb)) { |
| 2153 | 2180 | DRM_DEBUG_KMS("could not create framebuffer\n"); |
| 2154 | | − ret = PTR_ERR(fb); |
| 2155 | | − goto out; |
| 2181 | + drm_modeset_unlock_all(dev); |
| 2182 | + return PTR_ERR(fb); |
| 2156 | 2183 | } |
| 2157 | 2184 | |
| 2185 | + mutex_lock(&file_priv->fbs_lock); |
| 2158 | 2186 | or->fb_id = fb->base.id; |
| 2159 | 2187 | list_add(&fb->filp_head, &file_priv->fbs); |
| 2160 | 2188 | DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); |
| 2161 | | − |
| 2162 | | −out: |
| 2189 | + mutex_unlock(&file_priv->fbs_lock); |
| 2163 | 2190 | drm_modeset_unlock_all(dev); |
| 2191 | + |
| 2164 | 2192 | return ret; |
| 2165 | 2193 | } |
| 2166 | 2194 | |
| @@ -2333,16 +2361,18 @@ int drm_mode_addfb2(struct drm_device *dev, · 166 unmodified lines |
| 2333 | 2361 | fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); |
| 2334 | 2362 | if (IS_ERR(fb)) { |
| 2335 | 2363 | DRM_DEBUG_KMS("could not create framebuffer\n"); |
| 2336 | | − ret = PTR_ERR(fb); |
| 2337 | | − goto out; |
| 2364 | + drm_modeset_unlock_all(dev); |
| 2365 | + return PTR_ERR(fb); |
| 2338 | 2366 | } |
| 2339 | 2367 | |
| 2368 | + mutex_lock(&file_priv->fbs_lock); |
| 2340 | 2369 | r->fb_id = fb->base.id; |
| 2341 | 2370 | list_add(&fb->filp_head, &file_priv->fbs); |
| 2342 | 2371 | DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); |
| 2372 | + mutex_unlock(&file_priv->fbs_lock); |
| 2343 | 2373 | |
| 2344 | | −out: |
| 2345 | 2374 | drm_modeset_unlock_all(dev); |
| 2375 | + |
| 2346 | 2376 | return ret; |
| 2347 | 2377 | } |
| 2348 | 2378 | |
| @@ -2373,27 +2403,34 @@ int drm_mode_rmfb(struct drm_device *dev, · 24 unmodified lines |
| 2373 | 2403 | return -EINVAL; |
| 2374 | 2404 | |
| 2375 | 2405 | drm_modeset_lock_all(dev); |
| 2406 | + mutex_lock(&dev->mode_config.fb_lock); |
| 2376 | 2407 | obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB); |
| 2377 | 2408 | /* TODO check that we really get a framebuffer back. */ |
| 2378 | 2409 | if (!obj) { |
| 2410 | + mutex_unlock(&dev->mode_config.fb_lock); |
| 2379 | 2411 | ret = -EINVAL; |
| 2380 | 2412 | goto out; |
| 2381 | 2413 | } |
| 2382 | 2414 | fb = obj_to_fb(obj); |
| 2415 | + mutex_unlock(&dev->mode_config.fb_lock); |
| 2383 | 2416 | |
| 2417 | + mutex_lock(&file_priv->fbs_lock); |
| 2384 | 2418 | list_for_each_entry(fbl, &file_priv->fbs, filp_head) |
| 2385 | 2419 | if (fb == fbl) |
| 2386 | 2420 | found = 1; |
| 2387 | | − |
| 2388 | 2421 | if (!found) { |
| 2389 | 2422 | ret = -EINVAL; |
| 2423 | + mutex_unlock(&file_priv->fbs_lock); |
| 2390 | 2424 | goto out; |
| 2391 | 2425 | } |
| 2392 | 2426 | |
| 2393 | | − drm_framebuffer_remove(fb); |
| 2427 | + list_del_init(&fb->filp_head); |
| 2428 | + mutex_unlock(&file_priv->fbs_lock); |
| 2394 | 2429 | |
| 2430 | + drm_framebuffer_remove(fb); |
| 2395 | 2431 | out: |
| 2396 | 2432 | drm_modeset_unlock_all(dev); |
| 2433 | + |
| 2397 | 2434 | return ret; |
| 2398 | 2435 | } |
| 2399 | 2436 | |
| @@ -2422,7 +2459,9 @@ int drm_mode_getfb(struct drm_device *dev, · 22 unmodified lines |
| 2422 | 2459 | return -EINVAL; |
| 2423 | 2460 | |
| 2424 | 2461 | drm_modeset_lock_all(dev); |
| 2462 | + mutex_lock(&dev->mode_config.fb_lock); |
| 2425 | 2463 | obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); |
| 2464 | + mutex_unlock(&dev->mode_config.fb_lock); |
| 2426 | 2465 | if (!obj) { |
| 2427 | 2466 | ret = -EINVAL; |
| 2428 | 2467 | goto out; |
| @@ -2460,7 +2499,9 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, · 31 unmodified lines |
| 2460 | 2499 | return -EINVAL; |
| 2461 | 2500 | |
| 2462 | 2501 | drm_modeset_lock_all(dev); |
| 2502 | + mutex_lock(&dev->mode_config.fb_lock); |
| 2463 | 2503 | obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); |
| 2504 | + mutex_unlock(&dev->mode_config.fb_lock); |
| 2464 | 2505 | if (!obj) { |
| 2465 | 2506 | ret = -EINVAL; |
| 2466 | 2507 | goto out_err1; |
| @@ -2535,9 +2576,12 @@ void drm_fb_release(struct drm_file *priv) · 68 unmodified lines |
| 2535 | 2576 | struct drm_framebuffer *fb, *tfb; |
| 2536 | 2577 | |
| 2537 | 2578 | drm_modeset_lock_all(dev); |
| 2579 | + mutex_lock(&priv->fbs_lock); |
| 2538 | 2580 | list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { |
| 2581 | + list_del_init(&fb->filp_head); |
| 2539 | 2582 | drm_framebuffer_remove(fb); |
| 2540 | 2583 | } |
| 2584 | + mutex_unlock(&priv->fbs_lock); |
| 2541 | 2585 | drm_modeset_unlock_all(dev); |
| 2542 | 2586 | } |
| 2543 | 2587 | |
| @@ -3542,7 +3586,9 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, · 998 unmodified lines |
| 3542 | 3586 | if (crtc->funcs->page_flip == NULL) |
| 3543 | 3587 | goto out; |
| 3544 | 3588 | |
| 3589 | + mutex_lock(&dev->mode_config.fb_lock); |
| 3545 | 3590 | obj = drm_mode_object_find(dev, page_flip->fb_id, DRM_MODE_OBJECT_FB); |
| 3591 | + mutex_unlock(&dev->mode_config.fb_lock); |
| 3546 | 3592 | if (!obj) |
| 3547 | 3593 | goto out; |
| 3548 | 3594 | fb = obj_to_fb(obj); |