diff options
author | David Herrmann <dh.herrmann@gmail.com> | 2014-07-23 05:38:38 -0400 |
---|---|---|
committer | David Herrmann <dh.herrmann@gmail.com> | 2014-08-05 10:08:21 -0400 |
commit | e17280758cc0b4f3d7065554006adcb87448f6c0 (patch) | |
tree | 71641f392c6ab39518ac56b2efa4763e7f5ecdd8 | |
parent | f1b8596283f681b82546d6302bc7c372fdb2d422 (diff) |
drm: make sysfs device always available for minors
For each minor we allocate a sysfs device as minor->kdev. Currently, this
is allocated and registered in drm_minor_register(). This makes it
impossible to add sysfs-attributes to the device before it is registered.
Therefore, they are not added atomically, nor can we move device_add()
*after* ->load() is called.
This patch makes minor->kdev available early, but only adds the device
during minor-registration. Note that the registration is still called
before ->load() as debugfs needs to be split, too. This will be fixed in
follow-ups.
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
Signed-off-by: David Herrmann <dh.herrmann@gmail.com>
-rw-r--r-- | drivers/gpu/drm/drm_stub.c | 22 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_sysfs.c | 90 | ||||
-rw-r--r-- | include/drm/drmP.h | 3 |
3 files changed, 54 insertions, 61 deletions
diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 8b24db51116e..09c6bfb86a66 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c | |||
@@ -281,9 +281,19 @@ static int drm_minor_alloc(struct drm_device *dev, unsigned int type) | |||
281 | 281 | ||
282 | minor->index = r; | 282 | minor->index = r; |
283 | 283 | ||
284 | minor->kdev = drm_sysfs_minor_alloc(minor); | ||
285 | if (IS_ERR(minor->kdev)) { | ||
286 | r = PTR_ERR(minor->kdev); | ||
287 | goto err_index; | ||
288 | } | ||
289 | |||
284 | *drm_minor_get_slot(dev, type) = minor; | 290 | *drm_minor_get_slot(dev, type) = minor; |
285 | return 0; | 291 | return 0; |
286 | 292 | ||
293 | err_index: | ||
294 | spin_lock_irqsave(&drm_minor_lock, flags); | ||
295 | idr_remove(&drm_minors_idr, minor->index); | ||
296 | spin_unlock_irqrestore(&drm_minor_lock, flags); | ||
287 | err_free: | 297 | err_free: |
288 | kfree(minor); | 298 | kfree(minor); |
289 | return r; | 299 | return r; |
@@ -300,6 +310,7 @@ static void drm_minor_free(struct drm_device *dev, unsigned int type) | |||
300 | return; | 310 | return; |
301 | 311 | ||
302 | drm_mode_group_destroy(&minor->mode_group); | 312 | drm_mode_group_destroy(&minor->mode_group); |
313 | put_device(minor->kdev); | ||
303 | 314 | ||
304 | spin_lock_irqsave(&drm_minor_lock, flags); | 315 | spin_lock_irqsave(&drm_minor_lock, flags); |
305 | idr_remove(&drm_minors_idr, minor->index); | 316 | idr_remove(&drm_minors_idr, minor->index); |
@@ -327,11 +338,9 @@ static int drm_minor_register(struct drm_device *dev, unsigned int type) | |||
327 | return ret; | 338 | return ret; |
328 | } | 339 | } |
329 | 340 | ||
330 | ret = drm_sysfs_device_add(minor); | 341 | ret = device_add(minor->kdev); |
331 | if (ret) { | 342 | if (ret) |
332 | DRM_ERROR("DRM: Error sysfs_device_add.\n"); | ||
333 | goto err_debugfs; | 343 | goto err_debugfs; |
334 | } | ||
335 | 344 | ||
336 | /* replace NULL with @minor so lookups will succeed from now on */ | 345 | /* replace NULL with @minor so lookups will succeed from now on */ |
337 | spin_lock_irqsave(&drm_minor_lock, flags); | 346 | spin_lock_irqsave(&drm_minor_lock, flags); |
@@ -352,7 +361,7 @@ static void drm_minor_unregister(struct drm_device *dev, unsigned int type) | |||
352 | unsigned long flags; | 361 | unsigned long flags; |
353 | 362 | ||
354 | minor = *drm_minor_get_slot(dev, type); | 363 | minor = *drm_minor_get_slot(dev, type); |
355 | if (!minor || !minor->kdev) | 364 | if (!minor || !device_is_registered(minor->kdev)) |
356 | return; | 365 | return; |
357 | 366 | ||
358 | /* replace @minor with NULL so lookups will fail from now on */ | 367 | /* replace @minor with NULL so lookups will fail from now on */ |
@@ -360,8 +369,9 @@ static void drm_minor_unregister(struct drm_device *dev, unsigned int type) | |||
360 | idr_replace(&drm_minors_idr, NULL, minor->index); | 369 | idr_replace(&drm_minors_idr, NULL, minor->index); |
361 | spin_unlock_irqrestore(&drm_minor_lock, flags); | 370 | spin_unlock_irqrestore(&drm_minor_lock, flags); |
362 | 371 | ||
372 | device_del(minor->kdev); | ||
373 | dev_set_drvdata(minor->kdev, NULL); /* safety belt */ | ||
363 | drm_debugfs_cleanup(minor); | 374 | drm_debugfs_cleanup(minor); |
364 | drm_sysfs_device_remove(minor); | ||
365 | } | 375 | } |
366 | 376 | ||
367 | /** | 377 | /** |
diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 7827dad8fcf4..ab1a5f6dde8a 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c | |||
@@ -493,71 +493,55 @@ static void drm_sysfs_release(struct device *dev) | |||
493 | } | 493 | } |
494 | 494 | ||
495 | /** | 495 | /** |
496 | * drm_sysfs_device_add - adds a class device to sysfs for a character driver | 496 | * drm_sysfs_minor_alloc() - Allocate sysfs device for given minor |
497 | * @dev: DRM device to be added | 497 | * @minor: minor to allocate sysfs device for |
498 | * @head: DRM head in question | ||
499 | * | 498 | * |
500 | * Add a DRM device to the DRM's device model class. We use @dev's PCI device | 499 | * This allocates a new sysfs device for @minor and returns it. The device is |
501 | * as the parent for the Linux device, and make sure it has a file containing | 500 | * not registered nor linked. The caller has to use device_add() and |
502 | * the driver we're using (for userspace compatibility). | 501 | * device_del() to register and unregister it. |
502 | * | ||
503 | * Note that dev_get_drvdata() on the new device will return the minor. | ||
504 | * However, the device does not hold a ref-count to the minor nor to the | ||
505 | * underlying drm_device. This is unproblematic as long as you access the | ||
506 | * private data only in sysfs callbacks. device_del() disables those | ||
507 | * synchronously, so they cannot be called after you cleanup a minor. | ||
503 | */ | 508 | */ |
504 | int drm_sysfs_device_add(struct drm_minor *minor) | 509 | struct device *drm_sysfs_minor_alloc(struct drm_minor *minor) |
505 | { | 510 | { |
506 | char *minor_str; | 511 | const char *minor_str; |
512 | struct device *kdev; | ||
507 | int r; | 513 | int r; |
508 | 514 | ||
509 | if (minor->type == DRM_MINOR_CONTROL) | 515 | if (minor->type == DRM_MINOR_CONTROL) |
510 | minor_str = "controlD%d"; | 516 | minor_str = "controlD%d"; |
511 | else if (minor->type == DRM_MINOR_RENDER) | 517 | else if (minor->type == DRM_MINOR_RENDER) |
512 | minor_str = "renderD%d"; | 518 | minor_str = "renderD%d"; |
513 | else | 519 | else |
514 | minor_str = "card%d"; | 520 | minor_str = "card%d"; |
515 | 521 | ||
516 | minor->kdev = kzalloc(sizeof(*minor->kdev), GFP_KERNEL); | 522 | kdev = kzalloc(sizeof(*kdev), GFP_KERNEL); |
517 | if (!minor->kdev) { | 523 | if (!kdev) |
518 | r = -ENOMEM; | 524 | return ERR_PTR(-ENOMEM); |
519 | goto error; | 525 | |
520 | } | 526 | device_initialize(kdev); |
521 | 527 | kdev->devt = MKDEV(DRM_MAJOR, minor->index); | |
522 | device_initialize(minor->kdev); | 528 | kdev->class = drm_class; |
523 | minor->kdev->devt = MKDEV(DRM_MAJOR, minor->index); | 529 | kdev->type = &drm_sysfs_device_minor; |
524 | minor->kdev->class = drm_class; | 530 | kdev->parent = minor->dev->dev; |
525 | minor->kdev->type = &drm_sysfs_device_minor; | 531 | kdev->release = drm_sysfs_release; |
526 | minor->kdev->parent = minor->dev->dev; | 532 | dev_set_drvdata(kdev, minor); |
527 | minor->kdev->release = drm_sysfs_release; | 533 | |
528 | dev_set_drvdata(minor->kdev, minor); | 534 | r = dev_set_name(kdev, minor_str, minor->index); |
529 | |||
530 | r = dev_set_name(minor->kdev, minor_str, minor->index); | ||
531 | if (r < 0) | 535 | if (r < 0) |
532 | goto error; | 536 | goto err_free; |
533 | |||
534 | r = device_add(minor->kdev); | ||
535 | if (r < 0) | ||
536 | goto error; | ||
537 | |||
538 | return 0; | ||
539 | 537 | ||
540 | error: | 538 | return kdev; |
541 | DRM_ERROR("device create failed %d\n", r); | ||
542 | put_device(minor->kdev); | ||
543 | return r; | ||
544 | } | ||
545 | 539 | ||
546 | /** | 540 | err_free: |
547 | * drm_sysfs_device_remove - remove DRM device | 541 | put_device(kdev); |
548 | * @dev: DRM device to remove | 542 | return ERR_PTR(r); |
549 | * | ||
550 | * This call unregisters and cleans up a class device that was created with a | ||
551 | * call to drm_sysfs_device_add() | ||
552 | */ | ||
553 | void drm_sysfs_device_remove(struct drm_minor *minor) | ||
554 | { | ||
555 | if (minor->kdev) | ||
556 | device_unregister(minor->kdev); | ||
557 | minor->kdev = NULL; | ||
558 | } | 543 | } |
559 | 544 | ||
560 | |||
561 | /** | 545 | /** |
562 | * drm_class_device_register - Register a struct device in the drm class. | 546 | * drm_class_device_register - Register a struct device in the drm class. |
563 | * | 547 | * |
diff --git a/include/drm/drmP.h b/include/drm/drmP.h index c480b448ce65..458385ec15f3 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h | |||
@@ -1502,9 +1502,8 @@ extern int drm_pci_set_unique(struct drm_device *dev, | |||
1502 | struct drm_sysfs_class; | 1502 | struct drm_sysfs_class; |
1503 | extern struct class *drm_sysfs_create(struct module *owner, char *name); | 1503 | extern struct class *drm_sysfs_create(struct module *owner, char *name); |
1504 | extern void drm_sysfs_destroy(void); | 1504 | extern void drm_sysfs_destroy(void); |
1505 | extern int drm_sysfs_device_add(struct drm_minor *minor); | 1505 | extern struct device *drm_sysfs_minor_alloc(struct drm_minor *minor); |
1506 | extern void drm_sysfs_hotplug_event(struct drm_device *dev); | 1506 | extern void drm_sysfs_hotplug_event(struct drm_device *dev); |
1507 | extern void drm_sysfs_device_remove(struct drm_minor *minor); | ||
1508 | extern int drm_sysfs_connector_add(struct drm_connector *connector); | 1507 | extern int drm_sysfs_connector_add(struct drm_connector *connector); |
1509 | extern void drm_sysfs_connector_remove(struct drm_connector *connector); | 1508 | extern void drm_sysfs_connector_remove(struct drm_connector *connector); |
1510 | 1509 | ||