linux/drivers/scsi/scsi_pm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 *      scsi_pm.c       Copyright (C) 2010 Alan Stern
   4 *
   5 *      SCSI dynamic Power Management
   6 *              Initial version: Alan Stern <stern@rowland.harvard.edu>
   7 */
   8
   9#include <linux/pm_runtime.h>
  10#include <linux/export.h>
  11#include <linux/async.h>
  12#include <linux/blk-pm.h>
  13
  14#include <scsi/scsi.h>
  15#include <scsi/scsi_device.h>
  16#include <scsi/scsi_driver.h>
  17#include <scsi/scsi_host.h>
  18
  19#include "scsi_priv.h"
  20
  21#ifdef CONFIG_PM_SLEEP
  22
  23static int do_scsi_suspend(struct device *dev, const struct dev_pm_ops *pm)
  24{
  25        return pm && pm->suspend ? pm->suspend(dev) : 0;
  26}
  27
  28static int do_scsi_freeze(struct device *dev, const struct dev_pm_ops *pm)
  29{
  30        return pm && pm->freeze ? pm->freeze(dev) : 0;
  31}
  32
  33static int do_scsi_poweroff(struct device *dev, const struct dev_pm_ops *pm)
  34{
  35        return pm && pm->poweroff ? pm->poweroff(dev) : 0;
  36}
  37
  38static int do_scsi_resume(struct device *dev, const struct dev_pm_ops *pm)
  39{
  40        return pm && pm->resume ? pm->resume(dev) : 0;
  41}
  42
  43static int do_scsi_thaw(struct device *dev, const struct dev_pm_ops *pm)
  44{
  45        return pm && pm->thaw ? pm->thaw(dev) : 0;
  46}
  47
  48static int do_scsi_restore(struct device *dev, const struct dev_pm_ops *pm)
  49{
  50        return pm && pm->restore ? pm->restore(dev) : 0;
  51}
  52
  53static int scsi_dev_type_suspend(struct device *dev,
  54                int (*cb)(struct device *, const struct dev_pm_ops *))
  55{
  56        const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
  57        int err;
  58
  59        /* flush pending in-flight resume operations, suspend is synchronous */
  60        async_synchronize_full_domain(&scsi_sd_pm_domain);
  61
  62        err = scsi_device_quiesce(to_scsi_device(dev));
  63        if (err == 0) {
  64                err = cb(dev, pm);
  65                if (err)
  66                        scsi_device_resume(to_scsi_device(dev));
  67        }
  68        dev_dbg(dev, "scsi suspend: %d\n", err);
  69        return err;
  70}
  71
  72static int scsi_dev_type_resume(struct device *dev,
  73                int (*cb)(struct device *, const struct dev_pm_ops *))
  74{
  75        const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
  76        int err = 0;
  77
  78        err = cb(dev, pm);
  79        scsi_device_resume(to_scsi_device(dev));
  80        dev_dbg(dev, "scsi resume: %d\n", err);
  81
  82        if (err == 0) {
  83                pm_runtime_disable(dev);
  84                err = pm_runtime_set_active(dev);
  85                pm_runtime_enable(dev);
  86
  87                /*
  88                 * Forcibly set runtime PM status of request queue to "active"
  89                 * to make sure we can again get requests from the queue
  90                 * (see also blk_pm_peek_request()).
  91                 *
  92                 * The resume hook will correct runtime PM status of the disk.
  93                 */
  94                if (!err && scsi_is_sdev_device(dev)) {
  95                        struct scsi_device *sdev = to_scsi_device(dev);
  96
  97                        if (sdev->request_queue->dev)
  98                                blk_set_runtime_active(sdev->request_queue);
  99                }
 100        }
 101
 102        return err;
 103}
 104
 105static int
 106scsi_bus_suspend_common(struct device *dev,
 107                int (*cb)(struct device *, const struct dev_pm_ops *))
 108{
 109        int err = 0;
 110
 111        if (scsi_is_sdev_device(dev)) {
 112                /*
 113                 * All the high-level SCSI drivers that implement runtime
 114                 * PM treat runtime suspend, system suspend, and system
 115                 * hibernate nearly identically. In all cases the requirements
 116                 * for runtime suspension are stricter.
 117                 */
 118                if (pm_runtime_suspended(dev))
 119                        return 0;
 120
 121                err = scsi_dev_type_suspend(dev, cb);
 122        }
 123
 124        return err;
 125}
 126
 127static void async_sdev_resume(void *dev, async_cookie_t cookie)
 128{
 129        scsi_dev_type_resume(dev, do_scsi_resume);
 130}
 131
 132static void async_sdev_thaw(void *dev, async_cookie_t cookie)
 133{
 134        scsi_dev_type_resume(dev, do_scsi_thaw);
 135}
 136
 137static void async_sdev_restore(void *dev, async_cookie_t cookie)
 138{
 139        scsi_dev_type_resume(dev, do_scsi_restore);
 140}
 141
 142static int scsi_bus_resume_common(struct device *dev,
 143                int (*cb)(struct device *, const struct dev_pm_ops *))
 144{
 145        async_func_t fn;
 146
 147        if (!scsi_is_sdev_device(dev))
 148                fn = NULL;
 149        else if (cb == do_scsi_resume)
 150                fn = async_sdev_resume;
 151        else if (cb == do_scsi_thaw)
 152                fn = async_sdev_thaw;
 153        else if (cb == do_scsi_restore)
 154                fn = async_sdev_restore;
 155        else
 156                fn = NULL;
 157
 158        if (fn) {
 159                async_schedule_domain(fn, dev, &scsi_sd_pm_domain);
 160
 161                /*
 162                 * If a user has disabled async probing a likely reason
 163                 * is due to a storage enclosure that does not inject
 164                 * staggered spin-ups.  For safety, make resume
 165                 * synchronous as well in that case.
 166                 */
 167                if (strncmp(scsi_scan_type, "async", 5) != 0)
 168                        async_synchronize_full_domain(&scsi_sd_pm_domain);
 169        } else {
 170                pm_runtime_disable(dev);
 171                pm_runtime_set_active(dev);
 172                pm_runtime_enable(dev);
 173        }
 174        return 0;
 175}
 176
 177static int scsi_bus_prepare(struct device *dev)
 178{
 179        if (scsi_is_host_device(dev)) {
 180                /* Wait until async scanning is finished */
 181                scsi_complete_async_scans();
 182        }
 183        return 0;
 184}
 185
 186static int scsi_bus_suspend(struct device *dev)
 187{
 188        return scsi_bus_suspend_common(dev, do_scsi_suspend);
 189}
 190
 191static int scsi_bus_resume(struct device *dev)
 192{
 193        return scsi_bus_resume_common(dev, do_scsi_resume);
 194}
 195
 196static int scsi_bus_freeze(struct device *dev)
 197{
 198        return scsi_bus_suspend_common(dev, do_scsi_freeze);
 199}
 200
 201static int scsi_bus_thaw(struct device *dev)
 202{
 203        return scsi_bus_resume_common(dev, do_scsi_thaw);
 204}
 205
 206static int scsi_bus_poweroff(struct device *dev)
 207{
 208        return scsi_bus_suspend_common(dev, do_scsi_poweroff);
 209}
 210
 211static int scsi_bus_restore(struct device *dev)
 212{
 213        return scsi_bus_resume_common(dev, do_scsi_restore);
 214}
 215
 216#else /* CONFIG_PM_SLEEP */
 217
 218#define scsi_bus_prepare                NULL
 219#define scsi_bus_suspend                NULL
 220#define scsi_bus_resume                 NULL
 221#define scsi_bus_freeze                 NULL
 222#define scsi_bus_thaw                   NULL
 223#define scsi_bus_poweroff               NULL
 224#define scsi_bus_restore                NULL
 225
 226#endif /* CONFIG_PM_SLEEP */
 227
 228static int sdev_runtime_suspend(struct device *dev)
 229{
 230        const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 231        struct scsi_device *sdev = to_scsi_device(dev);
 232        int err = 0;
 233
 234        err = blk_pre_runtime_suspend(sdev->request_queue);
 235        if (err)
 236                return err;
 237        if (pm && pm->runtime_suspend)
 238                err = pm->runtime_suspend(dev);
 239        blk_post_runtime_suspend(sdev->request_queue, err);
 240
 241        return err;
 242}
 243
 244static int scsi_runtime_suspend(struct device *dev)
 245{
 246        int err = 0;
 247
 248        dev_dbg(dev, "scsi_runtime_suspend\n");
 249        if (scsi_is_sdev_device(dev))
 250                err = sdev_runtime_suspend(dev);
 251
 252        /* Insert hooks here for targets, hosts, and transport classes */
 253
 254        return err;
 255}
 256
 257static int sdev_runtime_resume(struct device *dev)
 258{
 259        struct scsi_device *sdev = to_scsi_device(dev);
 260        const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 261        int err = 0;
 262
 263        blk_pre_runtime_resume(sdev->request_queue);
 264        if (pm && pm->runtime_resume)
 265                err = pm->runtime_resume(dev);
 266        blk_post_runtime_resume(sdev->request_queue, err);
 267
 268        return err;
 269}
 270
 271static int scsi_runtime_resume(struct device *dev)
 272{
 273        int err = 0;
 274
 275        dev_dbg(dev, "scsi_runtime_resume\n");
 276        if (scsi_is_sdev_device(dev))
 277                err = sdev_runtime_resume(dev);
 278
 279        /* Insert hooks here for targets, hosts, and transport classes */
 280
 281        return err;
 282}
 283
 284static int scsi_runtime_idle(struct device *dev)
 285{
 286        dev_dbg(dev, "scsi_runtime_idle\n");
 287
 288        /* Insert hooks here for targets, hosts, and transport classes */
 289
 290        if (scsi_is_sdev_device(dev)) {
 291                pm_runtime_mark_last_busy(dev);
 292                pm_runtime_autosuspend(dev);
 293                return -EBUSY;
 294        }
 295
 296        return 0;
 297}
 298
 299int scsi_autopm_get_device(struct scsi_device *sdev)
 300{
 301        int     err;
 302
 303        err = pm_runtime_get_sync(&sdev->sdev_gendev);
 304        if (err < 0 && err !=-EACCES)
 305                pm_runtime_put_sync(&sdev->sdev_gendev);
 306        else
 307                err = 0;
 308        return err;
 309}
 310EXPORT_SYMBOL_GPL(scsi_autopm_get_device);
 311
 312void scsi_autopm_put_device(struct scsi_device *sdev)
 313{
 314        pm_runtime_put_sync(&sdev->sdev_gendev);
 315}
 316EXPORT_SYMBOL_GPL(scsi_autopm_put_device);
 317
 318void scsi_autopm_get_target(struct scsi_target *starget)
 319{
 320        pm_runtime_get_sync(&starget->dev);
 321}
 322
 323void scsi_autopm_put_target(struct scsi_target *starget)
 324{
 325        pm_runtime_put_sync(&starget->dev);
 326}
 327
 328int scsi_autopm_get_host(struct Scsi_Host *shost)
 329{
 330        int     err;
 331
 332        err = pm_runtime_get_sync(&shost->shost_gendev);
 333        if (err < 0 && err !=-EACCES)
 334                pm_runtime_put_sync(&shost->shost_gendev);
 335        else
 336                err = 0;
 337        return err;
 338}
 339
 340void scsi_autopm_put_host(struct Scsi_Host *shost)
 341{
 342        pm_runtime_put_sync(&shost->shost_gendev);
 343}
 344
 345const struct dev_pm_ops scsi_bus_pm_ops = {
 346        .prepare =              scsi_bus_prepare,
 347        .suspend =              scsi_bus_suspend,
 348        .resume =               scsi_bus_resume,
 349        .freeze =               scsi_bus_freeze,
 350        .thaw =                 scsi_bus_thaw,
 351        .poweroff =             scsi_bus_poweroff,
 352        .restore =              scsi_bus_restore,
 353        .runtime_suspend =      scsi_runtime_suspend,
 354        .runtime_resume =       scsi_runtime_resume,
 355        .runtime_idle =         scsi_runtime_idle,
 356};
 357