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

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>

+51 -58
+16 -6
drivers/gpu/drm/drm_stub.c
··· 281 281 282 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 290 *drm_minor_get_slot(dev, type) = minor; 285 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 297 err_free: 288 298 kfree(minor); 289 299 return r; ··· 310 300 return; 311 301 312 302 drm_mode_group_destroy(&minor->mode_group); 303 + put_device(minor->kdev); 313 304 314 305 spin_lock_irqsave(&drm_minor_lock, flags); 315 306 idr_remove(&drm_minors_idr, minor->index); ··· 338 327 return ret; 339 328 } 340 329 341 - ret = drm_sysfs_device_add(minor); 342 - if (ret) { 343 - DRM_ERROR("DRM: Error sysfs_device_add.\n"); 330 + ret = device_add(minor->kdev); 331 + if (ret) 344 332 goto err_debugfs; 345 - } 346 333 347 334 /* replace NULL with @minor so lookups will succeed from now on */ 348 335 spin_lock_irqsave(&drm_minor_lock, flags); ··· 361 352 unsigned long flags; 362 353 363 354 minor = *drm_minor_get_slot(dev, type); 364 - if (!minor || !minor->kdev) 355 + if (!minor || !device_is_registered(minor->kdev)) 365 356 return; 366 357 367 358 /* replace @minor with NULL so lookups will fail from now on */ ··· 369 360 idr_replace(&drm_minors_idr, NULL, minor->index); 370 361 spin_unlock_irqrestore(&drm_minor_lock, flags); 371 362 363 + device_del(minor->kdev); 364 + dev_set_drvdata(minor->kdev, NULL); /* safety belt */ 372 365 drm_debugfs_cleanup(minor); 373 - drm_sysfs_device_remove(minor); 374 366 } 375 367 376 368 /**
+34 -50
drivers/gpu/drm/drm_sysfs.c
··· 493 493 } 494 494 495 495 /** 496 - * drm_sysfs_device_add - adds a class device to sysfs for a character driver 497 - * @dev: DRM device to be added 498 - * @head: DRM head in question 496 + * drm_sysfs_minor_alloc() - Allocate sysfs device for given minor 497 + * @minor: minor to allocate sysfs device for 499 498 * 500 - * Add a DRM device to the DRM's device model class. We use @dev's PCI device 501 - * as the parent for the Linux device, and make sure it has a file containing 502 - * the driver we're using (for userspace compatibility). 499 + * This allocates a new sysfs device for @minor and returns it. The device is 500 + * not registered nor linked. The caller has to use device_add() and 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 513 int r; 508 514 509 515 if (minor->type == DRM_MINOR_CONTROL) 510 516 minor_str = "controlD%d"; 511 - else if (minor->type == DRM_MINOR_RENDER) 512 - minor_str = "renderD%d"; 513 - else 514 - minor_str = "card%d"; 517 + else if (minor->type == DRM_MINOR_RENDER) 518 + minor_str = "renderD%d"; 519 + else 520 + minor_str = "card%d"; 515 521 516 - minor->kdev = kzalloc(sizeof(*minor->kdev), GFP_KERNEL); 517 - if (!minor->kdev) { 518 - r = -ENOMEM; 519 - goto error; 520 - } 522 + kdev = kzalloc(sizeof(*kdev), GFP_KERNEL); 523 + if (!kdev) 524 + return ERR_PTR(-ENOMEM); 521 525 522 - device_initialize(minor->kdev); 523 - minor->kdev->devt = MKDEV(DRM_MAJOR, minor->index); 524 - minor->kdev->class = drm_class; 525 - minor->kdev->type = &drm_sysfs_device_minor; 526 - minor->kdev->parent = minor->dev->dev; 527 - minor->kdev->release = drm_sysfs_release; 528 - dev_set_drvdata(minor->kdev, minor); 526 + device_initialize(kdev); 527 + kdev->devt = MKDEV(DRM_MAJOR, minor->index); 528 + kdev->class = drm_class; 529 + kdev->type = &drm_sysfs_device_minor; 530 + kdev->parent = minor->dev->dev; 531 + kdev->release = drm_sysfs_release; 532 + dev_set_drvdata(kdev, minor); 529 533 530 - r = dev_set_name(minor->kdev, minor_str, minor->index); 534 + r = dev_set_name(kdev, minor_str, minor->index); 531 535 if (r < 0) 532 - goto error; 536 + goto err_free; 533 537 534 - r = device_add(minor->kdev); 535 - if (r < 0) 536 - goto error; 538 + return kdev; 537 539 538 - return 0; 539 - 540 - error: 541 - DRM_ERROR("device create failed %d\n", r); 542 - put_device(minor->kdev); 543 - return r; 540 + err_free: 541 + put_device(kdev); 542 + return ERR_PTR(r); 544 543 } 545 - 546 - /** 547 - * drm_sysfs_device_remove - remove DRM device 548 - * @dev: DRM device to remove 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 - } 559 - 560 544 561 545 /** 562 546 * drm_class_device_register - Register a struct device in the drm class.
+1 -2
include/drm/drmP.h
··· 1502 1502 struct drm_sysfs_class; 1503 1503 extern struct class *drm_sysfs_create(struct module *owner, char *name); 1504 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 1506 extern void drm_sysfs_hotplug_event(struct drm_device *dev); 1507 - extern void drm_sysfs_device_remove(struct drm_minor *minor); 1508 1507 extern int drm_sysfs_connector_add(struct drm_connector *connector); 1509 1508 extern void drm_sysfs_connector_remove(struct drm_connector *connector); 1510 1509