diff options
Diffstat (limited to 'drivers/scsi/libata-scsi.c')
-rw-r--r-- | drivers/scsi/libata-scsi.c | 137 |
1 files changed, 130 insertions, 7 deletions
diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c index 2915bca691e8..e92c31d698ff 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; | ||
412 | 491 | ||
413 | return ata_device_suspend(dev, state); | 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; | ||
501 | |||
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 | /** |
@@ -2244,6 +2353,19 @@ static void atapi_qc_complete(struct ata_queued_cmd *qc) | |||
2244 | ata_gen_ata_desc_sense(qc); | 2353 | ata_gen_ata_desc_sense(qc); |
2245 | } | 2354 | } |
2246 | 2355 | ||
2356 | /* SCSI EH automatically locks door if sdev->locked is | ||
2357 | * set. Sometimes door lock request continues to | ||
2358 | * fail, for example, when no media is present. This | ||
2359 | * creates a loop - SCSI EH issues door lock which | ||
2360 | * fails and gets invoked again to acquire sense data | ||
2361 | * for the failed command. | ||
2362 | * | ||
2363 | * If door lock fails, always clear sdev->locked to | ||
2364 | * avoid this infinite loop. | ||
2365 | */ | ||
2366 | if (qc->cdb[0] == ALLOW_MEDIUM_REMOVAL) | ||
2367 | qc->dev->sdev->locked = 0; | ||
2368 | |||
2247 | qc->scsicmd->result = SAM_STAT_CHECK_CONDITION; | 2369 | qc->scsicmd->result = SAM_STAT_CHECK_CONDITION; |
2248 | qc->scsidone(cmd); | 2370 | qc->scsidone(cmd); |
2249 | ata_qc_free(qc); | 2371 | ata_qc_free(qc); |
@@ -2930,7 +3052,7 @@ void ata_scsi_hotplug(void *data) | |||
2930 | struct ata_port *ap = data; | 3052 | struct ata_port *ap = data; |
2931 | int i; | 3053 | int i; |
2932 | 3054 | ||
2933 | if (ap->flags & ATA_FLAG_UNLOADING) { | 3055 | if (ap->pflags & ATA_PFLAG_UNLOADING) { |
2934 | DPRINTK("ENTER/EXIT - unloading\n"); | 3056 | DPRINTK("ENTER/EXIT - unloading\n"); |
2935 | return; | 3057 | return; |
2936 | } | 3058 | } |
@@ -3011,6 +3133,7 @@ static int ata_scsi_user_scan(struct Scsi_Host *shost, unsigned int channel, | |||
3011 | if (dev) { | 3133 | if (dev) { |
3012 | ap->eh_info.probe_mask |= 1 << dev->devno; | 3134 | ap->eh_info.probe_mask |= 1 << dev->devno; |
3013 | ap->eh_info.action |= ATA_EH_SOFTRESET; | 3135 | ap->eh_info.action |= ATA_EH_SOFTRESET; |
3136 | ap->eh_info.flags |= ATA_EHI_RESUME_LINK; | ||
3014 | } else | 3137 | } else |
3015 | rc = -EINVAL; | 3138 | rc = -EINVAL; |
3016 | } | 3139 | } |