diff options
Diffstat (limited to 'drivers/scsi/libata-scsi.c')
-rw-r--r-- | drivers/scsi/libata-scsi.c | 124 |
1 files changed, 117 insertions, 7 deletions
diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c index 2915bca691e8..7ced41ecde86 100644 --- a/drivers/scsi/libata-scsi.c +++ b/drivers/scsi/libata-scsi.c | |||
@@ -397,20 +397,129 @@ void ata_dump_status(unsigned id, struct ata_taskfile *tf) | |||
397 | } | 397 | } |
398 | } | 398 | } |
399 | 399 | ||
400 | int ata_scsi_device_resume(struct scsi_device *sdev) | 400 | /** |
401 | * ata_scsi_device_suspend - suspend ATA device associated with sdev | ||
402 | * @sdev: the SCSI device to suspend | ||
403 | * @state: target power management state | ||
404 | * | ||
405 | * Request suspend EH action on the ATA device associated with | ||
406 | * @sdev and wait for the operation to complete. | ||
407 | * | ||
408 | * LOCKING: | ||
409 | * Kernel thread context (may sleep). | ||
410 | * | ||
411 | * RETURNS: | ||
412 | * 0 on success, -errno otherwise. | ||
413 | */ | ||
414 | int ata_scsi_device_suspend(struct scsi_device *sdev, pm_message_t state) | ||
401 | { | 415 | { |
402 | struct ata_port *ap = ata_shost_to_port(sdev->host); | 416 | struct ata_port *ap = ata_shost_to_port(sdev->host); |
403 | struct ata_device *dev = __ata_scsi_find_dev(ap, sdev); | 417 | struct ata_device *dev = ata_scsi_find_dev(ap, sdev); |
418 | unsigned long flags; | ||
419 | unsigned int action; | ||
420 | int rc = 0; | ||
421 | |||
422 | if (!dev) | ||
423 | goto out; | ||
424 | |||
425 | spin_lock_irqsave(ap->lock, flags); | ||
426 | |||
427 | /* wait for the previous resume to complete */ | ||
428 | while (dev->flags & ATA_DFLAG_SUSPENDED) { | ||
429 | spin_unlock_irqrestore(ap->lock, flags); | ||
430 | ata_port_wait_eh(ap); | ||
431 | spin_lock_irqsave(ap->lock, flags); | ||
432 | } | ||
433 | |||
434 | /* if @sdev is already detached, nothing to do */ | ||
435 | if (sdev->sdev_state == SDEV_OFFLINE || | ||
436 | sdev->sdev_state == SDEV_CANCEL || sdev->sdev_state == SDEV_DEL) | ||
437 | goto out_unlock; | ||
438 | |||
439 | /* request suspend */ | ||
440 | action = ATA_EH_SUSPEND; | ||
441 | if (state.event != PM_EVENT_SUSPEND) | ||
442 | action |= ATA_EH_PM_FREEZE; | ||
443 | ap->eh_info.dev_action[dev->devno] |= action; | ||
444 | ap->eh_info.flags |= ATA_EHI_QUIET; | ||
445 | ata_port_schedule_eh(ap); | ||
446 | |||
447 | spin_unlock_irqrestore(ap->lock, flags); | ||
448 | |||
449 | /* wait for EH to do the job */ | ||
450 | ata_port_wait_eh(ap); | ||
451 | |||
452 | spin_lock_irqsave(ap->lock, flags); | ||
453 | |||
454 | /* If @sdev is still attached but the associated ATA device | ||
455 | * isn't suspended, the operation failed. | ||
456 | */ | ||
457 | if (sdev->sdev_state != SDEV_OFFLINE && | ||
458 | sdev->sdev_state != SDEV_CANCEL && sdev->sdev_state != SDEV_DEL && | ||
459 | !(dev->flags & ATA_DFLAG_SUSPENDED)) | ||
460 | rc = -EIO; | ||
404 | 461 | ||
405 | return ata_device_resume(dev); | 462 | out_unlock: |
463 | spin_unlock_irqrestore(ap->lock, flags); | ||
464 | out: | ||
465 | if (rc == 0) | ||
466 | sdev->sdev_gendev.power.power_state = state; | ||
467 | return rc; | ||
406 | } | 468 | } |
407 | 469 | ||
408 | int ata_scsi_device_suspend(struct scsi_device *sdev, pm_message_t state) | 470 | /** |
471 | * ata_scsi_device_resume - resume ATA device associated with sdev | ||
472 | * @sdev: the SCSI device to resume | ||
473 | * | ||
474 | * Request resume EH action on the ATA device associated with | ||
475 | * @sdev and return immediately. This enables parallel | ||
476 | * wakeup/spinup of devices. | ||
477 | * | ||
478 | * LOCKING: | ||
479 | * Kernel thread context (may sleep). | ||
480 | * | ||
481 | * RETURNS: | ||
482 | * 0. | ||
483 | */ | ||
484 | int ata_scsi_device_resume(struct scsi_device *sdev) | ||
409 | { | 485 | { |
410 | struct ata_port *ap = ata_shost_to_port(sdev->host); | 486 | struct ata_port *ap = ata_shost_to_port(sdev->host); |
411 | struct ata_device *dev = __ata_scsi_find_dev(ap, sdev); | 487 | struct ata_device *dev = ata_scsi_find_dev(ap, sdev); |
488 | struct ata_eh_info *ehi = &ap->eh_info; | ||
489 | unsigned long flags; | ||
490 | unsigned int action; | ||
491 | |||
492 | if (!dev) | ||
493 | goto out; | ||
494 | |||
495 | spin_lock_irqsave(ap->lock, flags); | ||
496 | |||
497 | /* if @sdev is already detached, nothing to do */ | ||
498 | if (sdev->sdev_state == SDEV_OFFLINE || | ||
499 | sdev->sdev_state == SDEV_CANCEL || sdev->sdev_state == SDEV_DEL) | ||
500 | goto out_unlock; | ||
412 | 501 | ||
413 | return ata_device_suspend(dev, state); | 502 | /* request resume */ |
503 | action = ATA_EH_RESUME; | ||
504 | if (sdev->sdev_gendev.power.power_state.event == PM_EVENT_SUSPEND) | ||
505 | __ata_ehi_hotplugged(ehi); | ||
506 | else | ||
507 | action |= ATA_EH_PM_FREEZE | ATA_EH_SOFTRESET; | ||
508 | ehi->dev_action[dev->devno] |= action; | ||
509 | |||
510 | /* We don't want autopsy and verbose EH messages. Disable | ||
511 | * those if we're the only device on this link. | ||
512 | */ | ||
513 | if (ata_port_max_devices(ap) == 1) | ||
514 | ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET; | ||
515 | |||
516 | ata_port_schedule_eh(ap); | ||
517 | |||
518 | out_unlock: | ||
519 | spin_unlock_irqrestore(ap->lock, flags); | ||
520 | out: | ||
521 | sdev->sdev_gendev.power.power_state = PMSG_ON; | ||
522 | return 0; | ||
414 | } | 523 | } |
415 | 524 | ||
416 | /** | 525 | /** |
@@ -2930,7 +3039,7 @@ void ata_scsi_hotplug(void *data) | |||
2930 | struct ata_port *ap = data; | 3039 | struct ata_port *ap = data; |
2931 | int i; | 3040 | int i; |
2932 | 3041 | ||
2933 | if (ap->flags & ATA_FLAG_UNLOADING) { | 3042 | if (ap->pflags & ATA_PFLAG_UNLOADING) { |
2934 | DPRINTK("ENTER/EXIT - unloading\n"); | 3043 | DPRINTK("ENTER/EXIT - unloading\n"); |
2935 | return; | 3044 | return; |
2936 | } | 3045 | } |
@@ -3011,6 +3120,7 @@ static int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel, | |||
3011 | if (dev) { | 3120 | if (dev) { |
3012 | ap->eh_info.probe_mask |= 1 << dev->devno; | 3121 | ap->eh_info.probe_mask |= 1 << dev->devno; |
3013 | ap->eh_info.action |= ATA_EH_SOFTRESET; | 3122 | ap->eh_info.action |= ATA_EH_SOFTRESET; |
3123 | ap->eh_info.flags |= ATA_EHI_RESUME_LINK; | ||
3014 | } else | 3124 | } else |
3015 | rc = -EINVAL; | 3125 | rc = -EINVAL; |
3016 | } | 3126 | } |