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

scsi: core: Support allocating a pseudo SCSI device

Allocate a pseudo SCSI device if 'nr_reserved_cmds' has been set. Pseudo
SCSI devices have the SCSI ID <max_id>:U64_MAX so they won't clash with
any devices the LLD might create. Pseudo SCSI devices are excluded from
scanning and will not show up in sysfs. Additionally, pseudo SCSI
devices are skipped by shost_for_each_device(). This prevents that the
SCSI error handler tries to submit a reset to a non-existent logical
unit.

Do not allocate a budget map for pseudo SCSI devices since the
cmd_per_lun limit does not apply to pseudo SCSI devices.

Do not perform queue depth ramp up / ramp down for pseudo SCSI devices.

Pseudo SCSI devices will be used to send internal commands to a storage
device.

[ bvanassche: edited patch description / renamed host_sdev into
pseudo_sdev / unexported scsi_get_host_dev() / modified error path in
scsi_get_pseudo_dev() / skip pseudo devices in __scsi_iterate_devices()
and also when calling sdev_init(), sdev_configure() and sdev_destroy().
See also
https://lore.kernel.org/linux-scsi/20211125151048.103910-2-hare@suse.de/ ]

Reviewed-by: John Garry <john.g.garry@oracle.com>
Signed-off-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
Link: https://patch.msgid.link/20251031204029.2883185-5-bvanassche@acm.org
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>

authored by

Hannes Reinecke and committed by
Martin K. Petersen
d630fbf6 a47c7bef

+106 -4
+8
drivers/scsi/hosts.c
··· 307 307 if (error) 308 308 goto out_del_dev; 309 309 310 + if (shost->nr_reserved_cmds) { 311 + shost->pseudo_sdev = scsi_get_pseudo_sdev(shost); 312 + if (!shost->pseudo_sdev) { 313 + error = -ENOMEM; 314 + goto out_del_dev; 315 + } 316 + } 317 + 310 318 scsi_proc_host_add(shost); 311 319 scsi_autopm_put_host(shost); 312 320 return error;
+5 -2
drivers/scsi/scsi.c
··· 831 831 spin_lock_irqsave(shost->host_lock, flags); 832 832 while (list->next != &shost->__devices) { 833 833 next = list_entry(list->next, struct scsi_device, siblings); 834 - /* skip devices that we can't get a reference to */ 835 - if (!scsi_device_get(next)) 834 + /* 835 + * Skip pseudo devices and also devices we can't get a 836 + * reference to. 837 + */ 838 + if (!scsi_device_is_pseudo_dev(next) && !scsi_device_get(next)) 836 839 break; 837 840 next = NULL; 838 841 list = list->next;
+1
drivers/scsi/scsi_priv.h
··· 135 135 extern int scsi_scan_host_selected(struct Scsi_Host *, unsigned int, 136 136 unsigned int, u64, enum scsi_scan_mode); 137 137 extern void scsi_forget_host(struct Scsi_Host *); 138 + struct scsi_device *scsi_get_pseudo_sdev(struct Scsi_Host *); 138 139 139 140 /* scsi_sysctl.c */ 140 141 #ifdef CONFIG_SYSCTL
+66 -1
drivers/scsi/scsi_scan.c
··· 349 349 350 350 scsi_sysfs_device_initialize(sdev); 351 351 352 + if (scsi_device_is_pseudo_dev(sdev)) 353 + return sdev; 354 + 352 355 depth = sdev->host->cmd_per_lun ?: 1; 353 356 354 357 /* ··· 1073 1070 1074 1071 sdev->sdev_bflags = *bflags; 1075 1072 1073 + if (scsi_device_is_pseudo_dev(sdev)) 1074 + return SCSI_SCAN_LUN_PRESENT; 1075 + 1076 1076 /* 1077 1077 * No need to freeze the queue as it isn't reachable to anyone else yet. 1078 1078 */ ··· 1218 1212 sdev = scsi_alloc_sdev(starget, lun, hostdata); 1219 1213 if (!sdev) 1220 1214 goto out; 1215 + 1216 + if (scsi_device_is_pseudo_dev(sdev)) { 1217 + if (bflagsp) 1218 + *bflagsp = BLIST_NOLUN; 1219 + return SCSI_SCAN_LUN_PRESENT; 1220 + } 1221 1221 1222 1222 result = kmalloc(result_len, GFP_KERNEL); 1223 1223 if (!result) ··· 2096 2084 restart: 2097 2085 spin_lock_irqsave(shost->host_lock, flags); 2098 2086 list_for_each_entry(sdev, &shost->__devices, siblings) { 2099 - if (sdev->sdev_state == SDEV_DEL) 2087 + if (scsi_device_is_pseudo_dev(sdev) || 2088 + sdev->sdev_state == SDEV_DEL) 2100 2089 continue; 2101 2090 spin_unlock_irqrestore(shost->host_lock, flags); 2102 2091 __scsi_remove_device(sdev); 2103 2092 goto restart; 2104 2093 } 2105 2094 spin_unlock_irqrestore(shost->host_lock, flags); 2095 + 2096 + /* 2097 + * Remove the pseudo device last since it may be needed during removal 2098 + * of other SCSI devices. 2099 + */ 2100 + if (shost->pseudo_sdev) 2101 + __scsi_remove_device(shost->pseudo_sdev); 2106 2102 } 2107 2103 2104 + /** 2105 + * scsi_get_pseudo_sdev() - Attach a pseudo SCSI device to a SCSI host 2106 + * @shost: Host that needs a pseudo SCSI device 2107 + * 2108 + * Lock status: None assumed. 2109 + * 2110 + * Returns: The scsi_device or NULL 2111 + * 2112 + * Notes: 2113 + * Attach a single scsi_device to the Scsi_Host. The primary aim for this 2114 + * device is to serve as a container from which SCSI commands can be 2115 + * allocated. Each SCSI command will carry a command tag allocated by the 2116 + * block layer. These SCSI commands can be used by the LLDD to send 2117 + * internal or passthrough commands without having to manage tag allocation 2118 + * inside the LLDD. 2119 + */ 2120 + struct scsi_device *scsi_get_pseudo_sdev(struct Scsi_Host *shost) 2121 + { 2122 + struct scsi_device *sdev = NULL; 2123 + struct scsi_target *starget; 2124 + 2125 + guard(mutex)(&shost->scan_mutex); 2126 + 2127 + if (!scsi_host_scan_allowed(shost)) 2128 + goto out; 2129 + 2130 + starget = scsi_alloc_target(&shost->shost_gendev, 0, shost->max_id); 2131 + if (!starget) 2132 + goto out; 2133 + 2134 + sdev = scsi_alloc_sdev(starget, U64_MAX, NULL); 2135 + if (!sdev) { 2136 + scsi_target_reap(starget); 2137 + goto put_target; 2138 + } 2139 + 2140 + sdev->borken = 0; 2141 + 2142 + put_target: 2143 + /* See also the get_device(dev) call in scsi_alloc_target(). */ 2144 + put_device(&starget->dev); 2145 + 2146 + out: 2147 + return sdev; 2148 + }
+4 -1
drivers/scsi/scsi_sysfs.c
··· 1406 1406 int error; 1407 1407 struct scsi_target *starget = sdev->sdev_target; 1408 1408 1409 + if (WARN_ON_ONCE(scsi_device_is_pseudo_dev(sdev))) 1410 + return -EINVAL; 1411 + 1409 1412 error = scsi_target_add(starget); 1410 1413 if (error) 1411 1414 return error; ··· 1516 1513 kref_put(&sdev->host->tagset_refcnt, scsi_mq_free_tags); 1517 1514 cancel_work_sync(&sdev->requeue_work); 1518 1515 1519 - if (sdev->host->hostt->sdev_destroy) 1516 + if (!scsi_device_is_pseudo_dev(sdev) && sdev->host->hostt->sdev_destroy) 1520 1517 sdev->host->hostt->sdev_destroy(sdev); 1521 1518 transport_destroy_device(dev); 1522 1519
+16
include/scsi/scsi_device.h
··· 589 589 #define scmd_id(scmd) sdev_id((scmd)->device) 590 590 #define scmd_channel(scmd) sdev_channel((scmd)->device) 591 591 592 + /** 593 + * scsi_device_is_pseudo_dev() - Whether a device is a pseudo SCSI device. 594 + * @sdev: SCSI device to examine 595 + * 596 + * A pseudo SCSI device can be used to allocate SCSI commands but does not show 597 + * up in sysfs. Additionally, the logical unit information in *@sdev is made up. 598 + * 599 + * This function tests the LUN number instead of comparing @sdev with 600 + * @sdev->host->pseudo_sdev because this function may be called before 601 + * @sdev->host->pseudo_sdev has been initialized. 602 + */ 603 + static inline bool scsi_device_is_pseudo_dev(struct scsi_device *sdev) 604 + { 605 + return sdev->lun == U64_MAX; 606 + } 607 + 592 608 /* 593 609 * checks for positions of the SCSI state machine 594 610 */
+6
include/scsi/scsi_host.h
··· 722 722 struct device shost_gendev, shost_dev; 723 723 724 724 /* 725 + * A SCSI device structure used for sending internal commands to the 726 + * HBA. There is no corresponding logical unit inside the SCSI device. 727 + */ 728 + struct scsi_device *pseudo_sdev; 729 + 730 + /* 725 731 * Points to the transport data (if any) which is allocated 726 732 * separately 727 733 */