2018-01-26 20:45:16 +03:00
// SPDX-License-Identifier: GPL-2.0
2005-04-17 02:20:36 +04:00
# include <linux/pci.h>
# include <linux/module.h>
# include "pci.h"
2014-02-01 18:38:29 +04:00
static void pci_free_resources ( struct pci_dev * dev )
{
int i ;
for ( i = 0 ; i < PCI_NUM_RESOURCES ; i + + ) {
struct resource * res = dev - > resource + i ;
if ( res - > parent )
release_resource ( res ) ;
}
}
2006-09-12 21:16:36 +04:00
static void pci_stop_dev ( struct pci_dev * dev )
2005-04-17 02:20:36 +04:00
{
2013-02-11 23:49:49 +04:00
pci_pme_active ( dev , false ) ;
2018-07-03 12:05:41 +03:00
if ( pci_dev_is_added ( dev ) ) {
2021-04-16 23:58:39 +03:00
2017-10-12 00:35:56 +03:00
device_release_driver ( & dev - > dev ) ;
2005-04-28 11:25:49 +04:00
pci_proc_detach_device ( dev ) ;
pci_remove_sysfs_dev_files ( dev ) ;
2018-07-03 12:05:41 +03:00
pci_dev_assign_added ( dev , false ) ;
2005-04-28 11:25:49 +04:00
}
2006-09-12 21:16:36 +04:00
}
static void pci_destroy_dev ( struct pci_dev * dev )
{
2014-01-14 23:04:51 +04:00
if ( ! dev - > dev . kobj . parent )
return ;
PCI: Move device_del() from pci_stop_dev() to pci_destroy_dev()
After commit bcdde7e221a8 (sysfs: make __sysfs_remove_dir() recursive)
I'm seeing traces analogous to the one below in Thunderbolt testing:
WARNING: CPU: 3 PID: 76 at /scratch/rafael/work/linux-pm/fs/sysfs/group.c:214 sysfs_remove_group+0x59/0xe0()
sysfs group ffffffff81c6c500 not found for kobject '0000:08'
Modules linked in: ...
CPU: 3 PID: 76 Comm: kworker/u16:7 Not tainted 3.13.0-rc1+ #76
Hardware name: Acer Aspire S5-391/Venus , BIOS V1.02 05/29/2012
Workqueue: kacpi_hotplug acpi_hotplug_work_fn
0000000000000009 ffff8801644b9ac8 ffffffff816b23bf 0000000000000007
ffff8801644b9b18 ffff8801644b9b08 ffffffff81046607 ffff88016925b800
0000000000000000 ffffffff81c6c500 ffff88016924f928 ffff88016924f800
Call Trace:
[<ffffffff816b23bf>] dump_stack+0x4e/0x71
[<ffffffff81046607>] warn_slowpath_common+0x87/0xb0
[<ffffffff810466d1>] warn_slowpath_fmt+0x41/0x50
[<ffffffff811e42ef>] ? sysfs_get_dirent_ns+0x6f/0x80
[<ffffffff811e5389>] sysfs_remove_group+0x59/0xe0
[<ffffffff8149f00b>] dpm_sysfs_remove+0x3b/0x50
[<ffffffff81495818>] device_del+0x58/0x1c0
[<ffffffff814959c8>] device_unregister+0x48/0x60
[<ffffffff813254fe>] pci_remove_bus+0x6e/0x80
[<ffffffff81325548>] pci_remove_bus_device+0x38/0x110
[<ffffffff8132555d>] pci_remove_bus_device+0x4d/0x110
[<ffffffff81325639>] pci_stop_and_remove_bus_device+0x19/0x20
[<ffffffff813418d0>] disable_slot+0x20/0xe0
[<ffffffff81341a38>] acpiphp_check_bridge+0xa8/0xd0
[<ffffffff813427ad>] hotplug_event+0x17d/0x220
[<ffffffff81342880>] hotplug_event_work+0x30/0x70
[<ffffffff8136d665>] acpi_hotplug_work_fn+0x18/0x24
[<ffffffff81061331>] process_one_work+0x261/0x450
[<ffffffff81061a7e>] worker_thread+0x21e/0x370
[<ffffffff81061860>] ? rescuer_thread+0x300/0x300
[<ffffffff81068342>] kthread+0xd2/0xe0
[<ffffffff81068270>] ? flush_kthread_worker+0x70/0x70
[<ffffffff816c19bc>] ret_from_fork+0x7c/0xb0
[<ffffffff81068270>] ? flush_kthread_worker+0x70/0x70
(Mika Westerberg sees them too in his tests).
Some investigation documented in kernel bug #65281 led me to the
conclusion that the source of the problem is the device_del() in
pci_stop_dev() as it now causes the sysfs directory of the device to be
removed recursively along with all of its subdirectories. That includes
the sysfs directory of the device's subordinate bus (dev->subordinate) and
its "power" group.
Consequently, when pci_remove_bus() is called for dev->subordinate in
pci_remove_bus_device(), it calls device_unregister(&bus->dev), but at this
point the sysfs directory of bus->dev doesn't exist any more and its
"power" group doesn't exist either. Thus, when dpm_sysfs_remove() called
from device_del() tries to remove that group, it triggers the above
warning.
That indicates a logical mistake in the design of
pci_stop_and_remove_bus_device(), which causes bus device objects to be
left behind their parents (bridge device objects) and can be fixed by
moving the device_del() from pci_stop_dev() into pci_destroy_dev(), so
pci_remove_bus() can be called for the device's subordinate bus before the
device itself is unregistered from the hierarchy. Still, the driver, if
any, should be detached from the device in pci_stop_dev(), so use
device_release_driver() directly from there.
References: https://bugzilla.kernel.org/show_bug.cgi?id=65281#c6
Reported-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
2013-12-19 00:53:32 +04:00
device_del ( & dev - > dev ) ;
2014-02-01 18:38:29 +04:00
down_write ( & pci_bus_sem ) ;
list_del ( & dev - > bus_list ) ;
up_write ( & pci_bus_sem ) ;
2018-09-04 20:34:18 +03:00
pcie_aspm_exit_link_state ( dev ) ;
2016-10-28 11:52:06 +03:00
pci_bridge_d3_update ( dev ) ;
2014-02-01 18:38:29 +04:00
pci_free_resources ( dev ) ;
2013-01-22 01:20:46 +04:00
put_device ( & dev - > dev ) ;
2005-04-17 02:20:36 +04:00
}
2012-08-17 06:43:11 +04:00
void pci_remove_bus ( struct pci_bus * bus )
2005-04-17 02:20:36 +04:00
{
2012-08-17 06:43:11 +04:00
pci_proc_detach_bus ( bus ) ;
2005-04-17 02:20:36 +04:00
2006-06-02 08:35:43 +04:00
down_write ( & pci_bus_sem ) ;
2012-08-17 06:43:11 +04:00
list_del ( & bus - > node ) ;
pci_bus_release_busn_res ( bus ) ;
2006-06-02 08:35:43 +04:00
up_write ( & pci_bus_sem ) ;
2012-08-17 06:43:11 +04:00
pci_remove_legacy_files ( bus ) ;
2016-02-09 17:30:47 +03:00
if ( bus - > ops - > remove_bus )
bus - > ops - > remove_bus ( bus ) ;
2013-04-12 09:44:20 +04:00
pcibios_remove_bus ( bus ) ;
2012-08-17 06:43:11 +04:00
device_unregister ( & bus - > dev ) ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( pci_remove_bus ) ;
2012-09-19 22:54:20 +04:00
static void pci_stop_bus_device ( struct pci_dev * dev )
2006-09-12 21:16:36 +04:00
{
2012-08-17 21:25:01 +04:00
struct pci_bus * bus = dev - > subordinate ;
struct pci_dev * child , * tmp ;
/*
2012-09-19 22:54:20 +04:00
* Stopping an SR - IOV PF device removes all the associated VFs ,
2012-08-17 21:25:01 +04:00
* which will update the bus - > devices list and confuse the
* iterator . Therefore , iterate in reverse so we remove the VFs
* first , then the PF .
*/
2012-08-17 21:57:48 +04:00
if ( bus ) {
2012-08-17 21:25:01 +04:00
list_for_each_entry_safe_reverse ( child , tmp ,
& bus - > devices , bus_list )
2012-09-19 22:54:20 +04:00
pci_stop_bus_device ( child ) ;
}
pci_stop_dev ( dev ) ;
}
static void pci_remove_bus_device ( struct pci_dev * dev )
{
struct pci_bus * bus = dev - > subordinate ;
struct pci_dev * child , * tmp ;
if ( bus ) {
list_for_each_entry_safe ( child , tmp ,
& bus - > devices , bus_list )
pci_remove_bus_device ( child ) ;
2012-08-17 21:57:48 +04:00
pci_remove_bus ( bus ) ;
dev - > subordinate = NULL ;
}
2006-09-12 21:16:36 +04:00
2012-08-17 21:57:48 +04:00
pci_destroy_dev ( dev ) ;
2006-09-12 21:16:36 +04:00
}
2012-09-19 22:54:20 +04:00
/**
* pci_stop_and_remove_bus_device - remove a PCI device and any children
* @ dev : the device to remove
*
* Remove a PCI device from the device lists , informing the drivers
* that the device has been removed . We also remove any subordinate
* buses and children in a depth - first manner .
*
* For each device we remove , delete the device structure from the
* device lists , remove the / proc entry , and notify userspace
* ( / sbin / hotplug ) .
*/
void pci_stop_and_remove_bus_device ( struct pci_dev * dev )
{
pci_stop_bus_device ( dev ) ;
pci_remove_bus_device ( dev ) ;
}
2012-02-26 01:54:20 +04:00
EXPORT_SYMBOL ( pci_stop_and_remove_bus_device ) ;
2012-10-31 00:31:38 +04:00
2014-01-10 18:22:18 +04:00
void pci_stop_and_remove_bus_device_locked ( struct pci_dev * dev )
{
pci_lock_rescan_remove ( ) ;
pci_stop_and_remove_bus_device ( dev ) ;
pci_unlock_rescan_remove ( ) ;
}
EXPORT_SYMBOL_GPL ( pci_stop_and_remove_bus_device_locked ) ;
2012-10-31 00:31:38 +04:00
void pci_stop_root_bus ( struct pci_bus * bus )
{
struct pci_dev * child , * tmp ;
struct pci_host_bridge * host_bridge ;
if ( ! pci_is_root_bus ( bus ) )
return ;
host_bridge = to_pci_host_bridge ( bus - > bridge ) ;
list_for_each_entry_safe_reverse ( child , tmp ,
& bus - > devices , bus_list )
pci_stop_bus_device ( child ) ;
/* stop the host bridge */
2013-12-01 02:40:27 +04:00
device_release_driver ( & host_bridge - > dev ) ;
2012-10-31 00:31:38 +04:00
}
2015-04-08 21:21:33 +03:00
EXPORT_SYMBOL_GPL ( pci_stop_root_bus ) ;
2012-10-31 00:31:38 +04:00
void pci_remove_root_bus ( struct pci_bus * bus )
{
struct pci_dev * child , * tmp ;
struct pci_host_bridge * host_bridge ;
if ( ! pci_is_root_bus ( bus ) )
return ;
host_bridge = to_pci_host_bridge ( bus - > bridge ) ;
list_for_each_entry_safe ( child , tmp ,
& bus - > devices , bus_list )
pci_remove_bus_device ( child ) ;
pci_remove_bus ( bus ) ;
host_bridge - > bus = NULL ;
/* remove the host bridge */
2020-05-14 01:38:59 +03:00
device_del ( & host_bridge - > dev ) ;
2012-10-31 00:31:38 +04:00
}
2015-04-08 21:21:33 +03:00
EXPORT_SYMBOL_GPL ( pci_remove_root_bus ) ;