linux/drivers/scsi/scsi_pm.c
<<
>>
Prefs
   1/*
   2 *      scsi_pm.c       Copyright (C) 2010 Alan Stern
   3 *
   4 *      SCSI dynamic Power Management
   5 *              Initial version: Alan Stern <stern@rowland.harvard.edu>
   6 */
   7
   8#include <linux/pm_runtime.h>
   9#include <linux/export.h>
  10#include <linux/async.h>
  11
  12#include <scsi/scsi.h>
  13#include <scsi/scsi_device.h>
  14#include <scsi/scsi_driver.h>
  15#include <scsi/scsi_host.h>
  16
  17#include "scsi_priv.h"
  18
  19static int scsi_dev_type_suspend(struct device *dev, int (*cb)(struct device *))
  20{
  21        int err;
  22
  23        err = scsi_device_quiesce(to_scsi_device(dev));
  24        if (err == 0) {
  25                if (cb) {
  26                        err = cb(dev);
  27                        if (err)
  28                                scsi_device_resume(to_scsi_device(dev));
  29                }
  30        }
  31        dev_dbg(dev, "scsi suspend: %d\n", err);
  32        return err;
  33}
  34
  35static int scsi_dev_type_resume(struct device *dev, int (*cb)(struct device *))
  36{
  37        int err = 0;
  38
  39        if (cb)
  40                err = cb(dev);
  41        scsi_device_resume(to_scsi_device(dev));
  42        dev_dbg(dev, "scsi resume: %d\n", err);
  43        return err;
  44}
  45
  46#ifdef CONFIG_PM_SLEEP
  47
  48static int
  49scsi_bus_suspend_common(struct device *dev, int (*cb)(struct device *))
  50{
  51        int err = 0;
  52
  53        if (scsi_is_sdev_device(dev)) {
  54                /*
  55                 * All the high-level SCSI drivers that implement runtime
  56                 * PM treat runtime suspend, system suspend, and system
  57                 * hibernate identically.
  58                 */
  59                if (pm_runtime_suspended(dev))
  60                        return 0;
  61
  62                err = scsi_dev_type_suspend(dev, cb);
  63        }
  64
  65        return err;
  66}
  67
  68static int
  69scsi_bus_resume_common(struct device *dev, int (*cb)(struct device *))
  70{
  71        int err = 0;
  72
  73        if (scsi_is_sdev_device(dev))
  74                err = scsi_dev_type_resume(dev, cb);
  75
  76        if (err == 0) {
  77                pm_runtime_disable(dev);
  78                pm_runtime_set_active(dev);
  79                pm_runtime_enable(dev);
  80        }
  81        return err;
  82}
  83
  84static int scsi_bus_prepare(struct device *dev)
  85{
  86        if (scsi_is_sdev_device(dev)) {
  87                /* sd probing uses async_schedule.  Wait until it finishes. */
  88                async_synchronize_full_domain(&scsi_sd_probe_domain);
  89
  90        } else if (scsi_is_host_device(dev)) {
  91                /* Wait until async scanning is finished */
  92                scsi_complete_async_scans();
  93        }
  94        return 0;
  95}
  96
  97static int scsi_bus_suspend(struct device *dev)
  98{
  99        const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 100        return scsi_bus_suspend_common(dev, pm ? pm->suspend : NULL);
 101}
 102
 103static int scsi_bus_resume(struct device *dev)
 104{
 105        const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 106        return scsi_bus_resume_common(dev, pm ? pm->resume : NULL);
 107}
 108
 109static int scsi_bus_freeze(struct device *dev)
 110{
 111        const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 112        return scsi_bus_suspend_common(dev, pm ? pm->freeze : NULL);
 113}
 114
 115static int scsi_bus_thaw(struct device *dev)
 116{
 117        const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 118        return scsi_bus_resume_common(dev, pm ? pm->thaw : NULL);
 119}
 120
 121static int scsi_bus_poweroff(struct device *dev)
 122{
 123        const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 124        return scsi_bus_suspend_common(dev, pm ? pm->poweroff : NULL);
 125}
 126
 127static int scsi_bus_restore(struct device *dev)
 128{
 129        const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 130        return scsi_bus_resume_common(dev, pm ? pm->restore : NULL);
 131}
 132
 133#else /* CONFIG_PM_SLEEP */
 134
 135#define scsi_bus_prepare                NULL
 136#define scsi_bus_suspend                NULL
 137#define scsi_bus_resume                 NULL
 138#define scsi_bus_freeze                 NULL
 139#define scsi_bus_thaw                   NULL
 140#define scsi_bus_poweroff               NULL
 141#define scsi_bus_restore                NULL
 142
 143#endif /* CONFIG_PM_SLEEP */
 144
 145#ifdef CONFIG_PM_RUNTIME
 146
 147static int sdev_blk_runtime_suspend(struct scsi_device *sdev,
 148                                        int (*cb)(struct device *))
 149{
 150        int err;
 151
 152        err = blk_pre_runtime_suspend(sdev->request_queue);
 153        if (err)
 154                return err;
 155        if (cb)
 156                err = cb(&sdev->sdev_gendev);
 157        blk_post_runtime_suspend(sdev->request_queue, err);
 158
 159        return err;
 160}
 161
 162static int sdev_runtime_suspend(struct device *dev)
 163{
 164        const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 165        int (*cb)(struct device *) = pm ? pm->runtime_suspend : NULL;
 166        struct scsi_device *sdev = to_scsi_device(dev);
 167        int err;
 168
 169        if (sdev->request_queue->dev)
 170                return sdev_blk_runtime_suspend(sdev, cb);
 171
 172        err = scsi_dev_type_suspend(dev, cb);
 173        if (err == -EAGAIN)
 174                pm_schedule_suspend(dev, jiffies_to_msecs(
 175                                        round_jiffies_up_relative(HZ/10)));
 176        return err;
 177}
 178
 179static int scsi_runtime_suspend(struct device *dev)
 180{
 181        int err = 0;
 182
 183        dev_dbg(dev, "scsi_runtime_suspend\n");
 184        if (scsi_is_sdev_device(dev))
 185                err = sdev_runtime_suspend(dev);
 186
 187        /* Insert hooks here for targets, hosts, and transport classes */
 188
 189        return err;
 190}
 191
 192static int sdev_blk_runtime_resume(struct scsi_device *sdev,
 193                                        int (*cb)(struct device *))
 194{
 195        int err = 0;
 196
 197        blk_pre_runtime_resume(sdev->request_queue);
 198        if (cb)
 199                err = cb(&sdev->sdev_gendev);
 200        blk_post_runtime_resume(sdev->request_queue, err);
 201
 202        return err;
 203}
 204
 205static int sdev_runtime_resume(struct device *dev)
 206{
 207        struct scsi_device *sdev = to_scsi_device(dev);
 208        const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 209        int (*cb)(struct device *) = pm ? pm->runtime_resume : NULL;
 210
 211        if (sdev->request_queue->dev)
 212                return sdev_blk_runtime_resume(sdev, cb);
 213        else
 214                return scsi_dev_type_resume(dev, cb);
 215}
 216
 217static int scsi_runtime_resume(struct device *dev)
 218{
 219        int err = 0;
 220
 221        dev_dbg(dev, "scsi_runtime_resume\n");
 222        if (scsi_is_sdev_device(dev))
 223                err = sdev_runtime_resume(dev);
 224
 225        /* Insert hooks here for targets, hosts, and transport classes */
 226
 227        return err;
 228}
 229
 230static int scsi_runtime_idle(struct device *dev)
 231{
 232        dev_dbg(dev, "scsi_runtime_idle\n");
 233
 234        /* Insert hooks here for targets, hosts, and transport classes */
 235
 236        if (scsi_is_sdev_device(dev)) {
 237                struct scsi_device *sdev = to_scsi_device(dev);
 238
 239                if (sdev->request_queue->dev) {
 240                        pm_runtime_mark_last_busy(dev);
 241                        pm_runtime_autosuspend(dev);
 242                        return -EBUSY;
 243                }
 244        }
 245        return 0;
 246}
 247
 248int scsi_autopm_get_device(struct scsi_device *sdev)
 249{
 250        int     err;
 251
 252        err = pm_runtime_get_sync(&sdev->sdev_gendev);
 253        if (err < 0 && err !=-EACCES)
 254                pm_runtime_put_sync(&sdev->sdev_gendev);
 255        else
 256                err = 0;
 257        return err;
 258}
 259EXPORT_SYMBOL_GPL(scsi_autopm_get_device);
 260
 261void scsi_autopm_put_device(struct scsi_device *sdev)
 262{
 263        pm_runtime_put_sync(&sdev->sdev_gendev);
 264}
 265EXPORT_SYMBOL_GPL(scsi_autopm_put_device);
 266
 267void scsi_autopm_get_target(struct scsi_target *starget)
 268{
 269        pm_runtime_get_sync(&starget->dev);
 270}
 271
 272void scsi_autopm_put_target(struct scsi_target *starget)
 273{
 274        pm_runtime_put_sync(&starget->dev);
 275}
 276
 277int scsi_autopm_get_host(struct Scsi_Host *shost)
 278{
 279        int     err;
 280
 281        err = pm_runtime_get_sync(&shost->shost_gendev);
 282        if (err < 0 && err !=-EACCES)
 283                pm_runtime_put_sync(&shost->shost_gendev);
 284        else
 285                err = 0;
 286        return err;
 287}
 288
 289void scsi_autopm_put_host(struct Scsi_Host *shost)
 290{
 291        pm_runtime_put_sync(&shost->shost_gendev);
 292}
 293
 294#else
 295
 296#define scsi_runtime_suspend    NULL
 297#define scsi_runtime_resume     NULL
 298#define scsi_runtime_idle       NULL
 299
 300#endif /* CONFIG_PM_RUNTIME */
 301
 302const struct dev_pm_ops scsi_bus_pm_ops = {
 303        .prepare =              scsi_bus_prepare,
 304        .suspend =              scsi_bus_suspend,
 305        .resume =               scsi_bus_resume,
 306        .freeze =               scsi_bus_freeze,
 307        .thaw =                 scsi_bus_thaw,
 308        .poweroff =             scsi_bus_poweroff,
 309        .restore =              scsi_bus_restore,
 310        .runtime_suspend =      scsi_runtime_suspend,
 311        .runtime_resume =       scsi_runtime_resume,
 312        .runtime_idle =         scsi_runtime_idle,
 313};
 314
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.