Skip to content

Commit 7ab5e10

Browse files
abhsahuawilliam
authored andcommitted
vfio/pci: Move the unused device into low power state with runtime PM
Currently, there is very limited power management support available in the upstream vfio_pci_core based drivers. If there are no users of the device, then the PCI device will be moved into D3hot state by writing directly into PCI PM registers. This D3hot state help in saving power but we can achieve zero power consumption if we go into the D3cold state. The D3cold state cannot be possible with native PCI PM. It requires interaction with platform firmware which is system-specific. To go into low power states (including D3cold), the runtime PM framework can be used which internally interacts with PCI and platform firmware and puts the device into the lowest possible D-States. This patch registers vfio_pci_core based drivers with the runtime PM framework. 1. The PCI core framework takes care of most of the runtime PM related things. For enabling the runtime PM, the PCI driver needs to decrement the usage count and needs to provide 'struct dev_pm_ops' at least. The runtime suspend/resume callbacks are optional and needed only if we need to do any extra handling. Now there are multiple vfio_pci_core based drivers. Instead of assigning the 'struct dev_pm_ops' in individual parent driver, the vfio_pci_core itself assigns the 'struct dev_pm_ops'. There are other drivers where the 'struct dev_pm_ops' is being assigned inside core layer (For example, wlcore_probe() and some sound based driver, etc.). 2. This patch provides the stub implementation of 'struct dev_pm_ops'. The subsequent patch will provide the runtime suspend/resume callbacks. All the config state saving, and PCI power management related things will be done by PCI core framework itself inside its runtime suspend/resume callbacks (pci_pm_runtime_suspend() and pci_pm_runtime_resume()). 3. Inside pci_reset_bus(), all the devices in dev_set needs to be runtime resumed. vfio_pci_dev_set_pm_runtime_get() will take care of the runtime resume and its error handling. 4. Inside vfio_pci_core_disable(), the device usage count always needs to be decremented which was incremented in vfio_pci_core_enable(). 5. Since the runtime PM framework will provide the same functionality, so directly writing into PCI PM config register can be replaced with the use of runtime PM routines. Also, the use of runtime PM can help us in more power saving. In the systems which do not support D3cold, With the existing implementation: // PCI device # cat /sys/bus/pci/devices/0000\:01\:00.0/power_state D3hot // upstream bridge # cat /sys/bus/pci/devices/0000\:00\:01.0/power_state D0 With runtime PM: // PCI device # cat /sys/bus/pci/devices/0000\:01\:00.0/power_state D3hot // upstream bridge # cat /sys/bus/pci/devices/0000\:00\:01.0/power_state D3hot So, with runtime PM, the upstream bridge or root port will also go into lower power state which is not possible with existing implementation. In the systems which support D3cold, // PCI device # cat /sys/bus/pci/devices/0000\:01\:00.0/power_state D3hot // upstream bridge # cat /sys/bus/pci/devices/0000\:00\:01.0/power_state D0 With runtime PM: // PCI device # cat /sys/bus/pci/devices/0000\:01\:00.0/power_state D3cold // upstream bridge # cat /sys/bus/pci/devices/0000\:00\:01.0/power_state D3cold So, with runtime PM, both the PCI device and upstream bridge will go into D3cold state. 6. If 'disable_idle_d3' module parameter is set, then also the runtime PM will be enabled, but in this case, the usage count should not be decremented. 7. vfio_pci_dev_set_try_reset() return value is unused now, so this function return type can be changed to void. 8. Use the runtime PM API's in vfio_pci_core_sriov_configure(). The device can be in low power state either with runtime power management (when there is no user) or PCI_PM_CTRL register write by the user. In both the cases, the PF should be moved to D0 state. For preventing any runtime usage mismatch, pci_num_vf() has been called explicitly during disable. Signed-off-by: Abhishek Sahu <abhsahu@nvidia.com> Link: https://lore.kernel.org/r/20220518111612.16985-5-abhsahu@nvidia.com Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
1 parent 54918c2 commit 7ab5e10

File tree

1 file changed

+113
-57
lines changed

1 file changed

+113
-57
lines changed

drivers/vfio/pci/vfio_pci_core.c

Lines changed: 113 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ static void vfio_pci_probe_mmaps(struct vfio_pci_core_device *vdev)
156156
}
157157

158158
struct vfio_pci_group_info;
159-
static bool vfio_pci_dev_set_try_reset(struct vfio_device_set *dev_set);
159+
static void vfio_pci_dev_set_try_reset(struct vfio_device_set *dev_set);
160160
static int vfio_pci_dev_set_hot_reset(struct vfio_device_set *dev_set,
161161
struct vfio_pci_group_info *groups);
162162

@@ -259,28 +259,41 @@ int vfio_pci_set_power_state(struct vfio_pci_core_device *vdev, pci_power_t stat
259259
return ret;
260260
}
261261

262+
/*
263+
* The dev_pm_ops needs to be provided to make pci-driver runtime PM working,
264+
* so use structure without any callbacks.
265+
*
266+
* The pci-driver core runtime PM routines always save the device state
267+
* before going into suspended state. If the device is going into low power
268+
* state with only with runtime PM ops, then no explicit handling is needed
269+
* for the devices which have NoSoftRst-.
270+
*/
271+
static const struct dev_pm_ops vfio_pci_core_pm_ops = { };
272+
262273
int vfio_pci_core_enable(struct vfio_pci_core_device *vdev)
263274
{
264275
struct pci_dev *pdev = vdev->pdev;
265276
int ret;
266277
u16 cmd;
267278
u8 msix_pos;
268279

269-
vfio_pci_set_power_state(vdev, PCI_D0);
280+
if (!disable_idle_d3) {
281+
ret = pm_runtime_resume_and_get(&pdev->dev);
282+
if (ret < 0)
283+
return ret;
284+
}
270285

271286
/* Don't allow our initial saved state to include busmaster */
272287
pci_clear_master(pdev);
273288

274289
ret = pci_enable_device(pdev);
275290
if (ret)
276-
return ret;
291+
goto out_power;
277292

278293
/* If reset fails because of the device lock, fail this path entirely */
279294
ret = pci_try_reset_function(pdev);
280-
if (ret == -EAGAIN) {
281-
pci_disable_device(pdev);
282-
return ret;
283-
}
295+
if (ret == -EAGAIN)
296+
goto out_disable_device;
284297

285298
vdev->reset_works = !ret;
286299
pci_save_state(pdev);
@@ -304,12 +317,8 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev)
304317
}
305318

306319
ret = vfio_config_init(vdev);
307-
if (ret) {
308-
kfree(vdev->pci_saved_state);
309-
vdev->pci_saved_state = NULL;
310-
pci_disable_device(pdev);
311-
return ret;
312-
}
320+
if (ret)
321+
goto out_free_state;
313322

314323
msix_pos = pdev->msix_cap;
315324
if (msix_pos) {
@@ -330,6 +339,16 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev)
330339

331340

332341
return 0;
342+
343+
out_free_state:
344+
kfree(vdev->pci_saved_state);
345+
vdev->pci_saved_state = NULL;
346+
out_disable_device:
347+
pci_disable_device(pdev);
348+
out_power:
349+
if (!disable_idle_d3)
350+
pm_runtime_put(&pdev->dev);
351+
return ret;
333352
}
334353
EXPORT_SYMBOL_GPL(vfio_pci_core_enable);
335354

@@ -437,8 +456,11 @@ void vfio_pci_core_disable(struct vfio_pci_core_device *vdev)
437456
out:
438457
pci_disable_device(pdev);
439458

440-
if (!vfio_pci_dev_set_try_reset(vdev->vdev.dev_set) && !disable_idle_d3)
441-
vfio_pci_set_power_state(vdev, PCI_D3hot);
459+
vfio_pci_dev_set_try_reset(vdev->vdev.dev_set);
460+
461+
/* Put the pm-runtime usage counter acquired during enable */
462+
if (!disable_idle_d3)
463+
pm_runtime_put(&pdev->dev);
442464
}
443465
EXPORT_SYMBOL_GPL(vfio_pci_core_disable);
444466

@@ -1823,10 +1845,11 @@ EXPORT_SYMBOL_GPL(vfio_pci_core_uninit_device);
18231845
int vfio_pci_core_register_device(struct vfio_pci_core_device *vdev)
18241846
{
18251847
struct pci_dev *pdev = vdev->pdev;
1848+
struct device *dev = &pdev->dev;
18261849
int ret;
18271850

18281851
/* Drivers must set the vfio_pci_core_device to their drvdata */
1829-
if (WARN_ON(vdev != dev_get_drvdata(&vdev->pdev->dev)))
1852+
if (WARN_ON(vdev != dev_get_drvdata(dev)))
18301853
return -EINVAL;
18311854

18321855
if (pdev->hdr_type != PCI_HEADER_TYPE_NORMAL)
@@ -1868,19 +1891,21 @@ int vfio_pci_core_register_device(struct vfio_pci_core_device *vdev)
18681891

18691892
vfio_pci_probe_power_state(vdev);
18701893

1871-
if (!disable_idle_d3) {
1872-
/*
1873-
* pci-core sets the device power state to an unknown value at
1874-
* bootup and after being removed from a driver. The only
1875-
* transition it allows from this unknown state is to D0, which
1876-
* typically happens when a driver calls pci_enable_device().
1877-
* We're not ready to enable the device yet, but we do want to
1878-
* be able to get to D3. Therefore first do a D0 transition
1879-
* before going to D3.
1880-
*/
1881-
vfio_pci_set_power_state(vdev, PCI_D0);
1882-
vfio_pci_set_power_state(vdev, PCI_D3hot);
1883-
}
1894+
/*
1895+
* pci-core sets the device power state to an unknown value at
1896+
* bootup and after being removed from a driver. The only
1897+
* transition it allows from this unknown state is to D0, which
1898+
* typically happens when a driver calls pci_enable_device().
1899+
* We're not ready to enable the device yet, but we do want to
1900+
* be able to get to D3. Therefore first do a D0 transition
1901+
* before enabling runtime PM.
1902+
*/
1903+
vfio_pci_set_power_state(vdev, PCI_D0);
1904+
1905+
dev->driver->pm = &vfio_pci_core_pm_ops;
1906+
pm_runtime_allow(dev);
1907+
if (!disable_idle_d3)
1908+
pm_runtime_put(dev);
18841909

18851910
ret = vfio_register_group_dev(&vdev->vdev);
18861911
if (ret)
@@ -1889,7 +1914,9 @@ int vfio_pci_core_register_device(struct vfio_pci_core_device *vdev)
18891914

18901915
out_power:
18911916
if (!disable_idle_d3)
1892-
vfio_pci_set_power_state(vdev, PCI_D0);
1917+
pm_runtime_get_noresume(dev);
1918+
1919+
pm_runtime_forbid(dev);
18931920
out_vf:
18941921
vfio_pci_vf_uninit(vdev);
18951922
return ret;
@@ -1906,7 +1933,9 @@ void vfio_pci_core_unregister_device(struct vfio_pci_core_device *vdev)
19061933
vfio_pci_vga_uninit(vdev);
19071934

19081935
if (!disable_idle_d3)
1909-
vfio_pci_set_power_state(vdev, PCI_D0);
1936+
pm_runtime_get_noresume(&vdev->pdev->dev);
1937+
1938+
pm_runtime_forbid(&vdev->pdev->dev);
19101939
}
19111940
EXPORT_SYMBOL_GPL(vfio_pci_core_unregister_device);
19121941

@@ -1951,22 +1980,33 @@ int vfio_pci_core_sriov_configure(struct vfio_pci_core_device *vdev,
19511980

19521981
/*
19531982
* The PF power state should always be higher than the VF power
1954-
* state. If PF is in the low power state, then change the
1955-
* power state to D0 first before enabling SR-IOV.
1956-
* Also, this function can be called at any time, and userspace
1957-
* PCI_PM_CTRL write can race against this code path,
1983+
* state. The PF can be in low power state either with runtime
1984+
* power management (when there is no user) or PCI_PM_CTRL
1985+
* register write by the user. If PF is in the low power state,
1986+
* then change the power state to D0 first before enabling
1987+
* SR-IOV. Also, this function can be called at any time, and
1988+
* userspace PCI_PM_CTRL write can race against this code path,
19581989
* so protect the same with 'memory_lock'.
19591990
*/
1991+
ret = pm_runtime_resume_and_get(&pdev->dev);
1992+
if (ret)
1993+
goto out_del;
1994+
19601995
down_write(&vdev->memory_lock);
19611996
vfio_pci_set_power_state(vdev, PCI_D0);
19621997
ret = pci_enable_sriov(pdev, nr_virtfn);
19631998
up_write(&vdev->memory_lock);
1964-
if (ret)
1999+
if (ret) {
2000+
pm_runtime_put(&pdev->dev);
19652001
goto out_del;
2002+
}
19662003
return nr_virtfn;
19672004
}
19682005

1969-
pci_disable_sriov(pdev);
2006+
if (pci_num_vf(pdev)) {
2007+
pci_disable_sriov(pdev);
2008+
pm_runtime_put(&pdev->dev);
2009+
}
19702010

19712011
out_del:
19722012
mutex_lock(&vfio_pci_sriov_pfs_mutex);
@@ -2041,6 +2081,27 @@ vfio_pci_dev_set_resettable(struct vfio_device_set *dev_set)
20412081
return pdev;
20422082
}
20432083

2084+
static int vfio_pci_dev_set_pm_runtime_get(struct vfio_device_set *dev_set)
2085+
{
2086+
struct vfio_pci_core_device *cur;
2087+
int ret;
2088+
2089+
list_for_each_entry(cur, &dev_set->device_list, vdev.dev_set_list) {
2090+
ret = pm_runtime_resume_and_get(&cur->pdev->dev);
2091+
if (ret)
2092+
goto unwind;
2093+
}
2094+
2095+
return 0;
2096+
2097+
unwind:
2098+
list_for_each_entry_continue_reverse(cur, &dev_set->device_list,
2099+
vdev.dev_set_list)
2100+
pm_runtime_put(&cur->pdev->dev);
2101+
2102+
return ret;
2103+
}
2104+
20442105
/*
20452106
* We need to get memory_lock for each device, but devices can share mmap_lock,
20462107
* therefore we need to zap and hold the vma_lock for each device, and only then
@@ -2147,43 +2208,38 @@ static bool vfio_pci_dev_set_needs_reset(struct vfio_device_set *dev_set)
21472208
* - At least one of the affected devices is marked dirty via
21482209
* needs_reset (such as by lack of FLR support)
21492210
* Then attempt to perform that bus or slot reset.
2150-
* Returns true if the dev_set was reset.
21512211
*/
2152-
static bool vfio_pci_dev_set_try_reset(struct vfio_device_set *dev_set)
2212+
static void vfio_pci_dev_set_try_reset(struct vfio_device_set *dev_set)
21532213
{
21542214
struct vfio_pci_core_device *cur;
21552215
struct pci_dev *pdev;
2156-
int ret;
2216+
bool reset_done = false;
21572217

21582218
if (!vfio_pci_dev_set_needs_reset(dev_set))
2159-
return false;
2219+
return;
21602220

21612221
pdev = vfio_pci_dev_set_resettable(dev_set);
21622222
if (!pdev)
2163-
return false;
2223+
return;
21642224

21652225
/*
2166-
* The pci_reset_bus() will reset all the devices in the bus.
2167-
* The power state can be non-D0 for some of the devices in the bus.
2168-
* For these devices, the pci_reset_bus() will internally set
2169-
* the power state to D0 without vfio driver involvement.
2170-
* For the devices which have NoSoftRst-, the reset function can
2171-
* cause the PCI config space reset without restoring the original
2172-
* state (saved locally in 'vdev->pm_save').
2226+
* Some of the devices in the bus can be in the runtime suspended
2227+
* state. Increment the usage count for all the devices in the dev_set
2228+
* before reset and decrement the same after reset.
21732229
*/
2174-
list_for_each_entry(cur, &dev_set->device_list, vdev.dev_set_list)
2175-
vfio_pci_set_power_state(cur, PCI_D0);
2230+
if (!disable_idle_d3 && vfio_pci_dev_set_pm_runtime_get(dev_set))
2231+
return;
21762232

2177-
ret = pci_reset_bus(pdev);
2178-
if (ret)
2179-
return false;
2233+
if (!pci_reset_bus(pdev))
2234+
reset_done = true;
21802235

21812236
list_for_each_entry(cur, &dev_set->device_list, vdev.dev_set_list) {
2182-
cur->needs_reset = false;
2237+
if (reset_done)
2238+
cur->needs_reset = false;
2239+
21832240
if (!disable_idle_d3)
2184-
vfio_pci_set_power_state(cur, PCI_D3hot);
2241+
pm_runtime_put(&cur->pdev->dev);
21852242
}
2186-
return true;
21872243
}
21882244

21892245
void vfio_pci_core_set_params(bool is_nointxmask, bool is_disable_vga,

0 commit comments

Comments
 (0)