diff mbox series

[01/15] scsi: allocate host device

Message ID 20211125151048.103910-2-hare@suse.de (mailing list archive)
State Changes Requested
Headers show
Series scsi: enabled reserved commands for LLDDs | expand

Commit Message

Hannes Reinecke Nov. 25, 2021, 3:10 p.m. UTC
Add a flag 'alloc_host_dev' to the SCSI host template and allocate
a virtual scsi device if the flag is set.
This device has the SCSI id <max_id + 1>:0, so won't clash with any
devices the HBA might allocate. It's also excluded from scanning and
will not show up in sysfs.
Intention is to use this device to send internal commands to the HBA.

Signed-off-by: Hannes Reinecke <hare@suse.de>
---
 drivers/scsi/hosts.c       |  8 +++++
 drivers/scsi/scsi_scan.c   | 67 +++++++++++++++++++++++++++++++++++++-
 drivers/scsi/scsi_sysfs.c  |  3 +-
 include/scsi/scsi_device.h |  2 +-
 include/scsi/scsi_host.h   | 21 ++++++++++++
 5 files changed, 98 insertions(+), 3 deletions(-)

Comments

Bart Van Assche Nov. 25, 2021, 11:16 p.m. UTC | #1
On 11/25/21 7:10 AM, Hannes Reinecke wrote:
> +/**
> + * scsi_get_host_dev - Create a virtual scsi_device to the host adapter
                           ^^^^^^
                           Attach?

> @@ -500,7 +500,8 @@ static void scsi_device_dev_release_usercontext(struct work_struct *work)
>   		kfree_rcu(vpd_pg80, rcu);
>   	if (vpd_pg89)
>   		kfree_rcu(vpd_pg89, rcu);
> -	kfree(sdev->inquiry);
> +	if (!scsi_device_is_host_dev(sdev))
> +		kfree(sdev->inquiry);
>   	kfree(sdev);

kfree() accepts a NULL pointer so please leave out the new if-test.

> -#define MODULE_ALIAS_SCSI_DEVICE(type) \
> +#define MODULE_ALIAS_SCSI_DEVICE(type)			\
>   	MODULE_ALIAS("scsi:t-" __stringify(type) "*")

The above change seems not related to the rest of this patch? Can it be left out?

Otherwise this patch looks good to me.

Thanks,

Bart.
chenxiang Nov. 26, 2021, 2:47 a.m. UTC | #2
Hi Hannes,


在 2021/11/25 23:10, Hannes Reinecke 写道:
> Add a flag 'alloc_host_dev' to the SCSI host template and allocate
> a virtual scsi device if the flag is set.
> This device has the SCSI id <max_id + 1>:0, so won't clash with any
> devices the HBA might allocate. It's also excluded from scanning and
> will not show up in sysfs.
> Intention is to use this device to send internal commands to the HBA.
>
> Signed-off-by: Hannes Reinecke <hare@suse.de>
> ---
>   drivers/scsi/hosts.c       |  8 +++++
>   drivers/scsi/scsi_scan.c   | 67 +++++++++++++++++++++++++++++++++++++-
>   drivers/scsi/scsi_sysfs.c  |  3 +-
>   include/scsi/scsi_device.h |  2 +-
>   include/scsi/scsi_host.h   | 21 ++++++++++++
>   5 files changed, 98 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
> index f69b77cbf538..a539fa2fb221 100644
> --- a/drivers/scsi/hosts.c
> +++ b/drivers/scsi/hosts.c
> @@ -290,6 +290,14 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
>   	if (error)
>   		goto out_del_dev;
>   
> +	if (sht->alloc_host_sdev) {
> +		shost->shost_sdev = scsi_get_host_dev(shost);
> +		if (!shost->shost_sdev) {
> +			error = -ENOMEM;
> +			goto out_del_dev;
> +		}
> +	}
> +
>   	scsi_proc_host_add(shost);
>   	scsi_autopm_put_host(shost);
>   	return error;
> diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
> index 328c0e79dfe7..e2910aa02a65 100644
> --- a/drivers/scsi/scsi_scan.c
> +++ b/drivers/scsi/scsi_scan.c
> @@ -1139,6 +1139,12 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget,
>   	if (!sdev)
>   		goto out;
>   
> +	if (scsi_device_is_host_dev(sdev)) {
> +		if (bflagsp)
> +			*bflagsp = BLIST_NOLUN;
> +		return SCSI_SCAN_LUN_PRESENT;
> +	}
> +
>   	result = kmalloc(result_len, GFP_KERNEL);
>   	if (!result)
>   		goto out_free_sdev;
> @@ -1755,6 +1761,9 @@ static void scsi_sysfs_add_devices(struct Scsi_Host *shost)
>   		/* If device is already visible, skip adding it to sysfs */
>   		if (sdev->is_visible)
>   			continue;
> +		/* Host devices should never be visible in sysfs */
> +		if (scsi_device_is_host_dev(sdev))
> +			continue;
>   		if (!scsi_host_scan_allowed(shost) ||
>   		    scsi_sysfs_add_sdev(sdev) != 0)
>   			__scsi_remove_device(sdev);
> @@ -1919,12 +1928,16 @@ EXPORT_SYMBOL(scsi_scan_host);
>   
>   void scsi_forget_host(struct Scsi_Host *shost)
>   {
> -	struct scsi_device *sdev;
> +	struct scsi_device *sdev, *host_sdev = NULL;
>   	unsigned long flags;
>   
>    restart:
>   	spin_lock_irqsave(shost->host_lock, flags);
>   	list_for_each_entry(sdev, &shost->__devices, siblings) {
> +		if (scsi_device_is_host_dev(sdev)) {
> +			host_sdev = sdev;
> +			continue;
> +		}
>   		if (sdev->sdev_state == SDEV_DEL)
>   			continue;
>   		spin_unlock_irqrestore(shost->host_lock, flags);
> @@ -1932,5 +1945,57 @@ void scsi_forget_host(struct Scsi_Host *shost)
>   		goto restart;
>   	}
>   	spin_unlock_irqrestore(shost->host_lock, flags);
> +	/* Remove host device last, might be needed to send commands */
> +	if (host_sdev)
> +		__scsi_remove_device(host_sdev);
>   }
>   
> +/**
> + * scsi_get_host_dev - Create a virtual scsi_device to the host adapter
> + * @shost: Host that needs a scsi_device
> + *
> + * Lock status: None assumed.
> + *
> + * Returns:     The scsi_device or NULL
> + *
> + * Notes:
> + *	Attach a single scsi_device to the Scsi_Host. The primary aim
> + *	for this device is to serve as a container from which valid
> + *	scsi commands can be allocated from. Each scsi command will carry
> + *	an unused/free command tag, which then can be used by the LLDD to
> + *	send internal or passthrough commands without having to find a
> + *	valid command tag internally.
> + */
> +struct scsi_device *scsi_get_host_dev(struct Scsi_Host *shost)
> +{
> +	struct scsi_device *sdev = NULL;
> +	struct scsi_target *starget;
> +
> +	mutex_lock(&shost->scan_mutex);
> +	if (!scsi_host_scan_allowed(shost))
> +		goto out;
> +	starget = scsi_alloc_target(&shost->shost_gendev, 0,
> +				    shost->max_id);
> +	if (!starget)
> +		goto out;
> +
> +	sdev = scsi_alloc_sdev(starget, 0, NULL);
> +	if (sdev)
> +		sdev->borken = 0;
> +	else
> +		scsi_target_reap(starget);

Currently many scsi drivers fill some interfaces such as 
target_alloc()/slave_alloc() for real disks.
When allocating scsi target and scsi device for host dev, it will also 
call those interfaces, and not sure whether it breaks those drivers.
 From function sas_target_alloc() (common interface in libsas layer), it 
seems break it as there is no sas_rphy for host dev.
Hannes Reinecke Nov. 27, 2021, 4:21 p.m. UTC | #3
On 11/26/21 12:16 AM, Bart Van Assche wrote:
> On 11/25/21 7:10 AM, Hannes Reinecke wrote:
>> +/**
>> + * scsi_get_host_dev - Create a virtual scsi_device to the host adapter
>                            ^^^^^^
>                            Attach?
> 
It's just words ... sure I can change it.

>> @@ -500,7 +500,8 @@ static void 
>> scsi_device_dev_release_usercontext(struct work_struct *work)
>>           kfree_rcu(vpd_pg80, rcu);
>>       if (vpd_pg89)
>>           kfree_rcu(vpd_pg89, rcu);
>> -    kfree(sdev->inquiry);
>> +    if (!scsi_device_is_host_dev(sdev))
>> +        kfree(sdev->inquiry);
>>       kfree(sdev);
> 
> kfree() accepts a NULL pointer so please leave out the new if-test.
> 
Actually a left-over from a different patch (to use 'real' inquiry data 
for dummy devices). Will be removing it.

>> -#define MODULE_ALIAS_SCSI_DEVICE(type) \
>> +#define MODULE_ALIAS_SCSI_DEVICE(type)            \
>>       MODULE_ALIAS("scsi:t-" __stringify(type) "*")
> 
> The above change seems not related to the rest of this patch? Can it be 
> left out?
> 
Sure.

Cheers,

Hannes
Hannes Reinecke Nov. 27, 2021, 4:52 p.m. UTC | #4
On 11/26/21 3:47 AM, chenxiang (M) wrote:
> Hi Hannes,
> 
> 
> 在 2021/11/25 23:10, Hannes Reinecke 写道:
>> Add a flag 'alloc_host_dev' to the SCSI host template and allocate
>> a virtual scsi device if the flag is set.
>> This device has the SCSI id <max_id + 1>:0, so won't clash with any
>> devices the HBA might allocate. It's also excluded from scanning and
>> will not show up in sysfs.
>> Intention is to use this device to send internal commands to the HBA.
>>
>> Signed-off-by: Hannes Reinecke <hare@suse.de>
>> ---
>>   drivers/scsi/hosts.c       |  8 +++++
>>   drivers/scsi/scsi_scan.c   | 67 +++++++++++++++++++++++++++++++++++++-
>>   drivers/scsi/scsi_sysfs.c  |  3 +-
>>   include/scsi/scsi_device.h |  2 +-
>>   include/scsi/scsi_host.h   | 21 ++++++++++++
>>   5 files changed, 98 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
>> index f69b77cbf538..a539fa2fb221 100644
>> --- a/drivers/scsi/hosts.c
>> +++ b/drivers/scsi/hosts.c
>> @@ -290,6 +290,14 @@ int scsi_add_host_with_dma(struct Scsi_Host 
>> *shost, struct device *dev,
>>       if (error)
>>           goto out_del_dev;
>> +    if (sht->alloc_host_sdev) {
>> +        shost->shost_sdev = scsi_get_host_dev(shost);
>> +        if (!shost->shost_sdev) {
>> +            error = -ENOMEM;
>> +            goto out_del_dev;
>> +        }
>> +    }
>> +
>>       scsi_proc_host_add(shost);
>>       scsi_autopm_put_host(shost);
>>       return error;
>> diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
>> index 328c0e79dfe7..e2910aa02a65 100644
>> --- a/drivers/scsi/scsi_scan.c
>> +++ b/drivers/scsi/scsi_scan.c
>> @@ -1139,6 +1139,12 @@ static int scsi_probe_and_add_lun(struct 
>> scsi_target *starget,
>>       if (!sdev)
>>           goto out;
>> +    if (scsi_device_is_host_dev(sdev)) {
>> +        if (bflagsp)
>> +            *bflagsp = BLIST_NOLUN;
>> +        return SCSI_SCAN_LUN_PRESENT;
>> +    }
>> +
>>       result = kmalloc(result_len, GFP_KERNEL);
>>       if (!result)
>>           goto out_free_sdev;
>> @@ -1755,6 +1761,9 @@ static void scsi_sysfs_add_devices(struct 
>> Scsi_Host *shost)
>>           /* If device is already visible, skip adding it to sysfs */
>>           if (sdev->is_visible)
>>               continue;
>> +        /* Host devices should never be visible in sysfs */
>> +        if (scsi_device_is_host_dev(sdev))
>> +            continue;
>>           if (!scsi_host_scan_allowed(shost) ||
>>               scsi_sysfs_add_sdev(sdev) != 0)
>>               __scsi_remove_device(sdev);
>> @@ -1919,12 +1928,16 @@ EXPORT_SYMBOL(scsi_scan_host);
>>   void scsi_forget_host(struct Scsi_Host *shost)
>>   {
>> -    struct scsi_device *sdev;
>> +    struct scsi_device *sdev, *host_sdev = NULL;
>>       unsigned long flags;
>>    restart:
>>       spin_lock_irqsave(shost->host_lock, flags);
>>       list_for_each_entry(sdev, &shost->__devices, siblings) {
>> +        if (scsi_device_is_host_dev(sdev)) {
>> +            host_sdev = sdev;
>> +            continue;
>> +        }
>>           if (sdev->sdev_state == SDEV_DEL)
>>               continue;
>>           spin_unlock_irqrestore(shost->host_lock, flags);
>> @@ -1932,5 +1945,57 @@ void scsi_forget_host(struct Scsi_Host *shost)
>>           goto restart;
>>       }
>>       spin_unlock_irqrestore(shost->host_lock, flags);
>> +    /* Remove host device last, might be needed to send commands */
>> +    if (host_sdev)
>> +        __scsi_remove_device(host_sdev);
>>   }
>> +/**
>> + * scsi_get_host_dev - Create a virtual scsi_device to the host adapter
>> + * @shost: Host that needs a scsi_device
>> + *
>> + * Lock status: None assumed.
>> + *
>> + * Returns:     The scsi_device or NULL
>> + *
>> + * Notes:
>> + *    Attach a single scsi_device to the Scsi_Host. The primary aim
>> + *    for this device is to serve as a container from which valid
>> + *    scsi commands can be allocated from. Each scsi command will carry
>> + *    an unused/free command tag, which then can be used by the LLDD to
>> + *    send internal or passthrough commands without having to find a
>> + *    valid command tag internally.
>> + */
>> +struct scsi_device *scsi_get_host_dev(struct Scsi_Host *shost)
>> +{
>> +    struct scsi_device *sdev = NULL;
>> +    struct scsi_target *starget;
>> +
>> +    mutex_lock(&shost->scan_mutex);
>> +    if (!scsi_host_scan_allowed(shost))
>> +        goto out;
>> +    starget = scsi_alloc_target(&shost->shost_gendev, 0,
>> +                    shost->max_id);
>> +    if (!starget)
>> +        goto out;
>> +
>> +    sdev = scsi_alloc_sdev(starget, 0, NULL);
>> +    if (sdev)
>> +        sdev->borken = 0;
>> +    else
>> +        scsi_target_reap(starget);
> 
> Currently many scsi drivers fill some interfaces such as 
> target_alloc()/slave_alloc() for real disks.
> When allocating scsi target and scsi device for host dev, it will also 
> call those interfaces, and not sure whether it breaks those drivers.
>  From function sas_target_alloc() (common interface in libsas layer), it 
> seems break it as there is no sas_rphy for host dev.
> 
Ah. Didn't consider that.
Will be having a look and fixup the patch.

Cheers,

Hannes
Hannes Reinecke Nov. 29, 2021, 10:59 a.m. UTC | #5
On 11/27/21 5:52 PM, Hannes Reinecke wrote:
> On 11/26/21 3:47 AM, chenxiang (M) wrote:
>> Hi Hannes,
>>
>>
>> 在 2021/11/25 23:10, Hannes Reinecke 写道:
>>> Add a flag 'alloc_host_dev' to the SCSI host template and allocate
>>> a virtual scsi device if the flag is set.
>>> This device has the SCSI id <max_id + 1>:0, so won't clash with any
>>> devices the HBA might allocate. It's also excluded from scanning and
>>> will not show up in sysfs.
>>> Intention is to use this device to send internal commands to the HBA.
>>>
>>> Signed-off-by: Hannes Reinecke <hare@suse.de>
>>> ---
>>>   drivers/scsi/hosts.c       |  8 +++++
>>>   drivers/scsi/scsi_scan.c   | 67 +++++++++++++++++++++++++++++++++++++-
>>>   drivers/scsi/scsi_sysfs.c  |  3 +-
>>>   include/scsi/scsi_device.h |  2 +-
>>>   include/scsi/scsi_host.h   | 21 ++++++++++++
>>>   5 files changed, 98 insertions(+), 3 deletions(-)
>>>
>>> diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
>>> index f69b77cbf538..a539fa2fb221 100644
>>> --- a/drivers/scsi/hosts.c
>>> +++ b/drivers/scsi/hosts.c
>>> @@ -290,6 +290,14 @@ int scsi_add_host_with_dma(struct Scsi_Host 
>>> *shost, struct device *dev,
>>>       if (error)
>>>           goto out_del_dev;
>>> +    if (sht->alloc_host_sdev) {
>>> +        shost->shost_sdev = scsi_get_host_dev(shost);
>>> +        if (!shost->shost_sdev) {
>>> +            error = -ENOMEM;
>>> +            goto out_del_dev;
>>> +        }
>>> +    }
>>> +
>>>       scsi_proc_host_add(shost);
>>>       scsi_autopm_put_host(shost);
>>>       return error;
>>> diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
>>> index 328c0e79dfe7..e2910aa02a65 100644
>>> --- a/drivers/scsi/scsi_scan.c
>>> +++ b/drivers/scsi/scsi_scan.c
>>> @@ -1139,6 +1139,12 @@ static int scsi_probe_and_add_lun(struct 
>>> scsi_target *starget,
>>>       if (!sdev)
>>>           goto out;
>>> +    if (scsi_device_is_host_dev(sdev)) {
>>> +        if (bflagsp)
>>> +            *bflagsp = BLIST_NOLUN;
>>> +        return SCSI_SCAN_LUN_PRESENT;
>>> +    }
>>> +
>>>       result = kmalloc(result_len, GFP_KERNEL);
>>>       if (!result)
>>>           goto out_free_sdev;
>>> @@ -1755,6 +1761,9 @@ static void scsi_sysfs_add_devices(struct 
>>> Scsi_Host *shost)
>>>           /* If device is already visible, skip adding it to sysfs */
>>>           if (sdev->is_visible)
>>>               continue;
>>> +        /* Host devices should never be visible in sysfs */
>>> +        if (scsi_device_is_host_dev(sdev))
>>> +            continue;
>>>           if (!scsi_host_scan_allowed(shost) ||
>>>               scsi_sysfs_add_sdev(sdev) != 0)
>>>               __scsi_remove_device(sdev);
>>> @@ -1919,12 +1928,16 @@ EXPORT_SYMBOL(scsi_scan_host);
>>>   void scsi_forget_host(struct Scsi_Host *shost)
>>>   {
>>> -    struct scsi_device *sdev;
>>> +    struct scsi_device *sdev, *host_sdev = NULL;
>>>       unsigned long flags;
>>>    restart:
>>>       spin_lock_irqsave(shost->host_lock, flags);
>>>       list_for_each_entry(sdev, &shost->__devices, siblings) {
>>> +        if (scsi_device_is_host_dev(sdev)) {
>>> +            host_sdev = sdev;
>>> +            continue;
>>> +        }
>>>           if (sdev->sdev_state == SDEV_DEL)
>>>               continue;
>>>           spin_unlock_irqrestore(shost->host_lock, flags);
>>> @@ -1932,5 +1945,57 @@ void scsi_forget_host(struct Scsi_Host *shost)
>>>           goto restart;
>>>       }
>>>       spin_unlock_irqrestore(shost->host_lock, flags);
>>> +    /* Remove host device last, might be needed to send commands */
>>> +    if (host_sdev)
>>> +        __scsi_remove_device(host_sdev);
>>>   }
>>> +/**
>>> + * scsi_get_host_dev - Create a virtual scsi_device to the host adapter
>>> + * @shost: Host that needs a scsi_device
>>> + *
>>> + * Lock status: None assumed.
>>> + *
>>> + * Returns:     The scsi_device or NULL
>>> + *
>>> + * Notes:
>>> + *    Attach a single scsi_device to the Scsi_Host. The primary aim
>>> + *    for this device is to serve as a container from which valid
>>> + *    scsi commands can be allocated from. Each scsi command will carry
>>> + *    an unused/free command tag, which then can be used by the LLDD to
>>> + *    send internal or passthrough commands without having to find a
>>> + *    valid command tag internally.
>>> + */
>>> +struct scsi_device *scsi_get_host_dev(struct Scsi_Host *shost)
>>> +{
>>> +    struct scsi_device *sdev = NULL;
>>> +    struct scsi_target *starget;
>>> +
>>> +    mutex_lock(&shost->scan_mutex);
>>> +    if (!scsi_host_scan_allowed(shost))
>>> +        goto out;
>>> +    starget = scsi_alloc_target(&shost->shost_gendev, 0,
>>> +                    shost->max_id);
>>> +    if (!starget)
>>> +        goto out;
>>> +
>>> +    sdev = scsi_alloc_sdev(starget, 0, NULL);
>>> +    if (sdev)
>>> +        sdev->borken = 0;
>>> +    else
>>> +        scsi_target_reap(starget);
>>
>> Currently many scsi drivers fill some interfaces such as 
>> target_alloc()/slave_alloc() for real disks.
>> When allocating scsi target and scsi device for host dev, it will also 
>> call those interfaces, and not sure whether it breaks those drivers.
>>  From function sas_target_alloc() (common interface in libsas layer), 
>> it seems break it as there is no sas_rphy for host dev.
>>
> Ah. Didn't consider that.
> Will be having a look and fixup the patch.
> 
After giving it some more consideration, I don't think that this is the 
best way to go.

An update to make sas_target_alloc() work correctly would mean a change 
in the SAS topology, as we'd need to create an rphy for the host port, 
and have the host device using that as a parent.
But then this rphy doesn't really exist (as it's the SAS host itself), 
so we would either need to modify libsas to convert the SAS host into 
being able to serve as a port/phy, or add a 'fake' rphy for the SAS 
host. And in either case it would be a bigger surgery.

I'd rather prefer to add checks to libsas to figure out if a given SCSI 
device is a SAS device or a the SCSI host device; John Garry did some 
patches here to libsas.

But anyway, I would first want to concentrate on the API _how_ reserved 
tags should be allocated, and once we have that we can work on porting 
it to more complex things like libsas.

Cheers,

Hannes
diff mbox series

Patch

diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index f69b77cbf538..a539fa2fb221 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -290,6 +290,14 @@  int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
 	if (error)
 		goto out_del_dev;
 
+	if (sht->alloc_host_sdev) {
+		shost->shost_sdev = scsi_get_host_dev(shost);
+		if (!shost->shost_sdev) {
+			error = -ENOMEM;
+			goto out_del_dev;
+		}
+	}
+
 	scsi_proc_host_add(shost);
 	scsi_autopm_put_host(shost);
 	return error;
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 328c0e79dfe7..e2910aa02a65 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -1139,6 +1139,12 @@  static int scsi_probe_and_add_lun(struct scsi_target *starget,
 	if (!sdev)
 		goto out;
 
+	if (scsi_device_is_host_dev(sdev)) {
+		if (bflagsp)
+			*bflagsp = BLIST_NOLUN;
+		return SCSI_SCAN_LUN_PRESENT;
+	}
+
 	result = kmalloc(result_len, GFP_KERNEL);
 	if (!result)
 		goto out_free_sdev;
@@ -1755,6 +1761,9 @@  static void scsi_sysfs_add_devices(struct Scsi_Host *shost)
 		/* If device is already visible, skip adding it to sysfs */
 		if (sdev->is_visible)
 			continue;
+		/* Host devices should never be visible in sysfs */
+		if (scsi_device_is_host_dev(sdev))
+			continue;
 		if (!scsi_host_scan_allowed(shost) ||
 		    scsi_sysfs_add_sdev(sdev) != 0)
 			__scsi_remove_device(sdev);
@@ -1919,12 +1928,16 @@  EXPORT_SYMBOL(scsi_scan_host);
 
 void scsi_forget_host(struct Scsi_Host *shost)
 {
-	struct scsi_device *sdev;
+	struct scsi_device *sdev, *host_sdev = NULL;
 	unsigned long flags;
 
  restart:
 	spin_lock_irqsave(shost->host_lock, flags);
 	list_for_each_entry(sdev, &shost->__devices, siblings) {
+		if (scsi_device_is_host_dev(sdev)) {
+			host_sdev = sdev;
+			continue;
+		}
 		if (sdev->sdev_state == SDEV_DEL)
 			continue;
 		spin_unlock_irqrestore(shost->host_lock, flags);
@@ -1932,5 +1945,57 @@  void scsi_forget_host(struct Scsi_Host *shost)
 		goto restart;
 	}
 	spin_unlock_irqrestore(shost->host_lock, flags);
+	/* Remove host device last, might be needed to send commands */
+	if (host_sdev)
+		__scsi_remove_device(host_sdev);
 }
 
+/**
+ * scsi_get_host_dev - Create a virtual scsi_device to the host adapter
+ * @shost: Host that needs a scsi_device
+ *
+ * Lock status: None assumed.
+ *
+ * Returns:     The scsi_device or NULL
+ *
+ * Notes:
+ *	Attach a single scsi_device to the Scsi_Host. The primary aim
+ *	for this device is to serve as a container from which valid
+ *	scsi commands can be allocated from. Each scsi command will carry
+ *	an unused/free command tag, which then can be used by the LLDD to
+ *	send internal or passthrough commands without having to find a
+ *	valid command tag internally.
+ */
+struct scsi_device *scsi_get_host_dev(struct Scsi_Host *shost)
+{
+	struct scsi_device *sdev = NULL;
+	struct scsi_target *starget;
+
+	mutex_lock(&shost->scan_mutex);
+	if (!scsi_host_scan_allowed(shost))
+		goto out;
+	starget = scsi_alloc_target(&shost->shost_gendev, 0,
+				    shost->max_id);
+	if (!starget)
+		goto out;
+
+	sdev = scsi_alloc_sdev(starget, 0, NULL);
+	if (sdev)
+		sdev->borken = 0;
+	else
+		scsi_target_reap(starget);
+	put_device(&starget->dev);
+ out:
+	mutex_unlock(&shost->scan_mutex);
+	return sdev;
+}
+EXPORT_SYMBOL(scsi_get_host_dev);
+
+/*
+ * Test if a given device is a SCSI host device
+ */
+bool scsi_device_is_host_dev(struct scsi_device *sdev)
+{
+	return sdev == sdev->host->shost_sdev;
+}
+EXPORT_SYMBOL_GPL(scsi_device_is_host_dev);
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 61839773cc72..a6e0a70ca6f5 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -500,7 +500,8 @@  static void scsi_device_dev_release_usercontext(struct work_struct *work)
 		kfree_rcu(vpd_pg80, rcu);
 	if (vpd_pg89)
 		kfree_rcu(vpd_pg89, rcu);
-	kfree(sdev->inquiry);
+	if (!scsi_device_is_host_dev(sdev))
+		kfree(sdev->inquiry);
 	kfree(sdev);
 
 	if (parent)
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index d1c6fc83b1e3..5d7204186831 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -604,7 +604,7 @@  static inline int scsi_device_busy(struct scsi_device *sdev)
 	return sbitmap_weight(&sdev->budget_map);
 }
 
-#define MODULE_ALIAS_SCSI_DEVICE(type) \
+#define MODULE_ALIAS_SCSI_DEVICE(type)			\
 	MODULE_ALIAS("scsi:t-" __stringify(type) "*")
 #define SCSI_DEVICE_MODALIAS_FMT "scsi:t-0x%02x"
 
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
index 72e1a347baa6..6f49a8940dc4 100644
--- a/include/scsi/scsi_host.h
+++ b/include/scsi/scsi_host.h
@@ -459,6 +459,9 @@  struct scsi_host_template {
 	/* True if the host uses host-wide tagspace */
 	unsigned host_tagset:1;
 
+	/* True if a host sdev should be allocated */
+	unsigned alloc_host_sdev:1;
+
 	/*
 	 * Countdown for host blocking with no commands outstanding.
 	 */
@@ -704,6 +707,12 @@  struct Scsi_Host {
 	 */
 	struct device *dma_dev;
 
+	/*
+	 * Points to a virtual SCSI device used for sending
+	 * internal commands to the HBA.
+	 */
+	struct scsi_device *shost_sdev;
+
 	/*
 	 * We should ensure that this is aligned, both for better performance
 	 * and also because some compilers (m68k) don't automatically force
@@ -793,6 +802,18 @@  void scsi_host_busy_iter(struct Scsi_Host *,
 
 struct class_container;
 
+/*
+ * These functions are used to allocate and test a pseudo device
+ * which will refer to the host adapter itself rather than any
+ * physical device.  The device will be deallocated together with
+ * all other scsi devices, so there is no need to have a separate
+ * function to free it.
+ * This device will not show up in sysfs and won't be available
+ * from any high-level drivers.
+ */
+struct scsi_device *scsi_get_host_dev(struct Scsi_Host *);
+bool scsi_device_is_host_dev(struct scsi_device *sdev);
+
 /*
  * DIF defines the exchange of protection information between
  * initiator and SBC block device.