Re: [PATCH v3 03/23] ata: libata-scsi: link ata port and scsi device

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Sep 15, 2023 at 05:14:47PM +0900, Damien Le Moal wrote:
> There is no direct device ancestry defined between an ata_device and
> its scsi device which prevents the power management code from correctly
> ordering suspend and resume operations. Create such ancestry with the
> ata device as the parent to ensure that the scsi device (child) is
> suspended before the ata device and that resume handles the ata device
> before the scsi device.
> 
> The parent-child (supplier-consumer) relationship is established between
> the ata_port (parent) and the scsi device (child) with the function
> device_add_link(). The parent used is not the ata_device as the PM
> operations are defined per port and the status of all devices connected
> through that port is controlled from the port operations.
> 
> The device link is established with the new function
> ata_scsi_dev_alloc(). This function is used to define the ->slave_alloc
> callback of the scsi host template of most drivers.
> 
> Fixes: a19a93e4c6a9 ("scsi: core: pm: Rely on the device driver core for async power management")
> Cc: stable@xxxxxxxxxxxxxxx
> Signed-off-by: Damien Le Moal <dlemoal@xxxxxxxxxx>
> Reviewed-by: Hannes Reinecke <hare@xxxxxxx>
> ---
>  drivers/ata/ahci.h        |  1 +
>  drivers/ata/libata-scsi.c | 49 +++++++++++++++++++++++++++++++++++----
>  drivers/ata/libata.h      |  1 +
>  drivers/ata/pata_macio.c  |  1 +
>  drivers/ata/sata_mv.c     |  1 +
>  drivers/ata/sata_nv.c     |  2 ++
>  drivers/ata/sata_sil24.c  |  1 +
>  include/linux/libata.h    |  3 +++
>  8 files changed, 54 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
> index 4bae95b06ae3..72085756f4ba 100644
> --- a/drivers/ata/ahci.h
> +++ b/drivers/ata/ahci.h
> @@ -398,6 +398,7 @@ extern const struct attribute_group *ahci_sdev_groups[];
>  	.sdev_groups		= ahci_sdev_groups,			\
>  	.change_queue_depth     = ata_scsi_change_queue_depth,		\
>  	.tag_alloc_policy       = BLK_TAG_ALLOC_RR,             	\
> +	.slave_alloc		= ata_scsi_slave_alloc,			\
>  	.slave_configure        = ata_scsi_slave_config
>  
>  extern struct ata_port_operations ahci_ops;
> diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
> index d3f28b82c97b..eef76af1af90 100644
> --- a/drivers/ata/libata-scsi.c
> +++ b/drivers/ata/libata-scsi.c
> @@ -1089,6 +1089,46 @@ int ata_scsi_dev_config(struct scsi_device *sdev, struct ata_device *dev)
>  	return 0;
>  }
>  
> +int ata_scsi_dev_alloc(struct scsi_device *sdev, struct ata_port *ap)
> +{
> +	struct device_link *link;
> +
> +	ata_scsi_sdev_config(sdev);
> +
> +	/*
> +	 * Create a link from the ata_port device to the scsi device to ensure
> +	 * that PM does suspend/resume in the correct order: the scsi device is
> +	 * consumer (child) and the ata port the supplier (parent).
> +	 */
> +	link = device_link_add(&sdev->sdev_gendev, &ap->tdev,
> +			       DL_FLAG_STATELESS |
> +			       DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE);
> +	if (!link) {
> +		ata_port_err(ap, "Failed to create link to scsi device %s\n",
> +			     dev_name(&sdev->sdev_gendev));
> +		return -ENODEV;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + *	ata_scsi_slave_alloc - Early setup of SCSI device
> + *	@sdev: SCSI device to examine
> + *
> + *	This is called from scsi_alloc_sdev() when the scsi device
> + *	associated with an ATA device is scanned on a port.
> + *
> + *	LOCKING:
> + *	Defined by SCSI layer.  We don't really care.
> + */
> +
> +int ata_scsi_slave_alloc(struct scsi_device *sdev)
> +{
> +	return ata_scsi_dev_alloc(sdev, ata_shost_to_port(sdev->host));
> +}
> +EXPORT_SYMBOL_GPL(ata_scsi_slave_alloc);
> +
>  /**
>   *	ata_scsi_slave_config - Set SCSI device attributes
>   *	@sdev: SCSI device to examine
> @@ -1105,14 +1145,11 @@ int ata_scsi_slave_config(struct scsi_device *sdev)
>  {
>  	struct ata_port *ap = ata_shost_to_port(sdev->host);
>  	struct ata_device *dev = __ata_scsi_find_dev(ap, sdev);
> -	int rc = 0;
> -
> -	ata_scsi_sdev_config(sdev);
>  
>  	if (dev)
> -		rc = ata_scsi_dev_config(sdev, dev);
> +		return ata_scsi_dev_config(sdev, dev);
>  
> -	return rc;
> +	return 0;
>  }
>  EXPORT_SYMBOL_GPL(ata_scsi_slave_config);
>  
> @@ -1136,6 +1173,8 @@ void ata_scsi_slave_destroy(struct scsi_device *sdev)
>  	unsigned long flags;
>  	struct ata_device *dev;
>  
> +	device_link_remove(&sdev->sdev_gendev, &ap->tdev);
> +
>  	spin_lock_irqsave(ap->lock, flags);
>  	dev = __ata_scsi_find_dev(ap, sdev);
>  	if (dev && dev->sdev) {
> diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
> index 6e7d352803bd..079981e7156a 100644
> --- a/drivers/ata/libata.h
> +++ b/drivers/ata/libata.h
> @@ -111,6 +111,7 @@ extern struct ata_device *ata_scsi_find_dev(struct ata_port *ap,
>  extern int ata_scsi_add_hosts(struct ata_host *host,
>  			      const struct scsi_host_template *sht);
>  extern void ata_scsi_scan_host(struct ata_port *ap, int sync);
> +extern int ata_scsi_dev_alloc(struct scsi_device *sdev, struct ata_port *ap);
>  extern int ata_scsi_offline_dev(struct ata_device *dev);
>  extern bool ata_scsi_sense_is_valid(u8 sk, u8 asc, u8 ascq);
>  extern void ata_scsi_set_sense(struct ata_device *dev,
> diff --git a/drivers/ata/pata_macio.c b/drivers/ata/pata_macio.c
> index 17f6ccee53c7..32968b4cf8e4 100644
> --- a/drivers/ata/pata_macio.c
> +++ b/drivers/ata/pata_macio.c
> @@ -918,6 +918,7 @@ static const struct scsi_host_template pata_macio_sht = {
>  	 * use 64K minus 256
>  	 */
>  	.max_segment_size	= MAX_DBDMA_SEG,
> +	.slave_alloc		= ata_scsi_slave_alloc,
>  	.slave_configure	= pata_macio_slave_config,
>  	.sdev_groups		= ata_common_sdev_groups,
>  	.can_queue		= ATA_DEF_QUEUE,
> diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
> index d105db5c7d81..353ac7b2f14a 100644
> --- a/drivers/ata/sata_mv.c
> +++ b/drivers/ata/sata_mv.c
> @@ -673,6 +673,7 @@ static const struct scsi_host_template mv6_sht = {
>  	.sdev_groups		= ata_ncq_sdev_groups,
>  	.change_queue_depth	= ata_scsi_change_queue_depth,
>  	.tag_alloc_policy	= BLK_TAG_ALLOC_RR,
> +	.slave_alloc		= ata_scsi_slave_alloc,

It seems wrong to add .slave_alloc to all different ata_port_operations structs.
The .slave_configure is added on all different ata_port_operations structs,
because the callback can be different for different drivers.

However, adding the device link is done in .ata_scsi_slave_alloc,
removing the device link is done in .ata_scsi_slave_destroy.

Thus, I suggest that we only add the
.slave_alloc = ata_scsi_slave_alloc,

to __ATA_BASE_SHT() in libata.h, which is currently the only
place which has .slave_destroy defined.

All the other port operations inherit from __ATA_BASE_SHT().


>  	.slave_configure	= ata_scsi_slave_config
>  };
>  
> diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c
> index 0a0cee755bde..5428dc2ec5e3 100644
> --- a/drivers/ata/sata_nv.c
> +++ b/drivers/ata/sata_nv.c
> @@ -380,6 +380,7 @@ static const struct scsi_host_template nv_adma_sht = {
>  	.can_queue		= NV_ADMA_MAX_CPBS,
>  	.sg_tablesize		= NV_ADMA_SGTBL_TOTAL_LEN,
>  	.dma_boundary		= NV_ADMA_DMA_BOUNDARY,
> +	.slave_alloc		= ata_scsi_slave_alloc,
>  	.slave_configure	= nv_adma_slave_config,
>  	.sdev_groups		= ata_ncq_sdev_groups,
>  	.change_queue_depth     = ata_scsi_change_queue_depth,
> @@ -391,6 +392,7 @@ static const struct scsi_host_template nv_swncq_sht = {
>  	.can_queue		= ATA_MAX_QUEUE - 1,
>  	.sg_tablesize		= LIBATA_MAX_PRD,
>  	.dma_boundary		= ATA_DMA_BOUNDARY,
> +	.slave_alloc		= ata_scsi_slave_alloc,
>  	.slave_configure	= nv_swncq_slave_config,
>  	.sdev_groups		= ata_ncq_sdev_groups,
>  	.change_queue_depth     = ata_scsi_change_queue_depth,
> diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c
> index 142e70bfc498..e0b1b3625031 100644
> --- a/drivers/ata/sata_sil24.c
> +++ b/drivers/ata/sata_sil24.c
> @@ -381,6 +381,7 @@ static const struct scsi_host_template sil24_sht = {
>  	.tag_alloc_policy	= BLK_TAG_ALLOC_FIFO,
>  	.sdev_groups		= ata_ncq_sdev_groups,
>  	.change_queue_depth	= ata_scsi_change_queue_depth,
> +	.slave_alloc		= ata_scsi_slave_alloc,
>  	.slave_configure	= ata_scsi_slave_config
>  };
>  
> diff --git a/include/linux/libata.h b/include/linux/libata.h
> index 52d58b13e5ee..c8cfea386c16 100644
> --- a/include/linux/libata.h
> +++ b/include/linux/libata.h
> @@ -1144,6 +1144,7 @@ extern int ata_std_bios_param(struct scsi_device *sdev,
>  			      struct block_device *bdev,
>  			      sector_t capacity, int geom[]);
>  extern void ata_scsi_unlock_native_capacity(struct scsi_device *sdev);
> +extern int ata_scsi_slave_alloc(struct scsi_device *sdev);
>  extern int ata_scsi_slave_config(struct scsi_device *sdev);
>  extern void ata_scsi_slave_destroy(struct scsi_device *sdev);
>  extern int ata_scsi_change_queue_depth(struct scsi_device *sdev,
> @@ -1401,12 +1402,14 @@ extern const struct attribute_group *ata_common_sdev_groups[];
>  	__ATA_BASE_SHT(drv_name),				\
>  	.can_queue		= ATA_DEF_QUEUE,		\
>  	.tag_alloc_policy	= BLK_TAG_ALLOC_RR,		\
> +	.slave_alloc		= ata_scsi_slave_alloc,		\
>  	.slave_configure	= ata_scsi_slave_config
>  
>  #define ATA_SUBBASE_SHT_QD(drv_name, drv_qd)			\
>  	__ATA_BASE_SHT(drv_name),				\
>  	.can_queue		= drv_qd,			\
>  	.tag_alloc_policy	= BLK_TAG_ALLOC_RR,		\
> +	.slave_alloc		= ata_scsi_slave_alloc,		\
>  	.slave_configure	= ata_scsi_slave_config
>  
>  #define ATA_BASE_SHT(drv_name)					\
> -- 
> 2.41.0
> 



[Index of Archives]     [Linux Filesystems]     [Linux SCSI]     [Linux RAID]     [Git]     [Kernel Newbies]     [Linux Newbie]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Samba]     [Device Mapper]

  Powered by Linux