Skip to content

Commit

Permalink
ata: libata-scsi: link ata port and scsi device
Browse files Browse the repository at this point in the history
commit fb99ef1 upstream.

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_slave_alloc(), and this function is used to define the
->slave_alloc callback of the scsi host template of all ata drivers.

Fixes: a19a93e ("scsi: core: pm: Rely on the device driver core for async power management")
Cc: [email protected]
Signed-off-by: Damien Le Moal <[email protected]>
Reviewed-by: Hannes Reinecke <[email protected]>
Reviewed-by: Niklas Cassel <[email protected]>
Tested-by: Geert Uytterhoeven <[email protected]>
Reviewed-by: Martin K. Petersen <[email protected]>
Reviewed-by: John Garry <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
damien-lemoal authored and gregkh committed Oct 6, 2023
1 parent 2447c5b commit b1a0761
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 5 deletions.
45 changes: 40 additions & 5 deletions drivers/ata/libata-scsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -1139,6 +1139,42 @@ int ata_scsi_dev_config(struct scsi_device *sdev, struct ata_device *dev)
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)
{
struct ata_port *ap = ata_shost_to_port(sdev->host);
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;
}
EXPORT_SYMBOL_GPL(ata_scsi_slave_alloc);

/**
* ata_scsi_slave_config - Set SCSI device attributes
* @sdev: SCSI device to examine
Expand All @@ -1155,14 +1191,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);

Expand All @@ -1189,6 +1222,8 @@ void ata_scsi_slave_destroy(struct scsi_device *sdev)
if (!ap->ops->error_handler)
return;

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) {
Expand Down
2 changes: 2 additions & 0 deletions include/linux/libata.h
Original file line number Diff line number Diff line change
Expand Up @@ -1155,6 +1155,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,
Expand Down Expand Up @@ -1408,6 +1409,7 @@ extern const struct attribute_group *ata_common_sdev_groups[];
.this_id = ATA_SHT_THIS_ID, \
.emulated = ATA_SHT_EMULATED, \
.proc_name = drv_name, \
.slave_alloc = ata_scsi_slave_alloc, \
.slave_destroy = ata_scsi_slave_destroy, \
.bios_param = ata_std_bios_param, \
.unlock_native_capacity = ata_scsi_unlock_native_capacity,\
Expand Down

0 comments on commit b1a0761

Please sign in to comment.