public inbox for linux-scsi@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2] scsi: core: Don't free dev_name() and hold refcount for parent manually
@ 2026-01-19 14:23 Tzung-Bi Shih
  2026-01-19 15:54 ` Greg KH
  0 siblings, 1 reply; 2+ messages in thread
From: Tzung-Bi Shih @ 2026-01-19 14:23 UTC (permalink / raw)
  To: James E.J. Bottomley, Martin K. Petersen, Greg KH
  Cc: linux-scsi, linux-kernel, tzungbi

Drivers shouldn't need to free the device name as the driver core
handles that automatically.  All it needs to do is dropping the
reference count of the device.

An intuitive fix is:
>  @@ -373,7 +373,7 @@ static void scsi_host_dev_release(struct device *dev)
>   		 * name as well as the proc dir structure are leaked.
>   		 */
>   		scsi_proc_hostdir_rm(shost->hostt);
>  -		kfree(dev_name(&shost->shost_dev));
>  +		put_device(&shost->shost_dev);
>   	}
>
>   	kfree(shost->shost_data);

However, it doesn't work well.  It generates an underflow on the
refcount of `&shost->shost_gendev` on errors.  This is what happens:
- The refcount of `shost_gendev` and `shost_dev` are both 1 after
  device_initialize().
- On the error handling path (i.e., "fail" label), it drops refcount of
  `shost_gendev` to 0.
- The .release() callback of `shost_gendev` (i.e.,
  scsi_host_dev_release()) is called, and it drops refcount of
  `shost_dev` to 0 due to the above change.
- The .release() callback of `shost_dev` (i.e., scsi_host_cls_release())
  is called, and it drops refcount of `shost_gendev` again.  The
  underflow happens.

A subsequent fix is:
>  @@ -55,7 +55,6 @@ static DEFINE_IDA(host_index_ida);
>
>   static void scsi_host_cls_release(struct device *dev)
>   {
>  -	put_device(&class_to_shost(dev)->shost_gendev);
>   }
>
>   static struct class shost_class = {

However, it introduces unbalance refcount of `shost_gendev` as
scsi_add_host_with_dma() increases it but scsi_host_cls_release()
doesn't drop it with the above change.

Drivers shouldn't need to hold a reference count to its parent device as
the driver core automatically holds one in device_add().  Eliminate the
case for holding refcount of `shost_gendev` to fix the unbalance
refcount.  Eliminate yet another case for `shost_gendev.parent` as well.

Fixes: b49493f99690 ("Fix a memory leak in scsi_host_dev_release()")
Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
---
v2:
- Provide more context in the commit message.
- Leave a comment in the empty .release() function.

v1: https://lore.kernel.org/r/20260117193221.152540-1-tzungbi@kernel.org

 drivers/scsi/hosts.c | 20 +++++++++-----------
 1 file changed, 9 insertions(+), 11 deletions(-)

diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index 1b3fbd328277..7c39e228d72b 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -55,7 +55,10 @@ static DEFINE_IDA(host_index_ida);
 
 static void scsi_host_cls_release(struct device *dev)
 {
-	put_device(&class_to_shost(dev)->shost_gendev);
+	/*
+	 * Keep an empty release() to prevent device_release() from emitting a
+	 * warning.
+	 */
 }
 
 static struct class shost_class = {
@@ -279,11 +282,9 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
 		goto out_disable_runtime_pm;
 
 	scsi_host_set_state(shost, SHOST_RUNNING);
-	get_device(shost->shost_gendev.parent);
 
 	device_enable_async_suspend(&shost->shost_dev);
 
-	get_device(&shost->shost_gendev);
 	error = device_add(&shost->shost_dev);
 	if (error)
 		goto out_del_gendev;
@@ -352,7 +353,6 @@ EXPORT_SYMBOL(scsi_add_host_with_dma);
 static void scsi_host_dev_release(struct device *dev)
 {
 	struct Scsi_Host *shost = dev_to_shost(dev);
-	struct device *parent = dev->parent;
 
 	/* Wait for functions invoked through call_rcu(&scmd->rcu, ...) */
 	rcu_barrier();
@@ -366,22 +366,20 @@ static void scsi_host_dev_release(struct device *dev)
 
 	if (shost->shost_state == SHOST_CREATED) {
 		/*
-		 * Free the shost_dev device name and remove the proc host dir
+		 * Drop the reference to shost_dev and remove the proc host dir
 		 * here if scsi_host_{alloc,put}() have been called but neither
-		 * scsi_host_add() nor scsi_remove_host() has been called.
+		 * scsi_add_host() nor scsi_remove_host() has been called.
 		 * This avoids that the memory allocated for the shost_dev
 		 * name as well as the proc dir structure are leaked.
 		 */
 		scsi_proc_hostdir_rm(shost->hostt);
-		kfree(dev_name(&shost->shost_dev));
+		put_device(&shost->shost_dev);
 	}
 
 	kfree(shost->shost_data);
 
 	ida_free(&host_index_ida, shost->host_no);
 
-	if (shost->shost_state != SHOST_CREATED)
-		put_device(parent);
 	kfree(shost);
 }
 
@@ -550,8 +548,8 @@ struct Scsi_Host *scsi_host_alloc(const struct scsi_host_template *sht, int priv
  fail:
 	/*
 	 * Host state is still SHOST_CREATED and that is enough to release
-	 * ->shost_gendev. scsi_host_dev_release() will free
-	 * dev_name(&shost->shost_dev).
+	 * ->shost_gendev. scsi_host_dev_release() will
+	 * put_device(&shost->shost_dev).
 	 */
 	put_device(&shost->shost_gendev);
 
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-01-19 15:54 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-19 14:23 [PATCH v2] scsi: core: Don't free dev_name() and hold refcount for parent manually Tzung-Bi Shih
2026-01-19 15:54 ` Greg KH

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox