Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 29494
b: refs/heads/master
c: 580b210
h: refs/heads/master
v: v3
  • Loading branch information
Tejun Heo committed May 31, 2006
1 parent 1ca413c commit 3ed787c
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 3 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 084fe639b81c4d418a2cf714acb0475e3713cb73
refs/heads/master: 580b2102327ab8444af5bde4e70b50d268a1d558
1 change: 1 addition & 0 deletions trunk/drivers/scsi/libata-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -5370,6 +5370,7 @@ static void ata_host_init(struct ata_port *ap, struct Scsi_Host *host,
ap->msg_enable = ATA_MSG_DRV;

INIT_WORK(&ap->port_task, NULL, NULL);
INIT_WORK(&ap->hotplug_task, ata_scsi_hotplug, ap);
INIT_LIST_HEAD(&ap->eh_done_q);
init_waitqueue_head(&ap->eh_wait_q);

Expand Down
6 changes: 5 additions & 1 deletion trunk/drivers/scsi/libata-eh.c
Original file line number Diff line number Diff line change
Expand Up @@ -287,9 +287,13 @@ void ata_scsi_error(struct Scsi_Host *host)
/* clean up */
spin_lock_irqsave(hs_lock, flags);

if (ap->flags & ATA_FLAG_SCSI_HOTPLUG)
queue_work(ata_aux_wq, &ap->hotplug_task);

if (ap->flags & ATA_FLAG_RECOVERED)
ata_port_printk(ap, KERN_INFO, "EH complete\n");
ap->flags &= ~ATA_FLAG_RECOVERED;

ap->flags &= ~(ATA_FLAG_SCSI_HOTPLUG | ATA_FLAG_RECOVERED);

/* tell wait_eh that we're done */
ap->flags &= ~ATA_FLAG_EH_IN_PROGRESS;
Expand Down
116 changes: 116 additions & 0 deletions trunk/drivers/scsi/libata-scsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -2786,3 +2786,119 @@ int ata_scsi_offline_dev(struct ata_device *dev)
}
return 0;
}

/**
* ata_scsi_remove_dev - remove attached SCSI device
* @dev: ATA device to remove attached SCSI device for
*
* This function is called from ata_eh_scsi_hotplug() and
* responsible for removing the SCSI device attached to @dev.
*
* LOCKING:
* Kernel thread context (may sleep).
*/
static void ata_scsi_remove_dev(struct ata_device *dev)
{
struct ata_port *ap = dev->ap;
struct scsi_device *sdev;
unsigned long flags;

/* Alas, we need to grab scan_mutex to ensure SCSI device
* state doesn't change underneath us and thus
* scsi_device_get() always succeeds. The mutex locking can
* be removed if there is __scsi_device_get() interface which
* increments reference counts regardless of device state.
*/
mutex_lock(&ap->host->scan_mutex);
spin_lock_irqsave(&ap->host_set->lock, flags);

/* clearing dev->sdev is protected by host_set lock */
sdev = dev->sdev;
dev->sdev = NULL;

if (sdev) {
/* If user initiated unplug races with us, sdev can go
* away underneath us after the host_set lock and
* scan_mutex are released. Hold onto it.
*/
if (scsi_device_get(sdev) == 0) {
/* The following ensures the attached sdev is
* offline on return from ata_scsi_offline_dev()
* regardless it wins or loses the race
* against this function.
*/
scsi_device_set_state(sdev, SDEV_OFFLINE);
} else {
WARN_ON(1);
sdev = NULL;
}
}

spin_unlock_irqrestore(&ap->host_set->lock, flags);
mutex_unlock(&ap->host->scan_mutex);

if (sdev) {
ata_dev_printk(dev, KERN_INFO, "detaching (SCSI %s)\n",
sdev->sdev_gendev.bus_id);

scsi_remove_device(sdev);
scsi_device_put(sdev);
}
}

/**
* ata_scsi_hotplug - SCSI part of hotplug
* @data: Pointer to ATA port to perform SCSI hotplug on
*
* Perform SCSI part of hotplug. It's executed from a separate
* workqueue after EH completes. This is necessary because SCSI
* hot plugging requires working EH and hot unplugging is
* synchronized with hot plugging with a mutex.
*
* LOCKING:
* Kernel thread context (may sleep).
*/
void ata_scsi_hotplug(void *data)
{
struct ata_port *ap = data;
int i;

if (ap->flags & ATA_FLAG_UNLOADING) {
DPRINTK("ENTER/EXIT - unloading\n");
return;
}

DPRINTK("ENTER\n");

/* unplug detached devices */
for (i = 0; i < ATA_MAX_DEVICES; i++) {
struct ata_device *dev = &ap->device[i];
unsigned long flags;

if (!(dev->flags & ATA_DFLAG_DETACHED))
continue;

spin_lock_irqsave(&ap->host_set->lock, flags);
dev->flags &= ~ATA_DFLAG_DETACHED;
spin_unlock_irqrestore(&ap->host_set->lock, flags);

ata_scsi_remove_dev(dev);
}

/* scan for new ones */
ata_scsi_scan_host(ap);

/* If we scanned while EH was in progress, scan would have
* failed silently. Requeue if there are enabled but
* unattached devices.
*/
for (i = 0; i < ATA_MAX_DEVICES; i++) {
struct ata_device *dev = &ap->device[i];
if (ata_dev_enabled(dev) && !dev->sdev) {
queue_delayed_work(ata_aux_wq, &ap->hotplug_task, HZ);
break;
}
}

DPRINTK("EXIT\n");
}
1 change: 1 addition & 0 deletions trunk/drivers/scsi/libata.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ extern struct scsi_transport_template ata_scsi_transport_template;

extern void ata_scsi_scan_host(struct ata_port *ap);
extern int ata_scsi_offline_dev(struct ata_device *dev);
extern void ata_scsi_hotplug(void *data);
extern unsigned int ata_scsiop_inq_std(struct ata_scsi_args *args, u8 *rbuf,
unsigned int buflen);

Expand Down
2 changes: 1 addition & 1 deletion trunk/include/linux/libata.h
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ struct ata_port {
struct ata_host_set *host_set;
struct device *dev;

struct work_struct port_task;
struct work_struct port_task, hotplug_task;

unsigned int hsm_task_state;

Expand Down

0 comments on commit 3ed787c

Please sign in to comment.