All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jiang Liu <liuj97@gmail.com>
To: Bjorn Helgaas <bhelgaas@google.com>,
	Don Dutile <ddutile@redhat.com>, Yinghai Lu <yinghai@kernel.org>,
	Greg KH <gregkh@linuxfoundation.org>,
	Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Cc: Jiang Liu <jiang.liu@huawei.com>,
	Taku Izumi <izumi.taku@jp.fujitsu.com>,
	"Rafael J . Wysocki" <rjw@sisk.pl>,
	Yijing Wang <wangyijing@huawei.com>,
	Xinwei Hu <huxinwei@huawei.com>,
	linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org,
	Jiang Liu <liuj97@gmail.com>
Subject: [RFC PATCH v1 07/22] PCI: introduce PCI bus lock to serialize PCI hotplug operations
Date: Wed,  8 Aug 2012 00:10:47 +0800	[thread overview]
Message-ID: <1344355862-2726-8-git-send-email-jiang.liu@huawei.com> (raw)
In-Reply-To: <1344355862-2726-1-git-send-email-jiang.liu@huawei.com>

There are multiple ways to trigger concurrent PCI hotplug operations for
a specific PCI bus, but we have no way to serialize those PCI hotplug
operations yet and thus breaks the PCI hotplug logic. This patch introduces
a bus lock mechanism and state machine for PCI buses to serialize PCI
hotplug operations.

The state machine for PCI buses is:
          __________________________     ______________
          |                        v     |            v
INITIALIZED->REGISTERED->WORKING->STOPPING->STOPPED->REMOVED->DESTOYED
                     |_________________________^

The PCI buses is hierarchy, so need to obey the locking rules:
1) The PCI bus must be locked when changing any child devices of it.
2) The PCI bus must be locked when changing its state
3) The global PCI host bridge hotplug lock must be held when hotplugging
   PCI root buses

The lock interfaces cordinated with the state machine will be used to
avoid race conditions when hotplugging PCI devices/host bridges.
A typical usage is (lock bus if it's in WORKING state, and then do hotplug):
if (pci_bus_lock_states(bus, PCI_BUS_STATE_WORKING) > 0) {
	do_pci_hotplug();
	pci_bus_unlock(bus);
}

The PCI_BUS_LOCK config option is a temporary solution to avoid breaking
bisect, it will be removed when all Archs have been converted to the new
PCI bus lock mechanism.

Signed-off-by: Jiang Liu <liuj97@gmail.com>
---
 drivers/pci/Kconfig |    4 +++
 drivers/pci/bus.c   |   86 +++++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pci.h |   44 ++++++++++++++++++++++++++
 3 files changed, 134 insertions(+)

diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 848bfb8..a6df8b1 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -120,3 +120,7 @@ config PCI_IOAPIC
 config PCI_LABEL
 	def_bool y if (DMI || ACPI)
 	select NLS
+
+config PCI_BUS_LOCK
+	bool
+	default n
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 0e18270..aa25fcf 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -15,9 +15,12 @@
 #include <linux/proc_fs.h>
 #include <linux/init.h>
 #include <linux/slab.h>
+#include <linux/sched.h>
 
 #include "pci.h"
 
+static DECLARE_WAIT_QUEUE_HEAD(pci_bus_state_wait_queue);
+
 void pci_add_resource_offset(struct list_head *resources, struct resource *res,
 			     resource_size_t offset)
 {
@@ -340,6 +343,89 @@ void pci_bus_put(struct pci_bus *bus)
 }
 EXPORT_SYMBOL(pci_bus_put);
 
+static bool pci_bus_wait_for_states(struct pci_bus *bus, int states)
+{
+	int t = atomic_read(&bus->state);
+
+	/* Bus state is bigger than any of the requested states. */
+	if ((t & PCI_BUS_STATE_MASK) > states)
+		return true;
+
+	/* Bus is in one of the requested states and unlocked. */
+	if ((t & states) && !(t & PCI_BUS_STATE_LOCK))
+		return true;
+
+	return false;
+}
+
+/*
+ * Wait for the bus to reach one of the requested states and then lock it.
+ * Return current bus state if succeed to lock the bus, and return -EINVAL
+ * if current bus state is already bigger than any of the requested states.
+ */
+int pci_bus_lock_states(struct pci_bus *bus, int states)
+{
+	int t;
+
+	BUG_ON(states & ~PCI_BUS_STATE_MASK);
+	do {
+		do {
+			wait_event(pci_bus_state_wait_queue,
+				   pci_bus_wait_for_states(bus, states));
+			t = atomic_read(&bus->state);
+			if ((t & PCI_BUS_STATE_MASK) > states)
+				return -EINVAL;
+		} while (!(t & states));
+
+		t &= ~PCI_BUS_STATE_LOCK;
+	} while (atomic_cmpxchg(&bus->state, t , t | PCI_BUS_STATE_LOCK) != t);
+
+	return t & PCI_BUS_STATE_MASK;
+}
+EXPORT_SYMBOL(pci_bus_lock_states);
+
+/* Unlock the bus and wake up waiters, must be called with the bus locked. */
+void pci_bus_unlock(struct pci_bus *bus)
+{
+	int t;
+
+	BUG_ON(!pci_bus_is_locked(bus));
+	do {
+		t = atomic_read(&bus->state);
+	} while (atomic_cmpxchg(&bus->state,
+				t, t & ~PCI_BUS_STATE_LOCK) != t);
+
+	if (waitqueue_active(&pci_bus_state_wait_queue))
+		wake_up_all(&pci_bus_state_wait_queue);
+}
+EXPORT_SYMBOL(pci_bus_unlock);
+
+/*
+ * Change the bus from old state to new state. It must be called with the bus
+ * locked, and the new state must be bigger than the old state.
+ */
+void pci_bus_change_state(struct pci_bus *bus, int old, int new, bool unlock)
+{
+	int t;
+
+	BUG_ON(!pci_bus_is_locked(bus));
+	BUG_ON(new < old || pci_bus_get_state(bus) != old ||
+	       (new & ~PCI_BUS_STATE_MASK));
+
+	old |= PCI_BUS_STATE_LOCK;
+	if (!unlock)
+		new |= PCI_BUS_STATE_LOCK;
+
+	do {
+		t = atomic_read(&bus->state);
+		t &= ~(PCI_BUS_STATE_MASK | PCI_BUS_STATE_LOCK);
+	} while (atomic_cmpxchg(&bus->state, t | old, t | new) != (t | old));
+
+	if (waitqueue_active(&pci_bus_state_wait_queue))
+		wake_up_all(&pci_bus_state_wait_queue);
+}
+EXPORT_SYMBOL(pci_bus_change_state);
+
 EXPORT_SYMBOL(pci_bus_alloc_resource);
 EXPORT_SYMBOL_GPL(pci_bus_add_device);
 EXPORT_SYMBOL(pci_bus_add_devices);
diff --git a/include/linux/pci.h b/include/linux/pci.h
index e02f130..e2ef517 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -443,8 +443,52 @@ struct pci_bus {
 	struct bin_attribute	*legacy_io; /* legacy I/O for this bus */
 	struct bin_attribute	*legacy_mem; /* legacy mem */
 	unsigned int		is_added:1;
+	atomic_t		state;
 };
 
+/*
+ * State machine for PCI buses.
+ *          __________________________     ______________
+ *          |                        v     |            v
+ * INITIALIZED->REGISTERED->WORKING->STOPPING->STOPPED->REMOVED->DESTOYED
+ *                     |_________________________^
+ */
+#define	PCI_BUS_STATE_UNKNOWN		0x0	/* invalid state */
+#define	PCI_BUS_STATE_INITIALIZED	0x1	/* device_initialize called */
+#define	PCI_BUS_STATE_REGISTERED	0x2	/* device_add called */
+#define	PCI_BUS_STATE_WORKING		0x4	/* working state */
+#define	PCI_BUS_STATE_STOPPING		0x8	/* stopping devices */
+#define	PCI_BUS_STATE_STOPPED		0x10	/* device_del called */
+#define	PCI_BUS_STATE_REMOVED		0x20	/* bus deleted  */
+#define	PCI_BUS_STATE_DESTROYED		0x40	/* invalid state */
+#define	PCI_BUS_STATE_MASK		0x7F
+
+#ifdef	CONFIG_PCI_BUS_LOCK
+#define	PCI_BUS_STATE_LOCK		0x10000	/* for pci core only */
+
+static inline bool pci_bus_is_locked(struct pci_bus *bus)
+{
+	return !!(atomic_read(&bus->state) & PCI_BUS_STATE_LOCK);
+}
+#else /* CONFIG_PCI_BUS_LOCK */
+#define	PCI_BUS_STATE_LOCK		0x0000	/* for pci core only */
+
+static inline bool pci_bus_is_locked(struct pci_bus *bus)
+{
+	return true;
+}
+#endif /* CONFIG_PCI_BUS_LOCK */
+
+static inline int pci_bus_get_state(struct pci_bus *bus)
+{
+	return atomic_read(&bus->state) & PCI_BUS_STATE_MASK;
+}
+
+extern int pci_bus_lock_states(struct pci_bus *bus, int states);
+extern void pci_bus_unlock(struct pci_bus *bus);
+extern void pci_bus_change_state(struct pci_bus *bus, int new, int old,
+				 bool unlock);
+
 #define pci_bus_b(n)	list_entry(n, struct pci_bus, node)
 #define to_pci_bus(n)	container_of(n, struct pci_bus, dev)
 
-- 
1.7.9.5


  parent reply	other threads:[~2012-08-07 16:19 UTC|newest]

Thread overview: 51+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-08-07 16:10 [RFC PATCH v1 00/22] introduce PCI bus lock to serialize PCI hotplug operations Jiang Liu
2012-08-07 16:10 ` [RFC PATCH v1 01/22] PCI: use pci_get_domain_bus_and_slot() to avoid race conditions Jiang Liu
2012-09-11 22:00   ` Bjorn Helgaas
2012-09-12  8:37     ` Jiang Liu
2012-08-07 16:10 ` [RFC PATCH v1 02/22] PCI: trivial cleanups for drivers/pci/remove.c Jiang Liu
2012-09-11 22:03   ` Bjorn Helgaas
2012-09-12  8:50     ` Jiang Liu
2012-08-07 16:10 ` [RFC PATCH v1 03/22] PCI: change PCI device management code to better follow device model Jiang Liu
2012-09-11 22:03   ` Bjorn Helgaas
2012-08-07 16:10 ` [RFC PATCH v1 04/22] PCI: split PCI bus device registration into two stages Jiang Liu
2012-08-07 16:10 ` [RFC PATCH v1 05/22] PCI: introduce pci_bus_{get|put}() to manage PCI bus reference count Jiang Liu
2012-08-07 16:10 ` [RFC PATCH v1 06/22] PCI: use a global lock to serialize PCI root bridge hotplug operations Jiang Liu
2012-09-11 22:57   ` Bjorn Helgaas
2012-09-12 15:42     ` Jiang Liu
2012-09-12 16:51       ` Bjorn Helgaas
2012-09-13 16:00         ` [PATCH 1/2] PCI: introduce root bridge hotplug safe interfaces to walk root buses Jiang Liu
2012-09-13 17:40           ` Bjorn Helgaas
2012-09-17 15:55             ` Jiang Liu
2012-09-17 16:24               ` Bjorn Helgaas
2012-09-18 21:39                 ` Bjorn Helgaas
2012-09-21 16:07                   ` [PATCH v4] PCI: introduce two interfaces to walk PCI buses Jiang Liu
2012-09-26 20:14                     ` Bjorn Helgaas
2012-09-13 16:00         ` [PATCH 2/2] PCI: remove host bridge hotplug unsafe interface pci_get_next_bus() Jiang Liu
2012-09-17 15:51         ` [RFC PATCH v1 06/22] PCI: use a global lock to serialize PCI root bridge hotplug operations Jiang Liu
2012-09-20 18:49         ` Paul E. McKenney
2012-08-07 16:10 ` Jiang Liu [this message]
2012-09-11 23:24   ` [RFC PATCH v1 07/22] PCI: introduce PCI bus lock to serialize PCI " Bjorn Helgaas
2012-08-07 16:10 ` [RFC PATCH v1 08/22] PCI: introduce hotplug safe search interfaces for PCI bus/device Jiang Liu
2012-08-07 16:10 ` [RFC PATCH v1 09/22] PCI: enhance PCI probe logic to support PCI bus lock mechanism Jiang Liu
2012-08-07 16:10 ` [RFC PATCH v1 10/22] PCI: enhance PCI bus specific " Jiang Liu
2012-08-07 16:10 ` [RFC PATCH v1 11/22] PCI: enhance PCI resource assignment " Jiang Liu
2012-08-07 16:10 ` [RFC PATCH v1 12/22] PCI: enhance PCI remove " Jiang Liu
2012-08-07 16:10 ` [RFC PATCH v1 13/22] PCI: make each PCI device hold a reference to its parent PCI bus Jiang Liu
2012-08-07 16:10 ` [RFC PATCH v1 14/22] PCI/sysfs: use PCI bus lock to avoid race conditions Jiang Liu
2012-08-07 16:10 ` [RFC PATCH v1 15/22] PCI/eeepc: " Jiang Liu
2012-09-11 23:18   ` Bjorn Helgaas
2012-09-12 14:24     ` [PATCH] eeepc-laptop: fix device reference count leakage in eeepc_rfkill_hotplug() Jiang Liu
2012-09-12 19:59       ` Bjorn Helgaas
2012-08-07 16:10 ` [RFC PATCH v1 16/22] PCI/asus-wmi: use PCI bus lock to avoid race conditions Jiang Liu
2012-08-07 16:10 ` [RFC PATCH v1 17/22] PCI/pciehp: " Jiang Liu
2012-08-07 16:10 ` [RFC PATCH v1 18/22] PCI/acpiphp: " Jiang Liu
2012-08-07 16:10 ` [RFC PATCH v1 19/22] PCI/x86: enable PCI bus lock mechanism for x86 platforms Jiang Liu
2012-09-11 23:22   ` Bjorn Helgaas
2012-09-12  9:56     ` Jiang Liu
2012-08-07 16:11 ` [RFC PATCH v1 20/22] PCI/IA64: enable PCI bus lock mechanism for IA64 platforms Jiang Liu
2012-08-07 16:11 ` [RFC PATCH v1 21/22] PCI: cleanups for PCI bus lock implementation Jiang Liu
2012-09-11 23:21   ` Bjorn Helgaas
2012-09-12  8:58     ` Jiang Liu
2012-08-07 16:11 ` [RFC PATCH v1 22/22] PCI: unexport pci_root_buses Jiang Liu
2012-08-07 18:11 ` [RFC PATCH v1 00/22] introduce PCI bus lock to serialize PCI hotplug operations Don Dutile
2012-08-08 15:49   ` Jiang Liu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1344355862-2726-8-git-send-email-jiang.liu@huawei.com \
    --to=liuj97@gmail.com \
    --cc=bhelgaas@google.com \
    --cc=ddutile@redhat.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=huxinwei@huawei.com \
    --cc=izumi.taku@jp.fujitsu.com \
    --cc=jiang.liu@huawei.com \
    --cc=kaneshige.kenji@jp.fujitsu.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=rjw@sisk.pl \
    --cc=wangyijing@huawei.com \
    --cc=yinghai@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.