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

scsi: core: Register sysfs attributes earlier

A quote from Documentation/driver-api/driver-model/device.rst:
"Word of warning: While the kernel allows device_create_file() and
device_remove_file() to be called on a device at any time, userspace has
strict expectations on when attributes get created. When a new device is
registered in the kernel, a uevent is generated to notify userspace (like
udev) that a new device is available. If attributes are added after the
device is registered, then userspace won't get notified and userspace will
not know about the new attributes."

Hence register SCSI host sysfs attributes before the SCSI host shost_dev
uevent is emitted instead of after that event has been emitted.

Link: https://lore.kernel.org/r/20211012233558.4066756-2-bvanassche@acm.org
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: Benjamin Block <bblock@linux.ibm.com>
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>

authored by

Bart Van Assche and committed by
Martin K. Petersen
92c4b58b af049dfd

+84 -43
+21 -2
drivers/scsi/hosts.c
··· 376 376 struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) 377 377 { 378 378 struct Scsi_Host *shost; 379 - int index; 379 + int index, i, j = 0; 380 380 381 381 shost = kzalloc(sizeof(struct Scsi_Host) + privsize, GFP_KERNEL); 382 382 if (!shost) ··· 481 481 shost->shost_dev.parent = &shost->shost_gendev; 482 482 shost->shost_dev.class = &shost_class; 483 483 dev_set_name(&shost->shost_dev, "host%d", shost->host_no); 484 - shost->shost_dev.groups = scsi_sysfs_shost_attr_groups; 484 + shost->shost_dev.groups = shost->shost_dev_attr_groups; 485 + shost->shost_dev_attr_groups[j++] = &scsi_shost_attr_group; 486 + if (sht->shost_attrs) { 487 + shost->lld_attr_group = (struct attribute_group){ 488 + .attrs = scsi_convert_dev_attrs(&shost->shost_gendev, 489 + sht->shost_attrs) 490 + }; 491 + if (shost->lld_attr_group.attrs) 492 + shost->shost_dev_attr_groups[j++] = 493 + &shost->lld_attr_group; 494 + } 495 + if (sht->shost_groups) { 496 + for (i = 0; sht->shost_groups[i] && 497 + j < ARRAY_SIZE(shost->shost_dev_attr_groups); 498 + i++, j++) { 499 + shost->shost_dev_attr_groups[j] = 500 + sht->shost_groups[i]; 501 + } 502 + } 503 + WARN_ON_ONCE(j >= ARRAY_SIZE(shost->shost_dev_attr_groups)); 485 504 486 505 shost->ehandler = kthread_run(scsi_error_handler, shost, 487 506 "scsi_eh_%d", shost->host_no);
+3 -1
drivers/scsi/scsi_priv.h
··· 138 138 extern int scsi_sysfs_add_host(struct Scsi_Host *); 139 139 extern int scsi_sysfs_register(void); 140 140 extern void scsi_sysfs_unregister(void); 141 + struct attribute **scsi_convert_dev_attrs(struct device *dev, 142 + struct device_attribute **dev_attr); 141 143 extern void scsi_sysfs_device_initialize(struct scsi_device *); 142 144 extern int scsi_sysfs_target_initialize(struct scsi_device *); 143 145 extern struct scsi_transport_template blank_transport_template; 144 146 extern void __scsi_remove_device(struct scsi_device *); 145 147 146 148 extern struct bus_type scsi_bus_type; 147 - extern const struct attribute_group *scsi_sysfs_shost_attr_groups[]; 149 + extern const struct attribute_group scsi_shost_attr_group; 148 150 149 151 /* scsi_netlink.c */ 150 152 #ifdef CONFIG_SCSI_NETLINK
+41 -40
drivers/scsi/scsi_sysfs.c
··· 424 424 NULL 425 425 }; 426 426 427 - static struct attribute_group scsi_shost_attr_group = { 427 + const struct attribute_group scsi_shost_attr_group = { 428 428 .attrs = scsi_sysfs_shost_attrs, 429 - }; 430 - 431 - const struct attribute_group *scsi_sysfs_shost_attr_groups[] = { 432 - &scsi_shost_attr_group, 433 - NULL 434 429 }; 435 430 436 431 static void scsi_device_cls_release(struct device *class_dev) ··· 1328 1333 **/ 1329 1334 int scsi_sysfs_add_sdev(struct scsi_device *sdev) 1330 1335 { 1331 - int error, i; 1336 + int error; 1332 1337 struct scsi_target *starget = sdev->sdev_target; 1333 1338 1334 1339 error = scsi_target_add(starget); ··· 1381 1386 } 1382 1387 } 1383 1388 1384 - /* add additional host specific attributes */ 1385 - if (sdev->host->hostt->sdev_attrs) { 1386 - for (i = 0; sdev->host->hostt->sdev_attrs[i]; i++) { 1387 - error = device_create_file(&sdev->sdev_gendev, 1388 - sdev->host->hostt->sdev_attrs[i]); 1389 - if (error) 1390 - return error; 1391 - } 1392 - } 1393 - 1394 - if (sdev->host->hostt->sdev_groups) { 1395 - error = sysfs_create_groups(&sdev->sdev_gendev.kobj, 1396 - sdev->host->hostt->sdev_groups); 1397 - if (error) 1398 - return error; 1399 - } 1400 - 1401 1389 scsi_autopm_put_device(sdev); 1402 1390 return error; 1403 1391 } ··· 1419 1441 1420 1442 if (res != 0) 1421 1443 return; 1422 - 1423 - if (sdev->host->hostt->sdev_groups) 1424 - sysfs_remove_groups(&sdev->sdev_gendev.kobj, 1425 - sdev->host->hostt->sdev_groups); 1426 1444 1427 1445 if (IS_ENABLED(CONFIG_BLK_DEV_BSG) && sdev->bsg_dev) 1428 1446 bsg_unregister_queue(sdev->bsg_dev); ··· 1558 1584 **/ 1559 1585 int scsi_sysfs_add_host(struct Scsi_Host *shost) 1560 1586 { 1561 - int error, i; 1562 - 1563 - /* add host specific attributes */ 1564 - if (shost->hostt->shost_attrs) { 1565 - for (i = 0; shost->hostt->shost_attrs[i]; i++) { 1566 - error = device_create_file(&shost->shost_dev, 1567 - shost->hostt->shost_attrs[i]); 1568 - if (error) 1569 - return error; 1570 - } 1571 - } 1572 - 1573 1587 transport_register_device(&shost->shost_gendev); 1574 1588 transport_configure_device(&shost->shost_gendev); 1575 1589 return 0; 1590 + } 1591 + 1592 + /* 1593 + * Convert an array of struct device_attribute pointers into an array of 1594 + * struct attribute pointers. 1595 + */ 1596 + struct attribute **scsi_convert_dev_attrs(struct device *dev, 1597 + struct device_attribute **dev_attr) 1598 + { 1599 + struct attribute **attrs; 1600 + int i; 1601 + 1602 + for (i = 0; dev_attr[i]; i++) 1603 + ; 1604 + attrs = devm_kzalloc(dev, (i + 1) * sizeof(*attrs), GFP_KERNEL); 1605 + if (!attrs) 1606 + return NULL; 1607 + for (i = 0; dev_attr[i]; i++) 1608 + attrs[i] = &dev_attr[i]->attr; 1609 + return attrs; 1576 1610 } 1577 1611 1578 1612 static struct device_type scsi_dev_type = { ··· 1591 1609 1592 1610 void scsi_sysfs_device_initialize(struct scsi_device *sdev) 1593 1611 { 1612 + int i, j = 0; 1594 1613 unsigned long flags; 1595 1614 struct Scsi_Host *shost = sdev->host; 1615 + struct scsi_host_template *hostt = shost->hostt; 1596 1616 struct scsi_target *starget = sdev->sdev_target; 1597 1617 1598 1618 device_initialize(&sdev->sdev_gendev); ··· 1603 1619 scsi_enable_async_suspend(&sdev->sdev_gendev); 1604 1620 dev_set_name(&sdev->sdev_gendev, "%d:%d:%d:%llu", 1605 1621 sdev->host->host_no, sdev->channel, sdev->id, sdev->lun); 1622 + sdev->gendev_attr_groups[j++] = &scsi_sdev_attr_group; 1623 + if (hostt->sdev_attrs) { 1624 + sdev->lld_attr_group = (struct attribute_group){ 1625 + .attrs = scsi_convert_dev_attrs(&sdev->sdev_gendev, 1626 + hostt->sdev_attrs) 1627 + }; 1628 + if (sdev->lld_attr_group.attrs) 1629 + sdev->gendev_attr_groups[j++] = &sdev->lld_attr_group; 1630 + } 1631 + if (hostt->sdev_groups) { 1632 + for (i = 0; hostt->sdev_groups[i] && 1633 + j < ARRAY_SIZE(sdev->gendev_attr_groups); 1634 + i++, j++) { 1635 + sdev->gendev_attr_groups[j] = hostt->sdev_groups[i]; 1636 + } 1637 + } 1638 + WARN_ON_ONCE(j >= ARRAY_SIZE(sdev->gendev_attr_groups)); 1606 1639 1607 1640 device_initialize(&sdev->sdev_dev); 1608 1641 sdev->sdev_dev.parent = get_device(&sdev->sdev_gendev);
+7
include/scsi/scsi_device.h
··· 225 225 226 226 struct device sdev_gendev, 227 227 sdev_dev; 228 + struct attribute_group lld_attr_group; 229 + /* 230 + * The array size 6 provides space for one attribute group for the 231 + * SCSI core, four attribute groups defined by SCSI LLDs and one 232 + * terminating NULL pointer. 233 + */ 234 + const struct attribute_group *gendev_attr_groups[6]; 228 235 229 236 struct execute_work ew; /* used to get process context on put */ 230 237 struct work_struct requeue_work;
+12
include/scsi/scsi_host.h
··· 484 484 struct device_attribute **sdev_attrs; 485 485 486 486 /* 487 + * Pointer to the SCSI host sysfs attribute groups, NULL terminated. 488 + */ 489 + const struct attribute_group **shost_groups; 490 + 491 + /* 487 492 * Pointer to the SCSI device attribute groups for this host, 488 493 * NULL terminated. 489 494 */ ··· 700 695 701 696 /* ldm bits */ 702 697 struct device shost_gendev, shost_dev; 698 + struct attribute_group lld_attr_group; 699 + /* 700 + * The array size 3 provides space for one attribute group defined by 701 + * the SCSI core, one attribute group defined by the SCSI LLD and one 702 + * terminating NULL pointer. 703 + */ 704 + const struct attribute_group *shost_dev_attr_groups[3]; 703 705 704 706 /* 705 707 * Points to the transport data (if any) which is allocated