2007-11-17 18:30:10

by Alex Chiang

[permalink] [raw]
Subject: [PATCH 0/4, v3] Physical PCI slot objects

Hi all,

This is v3 of the pci_slot patch series.

The major change is making the ACPI-PCI slot driver a Kconfig
option, as per the recommendations of others (Gary, Kenji-san).
In the process of doing so, it made sense to collapse the former
3/5 and 4/5 patches into a single 3/4 patch. There really wasn't
a reason to introduce a pci_slot patch, and then immediately
follow it with another patch modifying its interface; logically,
the changes should have been in the same patch.

Combining the patches also has the nice side benefit of keeping
the tree fully buildable and bisectable at all stages of series.

I have done quite a bit more testing, and verified that this
series plays nicely with acpiphp during all stages of the series.
Notably, you can modprobe/rmmod acpiphp repeatedly no matter
where you are in the series, and no matter whether you have
CONFIG_ACPI_PCI_SLOT turned on. The correct entries in
/sys/bus/pci/slots/ will appear and disappear, and we correctly
register/deregister ACPI slots with the pci_hp core.

Of course, if you *do* have the ACPI-PCI slot driver configured,
the slots/ entries in sysfs will be persistent. What you will see
is the hotplug attributes appear/disappear, depending on whether
you have acpiphp loaded or not.

Thanks for your consideration and all the feedback comments.
They're appreciated.

/ac

v2 -> v3:
Patch 1/4 - no change
Patch 2/4 - incorporate Eike's comments around snprintf
Patch 3/4 - Separated slot creation and slot hotplug ability
into two interfaces. Fixed bugs in pci_destroy_slot(),
and now properly calling from pci_hp_deregister.
Patch 4/4 - Add Kconfig option to driver, allowing users to
[de]config this driver. If configured, take slightly
different code paths in pci_hp_register and pci_hp_deregister.

v1 -> v2:
Patch 1/5 - reworked to fix stupid compile bug
Patch 2/5 - incorporate Eike, Linas, and Willy's comments
Patch 3/5 - no change
Patch 4/5 - was acpi-pci-slot-driver patch, now modifies
pci_add_hotplug(). I changed the ordering on this so
the tree doesn't break at this point in the series
Patch 5/5 - now is acpi-pci-slot-driver patch, cleaned up
implementation so our slot detection is a little
better


2007-11-17 18:35:29

by Alex Chiang

[permalink] [raw]
Subject: [PATCH 1/4, v3] PCI Hotplug: Remove path attribute from sgi_hotplug

Rename the slot to be the contents of the 'path' sysfs attribute, and
delete the attribute. The mapping from pci address to slot name is
supposed to be done through the 'address' file, which will be provided
automatically later in this series of patches.

Signed-off-by: Alex Chiang <[email protected]>
Signed-off-by: Matthew Wilcox <[email protected]>
---
drivers/pci/hotplug/sgi_hotplug.c | 32 +-------------------------------
1 files changed, 1 insertions(+), 31 deletions(-)

diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c
index ef07c36..693519e 100644
--- a/drivers/pci/hotplug/sgi_hotplug.c
+++ b/drivers/pci/hotplug/sgi_hotplug.c
@@ -91,21 +91,6 @@ static struct hotplug_slot_ops sn_hotplug_slot_ops = {

static DEFINE_MUTEX(sn_hotplug_mutex);

-static ssize_t path_show (struct hotplug_slot *bss_hotplug_slot,
- char *buf)
-{
- int retval = -ENOENT;
- struct slot *slot = bss_hotplug_slot->private;
-
- if (!slot)
- return retval;
-
- retval = sprintf (buf, "%s\n", slot->physical_path);
- return retval;
-}
-
-static struct hotplug_slot_attribute sn_slot_path_attr = __ATTR_RO(path);
-
static int sn_pci_slot_valid(struct pci_bus *pci_bus, int device)
{
struct pcibus_info *pcibus_info;
@@ -173,18 +158,10 @@ static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot,
return -ENOMEM;
bss_hotplug_slot->private = slot;

- bss_hotplug_slot->name = kmalloc(SN_SLOT_NAME_SIZE, GFP_KERNEL);
- if (!bss_hotplug_slot->name) {
- kfree(bss_hotplug_slot->private);
- return -ENOMEM;
- }
+ bss_hotplug_slot->name = slot->physical_path;

slot->device_num = device;
slot->pci_bus = pci_bus;
- sprintf(bss_hotplug_slot->name, "%04x:%02x:%02x",
- pci_domain_nr(pci_bus),
- ((u16)pcibus_info->pbi_buscommon.bs_persist_busnum),
- device + 1);

sn_generate_path(pci_bus, slot->physical_path);

@@ -203,8 +180,6 @@ static struct hotplug_slot * sn_hp_destroy(void)
bss_hotplug_slot = slot->hotplug_slot;
list_del(&((struct slot *)bss_hotplug_slot->private)->
hp_list);
- sysfs_remove_file(&bss_hotplug_slot->kobj,
- &sn_slot_path_attr.attr);
break;
}
return bss_hotplug_slot;
@@ -653,11 +628,6 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
rc = pci_hp_register(bss_hotplug_slot);
if (rc)
goto register_err;
-
- rc = sysfs_create_file(&bss_hotplug_slot->kobj,
- &sn_slot_path_attr.attr);
- if (rc)
- goto register_err;
}
dev_dbg(&pci_bus->self->dev, "Registered bus with hotplug\n");
return rc;
--
1.5.3.1.1.g1e61

2007-11-17 18:36:15

by Alex Chiang

[permalink] [raw]
Subject: [PATCH 2/4, v3] PCI Hotplug: Construct one fakephp slot per pci slot

Register one slot per slot, rather than one slot per function.
Change the name of the slot to fake%d instead of the pci address.

Signed-off-by: Alex Chiang <[email protected]>
Signed-off-by: Matthew Wilcox <[email protected]>
---
drivers/pci/hotplug/fakephp.c | 80 +++++++++++++++-------------------------
1 files changed, 30 insertions(+), 50 deletions(-)

diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c
index 027f686..996c942 100644
--- a/drivers/pci/hotplug/fakephp.c
+++ b/drivers/pci/hotplug/fakephp.c
@@ -63,6 +63,7 @@ struct dummy_slot {
struct list_head node;
struct hotplug_slot *slot;
struct pci_dev *dev;
+ char name[8];
};

static int debug;
@@ -93,6 +94,7 @@ static int add_slot(struct pci_dev *dev)
struct dummy_slot *dslot;
struct hotplug_slot *slot;
int retval = -ENOMEM;
+ static int count = 1;

slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
if (!slot)
@@ -106,13 +108,14 @@ static int add_slot(struct pci_dev *dev)
slot->info->max_bus_speed = PCI_SPEED_UNKNOWN;
slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN;

- slot->name = &dev->dev.bus_id[0];
- dbg("slot->name = %s\n", slot->name);
-
dslot = kmalloc(sizeof(struct dummy_slot), GFP_KERNEL);
if (!dslot)
goto error_info;

+ slot->name = dslot->name;
+ snprintf(slot->name, sizeof(dslot->name), "fake%d", count++);
+ dbg("slot->name = %s\n", slot->name);
+
slot->ops = &dummy_hotplug_slot_ops;
slot->release = &dummy_release;
slot->private = dslot;
@@ -141,17 +144,17 @@ error:
static int __init pci_scan_buses(void)
{
struct pci_dev *dev = NULL;
- int retval = 0;
+ int lastslot = 0;

while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
- retval = add_slot(dev);
- if (retval) {
- pci_dev_put(dev);
- break;
- }
+ if (PCI_FUNC(dev->devfn) > 0 &&
+ lastslot == PCI_SLOT(dev->devfn))
+ continue;
+ lastslot = PCI_SLOT(dev->devfn);
+ add_slot(dev);
}

- return retval;
+ return 0;
}

static void remove_slot(struct dummy_slot *dslot)
@@ -275,23 +278,9 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)
return -ENODEV;
}

-/* find the hotplug_slot for the pci_dev */
-static struct hotplug_slot *get_slot_from_dev(struct pci_dev *dev)
-{
- struct dummy_slot *dslot;
-
- list_for_each_entry(dslot, &slot_list, node) {
- if (dslot->dev == dev)
- return dslot->slot;
- }
- return NULL;
-}
-
-
static int disable_slot(struct hotplug_slot *slot)
{
struct dummy_slot *dslot;
- struct hotplug_slot *hslot;
struct pci_dev *dev;
int func;

@@ -301,36 +290,27 @@ static int disable_slot(struct hotplug_slot *slot)

dbg("%s - physical_slot = %s\n", __FUNCTION__, slot->name);

- /* don't disable bridged devices just yet, we can't handle them easily... */
- if (dslot->dev->subordinate) {
- err("Can't remove PCI devices with other PCI devices behind it yet.\n");
- return -ENODEV;
- }
- /* search for subfunctions and disable them first */
- if (!(dslot->dev->devfn & 7)) {
- for (func = 1; func < 8; func++) {
- dev = pci_get_slot(dslot->dev->bus,
- dslot->dev->devfn + func);
- if (dev) {
- hslot = get_slot_from_dev(dev);
- if (hslot)
- disable_slot(hslot);
- else {
- err("Hotplug slot not found for subfunction of PCI device\n");
- return -ENODEV;
- }
- pci_dev_put(dev);
- } else
- dbg("No device in slot found\n");
+ for (func = 7; func >= 0; func--) {
+ dev = pci_get_slot(dslot->dev->bus, dslot->dev->devfn + func);
+ if (!dev)
+ continue;
+
+ /* don't disable bridged devices just yet, we can't handle
+ * them easily... */
+ if (dev->subordinate) {
+ err("Can't remove PCI devices with other PCI devices behind it yet.\n");
+ return -ENODEV;
}
- }

- /* remove the device from the pci core */
- pci_remove_bus_device(dslot->dev);

- /* blow away this sysfs entry and other parts. */
- remove_slot(dslot);
+ /* remove the device from the pci core */
+ pci_remove_bus_device(dslot->dev);

+ /* blow away this sysfs entry and other parts. */
+ remove_slot(dslot);
+
+ pci_dev_put(dev);
+ }
return 0;
}

--
1.5.3.1.1.g1e61

2007-11-17 18:38:41

by Alex Chiang

[permalink] [raw]
Subject: [PATCH 3/4, v3] PCI, PCI Hotplug: Introduce pci_slot

- Make pci_slot the primary sysfs entity. hotplug_slot becomes a
subsidiary structure.
o pci_create_slot() creates and registers a slot with the PCI core
o pci_slot_add_hotplug() gives it hotplug capability

- Change the prototype of pci_hp_register() to take the bus and
slot number (on parent bus) as parameters.

- Remove all the ->get_address methods since this functionality is
now handled by pci_slot directly.

v2 -> v3:
Separated slot creation and slot hotplug ability into two
interfaces. Fixed bugs in pci_destroy_slot(), and now
properly calling from pci_hp_deregister.

v1 -> v2:
No change

Signed-off-by: Alex Chiang <[email protected]>
Signed-off-by: Matthew Wilcox <[email protected]>
---
drivers/pci/Makefile | 2 +-
drivers/pci/hotplug/acpiphp.h | 1 -
drivers/pci/hotplug/acpiphp_core.c | 23 +---
drivers/pci/hotplug/acpiphp_glue.c | 16 --
drivers/pci/hotplug/acpiphp_ibm.c | 5 +-
drivers/pci/hotplug/cpci_hotplug_core.c | 2 +-
drivers/pci/hotplug/cpqphp_core.c | 4 +-
drivers/pci/hotplug/fakephp.c | 2 +-
drivers/pci/hotplug/ibmphp_ebda.c | 3 +-
drivers/pci/hotplug/pci_hotplug_core.c | 242 +++++++++++--------------------
drivers/pci/hotplug/pciehp_core.c | 22 +--
drivers/pci/hotplug/rpadlpar_sysfs.c | 4 +-
drivers/pci/hotplug/sgi_hotplug.c | 2 +-
drivers/pci/hotplug/shpchp_core.c | 17 +--
drivers/pci/pci.h | 13 ++
drivers/pci/slot.c | 184 +++++++++++++++++++++++
include/linux/pci.h | 17 ++
include/linux/pci_hotplug.h | 12 +-
18 files changed, 328 insertions(+), 243 deletions(-)
create mode 100644 drivers/pci/slot.c

diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 5550556..12f0b2d 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -2,7 +2,7 @@
# Makefile for the PCI bus specific drivers.
#

-obj-y += access.o bus.o probe.o remove.o pci.o quirks.o \
+obj-y += access.o bus.o probe.o remove.o pci.o quirks.o slot.o \
pci-driver.o search.o pci-sysfs.o rom.o setup-res.o
obj-$(CONFIG_PROC_FS) += proc.o

diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h
index f6cc0c5..ab46189 100644
--- a/drivers/pci/hotplug/acpiphp.h
+++ b/drivers/pci/hotplug/acpiphp.h
@@ -216,7 +216,6 @@ extern u8 acpiphp_get_power_status (struct acpiphp_slot *slot);
extern u8 acpiphp_get_attention_status (struct acpiphp_slot *slot);
extern u8 acpiphp_get_latch_status (struct acpiphp_slot *slot);
extern u8 acpiphp_get_adapter_status (struct acpiphp_slot *slot);
-extern u32 acpiphp_get_address (struct acpiphp_slot *slot);

/* variables */
extern int acpiphp_debug;
diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c
index a0ca63a..34b8d0b 100644
--- a/drivers/pci/hotplug/acpiphp_core.c
+++ b/drivers/pci/hotplug/acpiphp_core.c
@@ -70,7 +70,6 @@ static int disable_slot (struct hotplug_slot *slot);
static int set_attention_status (struct hotplug_slot *slot, u8 value);
static int get_power_status (struct hotplug_slot *slot, u8 *value);
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
-static int get_address (struct hotplug_slot *slot, u32 *value);
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);

@@ -83,7 +82,6 @@ static struct hotplug_slot_ops acpi_hotplug_slot_ops = {
.get_attention_status = get_attention_status,
.get_latch_status = get_latch_status,
.get_adapter_status = get_adapter_status,
- .get_address = get_address,
};


@@ -279,23 +277,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
return 0;
}

-
-/**
- * get_address - get pci address of a slot
- * @hotplug_slot: slot to get status
- * @value: pointer to struct pci_busdev (seg, bus, dev)
- */
-static int get_address(struct hotplug_slot *hotplug_slot, u32 *value)
-{
- struct slot *slot = hotplug_slot->private;
-
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
-
- *value = acpiphp_get_address(slot->acpi_slot);
-
- return 0;
-}
-
static int __init init_acpi(void)
{
int retval;
@@ -362,7 +343,9 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
acpiphp_slot->slot = slot;
snprintf(slot->name, sizeof(slot->name), "%u", slot->acpi_slot->sun);

- retval = pci_hp_register(slot->hotplug_slot);
+ retval = pci_hp_register(slot->hotplug_slot,
+ acpiphp_slot->bridge->pci_bus,
+ acpiphp_slot->device);
if (retval) {
err("pci_hp_register failed with error %d\n", retval);
goto error_hpslot;
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index 1e125b5..02a2fd7 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -1874,19 +1874,3 @@ u8 acpiphp_get_adapter_status(struct acpiphp_slot *slot)

return (sta == 0) ? 0 : 1;
}
-
-
-/*
- * pci address (seg/bus/dev)
- */
-u32 acpiphp_get_address(struct acpiphp_slot *slot)
-{
- u32 address;
- struct pci_bus *pci_bus = slot->bridge->pci_bus;
-
- address = (pci_domain_nr(pci_bus) << 16) |
- (pci_bus->number << 8) |
- slot->device;
-
- return address;
-}
diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c
index 56829f8..927b1fb 100644
--- a/drivers/pci/hotplug/acpiphp_ibm.c
+++ b/drivers/pci/hotplug/acpiphp_ibm.c
@@ -35,6 +35,7 @@
#include <linux/moduleparam.h>

#include "acpiphp.h"
+extern struct kset pci_slots_subsys;

#define DRIVER_VERSION "1.0.1"
#define DRIVER_AUTHOR "Irene Zubarev <[email protected]>, Vernon Mauery <[email protected]>"
@@ -428,7 +429,7 @@ static int __init ibm_acpiphp_init(void)
int retval = 0;
acpi_status status;
struct acpi_device *device;
- struct kobject *sysdir = &pci_hotplug_slots_subsys.kobj;
+ struct kobject *sysdir = &pci_slots_subsys.kobj;

dbg("%s\n", __FUNCTION__);

@@ -475,7 +476,7 @@ init_return:
static void __exit ibm_acpiphp_exit(void)
{
acpi_status status;
- struct kobject *sysdir = &pci_hotplug_slots_subsys.kobj;
+ struct kobject *sysdir = &pci_slots_subsys.kobj;

dbg("%s\n", __FUNCTION__);

diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c
index ed4d44e..aa47b80 100644
--- a/drivers/pci/hotplug/cpci_hotplug_core.c
+++ b/drivers/pci/hotplug/cpci_hotplug_core.c
@@ -285,7 +285,7 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
info->attention_status = cpci_get_attention_status(slot);

dbg("registering slot %s", slot->hotplug_slot->name);
- status = pci_hp_register(slot->hotplug_slot);
+ status = pci_hp_register(slot->hotplug_slot, bus, i);
if (status) {
err("pci_hp_register failed with error %d", status);
goto error_name;
diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c
index a96b739..67f6a0c 100644
--- a/drivers/pci/hotplug/cpqphp_core.c
+++ b/drivers/pci/hotplug/cpqphp_core.c
@@ -434,7 +434,9 @@ static int ctrl_slot_setup(struct controller *ctrl,
slot->bus, slot->device,
slot->number, ctrl->slot_device_offset,
slot_number);
- result = pci_hp_register(hotplug_slot);
+ result = pci_hp_register(hotplug_slot,
+ ctrl->pci_dev->subordinate,
+ slot->device);
if (result) {
err("pci_hp_register failed with error %d\n", result);
goto error_name;
diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c
index 996c942..405c608 100644
--- a/drivers/pci/hotplug/fakephp.c
+++ b/drivers/pci/hotplug/fakephp.c
@@ -120,7 +120,7 @@ static int add_slot(struct pci_dev *dev)
slot->release = &dummy_release;
slot->private = dslot;

- retval = pci_hp_register(slot);
+ retval = pci_hp_register(slot, dev->bus, PCI_SLOT(dev->devfn));
if (retval) {
err("pci_hp_register failed with error %d\n", retval);
goto error_dslot;
diff --git a/drivers/pci/hotplug/ibmphp_ebda.c b/drivers/pci/hotplug/ibmphp_ebda.c
index 600ed7b..eb7a1c0 100644
--- a/drivers/pci/hotplug/ibmphp_ebda.c
+++ b/drivers/pci/hotplug/ibmphp_ebda.c
@@ -1000,7 +1000,8 @@ static int __init ebda_rsrc_controller (void)
tmp_slot = list_entry (list, struct slot, ibm_slot_list);

snprintf (tmp_slot->hotplug_slot->name, 30, "%s", create_file_name (tmp_slot));
- pci_hp_register (tmp_slot->hotplug_slot);
+ pci_hp_register(tmp_slot->hotplug_slot,
+ pci_find_bus(0, tmp_slot->bus), tmp_slot->device);
}

print_ebda_hpc ();
diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c
index 01c351c..1be5b0d 100644
--- a/drivers/pci/hotplug/pci_hotplug_core.c
+++ b/drivers/pci/hotplug/pci_hotplug_core.c
@@ -40,6 +40,7 @@
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
#include <asm/uaccess.h>
+#include "../pci.h"

#define MY_NAME "pci_hotplug"

@@ -61,43 +62,6 @@ static int debug;

static LIST_HEAD(pci_hotplug_slot_list);

-struct kset pci_hotplug_slots_subsys;
-
-static ssize_t hotplug_slot_attr_show(struct kobject *kobj,
- struct attribute *attr, char *buf)
-{
- struct hotplug_slot *slot = to_hotplug_slot(kobj);
- struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr);
- return attribute->show ? attribute->show(slot, buf) : -EIO;
-}
-
-static ssize_t hotplug_slot_attr_store(struct kobject *kobj,
- struct attribute *attr, const char *buf, size_t len)
-{
- struct hotplug_slot *slot = to_hotplug_slot(kobj);
- struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr);
- return attribute->store ? attribute->store(slot, buf, len) : -EIO;
-}
-
-static struct sysfs_ops hotplug_slot_sysfs_ops = {
- .show = hotplug_slot_attr_show,
- .store = hotplug_slot_attr_store,
-};
-
-static void hotplug_slot_release(struct kobject *kobj)
-{
- struct hotplug_slot *slot = to_hotplug_slot(kobj);
- if (slot->release)
- slot->release(slot);
-}
-
-static struct kobj_type hotplug_slot_ktype = {
- .sysfs_ops = &hotplug_slot_sysfs_ops,
- .release = &hotplug_slot_release,
-};
-
-decl_subsys_name(pci_hotplug_slots, slots, &hotplug_slot_ktype, NULL);
-
/* these strings match up with the values in pci_bus_speed */
static char *pci_bus_speed_strings[] = {
"33 MHz PCI", /* 0x00 */
@@ -151,16 +115,15 @@ GET_STATUS(power_status, u8)
GET_STATUS(attention_status, u8)
GET_STATUS(latch_status, u8)
GET_STATUS(adapter_status, u8)
-GET_STATUS(address, u32)
GET_STATUS(max_bus_speed, enum pci_bus_speed)
GET_STATUS(cur_bus_speed, enum pci_bus_speed)

-static ssize_t power_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t power_read_file(struct pci_slot *slot, char *buf)
{
int retval;
u8 value;

- retval = get_power_status (slot, &value);
+ retval = get_power_status(slot->hotplug, &value);
if (retval)
goto exit;
retval = sprintf (buf, "%d\n", value);
@@ -168,9 +131,10 @@ exit:
return retval;
}

-static ssize_t power_write_file (struct hotplug_slot *slot, const char *buf,
+static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,
size_t count)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
unsigned long lpower;
u8 power;
int retval = 0;
@@ -206,29 +170,30 @@ exit:
return count;
}

-static struct hotplug_slot_attribute hotplug_slot_attr_power = {
+static struct pci_slot_attribute hotplug_slot_attr_power = {
.attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR},
.show = power_read_file,
.store = power_write_file
};

-static ssize_t attention_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t attention_read_file(struct pci_slot *slot, char *buf)
{
int retval;
u8 value;

- retval = get_attention_status (slot, &value);
+ retval = get_attention_status(slot->hotplug, &value);
if (retval)
goto exit;
- retval = sprintf (buf, "%d\n", value);
+ retval = sprintf(buf, "%d\n", value);

exit:
return retval;
}

-static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf,
+static ssize_t attention_write_file(struct pci_slot *slot, const char *buf,
size_t count)
{
+ struct hotplug_slot_ops *ops = slot->hotplug->ops;
unsigned long lattention;
u8 attention;
int retval = 0;
@@ -237,13 +202,13 @@ static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf,
attention = (u8)(lattention & 0xff);
dbg (" - attention = %d\n", attention);

- if (!try_module_get(slot->ops->owner)) {
+ if (!try_module_get(ops->owner)) {
retval = -ENODEV;
goto exit;
}
- if (slot->ops->set_attention_status)
- retval = slot->ops->set_attention_status(slot, attention);
- module_put(slot->ops->owner);
+ if (ops->set_attention_status)
+ retval = ops->set_attention_status(slot->hotplug, attention);
+ module_put(ops->owner);

exit:
if (retval)
@@ -251,18 +216,18 @@ exit:
return count;
}

-static struct hotplug_slot_attribute hotplug_slot_attr_attention = {
+static struct pci_slot_attribute hotplug_slot_attr_attention = {
.attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR},
.show = attention_read_file,
.store = attention_write_file
};

-static ssize_t latch_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t latch_read_file(struct pci_slot *slot, char *buf)
{
int retval;
u8 value;

- retval = get_latch_status (slot, &value);
+ retval = get_latch_status(slot->hotplug, &value);
if (retval)
goto exit;
retval = sprintf (buf, "%d\n", value);
@@ -271,17 +236,17 @@ exit:
return retval;
}

-static struct hotplug_slot_attribute hotplug_slot_attr_latch = {
+static struct pci_slot_attribute hotplug_slot_attr_latch = {
.attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},
.show = latch_read_file,
};

-static ssize_t presence_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t presence_read_file(struct pci_slot *slot, char *buf)
{
int retval;
u8 value;

- retval = get_adapter_status (slot, &value);
+ retval = get_adapter_status(slot->hotplug, &value);
if (retval)
goto exit;
retval = sprintf (buf, "%d\n", value);
@@ -290,42 +255,20 @@ exit:
return retval;
}

-static struct hotplug_slot_attribute hotplug_slot_attr_presence = {
+static struct pci_slot_attribute hotplug_slot_attr_presence = {
.attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},
.show = presence_read_file,
};

-static ssize_t address_read_file (struct hotplug_slot *slot, char *buf)
-{
- int retval;
- u32 address;
-
- retval = get_address (slot, &address);
- if (retval)
- goto exit;
- retval = sprintf (buf, "%04x:%02x:%02x\n",
- (address >> 16) & 0xffff,
- (address >> 8) & 0xff,
- address & 0xff);
-
-exit:
- return retval;
-}
-
-static struct hotplug_slot_attribute hotplug_slot_attr_address = {
- .attr = {.name = "address", .mode = S_IFREG | S_IRUGO},
- .show = address_read_file,
-};
-
static char *unknown_speed = "Unknown bus speed";

-static ssize_t max_bus_speed_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t max_bus_speed_read_file(struct pci_slot *slot, char *buf)
{
char *speed_string;
int retval;
enum pci_bus_speed value;

- retval = get_max_bus_speed (slot, &value);
+ retval = get_max_bus_speed(slot->hotplug, &value);
if (retval)
goto exit;

@@ -340,18 +283,18 @@ exit:
return retval;
}

-static struct hotplug_slot_attribute hotplug_slot_attr_max_bus_speed = {
+static struct pci_slot_attribute hotplug_slot_attr_max_bus_speed = {
.attr = {.name = "max_bus_speed", .mode = S_IFREG | S_IRUGO},
.show = max_bus_speed_read_file,
};

-static ssize_t cur_bus_speed_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t cur_bus_speed_read_file(struct pci_slot *slot, char *buf)
{
char *speed_string;
int retval;
enum pci_bus_speed value;

- retval = get_cur_bus_speed (slot, &value);
+ retval = get_cur_bus_speed(slot->hotplug, &value);
if (retval)
goto exit;

@@ -366,14 +309,15 @@ exit:
return retval;
}

-static struct hotplug_slot_attribute hotplug_slot_attr_cur_bus_speed = {
+static struct pci_slot_attribute hotplug_slot_attr_cur_bus_speed = {
.attr = {.name = "cur_bus_speed", .mode = S_IFREG | S_IRUGO},
.show = cur_bus_speed_read_file,
};

-static ssize_t test_write_file (struct hotplug_slot *slot, const char *buf,
+static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,
size_t count)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
unsigned long ltest;
u32 test;
int retval = 0;
@@ -396,13 +340,14 @@ exit:
return count;
}

-static struct hotplug_slot_attribute hotplug_slot_attr_test = {
+static struct pci_slot_attribute hotplug_slot_attr_test = {
.attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},
.store = test_write_file
};

-static int has_power_file (struct hotplug_slot *slot)
+static int has_power_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if ((slot->ops->enable_slot) ||
@@ -412,8 +357,9 @@ static int has_power_file (struct hotplug_slot *slot)
return -ENOENT;
}

-static int has_attention_file (struct hotplug_slot *slot)
+static int has_attention_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if ((slot->ops->set_attention_status) ||
@@ -422,8 +368,9 @@ static int has_attention_file (struct hotplug_slot *slot)
return -ENOENT;
}

-static int has_latch_file (struct hotplug_slot *slot)
+static int has_latch_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->get_latch_status)
@@ -431,8 +378,9 @@ static int has_latch_file (struct hotplug_slot *slot)
return -ENOENT;
}

-static int has_adapter_file (struct hotplug_slot *slot)
+static int has_adapter_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->get_adapter_status)
@@ -440,17 +388,9 @@ static int has_adapter_file (struct hotplug_slot *slot)
return -ENOENT;
}

-static int has_address_file (struct hotplug_slot *slot)
-{
- if ((!slot) || (!slot->ops))
- return -ENODEV;
- if (slot->ops->get_address)
- return 0;
- return -ENOENT;
-}
-
-static int has_max_bus_speed_file (struct hotplug_slot *slot)
+static int has_max_bus_speed_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->get_max_bus_speed)
@@ -458,8 +398,9 @@ static int has_max_bus_speed_file (struct hotplug_slot *slot)
return -ENOENT;
}

-static int has_cur_bus_speed_file (struct hotplug_slot *slot)
+static int has_cur_bus_speed_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->get_cur_bus_speed)
@@ -467,8 +408,9 @@ static int has_cur_bus_speed_file (struct hotplug_slot *slot)
return -ENOENT;
}

-static int has_test_file (struct hotplug_slot *slot)
+static int has_test_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->hardware_test)
@@ -476,7 +418,7 @@ static int has_test_file (struct hotplug_slot *slot)
return -ENOENT;
}

-static int fs_add_slot (struct hotplug_slot *slot)
+static int fs_add_slot(struct pci_slot *slot)
{
int retval = 0;

@@ -507,13 +449,6 @@ static int fs_add_slot (struct hotplug_slot *slot)
goto exit_adapter;
}

- if (has_address_file(slot) == 0) {
- retval = sysfs_create_file(&slot->kobj,
- &hotplug_slot_attr_address.attr);
- if (retval)
- goto exit_address;
- }
-
if (has_max_bus_speed_file(slot) == 0) {
retval = sysfs_create_file(&slot->kobj,
&hotplug_slot_attr_max_bus_speed.attr);
@@ -546,10 +481,6 @@ exit_cur_speed:
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);

exit_max_speed:
- if (has_address_file(slot) == 0)
- sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr);
-
-exit_address:
if (has_adapter_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);

@@ -569,7 +500,7 @@ exit:
return retval;
}

-static void fs_remove_slot (struct hotplug_slot *slot)
+static void fs_remove_slot(struct pci_slot *slot)
{
if (has_power_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
@@ -583,9 +514,6 @@ static void fs_remove_slot (struct hotplug_slot *slot)
if (has_adapter_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);

- if (has_address_file(slot) == 0)
- sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr);
-
if (has_max_bus_speed_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);

@@ -609,6 +537,12 @@ static struct hotplug_slot *get_slot_from_name (const char *name)
return NULL;
}

+static void hotplug_release(struct pci_slot *slot)
+{
+ struct hotplug_slot *hotplug = slot->hotplug;
+ hotplug->release(hotplug);
+}
+
/**
* pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem
* @slot: pointer to the &struct hotplug_slot to register
@@ -618,8 +552,9 @@ static struct hotplug_slot *get_slot_from_name (const char *name)
*
* Returns 0 if successful, anything else for an error.
*/
-int pci_hp_register (struct hotplug_slot *slot)
+int pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, int slot_nr)
{
+ struct pci_slot *pci_slot;
int result;

if (slot == NULL)
@@ -632,18 +567,20 @@ int pci_hp_register (struct hotplug_slot *slot)
return -EINVAL;
}

- kobject_set_name(&slot->kobj, "%s", slot->name);
- kobj_set_kset_s(slot, pci_hotplug_slots_subsys);
+ pci_slot = pci_create_slot(bus, slot_nr, slot->name);
+ if (IS_ERR(pci_slot))
+ return PTR_ERR(pci_slot);

- /* this can fail if we have already registered a slot with the same name */
- if (kobject_register(&slot->kobj)) {
- err("Unable to register kobject");
- return -EINVAL;
- }
-
- list_add (&slot->slot_list, &pci_hotplug_slot_list);
+ pci_slot = pci_slot_add_hotplug(bus, slot_nr, hotplug_release);
+ if (IS_ERR(pci_slot))
+ return PTR_ERR(pci_slot);
+
+ slot->pci_slot = pci_slot;
+ pci_slot->hotplug = slot;

- result = fs_add_slot (slot);
+ list_add(&slot->slot_list, &pci_hotplug_slot_list);
+
+ result = fs_add_slot(pci_slot);
dbg ("Added slot %s to the list\n", slot->name);
return result;
}
@@ -657,22 +594,24 @@ int pci_hp_register (struct hotplug_slot *slot)
*
* Returns 0 if successful, anything else for an error.
*/
-int pci_hp_deregister (struct hotplug_slot *slot)
+int pci_hp_deregister(struct hotplug_slot *hotplug)
{
struct hotplug_slot *temp;
+ struct pci_slot *slot;

- if (slot == NULL)
+ if (!hotplug)
return -ENODEV;

- temp = get_slot_from_name (slot->name);
- if (temp != slot) {
+ temp = get_slot_from_name(hotplug->name);
+ if (temp != hotplug)
return -ENODEV;
- }
- list_del (&slot->slot_list);

- fs_remove_slot (slot);
- dbg ("Removed slot %s from the list\n", slot->name);
- kobject_unregister(&slot->kobj);
+ list_del(&hotplug->slot_list);
+
+ slot = hotplug->pci_slot;
+ fs_remove_slot(slot);
+ pci_destroy_slot(slot);
+ dbg("Removed slot %s from the list\n", hotplug->name);
return 0;
}

@@ -686,13 +625,15 @@ int pci_hp_deregister (struct hotplug_slot *slot)
*
* Returns 0 if successful, anything else for an error.
*/
-int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot,
+int __must_check pci_hp_change_slot_info(struct hotplug_slot *hotplug,
struct hotplug_slot_info *info)
{
- if ((slot == NULL) || (info == NULL))
+ struct pci_slot *slot;
+ if (!hotplug || !info)
return -ENODEV;
+ slot = hotplug->pci_slot;

- memcpy (slot->info, info, sizeof (struct hotplug_slot_info));
+ memcpy(hotplug->info, info, sizeof(struct hotplug_slot_info));

return 0;
}
@@ -701,31 +642,21 @@ static int __init pci_hotplug_init (void)
{
int result;

- kobj_set_kset_s(&pci_hotplug_slots_subsys, pci_bus_type.subsys);
- result = subsystem_register(&pci_hotplug_slots_subsys);
- if (result) {
- err("Register subsys with error %d\n", result);
- goto exit;
- }
result = cpci_hotplug_init(debug);
if (result) {
err ("cpci_hotplug_init with error %d\n", result);
- goto err_subsys;
+ goto err_cpci;
}

info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
- goto exit;
-
-err_subsys:
- subsystem_unregister(&pci_hotplug_slots_subsys);
-exit:
+
+err_cpci:
return result;
}

static void __exit pci_hotplug_exit (void)
{
cpci_hotplug_exit();
- subsystem_unregister(&pci_hotplug_slots_subsys);
}

module_init(pci_hotplug_init);
@@ -737,7 +668,6 @@ MODULE_LICENSE("GPL");
module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");

-EXPORT_SYMBOL_GPL(pci_hotplug_slots_subsys);
EXPORT_SYMBOL_GPL(pci_hp_register);
EXPORT_SYMBOL_GPL(pci_hp_deregister);
EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index 6462ac3..8fee402 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -69,7 +69,6 @@ static int get_power_status (struct hotplug_slot *slot, u8 *value);
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
-static int get_address (struct hotplug_slot *slot, u32 *value);
static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);

@@ -82,7 +81,6 @@ static struct hotplug_slot_ops pciehp_hotplug_slot_ops = {
.get_attention_status = get_attention_status,
.get_latch_status = get_latch_status,
.get_adapter_status = get_adapter_status,
- .get_address = get_address,
.get_max_bus_speed = get_max_bus_speed,
.get_cur_bus_speed = get_cur_bus_speed,
};
@@ -245,14 +243,16 @@ static int init_slots(struct controller *ctrl)
dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x "
"slot_device_offset=%x\n", slot->bus, slot->device,
slot->hp_slot, slot->number, ctrl->slot_device_offset);
- retval = pci_hp_register(hotplug_slot);
+ retval = pci_hp_register(hotplug_slot,
+ ctrl->pci_dev->subordinate,
+ slot->number);
if (retval) {
err ("pci_hp_register failed with error %d\n", retval);
goto error_info;
}
/* create additional sysfs entries */
if (EMI(ctrl->ctrlcap)) {
- retval = sysfs_create_file(&hotplug_slot->kobj,
+ retval = sysfs_create_file(&hotplug_slot->pci_slot->kobj,
&hotplug_slot_attr_lock.attr);
if (retval) {
pci_hp_deregister(hotplug_slot);
@@ -285,7 +285,7 @@ static void cleanup_slots(struct controller *ctrl)
slot = list_entry(tmp, struct slot, slot_list);
list_del(&slot->slot_list);
if (EMI(ctrl->ctrlcap))
- sysfs_remove_file(&slot->hotplug_slot->kobj,
+ sysfs_remove_file(&slot->hotplug_slot->pci_slot->kobj,
&hotplug_slot_attr_lock.attr);
cancel_delayed_work(&slot->work);
flush_scheduled_work();
@@ -387,18 +387,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
return 0;
}

-static int get_address(struct hotplug_slot *hotplug_slot, u32 *value)
-{
- struct slot *slot = hotplug_slot->private;
- struct pci_bus *bus = slot->ctrl->pci_dev->subordinate;
-
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
-
- *value = (pci_domain_nr(bus) << 16) | (slot->bus << 8) | slot->device;
-
- return 0;
-}
-
static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
{
struct slot *slot = hotplug_slot->private;
diff --git a/drivers/pci/hotplug/rpadlpar_sysfs.c b/drivers/pci/hotplug/rpadlpar_sysfs.c
index a080fed..3663507 100644
--- a/drivers/pci/hotplug/rpadlpar_sysfs.c
+++ b/drivers/pci/hotplug/rpadlpar_sysfs.c
@@ -23,6 +23,8 @@

#define MAX_DRC_NAME_LEN 64

+extern struct kset pci_slots_subsys;
+
/* Store return code of dlpar operation in attribute struct */
struct dlpar_io_attr {
int rc;
@@ -130,7 +132,7 @@ struct kobj_type ktype_dlpar_io = {

struct kset dlpar_io_kset = {
.kobj = {.ktype = &ktype_dlpar_io,
- .parent = &pci_hotplug_slots_subsys.kobj},
+ .parent = &pci_slots_subsys.kobj},
.ktype = &ktype_dlpar_io,
};

diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c
index 693519e..cc74602 100644
--- a/drivers/pci/hotplug/sgi_hotplug.c
+++ b/drivers/pci/hotplug/sgi_hotplug.c
@@ -625,7 +625,7 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
bss_hotplug_slot->ops = &sn_hotplug_slot_ops;
bss_hotplug_slot->release = &sn_release_slot;

- rc = pci_hp_register(bss_hotplug_slot);
+ rc = pci_hp_register(bss_hotplug_slot, pci_bus, device);
if (rc)
goto register_err;
}
diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c
index 80dec97..22c4d2e 100644
--- a/drivers/pci/hotplug/shpchp_core.c
+++ b/drivers/pci/hotplug/shpchp_core.c
@@ -65,7 +65,6 @@ static int get_power_status (struct hotplug_slot *slot, u8 *value);
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
-static int get_address (struct hotplug_slot *slot, u32 *value);
static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);

@@ -78,7 +77,6 @@ static struct hotplug_slot_ops shpchp_hotplug_slot_ops = {
.get_attention_status = get_attention_status,
.get_latch_status = get_latch_status,
.get_adapter_status = get_adapter_status,
- .get_address = get_address,
.get_max_bus_speed = get_max_bus_speed,
.get_cur_bus_speed = get_cur_bus_speed,
};
@@ -152,7 +150,8 @@ static int init_slots(struct controller *ctrl)
dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x "
"slot_device_offset=%x\n", slot->bus, slot->device,
slot->hp_slot, slot->number, ctrl->slot_device_offset);
- retval = pci_hp_register(slot->hotplug_slot);
+ retval = pci_hp_register(slot->hotplug_slot,
+ ctrl->pci_dev->subordinate, slot->device);
if (retval) {
err("pci_hp_register failed with error %d\n", retval);
goto error_info;
@@ -277,18 +276,6 @@ static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value)
return 0;
}

-static int get_address (struct hotplug_slot *hotplug_slot, u32 *value)
-{
- struct slot *slot = get_slot(hotplug_slot);
- struct pci_bus *bus = slot->ctrl->pci_dev->subordinate;
-
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
-
- *value = (pci_domain_nr(bus) << 16) | (slot->bus << 8) | slot->device;
-
- return 0;
-}
-
static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
{
struct slot *slot = get_slot(hotplug_slot);
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index fc87e14..4bcfabf 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -91,3 +91,16 @@ pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev)
}

struct pci_dev *pci_find_upstream_pcie_bridge(struct pci_dev *pdev);
+
+/* PCI slot sysfs helper code */
+#define to_pci_slot(s) container_of(s, struct pci_slot, kobj)
+
+extern struct kset pci_slots_subsys;
+
+struct pci_slot_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct pci_slot *, char *);
+ ssize_t (*store)(struct pci_slot *, const char *, size_t);
+};
+#define to_pci_slot_attr(s) container_of(s, struct pci_slot_attribute, attr)
+
diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c
new file mode 100644
index 0000000..f4ca61b
--- /dev/null
+++ b/drivers/pci/slot.c
@@ -0,0 +1,184 @@
+/*
+ * drivers/pci/slot.c
+ * Copyright (c) 2006 Matthew Wilcox <[email protected]>
+ * Copyright (c) 2006 Hewlett-Packard Company
+ */
+
+#include <linux/kobject.h>
+#include <linux/pci.h>
+#include "pci.h"
+
+struct kset pci_slots_subsys;
+EXPORT_SYMBOL_GPL(pci_slots_subsys);
+
+static ssize_t pci_slot_attr_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct pci_slot *slot = to_pci_slot(kobj);
+ struct pci_slot_attribute *attribute = to_pci_slot_attr(attr);
+ return attribute->show ? attribute->show(slot, buf) : -EIO;
+}
+
+static ssize_t pci_slot_attr_store(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t len)
+{
+ struct pci_slot *slot = to_pci_slot(kobj);
+ struct pci_slot_attribute *attribute = to_pci_slot_attr(attr);
+ return attribute->store ? attribute->store(slot, buf, len) : -EIO;
+}
+
+static struct sysfs_ops pci_slot_sysfs_ops = {
+ .show = pci_slot_attr_show,
+ .store = pci_slot_attr_store,
+};
+
+static void pci_slot_release(struct kobject *kobj)
+{
+ struct pci_slot *slot = to_pci_slot(kobj);
+ slot->release(slot);
+ kfree(slot);
+}
+
+static struct kobj_type pci_slot_ktype = {
+ .sysfs_ops = &pci_slot_sysfs_ops,
+ .release = &pci_slot_release,
+};
+
+decl_subsys_name(pci_slots, slots, &pci_slot_ktype, NULL);
+
+static ssize_t address_read_file(struct pci_slot *slot, char *buf)
+{
+ return sprintf(buf, "%04x:%02x:%02x\n", pci_domain_nr(slot->bus),
+ slot->bus->number, slot->number);
+}
+
+static struct pci_slot_attribute pci_slot_attr_address = {
+ .attr = { .name = "address", .mode = S_IFREG | S_IRUGO },
+ .show = address_read_file,
+};
+
+static void remove_sysfs_files(struct pci_slot *slot)
+{
+ sysfs_remove_file(&slot->kobj, &pci_slot_attr_address.attr);
+}
+
+static int create_sysfs_files(struct pci_slot *slot)
+{
+ int result;
+
+ result = sysfs_create_file(&slot->kobj, &pci_slot_attr_address.attr);
+
+ return result;
+}
+
+struct pci_slot *pci_slot_add_hotplug(struct pci_bus *parent, int slot_nr,
+ void (*release)(struct pci_slot *))
+{
+ struct pci_slot *slot;
+ int found = 0;
+
+ down_write(&pci_bus_sem);
+
+ /* This slot should have already been created, so look for it. If
+ * we can't find it, return -EEXIST.
+ */
+ for (slot = parent->slot; slot; slot = slot->next)
+ if (slot->number == slot_nr) {
+ found = 1;
+ break;
+ }
+
+ if (!found) {
+ slot = ERR_PTR(-EEXIST);
+ goto out;
+ }
+
+ slot->release = release;
+ out:
+ up_write(&pci_bus_sem);
+ return slot;
+}
+EXPORT_SYMBOL_GPL(pci_slot_add_hotplug);
+
+struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr,
+ const char *name)
+{
+ struct pci_slot *slot;
+ int err;
+
+ down_write(&pci_bus_sem);
+
+ /* If we've already created this slot, just return. */
+ for (slot = parent->slot; slot; slot = slot->next) {
+ if (slot->number != slot_nr)
+ continue;
+ goto out;
+ }
+
+ slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+ if (!slot) {
+ slot = ERR_PTR(-ENOMEM);
+ goto out;
+ }
+
+ slot->bus = parent;
+ slot->number = slot_nr;
+
+ kobject_set_name(&slot->kobj, "%s", name);
+ kobj_set_kset_s(slot, pci_slots_subsys);
+ if (kobject_register(&slot->kobj)) {
+ printk(KERN_ERR "Unable to register kobject %s", name);
+ err = -EINVAL;
+ goto err;
+ }
+
+ err = create_sysfs_files(slot);
+ if (err)
+ goto unregister;
+
+ slot->next = parent->slot;
+ parent->slot = slot;
+
+ out:
+ up_write(&pci_bus_sem);
+ return slot;
+
+ unregister:
+ kobject_unregister(&slot->kobj);
+ err:
+ kfree(slot);
+ slot = ERR_PTR(err);
+ goto out;
+}
+EXPORT_SYMBOL_GPL(pci_create_slot);
+
+int pci_destroy_slot(struct pci_slot *slot)
+{
+ struct pci_slot **pprev;
+
+ for (pprev = &slot->bus->slot; *pprev; pprev = &(*pprev)->next) {
+ if (*pprev == slot) {
+ *pprev = slot->next;
+ break;
+ }
+ }
+
+ remove_sysfs_files(slot);
+ kobject_unregister(&slot->kobj);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_destroy_slot);
+
+static int pci_slot_init(void)
+{
+ int result;
+
+ kobj_set_kset_s(&pci_slots_subsys, pci_bus_type.subsys);
+ result = subsystem_register(&pci_slots_subsys);
+ if (result)
+ printk(KERN_ERR "PCI: Slot initialisation failure (%d)",
+ result);
+ return result;
+}
+
+subsys_initcall(pci_slot_init);
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 0dd93bb..ae2245f 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -129,6 +129,16 @@ struct pci_cap_saved_state {
u32 data[0];
};

+/* pci_slot represents a physical slot */
+struct pci_slot {
+ struct pci_bus *bus; /* The bus this slot is on */
+ struct pci_slot *next; /* Next slot on this bus */
+ struct hotplug_slot *hotplug; /* Hotplug info (migrate over time) */
+ unsigned char number; /* PCI_SLOT(pci_dev->devfn) */
+ struct kobject kobj;
+ void (*release)(struct pci_slot *);
+};
+
/*
* The pci_dev structure is used to describe PCI devices.
*/
@@ -140,6 +150,7 @@ struct pci_dev {

void *sysdata; /* hook for sys-specific extension */
struct proc_dir_entry *procent; /* device entry in /proc/bus/pci */
+ struct pci_slot *slot; /* Physical slot this device is in */

unsigned int devfn; /* encoded device & function index */
unsigned short vendor;
@@ -261,6 +272,7 @@ struct pci_bus {
struct list_head children; /* list of child buses */
struct list_head devices; /* list of devices on this bus */
struct pci_dev *self; /* bridge device as seen by parent */
+ struct pci_slot *slot; /* First physical slot on this bus */
struct resource *resource[PCI_BUS_NUM_RESOURCES];
/* address space routed to this bus */

@@ -470,6 +482,11 @@ static inline struct pci_bus *pci_scan_bus(int bus, struct pci_ops *ops, void *s
}
struct pci_bus *pci_create_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata);
struct pci_bus * pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int busnr);
+struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr,
+ const char *name);
+struct pci_slot *pci_slot_add_hotplug(struct pci_bus *parent, int slot_nr,
+ void (*release)(struct pci_slot *));
+int pci_destroy_slot(struct pci_slot *slot);
int pci_scan_slot(struct pci_bus *bus, int devfn);
struct pci_dev * pci_scan_single_device(struct pci_bus *bus, int devfn);
void pci_device_add(struct pci_dev *dev, struct pci_bus *bus);
diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h
index ab4cb6e..bb36c59 100644
--- a/include/linux/pci_hotplug.h
+++ b/include/linux/pci_hotplug.h
@@ -95,9 +95,6 @@ struct hotplug_slot_attribute {
* @get_adapter_status: Called to get see if an adapter is present in the slot or not.
* If this field is NULL, the value passed in the struct hotplug_slot_info
* will be used when this value is requested by a user.
- * @get_address: Called to get pci address of a slot.
- * If this field is NULL, the value passed in the struct hotplug_slot_info
- * will be used when this value is requested by a user.
* @get_max_bus_speed: Called to get the max bus speed for a slot.
* If this field is NULL, the value passed in the struct hotplug_slot_info
* will be used when this value is requested by a user.
@@ -120,7 +117,6 @@ struct hotplug_slot_ops {
int (*get_attention_status) (struct hotplug_slot *slot, u8 *value);
int (*get_latch_status) (struct hotplug_slot *slot, u8 *value);
int (*get_adapter_status) (struct hotplug_slot *slot, u8 *value);
- int (*get_address) (struct hotplug_slot *slot, u32 *value);
int (*get_max_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value);
int (*get_cur_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value);
};
@@ -140,7 +136,6 @@ struct hotplug_slot_info {
u8 attention_status;
u8 latch_status;
u8 adapter_status;
- u32 address;
enum pci_bus_speed max_bus_speed;
enum pci_bus_speed cur_bus_speed;
};
@@ -166,15 +161,14 @@ struct hotplug_slot {

/* Variables below this are for use only by the hotplug pci core. */
struct list_head slot_list;
- struct kobject kobj;
+ struct pci_slot *pci_slot;
};
#define to_hotplug_slot(n) container_of(n, struct hotplug_slot, kobj)

-extern int pci_hp_register (struct hotplug_slot *slot);
-extern int pci_hp_deregister (struct hotplug_slot *slot);
+extern int pci_hp_register(struct hotplug_slot *, struct pci_bus *, int nr);
+extern int pci_hp_deregister(struct hotplug_slot *slot);
extern int __must_check pci_hp_change_slot_info (struct hotplug_slot *slot,
struct hotplug_slot_info *info);
-extern struct kset pci_hotplug_slots_subsys;

/* PCI Setting Record (Type 0) */
struct hpp_type0 {
--
1.5.3.1.1.g1e61

2007-11-17 18:39:36

by Alex Chiang

[permalink] [raw]
Subject: [PATCH 4/4, v3] ACPI, PCI: ACPI PCI slot detection driver

Detect all physical PCI slots as described by ACPI, and create
entries in /sys/bus/pci/slots/.

Not all physical slots are hotpluggable, and the acpiphp module
does not detect them. Now we know the physical PCI geography of
our system, without caring about hotplug.

v2 -> v3:
Add Kconfig option to driver, allowing users to [de]config
this driver. If configured, take slightly different code
paths in pci_hp_register and pci_hp_deregister.

v1 -> v2:
Now recursively discovering p2p bridges and slots
underneath them. Hopefully, this will prevent us
from trying to register the same slot multiple times.

Signed-off-by: Alex Chiang <[email protected]>
---
drivers/acpi/Kconfig | 9 ++
drivers/acpi/Makefile | 1 +
drivers/acpi/pci_slot.c | 203 ++++++++++++++++++++++++++++++++
drivers/pci/hotplug/pci_hotplug_core.c | 15 +++
4 files changed, 228 insertions(+), 0 deletions(-)
create mode 100644 drivers/acpi/pci_slot.c

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 087a702..b1ce260 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -293,6 +293,15 @@ config ACPI_EC
the battery and thermal drivers. If you are compiling for a
mobile system, say Y.

+config ACPI_PCI_SLOT
+ bool "PCI slot detection driver"
+ default n
+ help
+ This driver will attempt to discover all PCI slots in your system,
+ and creates entries in /sys/bus/pci/slots/. This feature can
+ help you correlate PCI bus addresses with the physical geography
+ of your slots. If you are unsure, say N.
+
config ACPI_POWER
bool
default y
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 54e3ab0..d89000e 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_ACPI_DOCK) += dock.o
obj-$(CONFIG_ACPI_BAY) += bay.o
obj-$(CONFIG_ACPI_VIDEO) += video.o
obj-y += pci_root.o pci_link.o pci_irq.o pci_bind.o
+obj-$(CONFIG_ACPI_PCI_SLOT) += pci_slot.o
obj-$(CONFIG_ACPI_POWER) += power.o
obj-$(CONFIG_ACPI_PROCESSOR) += processor.o
obj-$(CONFIG_ACPI_CONTAINER) += container.o
diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c
new file mode 100644
index 0000000..22f076b
--- /dev/null
+++ b/drivers/acpi/pci_slot.c
@@ -0,0 +1,203 @@
+/*
+ * pci_slot.c - ACPI PCI Slot Driver
+ *
+ * The code here is heavily leveraged from the acpiphp module.
+ * Thanks to Matthew Wilcox <[email protected]> for much guidance.
+ *
+ * Copyright (C) 2007 Alex Chiang <[email protected]>
+ * Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+
+#define _COMPONENT ACPI_PCI_COMPONENT
+ACPI_MODULE_NAME("pci_slot");
+
+#define MY_NAME "pci_slot"
+#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
+
+static int acpi_pci_slot_add(acpi_handle handle);
+static void acpi_pci_slot_remove(acpi_handle handle);
+
+static struct acpi_pci_driver acpi_pci_slot_driver = {
+ .add = acpi_pci_slot_add,
+ .remove = acpi_pci_slot_remove,
+};
+
+/*
+ * register_slot - callback function to discover / create physical PCI slots
+ * @handle: any device underneath an acpi_pci_root (sometimes it's a slot
+ * device, sometimes not)
+ * @context: struct pci_bus
+ * The possible error conditions are non-fatal, so we always return
+ * AE_OK, as to not terminate our namespace walk prematurely.
+ */
+static acpi_status
+register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+ int device;
+ unsigned long adr, sun;
+ acpi_status status;
+ char name[KOBJ_NAME_LEN];
+
+ struct pci_slot *pci_slot;
+ struct pci_bus *pci_bus = context;
+
+ status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
+ if (ACPI_FAILURE(status))
+ return AE_OK;
+ device = (adr >> 16) & 0xffff;
+
+ /* No _SUN == not a slot == bail */
+ status = acpi_evaluate_integer(handle, "_SUN", NULL, &sun);
+ if (ACPI_FAILURE(status))
+ return AE_OK;
+
+ snprintf(name, sizeof(name), "%u", (u32)sun);
+ pci_slot = pci_create_slot(pci_bus, device, name);
+ if (IS_ERR(pci_slot)) {
+ err("pci_create_slot returned %ld\n", PTR_ERR(pci_slot));
+ return AE_OK;
+ }
+
+ return AE_OK;
+}
+
+/*
+ * find_p2p_bridge - callback function to discover p2p bridges
+ */
+static acpi_status
+find_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+ int device, function;
+ unsigned long adr;
+ acpi_status status;
+ acpi_handle dummy_handle;
+
+ struct pci_dev *dev;
+ struct pci_bus *pci_bus = context;
+
+ status = acpi_get_handle(handle, "_ADR", &dummy_handle);
+ if (ACPI_FAILURE(status))
+ return AE_OK;
+
+ status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
+ if (ACPI_FAILURE(status))
+ return AE_OK;
+
+ device = (adr >> 16) & 0xffff;
+ function = adr & 0xffff;
+
+ dev = pci_get_slot(pci_bus, PCI_DEVFN(device, function));
+ if (!dev || !dev->subordinate)
+ goto out;
+
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
+ register_slot, dev->subordinate, NULL);
+ if (ACPI_FAILURE(status))
+ goto out;
+
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
+ find_p2p_bridge, dev->subordinate, NULL);
+out:
+ pci_dev_put(dev);
+ return AE_OK;
+}
+
+#define ACPI_STA_FUNCTIONING (0x00000008)
+
+/*
+ * acpi_pci_slot_add - walk namespace under a PCI root bridge
+ * @handle: points to an acpi_pci_root
+ */
+static int
+acpi_pci_slot_add(acpi_handle handle)
+{
+ int seg, bus;
+ unsigned long tmp;
+ acpi_status status;
+ acpi_handle dummy_handle;
+ struct pci_bus *pci_bus;
+
+ /* If the bridge doesn't have _STA, we assume it is always there */
+ status = acpi_get_handle(handle, "_STA", &dummy_handle);
+ if (ACPI_SUCCESS(status)) {
+ status = acpi_evaluate_integer(handle, "_STA", NULL, &tmp);
+ if (ACPI_FAILURE(status)) {
+ info("%s: _STA evaluation failure\n", __FUNCTION__);
+ return 0;
+ }
+ if ((tmp & ACPI_STA_FUNCTIONING) == 0)
+ /* don't register this object */
+ return 0;
+ }
+
+ status = acpi_evaluate_integer(handle, "_SEG", NULL, &tmp);
+ seg = ACPI_SUCCESS(status) ? tmp : 0;
+
+ status = acpi_evaluate_integer(handle, "_BBN", NULL, &tmp);
+ bus = ACPI_SUCCESS(status) ? tmp : 0;
+
+ pci_bus = pci_find_bus(seg, bus);
+ if (!pci_bus)
+ return 0;
+
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
+ register_slot, pci_bus, NULL);
+ if (ACPI_FAILURE(status)) {
+ err("%s: register_slot failure - %d\n", __FUNCTION__, status);
+ return status;
+ }
+
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
+ find_p2p_bridge, pci_bus, NULL);
+ if (ACPI_FAILURE(status))
+ err("%s: find_p2p_bridge failure - %d\n", __FUNCTION__, status);
+
+ return status;
+}
+
+static int __init
+acpi_pci_slot_init(void)
+{
+ acpi_pci_register_driver(&acpi_pci_slot_driver);
+ return 0;
+}
+
+/*
+ * acpi_pci_slot_remove and acpi_pci_slot_exit are empty for now, since
+ * /sys/bus/pci/slots/ entries shouldn't ever really go away.
+ */
+static void
+acpi_pci_slot_remove(acpi_handle handle)
+{
+}
+
+static void __exit
+acpi_pci_slot_exit(void)
+{
+}
+
+module_init(acpi_pci_slot_init);
+module_exit(acpi_pci_slot_exit);
diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c
index 1be5b0d..39bb5d3 100644
--- a/drivers/pci/hotplug/pci_hotplug_core.c
+++ b/drivers/pci/hotplug/pci_hotplug_core.c
@@ -567,9 +567,15 @@ int pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, int slot_nr)
return -EINVAL;
}

+#ifndef CONFIG_ACPI_PCI_SLOT
+ /*
+ * ACPI-PCI slot driver would have created the pci_slot for us,
+ * but we haven't configured it, so do it ourselves.
+ */
pci_slot = pci_create_slot(bus, slot_nr, slot->name);
if (IS_ERR(pci_slot))
return PTR_ERR(pci_slot);
+#endif

pci_slot = pci_slot_add_hotplug(bus, slot_nr, hotplug_release);
if (IS_ERR(pci_slot))
@@ -610,7 +616,16 @@ int pci_hp_deregister(struct hotplug_slot *hotplug)

slot = hotplug->pci_slot;
fs_remove_slot(slot);
+#ifdef CONFIG_ACPI_PCI_SLOT
+ /*
+ * If we are using the ACPI-PCI slot driver, we don't want to
+ * destroy the pci_slot object. Rather, just release the
+ * hotplug_slot associated with it.
+ */
+ hotplug_release(slot);
+#else
pci_destroy_slot(slot);
+#endif
dbg("Removed slot %s from the list\n", hotplug->name);
return 0;
}
--
1.5.3.1.1.g1e61

2007-11-19 17:44:55

by Kristen Carlson Accardi

[permalink] [raw]
Subject: Re: [PATCH 0/4, v3] Physical PCI slot objects

On Sat, 17 Nov 2007 11:29:54 -0700
Alex Chiang <[email protected]> wrote:

> I have done quite a bit more testing, and verified that this
> series plays nicely with acpiphp during all stages of the series.
> Notably, you can modprobe/rmmod acpiphp repeatedly no matter
> where you are in the series, and no matter whether you have
> CONFIG_ACPI_PCI_SLOT turned on. The correct entries in
> /sys/bus/pci/slots/ will appear and disappear, and we correctly
> register/deregister ACPI slots with the pci_hp core.

Hi Alex,
How does this patch play with non-acpi based hotplug such as the pciehp
driver or the shpchp driver for example?

Thanks,
Kristen

2007-11-19 17:57:19

by Alex Chiang

[permalink] [raw]
Subject: Re: [PATCH 0/4, v3] Physical PCI slot objects

* Kristen Carlson Accardi <[email protected]>:
> On Sat, 17 Nov 2007 11:29:54 -0700
> Alex Chiang <[email protected]> wrote:
>
> > I have done quite a bit more testing, and verified that this
> > series plays nicely with acpiphp during all stages of the series.
> > Notably, you can modprobe/rmmod acpiphp repeatedly no matter
> > where you are in the series, and no matter whether you have
> > CONFIG_ACPI_PCI_SLOT turned on. The correct entries in
> > /sys/bus/pci/slots/ will appear and disappear, and we correctly
> > register/deregister ACPI slots with the pci_hp core.
>
> How does this patch play with non-acpi based hotplug such as the pciehp
> driver

Not well. :(

pciehp: HPC vendor_id 103c device_id 403b ss_vid 0 ss_did 0
pciehp: pci_hp_register failed with error -17
pciehp: pciehp: slot initialization failed
pciehp: HPC vendor_id 111d device_id 801c ss_vid 0 ss_did 0
pciehp: Can't get irq 0 for the hotplug controller
pciehp: HPC vendor_id 111d device_id 801c ss_vid 0 ss_did 0
pciehp: Can't get irq 0 for the hotplug controller
pciehp: HPC vendor_id 103c device_id 403b ss_vid 0 ss_did 0
pciehp: pci_hp_register failed with error -17
pciehp: pciehp: slot initialization failed

I'll take a look at this today. Thanks for pointing it out.

> or the shpchp driver for example?

This one, I'm not sure on, as I don't have any shpc hardware.
I'll do my best by just looking at the code, but maybe if someone
out there has that hardware, they could let me know what breaks?

Thanks.

/ac

2007-11-19 22:02:53

by Alex Chiang

[permalink] [raw]
Subject: Re: [PATCH 0/4, v3] Physical PCI slot objects

Hi Kristin,

* Kristen Carlson Accardi <[email protected]>:
> Alex Chiang <[email protected]> wrote:
>
> > I have done quite a bit more testing, and verified that this
> > series plays nicely with acpiphp during all stages of the
> > series. Notably, you can modprobe/rmmod acpiphp repeatedly
> > no matter where you are in the series, and no matter whether
> > you have CONFIG_ACPI_PCI_SLOT turned on. The correct entries
> > in /sys/bus/pci/slots/ will appear and disappear, and we
> > correctly register/deregister ACPI slots with the pci_hp
> > core.
>
> How does this patch play with non-acpi based hotplug such as
> the pciehp driver or the shpchp driver for example?

Thanks for asking these questions -- I fixed some bugs in patches
3/4 and 4/4 that should lead to a much better experience.

First, it turns out I did not modify the pciehp driver correctly
when using the new pci_hp_register interface. I fixed this bug,
and noticed a problem in the rpaphp driver (which I fixed as
well). I visually inspected the shpchp driver, and it *seems* to
be correct, so no change there. I will send these fixes as Patch
3/4, v4.

Second, I resolved the issue of what happens when two different
hp drivers try to claim the same PCI slot. Basically, whoever
registered the slot first wins, and second place gets a -EBUSY
return value. I *think* that is the correct behavior, as Willy
informs me that having two drivers try to claim the same slot is
badness. These fixes will be sent as Patch 4/4, v4.

I tested by modprobe/rmmod both acpiphp and pciehp multiple
times, and in differing orders. I also tested both
CONFIG_ACPI_PCI_SLOT turned on and off. In all cases, at least
what I intended to happen did happen.

Now whether my intentions were correct or misguided might be a
different story... ;) I'm wondering most about the -EBUSY thing,
but I don't see a better option.

Patches 1/4 and 2/4 had no changes so I will not resend them.

Thanks.

/ac

2007-11-19 22:04:03

by Alex Chiang

[permalink] [raw]
Subject: [PATCH 3/4, v4] PCI, PCI Hotplug: Introduce pci_slot

- Make pci_slot the primary sysfs entity. hotplug_slot becomes a
subsidiary structure.
o pci_create_slot() creates and registers a slot with the PCI core
o pci_slot_add_hotplug() gives it hotplug capability

- Change the prototype of pci_hp_register() to take the bus and
slot number (on parent bus) as parameters.

- Remove all the ->get_address methods since this functionality is
now handled by pci_slot directly.

v3 -> v4:
Fixed bug with pciehp and rpaphp registering slots

v2 -> v3:
Separated slot creation and slot hotplug ability into two
interfaces. Fixed bugs in pci_destroy_slot(), and now
properly calling from pci_hp_deregister.

v1 -> v2:
No change

Signed-off-by: Alex Chiang <[email protected]>
Signed-off-by: Matthew Wilcox <[email protected]>
---
drivers/pci/Makefile | 2 +-
drivers/pci/hotplug/acpiphp.h | 1 -
drivers/pci/hotplug/acpiphp_core.c | 23 +---
drivers/pci/hotplug/acpiphp_glue.c | 16 --
drivers/pci/hotplug/acpiphp_ibm.c | 5 +-
drivers/pci/hotplug/cpci_hotplug_core.c | 2 +-
drivers/pci/hotplug/cpqphp_core.c | 4 +-
drivers/pci/hotplug/fakephp.c | 2 +-
drivers/pci/hotplug/ibmphp_ebda.c | 3 +-
drivers/pci/hotplug/pci_hotplug_core.c | 242 +++++++++++--------------------
drivers/pci/hotplug/pciehp_core.c | 22 +--
drivers/pci/hotplug/rpadlpar_sysfs.c | 4 +-
drivers/pci/hotplug/rpaphp_slot.c | 3 +-
drivers/pci/hotplug/sgi_hotplug.c | 2 +-
drivers/pci/hotplug/shpchp_core.c | 17 +--
drivers/pci/pci.h | 13 ++
drivers/pci/slot.c | 184 +++++++++++++++++++++++
include/linux/pci.h | 17 ++
include/linux/pci_hotplug.h | 12 +-
19 files changed, 330 insertions(+), 244 deletions(-)
create mode 100644 drivers/pci/slot.c

diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 5550556..12f0b2d 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -2,7 +2,7 @@
# Makefile for the PCI bus specific drivers.
#

-obj-y += access.o bus.o probe.o remove.o pci.o quirks.o \
+obj-y += access.o bus.o probe.o remove.o pci.o quirks.o slot.o \
pci-driver.o search.o pci-sysfs.o rom.o setup-res.o
obj-$(CONFIG_PROC_FS) += proc.o

diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h
index f6cc0c5..ab46189 100644
--- a/drivers/pci/hotplug/acpiphp.h
+++ b/drivers/pci/hotplug/acpiphp.h
@@ -216,7 +216,6 @@ extern u8 acpiphp_get_power_status (struct acpiphp_slot *slot);
extern u8 acpiphp_get_attention_status (struct acpiphp_slot *slot);
extern u8 acpiphp_get_latch_status (struct acpiphp_slot *slot);
extern u8 acpiphp_get_adapter_status (struct acpiphp_slot *slot);
-extern u32 acpiphp_get_address (struct acpiphp_slot *slot);

/* variables */
extern int acpiphp_debug;
diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c
index a0ca63a..34b8d0b 100644
--- a/drivers/pci/hotplug/acpiphp_core.c
+++ b/drivers/pci/hotplug/acpiphp_core.c
@@ -70,7 +70,6 @@ static int disable_slot (struct hotplug_slot *slot);
static int set_attention_status (struct hotplug_slot *slot, u8 value);
static int get_power_status (struct hotplug_slot *slot, u8 *value);
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
-static int get_address (struct hotplug_slot *slot, u32 *value);
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);

@@ -83,7 +82,6 @@ static struct hotplug_slot_ops acpi_hotplug_slot_ops = {
.get_attention_status = get_attention_status,
.get_latch_status = get_latch_status,
.get_adapter_status = get_adapter_status,
- .get_address = get_address,
};


@@ -279,23 +277,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
return 0;
}

-
-/**
- * get_address - get pci address of a slot
- * @hotplug_slot: slot to get status
- * @value: pointer to struct pci_busdev (seg, bus, dev)
- */
-static int get_address(struct hotplug_slot *hotplug_slot, u32 *value)
-{
- struct slot *slot = hotplug_slot->private;
-
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
-
- *value = acpiphp_get_address(slot->acpi_slot);
-
- return 0;
-}
-
static int __init init_acpi(void)
{
int retval;
@@ -362,7 +343,9 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
acpiphp_slot->slot = slot;
snprintf(slot->name, sizeof(slot->name), "%u", slot->acpi_slot->sun);

- retval = pci_hp_register(slot->hotplug_slot);
+ retval = pci_hp_register(slot->hotplug_slot,
+ acpiphp_slot->bridge->pci_bus,
+ acpiphp_slot->device);
if (retval) {
err("pci_hp_register failed with error %d\n", retval);
goto error_hpslot;
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index 1e125b5..02a2fd7 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -1874,19 +1874,3 @@ u8 acpiphp_get_adapter_status(struct acpiphp_slot *slot)

return (sta == 0) ? 0 : 1;
}
-
-
-/*
- * pci address (seg/bus/dev)
- */
-u32 acpiphp_get_address(struct acpiphp_slot *slot)
-{
- u32 address;
- struct pci_bus *pci_bus = slot->bridge->pci_bus;
-
- address = (pci_domain_nr(pci_bus) << 16) |
- (pci_bus->number << 8) |
- slot->device;
-
- return address;
-}
diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c
index 56829f8..927b1fb 100644
--- a/drivers/pci/hotplug/acpiphp_ibm.c
+++ b/drivers/pci/hotplug/acpiphp_ibm.c
@@ -35,6 +35,7 @@
#include <linux/moduleparam.h>

#include "acpiphp.h"
+extern struct kset pci_slots_subsys;

#define DRIVER_VERSION "1.0.1"
#define DRIVER_AUTHOR "Irene Zubarev <[email protected]>, Vernon Mauery <[email protected]>"
@@ -428,7 +429,7 @@ static int __init ibm_acpiphp_init(void)
int retval = 0;
acpi_status status;
struct acpi_device *device;
- struct kobject *sysdir = &pci_hotplug_slots_subsys.kobj;
+ struct kobject *sysdir = &pci_slots_subsys.kobj;

dbg("%s\n", __FUNCTION__);

@@ -475,7 +476,7 @@ init_return:
static void __exit ibm_acpiphp_exit(void)
{
acpi_status status;
- struct kobject *sysdir = &pci_hotplug_slots_subsys.kobj;
+ struct kobject *sysdir = &pci_slots_subsys.kobj;

dbg("%s\n", __FUNCTION__);

diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c
index ed4d44e..aa47b80 100644
--- a/drivers/pci/hotplug/cpci_hotplug_core.c
+++ b/drivers/pci/hotplug/cpci_hotplug_core.c
@@ -285,7 +285,7 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
info->attention_status = cpci_get_attention_status(slot);

dbg("registering slot %s", slot->hotplug_slot->name);
- status = pci_hp_register(slot->hotplug_slot);
+ status = pci_hp_register(slot->hotplug_slot, bus, i);
if (status) {
err("pci_hp_register failed with error %d", status);
goto error_name;
diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c
index a96b739..67f6a0c 100644
--- a/drivers/pci/hotplug/cpqphp_core.c
+++ b/drivers/pci/hotplug/cpqphp_core.c
@@ -434,7 +434,9 @@ static int ctrl_slot_setup(struct controller *ctrl,
slot->bus, slot->device,
slot->number, ctrl->slot_device_offset,
slot_number);
- result = pci_hp_register(hotplug_slot);
+ result = pci_hp_register(hotplug_slot,
+ ctrl->pci_dev->subordinate,
+ slot->device);
if (result) {
err("pci_hp_register failed with error %d\n", result);
goto error_name;
diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c
index 996c942..405c608 100644
--- a/drivers/pci/hotplug/fakephp.c
+++ b/drivers/pci/hotplug/fakephp.c
@@ -120,7 +120,7 @@ static int add_slot(struct pci_dev *dev)
slot->release = &dummy_release;
slot->private = dslot;

- retval = pci_hp_register(slot);
+ retval = pci_hp_register(slot, dev->bus, PCI_SLOT(dev->devfn));
if (retval) {
err("pci_hp_register failed with error %d\n", retval);
goto error_dslot;
diff --git a/drivers/pci/hotplug/ibmphp_ebda.c b/drivers/pci/hotplug/ibmphp_ebda.c
index 600ed7b..eb7a1c0 100644
--- a/drivers/pci/hotplug/ibmphp_ebda.c
+++ b/drivers/pci/hotplug/ibmphp_ebda.c
@@ -1000,7 +1000,8 @@ static int __init ebda_rsrc_controller (void)
tmp_slot = list_entry (list, struct slot, ibm_slot_list);

snprintf (tmp_slot->hotplug_slot->name, 30, "%s", create_file_name (tmp_slot));
- pci_hp_register (tmp_slot->hotplug_slot);
+ pci_hp_register(tmp_slot->hotplug_slot,
+ pci_find_bus(0, tmp_slot->bus), tmp_slot->device);
}

print_ebda_hpc ();
diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c
index 01c351c..1be5b0d 100644
--- a/drivers/pci/hotplug/pci_hotplug_core.c
+++ b/drivers/pci/hotplug/pci_hotplug_core.c
@@ -40,6 +40,7 @@
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
#include <asm/uaccess.h>
+#include "../pci.h"

#define MY_NAME "pci_hotplug"

@@ -61,43 +62,6 @@ static int debug;

static LIST_HEAD(pci_hotplug_slot_list);

-struct kset pci_hotplug_slots_subsys;
-
-static ssize_t hotplug_slot_attr_show(struct kobject *kobj,
- struct attribute *attr, char *buf)
-{
- struct hotplug_slot *slot = to_hotplug_slot(kobj);
- struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr);
- return attribute->show ? attribute->show(slot, buf) : -EIO;
-}
-
-static ssize_t hotplug_slot_attr_store(struct kobject *kobj,
- struct attribute *attr, const char *buf, size_t len)
-{
- struct hotplug_slot *slot = to_hotplug_slot(kobj);
- struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr);
- return attribute->store ? attribute->store(slot, buf, len) : -EIO;
-}
-
-static struct sysfs_ops hotplug_slot_sysfs_ops = {
- .show = hotplug_slot_attr_show,
- .store = hotplug_slot_attr_store,
-};
-
-static void hotplug_slot_release(struct kobject *kobj)
-{
- struct hotplug_slot *slot = to_hotplug_slot(kobj);
- if (slot->release)
- slot->release(slot);
-}
-
-static struct kobj_type hotplug_slot_ktype = {
- .sysfs_ops = &hotplug_slot_sysfs_ops,
- .release = &hotplug_slot_release,
-};
-
-decl_subsys_name(pci_hotplug_slots, slots, &hotplug_slot_ktype, NULL);
-
/* these strings match up with the values in pci_bus_speed */
static char *pci_bus_speed_strings[] = {
"33 MHz PCI", /* 0x00 */
@@ -151,16 +115,15 @@ GET_STATUS(power_status, u8)
GET_STATUS(attention_status, u8)
GET_STATUS(latch_status, u8)
GET_STATUS(adapter_status, u8)
-GET_STATUS(address, u32)
GET_STATUS(max_bus_speed, enum pci_bus_speed)
GET_STATUS(cur_bus_speed, enum pci_bus_speed)

-static ssize_t power_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t power_read_file(struct pci_slot *slot, char *buf)
{
int retval;
u8 value;

- retval = get_power_status (slot, &value);
+ retval = get_power_status(slot->hotplug, &value);
if (retval)
goto exit;
retval = sprintf (buf, "%d\n", value);
@@ -168,9 +131,10 @@ exit:
return retval;
}

-static ssize_t power_write_file (struct hotplug_slot *slot, const char *buf,
+static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,
size_t count)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
unsigned long lpower;
u8 power;
int retval = 0;
@@ -206,29 +170,30 @@ exit:
return count;
}

-static struct hotplug_slot_attribute hotplug_slot_attr_power = {
+static struct pci_slot_attribute hotplug_slot_attr_power = {
.attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR},
.show = power_read_file,
.store = power_write_file
};

-static ssize_t attention_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t attention_read_file(struct pci_slot *slot, char *buf)
{
int retval;
u8 value;

- retval = get_attention_status (slot, &value);
+ retval = get_attention_status(slot->hotplug, &value);
if (retval)
goto exit;
- retval = sprintf (buf, "%d\n", value);
+ retval = sprintf(buf, "%d\n", value);

exit:
return retval;
}

-static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf,
+static ssize_t attention_write_file(struct pci_slot *slot, const char *buf,
size_t count)
{
+ struct hotplug_slot_ops *ops = slot->hotplug->ops;
unsigned long lattention;
u8 attention;
int retval = 0;
@@ -237,13 +202,13 @@ static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf,
attention = (u8)(lattention & 0xff);
dbg (" - attention = %d\n", attention);

- if (!try_module_get(slot->ops->owner)) {
+ if (!try_module_get(ops->owner)) {
retval = -ENODEV;
goto exit;
}
- if (slot->ops->set_attention_status)
- retval = slot->ops->set_attention_status(slot, attention);
- module_put(slot->ops->owner);
+ if (ops->set_attention_status)
+ retval = ops->set_attention_status(slot->hotplug, attention);
+ module_put(ops->owner);

exit:
if (retval)
@@ -251,18 +216,18 @@ exit:
return count;
}

-static struct hotplug_slot_attribute hotplug_slot_attr_attention = {
+static struct pci_slot_attribute hotplug_slot_attr_attention = {
.attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR},
.show = attention_read_file,
.store = attention_write_file
};

-static ssize_t latch_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t latch_read_file(struct pci_slot *slot, char *buf)
{
int retval;
u8 value;

- retval = get_latch_status (slot, &value);
+ retval = get_latch_status(slot->hotplug, &value);
if (retval)
goto exit;
retval = sprintf (buf, "%d\n", value);
@@ -271,17 +236,17 @@ exit:
return retval;
}

-static struct hotplug_slot_attribute hotplug_slot_attr_latch = {
+static struct pci_slot_attribute hotplug_slot_attr_latch = {
.attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},
.show = latch_read_file,
};

-static ssize_t presence_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t presence_read_file(struct pci_slot *slot, char *buf)
{
int retval;
u8 value;

- retval = get_adapter_status (slot, &value);
+ retval = get_adapter_status(slot->hotplug, &value);
if (retval)
goto exit;
retval = sprintf (buf, "%d\n", value);
@@ -290,42 +255,20 @@ exit:
return retval;
}

-static struct hotplug_slot_attribute hotplug_slot_attr_presence = {
+static struct pci_slot_attribute hotplug_slot_attr_presence = {
.attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},
.show = presence_read_file,
};

-static ssize_t address_read_file (struct hotplug_slot *slot, char *buf)
-{
- int retval;
- u32 address;
-
- retval = get_address (slot, &address);
- if (retval)
- goto exit;
- retval = sprintf (buf, "%04x:%02x:%02x\n",
- (address >> 16) & 0xffff,
- (address >> 8) & 0xff,
- address & 0xff);
-
-exit:
- return retval;
-}
-
-static struct hotplug_slot_attribute hotplug_slot_attr_address = {
- .attr = {.name = "address", .mode = S_IFREG | S_IRUGO},
- .show = address_read_file,
-};
-
static char *unknown_speed = "Unknown bus speed";

-static ssize_t max_bus_speed_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t max_bus_speed_read_file(struct pci_slot *slot, char *buf)
{
char *speed_string;
int retval;
enum pci_bus_speed value;

- retval = get_max_bus_speed (slot, &value);
+ retval = get_max_bus_speed(slot->hotplug, &value);
if (retval)
goto exit;

@@ -340,18 +283,18 @@ exit:
return retval;
}

-static struct hotplug_slot_attribute hotplug_slot_attr_max_bus_speed = {
+static struct pci_slot_attribute hotplug_slot_attr_max_bus_speed = {
.attr = {.name = "max_bus_speed", .mode = S_IFREG | S_IRUGO},
.show = max_bus_speed_read_file,
};

-static ssize_t cur_bus_speed_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t cur_bus_speed_read_file(struct pci_slot *slot, char *buf)
{
char *speed_string;
int retval;
enum pci_bus_speed value;

- retval = get_cur_bus_speed (slot, &value);
+ retval = get_cur_bus_speed(slot->hotplug, &value);
if (retval)
goto exit;

@@ -366,14 +309,15 @@ exit:
return retval;
}

-static struct hotplug_slot_attribute hotplug_slot_attr_cur_bus_speed = {
+static struct pci_slot_attribute hotplug_slot_attr_cur_bus_speed = {
.attr = {.name = "cur_bus_speed", .mode = S_IFREG | S_IRUGO},
.show = cur_bus_speed_read_file,
};

-static ssize_t test_write_file (struct hotplug_slot *slot, const char *buf,
+static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,
size_t count)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
unsigned long ltest;
u32 test;
int retval = 0;
@@ -396,13 +340,14 @@ exit:
return count;
}

-static struct hotplug_slot_attribute hotplug_slot_attr_test = {
+static struct pci_slot_attribute hotplug_slot_attr_test = {
.attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},
.store = test_write_file
};

-static int has_power_file (struct hotplug_slot *slot)
+static int has_power_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if ((slot->ops->enable_slot) ||
@@ -412,8 +357,9 @@ static int has_power_file (struct hotplug_slot *slot)
return -ENOENT;
}

-static int has_attention_file (struct hotplug_slot *slot)
+static int has_attention_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if ((slot->ops->set_attention_status) ||
@@ -422,8 +368,9 @@ static int has_attention_file (struct hotplug_slot *slot)
return -ENOENT;
}

-static int has_latch_file (struct hotplug_slot *slot)
+static int has_latch_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->get_latch_status)
@@ -431,8 +378,9 @@ static int has_latch_file (struct hotplug_slot *slot)
return -ENOENT;
}

-static int has_adapter_file (struct hotplug_slot *slot)
+static int has_adapter_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->get_adapter_status)
@@ -440,17 +388,9 @@ static int has_adapter_file (struct hotplug_slot *slot)
return -ENOENT;
}

-static int has_address_file (struct hotplug_slot *slot)
-{
- if ((!slot) || (!slot->ops))
- return -ENODEV;
- if (slot->ops->get_address)
- return 0;
- return -ENOENT;
-}
-
-static int has_max_bus_speed_file (struct hotplug_slot *slot)
+static int has_max_bus_speed_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->get_max_bus_speed)
@@ -458,8 +398,9 @@ static int has_max_bus_speed_file (struct hotplug_slot *slot)
return -ENOENT;
}

-static int has_cur_bus_speed_file (struct hotplug_slot *slot)
+static int has_cur_bus_speed_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->get_cur_bus_speed)
@@ -467,8 +408,9 @@ static int has_cur_bus_speed_file (struct hotplug_slot *slot)
return -ENOENT;
}

-static int has_test_file (struct hotplug_slot *slot)
+static int has_test_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->hardware_test)
@@ -476,7 +418,7 @@ static int has_test_file (struct hotplug_slot *slot)
return -ENOENT;
}

-static int fs_add_slot (struct hotplug_slot *slot)
+static int fs_add_slot(struct pci_slot *slot)
{
int retval = 0;

@@ -507,13 +449,6 @@ static int fs_add_slot (struct hotplug_slot *slot)
goto exit_adapter;
}

- if (has_address_file(slot) == 0) {
- retval = sysfs_create_file(&slot->kobj,
- &hotplug_slot_attr_address.attr);
- if (retval)
- goto exit_address;
- }
-
if (has_max_bus_speed_file(slot) == 0) {
retval = sysfs_create_file(&slot->kobj,
&hotplug_slot_attr_max_bus_speed.attr);
@@ -546,10 +481,6 @@ exit_cur_speed:
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);

exit_max_speed:
- if (has_address_file(slot) == 0)
- sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr);
-
-exit_address:
if (has_adapter_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);

@@ -569,7 +500,7 @@ exit:
return retval;
}

-static void fs_remove_slot (struct hotplug_slot *slot)
+static void fs_remove_slot(struct pci_slot *slot)
{
if (has_power_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
@@ -583,9 +514,6 @@ static void fs_remove_slot (struct hotplug_slot *slot)
if (has_adapter_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);

- if (has_address_file(slot) == 0)
- sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr);
-
if (has_max_bus_speed_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);

@@ -609,6 +537,12 @@ static struct hotplug_slot *get_slot_from_name (const char *name)
return NULL;
}

+static void hotplug_release(struct pci_slot *slot)
+{
+ struct hotplug_slot *hotplug = slot->hotplug;
+ hotplug->release(hotplug);
+}
+
/**
* pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem
* @slot: pointer to the &struct hotplug_slot to register
@@ -618,8 +552,9 @@ static struct hotplug_slot *get_slot_from_name (const char *name)
*
* Returns 0 if successful, anything else for an error.
*/
-int pci_hp_register (struct hotplug_slot *slot)
+int pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, int slot_nr)
{
+ struct pci_slot *pci_slot;
int result;

if (slot == NULL)
@@ -632,18 +567,20 @@ int pci_hp_register (struct hotplug_slot *slot)
return -EINVAL;
}

- kobject_set_name(&slot->kobj, "%s", slot->name);
- kobj_set_kset_s(slot, pci_hotplug_slots_subsys);
+ pci_slot = pci_create_slot(bus, slot_nr, slot->name);
+ if (IS_ERR(pci_slot))
+ return PTR_ERR(pci_slot);

- /* this can fail if we have already registered a slot with the same name */
- if (kobject_register(&slot->kobj)) {
- err("Unable to register kobject");
- return -EINVAL;
- }
-
- list_add (&slot->slot_list, &pci_hotplug_slot_list);
+ pci_slot = pci_slot_add_hotplug(bus, slot_nr, hotplug_release);
+ if (IS_ERR(pci_slot))
+ return PTR_ERR(pci_slot);
+
+ slot->pci_slot = pci_slot;
+ pci_slot->hotplug = slot;

- result = fs_add_slot (slot);
+ list_add(&slot->slot_list, &pci_hotplug_slot_list);
+
+ result = fs_add_slot(pci_slot);
dbg ("Added slot %s to the list\n", slot->name);
return result;
}
@@ -657,22 +594,24 @@ int pci_hp_register (struct hotplug_slot *slot)
*
* Returns 0 if successful, anything else for an error.
*/
-int pci_hp_deregister (struct hotplug_slot *slot)
+int pci_hp_deregister(struct hotplug_slot *hotplug)
{
struct hotplug_slot *temp;
+ struct pci_slot *slot;

- if (slot == NULL)
+ if (!hotplug)
return -ENODEV;

- temp = get_slot_from_name (slot->name);
- if (temp != slot) {
+ temp = get_slot_from_name(hotplug->name);
+ if (temp != hotplug)
return -ENODEV;
- }
- list_del (&slot->slot_list);

- fs_remove_slot (slot);
- dbg ("Removed slot %s from the list\n", slot->name);
- kobject_unregister(&slot->kobj);
+ list_del(&hotplug->slot_list);
+
+ slot = hotplug->pci_slot;
+ fs_remove_slot(slot);
+ pci_destroy_slot(slot);
+ dbg("Removed slot %s from the list\n", hotplug->name);
return 0;
}

@@ -686,13 +625,15 @@ int pci_hp_deregister (struct hotplug_slot *slot)
*
* Returns 0 if successful, anything else for an error.
*/
-int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot,
+int __must_check pci_hp_change_slot_info(struct hotplug_slot *hotplug,
struct hotplug_slot_info *info)
{
- if ((slot == NULL) || (info == NULL))
+ struct pci_slot *slot;
+ if (!hotplug || !info)
return -ENODEV;
+ slot = hotplug->pci_slot;

- memcpy (slot->info, info, sizeof (struct hotplug_slot_info));
+ memcpy(hotplug->info, info, sizeof(struct hotplug_slot_info));

return 0;
}
@@ -701,31 +642,21 @@ static int __init pci_hotplug_init (void)
{
int result;

- kobj_set_kset_s(&pci_hotplug_slots_subsys, pci_bus_type.subsys);
- result = subsystem_register(&pci_hotplug_slots_subsys);
- if (result) {
- err("Register subsys with error %d\n", result);
- goto exit;
- }
result = cpci_hotplug_init(debug);
if (result) {
err ("cpci_hotplug_init with error %d\n", result);
- goto err_subsys;
+ goto err_cpci;
}

info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
- goto exit;
-
-err_subsys:
- subsystem_unregister(&pci_hotplug_slots_subsys);
-exit:
+
+err_cpci:
return result;
}

static void __exit pci_hotplug_exit (void)
{
cpci_hotplug_exit();
- subsystem_unregister(&pci_hotplug_slots_subsys);
}

module_init(pci_hotplug_init);
@@ -737,7 +668,6 @@ MODULE_LICENSE("GPL");
module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");

-EXPORT_SYMBOL_GPL(pci_hotplug_slots_subsys);
EXPORT_SYMBOL_GPL(pci_hp_register);
EXPORT_SYMBOL_GPL(pci_hp_deregister);
EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index 6462ac3..19aa33e 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -69,7 +69,6 @@ static int get_power_status (struct hotplug_slot *slot, u8 *value);
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
-static int get_address (struct hotplug_slot *slot, u32 *value);
static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);

@@ -82,7 +81,6 @@ static struct hotplug_slot_ops pciehp_hotplug_slot_ops = {
.get_attention_status = get_attention_status,
.get_latch_status = get_latch_status,
.get_adapter_status = get_adapter_status,
- .get_address = get_address,
.get_max_bus_speed = get_max_bus_speed,
.get_cur_bus_speed = get_cur_bus_speed,
};
@@ -245,14 +243,16 @@ static int init_slots(struct controller *ctrl)
dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x "
"slot_device_offset=%x\n", slot->bus, slot->device,
slot->hp_slot, slot->number, ctrl->slot_device_offset);
- retval = pci_hp_register(hotplug_slot);
+ retval = pci_hp_register(hotplug_slot,
+ ctrl->pci_dev->subordinate,
+ slot->device);
if (retval) {
err ("pci_hp_register failed with error %d\n", retval);
goto error_info;
}
/* create additional sysfs entries */
if (EMI(ctrl->ctrlcap)) {
- retval = sysfs_create_file(&hotplug_slot->kobj,
+ retval = sysfs_create_file(&hotplug_slot->pci_slot->kobj,
&hotplug_slot_attr_lock.attr);
if (retval) {
pci_hp_deregister(hotplug_slot);
@@ -285,7 +285,7 @@ static void cleanup_slots(struct controller *ctrl)
slot = list_entry(tmp, struct slot, slot_list);
list_del(&slot->slot_list);
if (EMI(ctrl->ctrlcap))
- sysfs_remove_file(&slot->hotplug_slot->kobj,
+ sysfs_remove_file(&slot->hotplug_slot->pci_slot->kobj,
&hotplug_slot_attr_lock.attr);
cancel_delayed_work(&slot->work);
flush_scheduled_work();
@@ -387,18 +387,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
return 0;
}

-static int get_address(struct hotplug_slot *hotplug_slot, u32 *value)
-{
- struct slot *slot = hotplug_slot->private;
- struct pci_bus *bus = slot->ctrl->pci_dev->subordinate;
-
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
-
- *value = (pci_domain_nr(bus) << 16) | (slot->bus << 8) | slot->device;
-
- return 0;
-}
-
static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
{
struct slot *slot = hotplug_slot->private;
diff --git a/drivers/pci/hotplug/rpadlpar_sysfs.c b/drivers/pci/hotplug/rpadlpar_sysfs.c
index a080fed..3663507 100644
--- a/drivers/pci/hotplug/rpadlpar_sysfs.c
+++ b/drivers/pci/hotplug/rpadlpar_sysfs.c
@@ -23,6 +23,8 @@

#define MAX_DRC_NAME_LEN 64

+extern struct kset pci_slots_subsys;
+
/* Store return code of dlpar operation in attribute struct */
struct dlpar_io_attr {
int rc;
@@ -130,7 +132,7 @@ struct kobj_type ktype_dlpar_io = {

struct kset dlpar_io_kset = {
.kobj = {.ktype = &ktype_dlpar_io,
- .parent = &pci_hotplug_slots_subsys.kobj},
+ .parent = &pci_slots_subsys.kobj},
.ktype = &ktype_dlpar_io,
};

diff --git a/drivers/pci/hotplug/rpaphp_slot.c b/drivers/pci/hotplug/rpaphp_slot.c
index d4ee872..3e1f24b 100644
--- a/drivers/pci/hotplug/rpaphp_slot.c
+++ b/drivers/pci/hotplug/rpaphp_slot.c
@@ -160,7 +160,8 @@ int rpaphp_register_slot(struct slot *slot)
return -EAGAIN;
}

- retval = pci_hp_register(php_slot);
+ retval = pci_hp_register(php_slot, slot->bus,
+ PCI_SLOT(PCI_DN(slot->dn->child)->devfn));
if (retval) {
err("pci_hp_register failed with error %d\n", retval);
return retval;
diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c
index 693519e..cc74602 100644
--- a/drivers/pci/hotplug/sgi_hotplug.c
+++ b/drivers/pci/hotplug/sgi_hotplug.c
@@ -625,7 +625,7 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
bss_hotplug_slot->ops = &sn_hotplug_slot_ops;
bss_hotplug_slot->release = &sn_release_slot;

- rc = pci_hp_register(bss_hotplug_slot);
+ rc = pci_hp_register(bss_hotplug_slot, pci_bus, device);
if (rc)
goto register_err;
}
diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c
index 80dec97..22c4d2e 100644
--- a/drivers/pci/hotplug/shpchp_core.c
+++ b/drivers/pci/hotplug/shpchp_core.c
@@ -65,7 +65,6 @@ static int get_power_status (struct hotplug_slot *slot, u8 *value);
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
-static int get_address (struct hotplug_slot *slot, u32 *value);
static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);

@@ -78,7 +77,6 @@ static struct hotplug_slot_ops shpchp_hotplug_slot_ops = {
.get_attention_status = get_attention_status,
.get_latch_status = get_latch_status,
.get_adapter_status = get_adapter_status,
- .get_address = get_address,
.get_max_bus_speed = get_max_bus_speed,
.get_cur_bus_speed = get_cur_bus_speed,
};
@@ -152,7 +150,8 @@ static int init_slots(struct controller *ctrl)
dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x "
"slot_device_offset=%x\n", slot->bus, slot->device,
slot->hp_slot, slot->number, ctrl->slot_device_offset);
- retval = pci_hp_register(slot->hotplug_slot);
+ retval = pci_hp_register(slot->hotplug_slot,
+ ctrl->pci_dev->subordinate, slot->device);
if (retval) {
err("pci_hp_register failed with error %d\n", retval);
goto error_info;
@@ -277,18 +276,6 @@ static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value)
return 0;
}

-static int get_address (struct hotplug_slot *hotplug_slot, u32 *value)
-{
- struct slot *slot = get_slot(hotplug_slot);
- struct pci_bus *bus = slot->ctrl->pci_dev->subordinate;
-
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
-
- *value = (pci_domain_nr(bus) << 16) | (slot->bus << 8) | slot->device;
-
- return 0;
-}
-
static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
{
struct slot *slot = get_slot(hotplug_slot);
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index fc87e14..4bcfabf 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -91,3 +91,16 @@ pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev)
}

struct pci_dev *pci_find_upstream_pcie_bridge(struct pci_dev *pdev);
+
+/* PCI slot sysfs helper code */
+#define to_pci_slot(s) container_of(s, struct pci_slot, kobj)
+
+extern struct kset pci_slots_subsys;
+
+struct pci_slot_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct pci_slot *, char *);
+ ssize_t (*store)(struct pci_slot *, const char *, size_t);
+};
+#define to_pci_slot_attr(s) container_of(s, struct pci_slot_attribute, attr)
+
diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c
new file mode 100644
index 0000000..f4ca61b
--- /dev/null
+++ b/drivers/pci/slot.c
@@ -0,0 +1,184 @@
+/*
+ * drivers/pci/slot.c
+ * Copyright (c) 2006 Matthew Wilcox <[email protected]>
+ * Copyright (c) 2006 Hewlett-Packard Company
+ */
+
+#include <linux/kobject.h>
+#include <linux/pci.h>
+#include "pci.h"
+
+struct kset pci_slots_subsys;
+EXPORT_SYMBOL_GPL(pci_slots_subsys);
+
+static ssize_t pci_slot_attr_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct pci_slot *slot = to_pci_slot(kobj);
+ struct pci_slot_attribute *attribute = to_pci_slot_attr(attr);
+ return attribute->show ? attribute->show(slot, buf) : -EIO;
+}
+
+static ssize_t pci_slot_attr_store(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t len)
+{
+ struct pci_slot *slot = to_pci_slot(kobj);
+ struct pci_slot_attribute *attribute = to_pci_slot_attr(attr);
+ return attribute->store ? attribute->store(slot, buf, len) : -EIO;
+}
+
+static struct sysfs_ops pci_slot_sysfs_ops = {
+ .show = pci_slot_attr_show,
+ .store = pci_slot_attr_store,
+};
+
+static void pci_slot_release(struct kobject *kobj)
+{
+ struct pci_slot *slot = to_pci_slot(kobj);
+ slot->release(slot);
+ kfree(slot);
+}
+
+static struct kobj_type pci_slot_ktype = {
+ .sysfs_ops = &pci_slot_sysfs_ops,
+ .release = &pci_slot_release,
+};
+
+decl_subsys_name(pci_slots, slots, &pci_slot_ktype, NULL);
+
+static ssize_t address_read_file(struct pci_slot *slot, char *buf)
+{
+ return sprintf(buf, "%04x:%02x:%02x\n", pci_domain_nr(slot->bus),
+ slot->bus->number, slot->number);
+}
+
+static struct pci_slot_attribute pci_slot_attr_address = {
+ .attr = { .name = "address", .mode = S_IFREG | S_IRUGO },
+ .show = address_read_file,
+};
+
+static void remove_sysfs_files(struct pci_slot *slot)
+{
+ sysfs_remove_file(&slot->kobj, &pci_slot_attr_address.attr);
+}
+
+static int create_sysfs_files(struct pci_slot *slot)
+{
+ int result;
+
+ result = sysfs_create_file(&slot->kobj, &pci_slot_attr_address.attr);
+
+ return result;
+}
+
+struct pci_slot *pci_slot_add_hotplug(struct pci_bus *parent, int slot_nr,
+ void (*release)(struct pci_slot *))
+{
+ struct pci_slot *slot;
+ int found = 0;
+
+ down_write(&pci_bus_sem);
+
+ /* This slot should have already been created, so look for it. If
+ * we can't find it, return -EEXIST.
+ */
+ for (slot = parent->slot; slot; slot = slot->next)
+ if (slot->number == slot_nr) {
+ found = 1;
+ break;
+ }
+
+ if (!found) {
+ slot = ERR_PTR(-EEXIST);
+ goto out;
+ }
+
+ slot->release = release;
+ out:
+ up_write(&pci_bus_sem);
+ return slot;
+}
+EXPORT_SYMBOL_GPL(pci_slot_add_hotplug);
+
+struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr,
+ const char *name)
+{
+ struct pci_slot *slot;
+ int err;
+
+ down_write(&pci_bus_sem);
+
+ /* If we've already created this slot, just return. */
+ for (slot = parent->slot; slot; slot = slot->next) {
+ if (slot->number != slot_nr)
+ continue;
+ goto out;
+ }
+
+ slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+ if (!slot) {
+ slot = ERR_PTR(-ENOMEM);
+ goto out;
+ }
+
+ slot->bus = parent;
+ slot->number = slot_nr;
+
+ kobject_set_name(&slot->kobj, "%s", name);
+ kobj_set_kset_s(slot, pci_slots_subsys);
+ if (kobject_register(&slot->kobj)) {
+ printk(KERN_ERR "Unable to register kobject %s", name);
+ err = -EINVAL;
+ goto err;
+ }
+
+ err = create_sysfs_files(slot);
+ if (err)
+ goto unregister;
+
+ slot->next = parent->slot;
+ parent->slot = slot;
+
+ out:
+ up_write(&pci_bus_sem);
+ return slot;
+
+ unregister:
+ kobject_unregister(&slot->kobj);
+ err:
+ kfree(slot);
+ slot = ERR_PTR(err);
+ goto out;
+}
+EXPORT_SYMBOL_GPL(pci_create_slot);
+
+int pci_destroy_slot(struct pci_slot *slot)
+{
+ struct pci_slot **pprev;
+
+ for (pprev = &slot->bus->slot; *pprev; pprev = &(*pprev)->next) {
+ if (*pprev == slot) {
+ *pprev = slot->next;
+ break;
+ }
+ }
+
+ remove_sysfs_files(slot);
+ kobject_unregister(&slot->kobj);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_destroy_slot);
+
+static int pci_slot_init(void)
+{
+ int result;
+
+ kobj_set_kset_s(&pci_slots_subsys, pci_bus_type.subsys);
+ result = subsystem_register(&pci_slots_subsys);
+ if (result)
+ printk(KERN_ERR "PCI: Slot initialisation failure (%d)",
+ result);
+ return result;
+}
+
+subsys_initcall(pci_slot_init);
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 0dd93bb..ae2245f 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -129,6 +129,16 @@ struct pci_cap_saved_state {
u32 data[0];
};

+/* pci_slot represents a physical slot */
+struct pci_slot {
+ struct pci_bus *bus; /* The bus this slot is on */
+ struct pci_slot *next; /* Next slot on this bus */
+ struct hotplug_slot *hotplug; /* Hotplug info (migrate over time) */
+ unsigned char number; /* PCI_SLOT(pci_dev->devfn) */
+ struct kobject kobj;
+ void (*release)(struct pci_slot *);
+};
+
/*
* The pci_dev structure is used to describe PCI devices.
*/
@@ -140,6 +150,7 @@ struct pci_dev {

void *sysdata; /* hook for sys-specific extension */
struct proc_dir_entry *procent; /* device entry in /proc/bus/pci */
+ struct pci_slot *slot; /* Physical slot this device is in */

unsigned int devfn; /* encoded device & function index */
unsigned short vendor;
@@ -261,6 +272,7 @@ struct pci_bus {
struct list_head children; /* list of child buses */
struct list_head devices; /* list of devices on this bus */
struct pci_dev *self; /* bridge device as seen by parent */
+ struct pci_slot *slot; /* First physical slot on this bus */
struct resource *resource[PCI_BUS_NUM_RESOURCES];
/* address space routed to this bus */

@@ -470,6 +482,11 @@ static inline struct pci_bus *pci_scan_bus(int bus, struct pci_ops *ops, void *s
}
struct pci_bus *pci_create_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata);
struct pci_bus * pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int busnr);
+struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr,
+ const char *name);
+struct pci_slot *pci_slot_add_hotplug(struct pci_bus *parent, int slot_nr,
+ void (*release)(struct pci_slot *));
+int pci_destroy_slot(struct pci_slot *slot);
int pci_scan_slot(struct pci_bus *bus, int devfn);
struct pci_dev * pci_scan_single_device(struct pci_bus *bus, int devfn);
void pci_device_add(struct pci_dev *dev, struct pci_bus *bus);
diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h
index ab4cb6e..bb36c59 100644
--- a/include/linux/pci_hotplug.h
+++ b/include/linux/pci_hotplug.h
@@ -95,9 +95,6 @@ struct hotplug_slot_attribute {
* @get_adapter_status: Called to get see if an adapter is present in the slot or not.
* If this field is NULL, the value passed in the struct hotplug_slot_info
* will be used when this value is requested by a user.
- * @get_address: Called to get pci address of a slot.
- * If this field is NULL, the value passed in the struct hotplug_slot_info
- * will be used when this value is requested by a user.
* @get_max_bus_speed: Called to get the max bus speed for a slot.
* If this field is NULL, the value passed in the struct hotplug_slot_info
* will be used when this value is requested by a user.
@@ -120,7 +117,6 @@ struct hotplug_slot_ops {
int (*get_attention_status) (struct hotplug_slot *slot, u8 *value);
int (*get_latch_status) (struct hotplug_slot *slot, u8 *value);
int (*get_adapter_status) (struct hotplug_slot *slot, u8 *value);
- int (*get_address) (struct hotplug_slot *slot, u32 *value);
int (*get_max_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value);
int (*get_cur_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value);
};
@@ -140,7 +136,6 @@ struct hotplug_slot_info {
u8 attention_status;
u8 latch_status;
u8 adapter_status;
- u32 address;
enum pci_bus_speed max_bus_speed;
enum pci_bus_speed cur_bus_speed;
};
@@ -166,15 +161,14 @@ struct hotplug_slot {

/* Variables below this are for use only by the hotplug pci core. */
struct list_head slot_list;
- struct kobject kobj;
+ struct pci_slot *pci_slot;
};
#define to_hotplug_slot(n) container_of(n, struct hotplug_slot, kobj)

-extern int pci_hp_register (struct hotplug_slot *slot);
-extern int pci_hp_deregister (struct hotplug_slot *slot);
+extern int pci_hp_register(struct hotplug_slot *, struct pci_bus *, int nr);
+extern int pci_hp_deregister(struct hotplug_slot *slot);
extern int __must_check pci_hp_change_slot_info (struct hotplug_slot *slot,
struct hotplug_slot_info *info);
-extern struct kset pci_hotplug_slots_subsys;

/* PCI Setting Record (Type 0) */
struct hpp_type0 {
--
1.5.3.1.1.g1e61

2007-11-19 23:32:41

by Gary Hade

[permalink] [raw]
Subject: Re: [PATCH 0/4, v3] Physical PCI slot objects

On Sat, Nov 17, 2007 at 11:29:54AM -0700, Alex Chiang wrote:
> Hi all,
>
> This is v3 of the pci_slot patch series.
>
> The major change is making the ACPI-PCI slot driver a Kconfig
> option, as per the recommendations of others (Gary, Kenji-san).

Alex, What I was trying to suggest is a boot-time kernel option,
not a kernel configuration option. The basic idea is to give
the user (with a single binary kernel) the ability to include
your ACPI-PCI slot driver feature changes only when they are
really needed. In addition to reducing the number of
system/PCI hotplug driver combinations where your changes
would need to be validated, I believe would also help
alleviate other worries (e.g. Andi Kleen's memory consumption
concern). I believe this goal could also be achieved with the
kernel config option by making the pci_slot module runtime
loadable with the PCI hotplug drivers only visiting your new
code when the pci_slot driver is loaded, although I think this
would be more difficult to implement.

Also, I notice that even with your current CONFIG_ACPI_PCI_SLOT
implementation your numerous PCI hotplug driver changes (except
for only two places in pci_hotplug_core.c where there is
`#ifndef CONFIG_ACPI_PCI_SLOT` and `#ifdef CONFIG_ACPI_PCI_SLOT`)
are _always_ exposed. So, even with CONFIG_ACPI_PCI_SLOT disabled
there is IMO a need for testing of the affected PCI hotplug drivers
on more than a small number of isolated systems.

The good news is that I was able to test your v3 changes
(w/2.6.24-rc3 source) on our x3850 today with 'acpiphp'
and, except for the above mentioned inability to run-time
include/exclude them, they seemed to work fine. The previous
boot-time ACPI error messages are gone and I was able to
successfully hot-remove and hot-add both PCI-X and PCIe
adapters.

Thanks,
Gary

--
Gary Hade
System x Enablement
IBM Linux Technology Center
503-578-4503 IBM T/L: 775-4503
[email protected]
http://www.ibm.com/linux/ltc


2007-11-20 01:39:59

by Kenji Kaneshige

[permalink] [raw]
Subject: Re: [PATCH 0/4, v3] Physical PCI slot objects

Gary Hade ????????:
> On Sat, Nov 17, 2007 at 11:29:54AM -0700, Alex Chiang wrote:
>> Hi all,
>>
>> This is v3 of the pci_slot patch series.
>>
>> The major change is making the ACPI-PCI slot driver a Kconfig
>> option, as per the recommendations of others (Gary, Kenji-san).
>
> Alex, What I was trying to suggest is a boot-time kernel option,
> not a kernel configuration option. The basic idea is to give
> the user (with a single binary kernel) the ability to include
> your ACPI-PCI slot driver feature changes only when they are
> really needed. In addition to reducing the number of
> system/PCI hotplug driver combinations where your changes
> would need to be validated, I believe would also help
> alleviate other worries (e.g. Andi Kleen's memory consumption
> concern). I believe this goal could also be achieved with the
> kernel config option by making the pci_slot module runtime
> loadable with the PCI hotplug drivers only visiting your new
> code when the pci_slot driver is loaded, although I think this
> would be more difficult to implement.
>

I agree to Gary very much.

Thanks,
Kenji Kaneshige



2007-11-20 02:17:26

by Matthew Garrett

[permalink] [raw]
Subject: Re: [PATCH 0/4, v3] Physical PCI slot objects

On Mon, Nov 19, 2007 at 03:32:25PM -0800, Gary Hade wrote:

> Alex, What I was trying to suggest is a boot-time kernel option,
> not a kernel configuration option. The basic idea is to give
> the user (with a single binary kernel) the ability to include
> your ACPI-PCI slot driver feature changes only when they are
> really needed. In addition to reducing the number of
> system/PCI hotplug driver combinations where your changes
> would need to be validated, I believe would also help
> alleviate other worries (e.g. Andi Kleen's memory consumption
> concern). I believe this goal could also be achieved with the
> kernel config option by making the pci_slot module runtime
> loadable with the PCI hotplug drivers only visiting your new
> code when the pci_slot driver is loaded, although I think this
> would be more difficult to implement.

If we're compiling something into the kernel, the default behaviour
should be for the functionality to be turned on unless the user
overrides it.

--
Matthew Garrett | [email protected]

2007-11-20 19:54:20

by Gary Hade

[permalink] [raw]
Subject: Re: [PATCH 0/4, v3] Physical PCI slot objects

On Tue, Nov 20, 2007 at 02:04:02AM +0000, Matthew Garrett wrote:
> On Mon, Nov 19, 2007 at 03:32:25PM -0800, Gary Hade wrote:
>
> > Alex, What I was trying to suggest is a boot-time kernel option,
> > not a kernel configuration option. The basic idea is to give
> > the user (with a single binary kernel) the ability to include
> > your ACPI-PCI slot driver feature changes only when they are
> > really needed. In addition to reducing the number of
> > system/PCI hotplug driver combinations where your changes
> > would need to be validated, I believe would also help
> > alleviate other worries (e.g. Andi Kleen's memory consumption
> > concern). I believe this goal could also be achieved with the
> > kernel config option by making the pci_slot module runtime
> > loadable with the PCI hotplug drivers only visiting your new
> > code when the pci_slot driver is loaded, although I think this
> > would be more difficult to implement.
>
> If we're compiling something into the kernel, the default behaviour
> should be for the functionality to be turned on unless the user
> overrides it.

It seems like others could have a problem with this but as
long as there is a way to exclude the functionality in the
event of problems without a kernel rebuild, "on" by default
would work for me.

Thanks,
Gary

--
Gary Hade
System x Enablement
IBM Linux Technology Center
503-578-4503 IBM T/L: 775-4503
[email protected]
http://www.ibm.com/linux/ltc

2007-11-26 22:24:14

by Alex Chiang

[permalink] [raw]
Subject: Re: [PATCH 0/4, v3] Physical PCI slot objects

Hi Gary, Kenji-san, et. al,

* Gary Hade <[email protected]>:
>
> Alex, What I was trying to suggest is a boot-time kernel
> option, not a kernel configuration option. The basic idea is
> to give the user (with a single binary kernel) the ability to
> include your ACPI-PCI slot driver feature changes only when
> they are really needed. In addition to reducing the number of
> system/PCI hotplug driver combinations where your changes would
> need to be validated, I believe would also help alleviate other
> worries (e.g. Andi Kleen's memory consumption concern). I
> believe this goal could also be achieved with the kernel config
> option by making the pci_slot module runtime loadable with the
> PCI hotplug drivers only visiting your new code when the
> pci_slot driver is loaded, although I think this would be more
> difficult to implement.

I have modified my patch series so that the final patch that
introduces my ACPI-PCI slot driver is a full-fledged module, that
has a tristate Kconfig option.

It can be modprobe'd/rmmod'ed in any combination, and in any
order with other PCI hotplug modules. There is no ordering
dependency, even at module unload time, so you can safely rmmod
pci_slot, and safely continue using features provided by the PCI
hotplug drivers (acpiphp, pciehp, etc.). The opposite works too.

The one limitation is that two separate hotplug drivers cannot
both claim the same device (2nd module loaded will get -EBUSY
errors), but I do not believe that is a regression from current
behavior.

I have only tested with acpiphp and pciehp, as that's the only
hardware I have, but I believe my code will play nicely with the
other PCI hp drivers as well.

The patch series is fully bisectable, and the correct behavior
occurs no matter which patch you happen to have applied.

I'll be sending v5 of patches 3 and 4 shortly (patches 1 and 2
did not change). It is still based on 2.6.24-rc2, because I was
too scared to do another git rebase while using stgit. :-/

> Also, I notice that even with your current CONFIG_ACPI_PCI_SLOT
> implementation your numerous PCI hotplug driver changes (except
> for only two places in pci_hotplug_core.c where there is
> `#ifndef CONFIG_ACPI_PCI_SLOT` and `#ifdef CONFIG_ACPI_PCI_SLOT`)
> are _always_ exposed. So, even with CONFIG_ACPI_PCI_SLOT disabled
> there is IMO a need for testing of the affected PCI hotplug drivers
> on more than a small number of isolated systems.

You are, of course, correct.

In my opinion, though, I would say most of the changes to the PCI
hotplug drivers themselves are pretty straightforward, as in
removing the different ways of getting the PCI address.

The scary part of the changes (aside from the ACPI-PCI slot
driver) revolve around the new struct pci_slot, which is
relatively self-contained, and only expose themselves via the
pci_create_slot/pci_destroy_slot interfaces which only the PCI
hotplug corecares about.

Regardless, your point stands. How do you suggest I get more
testing time? Is this patchset appropriate for the -mm tree yet?
Or do you think it still needs more work?

> The good news is that I was able to test your v3 changes
> (w/2.6.24-rc3 source) on our x3850 today with 'acpiphp' and,
> except for the above mentioned inability to run-time
> include/exclude them, they seemed to work fine. The previous
> boot-time ACPI error messages are gone and I was able to
> successfully hot-remove and hot-add both PCI-X and PCIe
> adapters.

Thanks for testing. Please let me know how v5 works for you too.

chers,

/ac

2007-11-26 22:27:20

by Alex Chiang

[permalink] [raw]
Subject: [PATCH 3/4, v5] PCI, PCI Hotplug: Introduce pci_slot

- Make pci_slot the primary sysfs entity. hotplug_slot becomes a
subsidiary structure.
o pci_create_slot() creates and registers a slot with the PCI core
o pci_slot_add_hotplug() gives it hotplug capability

- Change the prototype of pci_hp_register() to take the bus and
slot number (on parent bus) as parameters.

- Remove all the ->get_address methods since this functionality is
now handled by pci_slot directly.

v4 -> v5:
Add refcounting for pci_slot objects.

Return -EBUSY if an hp driver attempts to register a slot
that is already registered to another driver. Do not consider
that to be an error condition in acpiphp and pciehp.

v3 -> v4:
Fixed bug with pciehp and rpaphp registering slots

v2 -> v3:
Separated slot creation and slot hotplug ability into two
interfaces. Fixed bugs in pci_destroy_slot(), and now
properly calling from pci_hp_deregister.

v1 -> v2:
No change

Signed-off-by: Alex Chiang <[email protected]>
Signed-off-by: Matthew Wilcox <[email protected]>
---
drivers/pci/Makefile | 2 +-
drivers/pci/hotplug/acpiphp.h | 1 -
drivers/pci/hotplug/acpiphp_core.c | 25 +---
drivers/pci/hotplug/acpiphp_glue.c | 23 +--
drivers/pci/hotplug/acpiphp_ibm.c | 5 +-
drivers/pci/hotplug/cpci_hotplug_core.c | 2 +-
drivers/pci/hotplug/cpqphp_core.c | 4 +-
drivers/pci/hotplug/fakephp.c | 2 +-
drivers/pci/hotplug/ibmphp_ebda.c | 3 +-
drivers/pci/hotplug/pci_hotplug_core.c | 244 +++++++++++--------------------
drivers/pci/hotplug/pciehp_core.c | 31 ++---
drivers/pci/hotplug/rpadlpar_sysfs.c | 4 +-
drivers/pci/hotplug/rpaphp_slot.c | 3 +-
drivers/pci/hotplug/sgi_hotplug.c | 2 +-
drivers/pci/hotplug/shpchp_core.c | 17 +--
drivers/pci/pci.h | 13 ++
drivers/pci/slot.c | 197 +++++++++++++++++++++++++
include/linux/pci.h | 17 ++
include/linux/pci_hotplug.h | 12 +-
19 files changed, 361 insertions(+), 246 deletions(-)
create mode 100644 drivers/pci/slot.c

diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 5550556..12f0b2d 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -2,7 +2,7 @@
# Makefile for the PCI bus specific drivers.
#

-obj-y += access.o bus.o probe.o remove.o pci.o quirks.o \
+obj-y += access.o bus.o probe.o remove.o pci.o quirks.o slot.o \
pci-driver.o search.o pci-sysfs.o rom.o setup-res.o
obj-$(CONFIG_PROC_FS) += proc.o

diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h
index f6cc0c5..ab46189 100644
--- a/drivers/pci/hotplug/acpiphp.h
+++ b/drivers/pci/hotplug/acpiphp.h
@@ -216,7 +216,6 @@ extern u8 acpiphp_get_power_status (struct acpiphp_slot *slot);
extern u8 acpiphp_get_attention_status (struct acpiphp_slot *slot);
extern u8 acpiphp_get_latch_status (struct acpiphp_slot *slot);
extern u8 acpiphp_get_adapter_status (struct acpiphp_slot *slot);
-extern u32 acpiphp_get_address (struct acpiphp_slot *slot);

/* variables */
extern int acpiphp_debug;
diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c
index a0ca63a..518dcd6 100644
--- a/drivers/pci/hotplug/acpiphp_core.c
+++ b/drivers/pci/hotplug/acpiphp_core.c
@@ -70,7 +70,6 @@ static int disable_slot (struct hotplug_slot *slot);
static int set_attention_status (struct hotplug_slot *slot, u8 value);
static int get_power_status (struct hotplug_slot *slot, u8 *value);
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
-static int get_address (struct hotplug_slot *slot, u32 *value);
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);

@@ -83,7 +82,6 @@ static struct hotplug_slot_ops acpi_hotplug_slot_ops = {
.get_attention_status = get_attention_status,
.get_latch_status = get_latch_status,
.get_adapter_status = get_adapter_status,
- .get_address = get_address,
};


@@ -279,23 +277,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
return 0;
}

-
-/**
- * get_address - get pci address of a slot
- * @hotplug_slot: slot to get status
- * @value: pointer to struct pci_busdev (seg, bus, dev)
- */
-static int get_address(struct hotplug_slot *hotplug_slot, u32 *value)
-{
- struct slot *slot = hotplug_slot->private;
-
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
-
- *value = acpiphp_get_address(slot->acpi_slot);
-
- return 0;
-}
-
static int __init init_acpi(void)
{
int retval;
@@ -362,7 +343,11 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
acpiphp_slot->slot = slot;
snprintf(slot->name, sizeof(slot->name), "%u", slot->acpi_slot->sun);

- retval = pci_hp_register(slot->hotplug_slot);
+ retval = pci_hp_register(slot->hotplug_slot,
+ acpiphp_slot->bridge->pci_bus,
+ acpiphp_slot->device);
+ if (retval == -EBUSY)
+ goto error_hpslot;
if (retval) {
err("pci_hp_register failed with error %d\n", retval);
goto error_hpslot;
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index 1e125b5..bb0eada 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -259,7 +259,12 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
bridge->pci_bus->number, slot->device);
retval = acpiphp_register_hotplug_slot(slot);
if (retval) {
- warn("acpiphp_register_hotplug_slot failed(err code = 0x%x)\n", retval);
+ if (retval == -EBUSY)
+ warn("Slot %d already registered by another "
+ "hotplug driver\n", slot->sun);
+ else
+ warn("acpiphp_register_hotplug_slot failed "
+ "(err code = 0x%x)\n", retval);
goto err_exit;
}
}
@@ -1874,19 +1879,3 @@ u8 acpiphp_get_adapter_status(struct acpiphp_slot *slot)

return (sta == 0) ? 0 : 1;
}
-
-
-/*
- * pci address (seg/bus/dev)
- */
-u32 acpiphp_get_address(struct acpiphp_slot *slot)
-{
- u32 address;
- struct pci_bus *pci_bus = slot->bridge->pci_bus;
-
- address = (pci_domain_nr(pci_bus) << 16) |
- (pci_bus->number << 8) |
- slot->device;
-
- return address;
-}
diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c
index 56829f8..927b1fb 100644
--- a/drivers/pci/hotplug/acpiphp_ibm.c
+++ b/drivers/pci/hotplug/acpiphp_ibm.c
@@ -35,6 +35,7 @@
#include <linux/moduleparam.h>

#include "acpiphp.h"
+extern struct kset pci_slots_subsys;

#define DRIVER_VERSION "1.0.1"
#define DRIVER_AUTHOR "Irene Zubarev <[email protected]>, Vernon Mauery <[email protected]>"
@@ -428,7 +429,7 @@ static int __init ibm_acpiphp_init(void)
int retval = 0;
acpi_status status;
struct acpi_device *device;
- struct kobject *sysdir = &pci_hotplug_slots_subsys.kobj;
+ struct kobject *sysdir = &pci_slots_subsys.kobj;

dbg("%s\n", __FUNCTION__);

@@ -475,7 +476,7 @@ init_return:
static void __exit ibm_acpiphp_exit(void)
{
acpi_status status;
- struct kobject *sysdir = &pci_hotplug_slots_subsys.kobj;
+ struct kobject *sysdir = &pci_slots_subsys.kobj;

dbg("%s\n", __FUNCTION__);

diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c
index ed4d44e..aa47b80 100644
--- a/drivers/pci/hotplug/cpci_hotplug_core.c
+++ b/drivers/pci/hotplug/cpci_hotplug_core.c
@@ -285,7 +285,7 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
info->attention_status = cpci_get_attention_status(slot);

dbg("registering slot %s", slot->hotplug_slot->name);
- status = pci_hp_register(slot->hotplug_slot);
+ status = pci_hp_register(slot->hotplug_slot, bus, i);
if (status) {
err("pci_hp_register failed with error %d", status);
goto error_name;
diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c
index a96b739..67f6a0c 100644
--- a/drivers/pci/hotplug/cpqphp_core.c
+++ b/drivers/pci/hotplug/cpqphp_core.c
@@ -434,7 +434,9 @@ static int ctrl_slot_setup(struct controller *ctrl,
slot->bus, slot->device,
slot->number, ctrl->slot_device_offset,
slot_number);
- result = pci_hp_register(hotplug_slot);
+ result = pci_hp_register(hotplug_slot,
+ ctrl->pci_dev->subordinate,
+ slot->device);
if (result) {
err("pci_hp_register failed with error %d\n", result);
goto error_name;
diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c
index 996c942..405c608 100644
--- a/drivers/pci/hotplug/fakephp.c
+++ b/drivers/pci/hotplug/fakephp.c
@@ -120,7 +120,7 @@ static int add_slot(struct pci_dev *dev)
slot->release = &dummy_release;
slot->private = dslot;

- retval = pci_hp_register(slot);
+ retval = pci_hp_register(slot, dev->bus, PCI_SLOT(dev->devfn));
if (retval) {
err("pci_hp_register failed with error %d\n", retval);
goto error_dslot;
diff --git a/drivers/pci/hotplug/ibmphp_ebda.c b/drivers/pci/hotplug/ibmphp_ebda.c
index 600ed7b..eb7a1c0 100644
--- a/drivers/pci/hotplug/ibmphp_ebda.c
+++ b/drivers/pci/hotplug/ibmphp_ebda.c
@@ -1000,7 +1000,8 @@ static int __init ebda_rsrc_controller (void)
tmp_slot = list_entry (list, struct slot, ibm_slot_list);

snprintf (tmp_slot->hotplug_slot->name, 30, "%s", create_file_name (tmp_slot));
- pci_hp_register (tmp_slot->hotplug_slot);
+ pci_hp_register(tmp_slot->hotplug_slot,
+ pci_find_bus(0, tmp_slot->bus), tmp_slot->device);
}

print_ebda_hpc ();
diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c
index 01c351c..9a259eb 100644
--- a/drivers/pci/hotplug/pci_hotplug_core.c
+++ b/drivers/pci/hotplug/pci_hotplug_core.c
@@ -40,6 +40,7 @@
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
#include <asm/uaccess.h>
+#include "../pci.h"

#define MY_NAME "pci_hotplug"

@@ -61,43 +62,6 @@ static int debug;

static LIST_HEAD(pci_hotplug_slot_list);

-struct kset pci_hotplug_slots_subsys;
-
-static ssize_t hotplug_slot_attr_show(struct kobject *kobj,
- struct attribute *attr, char *buf)
-{
- struct hotplug_slot *slot = to_hotplug_slot(kobj);
- struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr);
- return attribute->show ? attribute->show(slot, buf) : -EIO;
-}
-
-static ssize_t hotplug_slot_attr_store(struct kobject *kobj,
- struct attribute *attr, const char *buf, size_t len)
-{
- struct hotplug_slot *slot = to_hotplug_slot(kobj);
- struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr);
- return attribute->store ? attribute->store(slot, buf, len) : -EIO;
-}
-
-static struct sysfs_ops hotplug_slot_sysfs_ops = {
- .show = hotplug_slot_attr_show,
- .store = hotplug_slot_attr_store,
-};
-
-static void hotplug_slot_release(struct kobject *kobj)
-{
- struct hotplug_slot *slot = to_hotplug_slot(kobj);
- if (slot->release)
- slot->release(slot);
-}
-
-static struct kobj_type hotplug_slot_ktype = {
- .sysfs_ops = &hotplug_slot_sysfs_ops,
- .release = &hotplug_slot_release,
-};
-
-decl_subsys_name(pci_hotplug_slots, slots, &hotplug_slot_ktype, NULL);
-
/* these strings match up with the values in pci_bus_speed */
static char *pci_bus_speed_strings[] = {
"33 MHz PCI", /* 0x00 */
@@ -151,16 +115,15 @@ GET_STATUS(power_status, u8)
GET_STATUS(attention_status, u8)
GET_STATUS(latch_status, u8)
GET_STATUS(adapter_status, u8)
-GET_STATUS(address, u32)
GET_STATUS(max_bus_speed, enum pci_bus_speed)
GET_STATUS(cur_bus_speed, enum pci_bus_speed)

-static ssize_t power_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t power_read_file(struct pci_slot *slot, char *buf)
{
int retval;
u8 value;

- retval = get_power_status (slot, &value);
+ retval = get_power_status(slot->hotplug, &value);
if (retval)
goto exit;
retval = sprintf (buf, "%d\n", value);
@@ -168,9 +131,10 @@ exit:
return retval;
}

-static ssize_t power_write_file (struct hotplug_slot *slot, const char *buf,
+static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,
size_t count)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
unsigned long lpower;
u8 power;
int retval = 0;
@@ -206,29 +170,30 @@ exit:
return count;
}

-static struct hotplug_slot_attribute hotplug_slot_attr_power = {
+static struct pci_slot_attribute hotplug_slot_attr_power = {
.attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR},
.show = power_read_file,
.store = power_write_file
};

-static ssize_t attention_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t attention_read_file(struct pci_slot *slot, char *buf)
{
int retval;
u8 value;

- retval = get_attention_status (slot, &value);
+ retval = get_attention_status(slot->hotplug, &value);
if (retval)
goto exit;
- retval = sprintf (buf, "%d\n", value);
+ retval = sprintf(buf, "%d\n", value);

exit:
return retval;
}

-static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf,
+static ssize_t attention_write_file(struct pci_slot *slot, const char *buf,
size_t count)
{
+ struct hotplug_slot_ops *ops = slot->hotplug->ops;
unsigned long lattention;
u8 attention;
int retval = 0;
@@ -237,13 +202,13 @@ static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf,
attention = (u8)(lattention & 0xff);
dbg (" - attention = %d\n", attention);

- if (!try_module_get(slot->ops->owner)) {
+ if (!try_module_get(ops->owner)) {
retval = -ENODEV;
goto exit;
}
- if (slot->ops->set_attention_status)
- retval = slot->ops->set_attention_status(slot, attention);
- module_put(slot->ops->owner);
+ if (ops->set_attention_status)
+ retval = ops->set_attention_status(slot->hotplug, attention);
+ module_put(ops->owner);

exit:
if (retval)
@@ -251,18 +216,18 @@ exit:
return count;
}

-static struct hotplug_slot_attribute hotplug_slot_attr_attention = {
+static struct pci_slot_attribute hotplug_slot_attr_attention = {
.attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR},
.show = attention_read_file,
.store = attention_write_file
};

-static ssize_t latch_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t latch_read_file(struct pci_slot *slot, char *buf)
{
int retval;
u8 value;

- retval = get_latch_status (slot, &value);
+ retval = get_latch_status(slot->hotplug, &value);
if (retval)
goto exit;
retval = sprintf (buf, "%d\n", value);
@@ -271,17 +236,17 @@ exit:
return retval;
}

-static struct hotplug_slot_attribute hotplug_slot_attr_latch = {
+static struct pci_slot_attribute hotplug_slot_attr_latch = {
.attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},
.show = latch_read_file,
};

-static ssize_t presence_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t presence_read_file(struct pci_slot *slot, char *buf)
{
int retval;
u8 value;

- retval = get_adapter_status (slot, &value);
+ retval = get_adapter_status(slot->hotplug, &value);
if (retval)
goto exit;
retval = sprintf (buf, "%d\n", value);
@@ -290,42 +255,20 @@ exit:
return retval;
}

-static struct hotplug_slot_attribute hotplug_slot_attr_presence = {
+static struct pci_slot_attribute hotplug_slot_attr_presence = {
.attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},
.show = presence_read_file,
};

-static ssize_t address_read_file (struct hotplug_slot *slot, char *buf)
-{
- int retval;
- u32 address;
-
- retval = get_address (slot, &address);
- if (retval)
- goto exit;
- retval = sprintf (buf, "%04x:%02x:%02x\n",
- (address >> 16) & 0xffff,
- (address >> 8) & 0xff,
- address & 0xff);
-
-exit:
- return retval;
-}
-
-static struct hotplug_slot_attribute hotplug_slot_attr_address = {
- .attr = {.name = "address", .mode = S_IFREG | S_IRUGO},
- .show = address_read_file,
-};
-
static char *unknown_speed = "Unknown bus speed";

-static ssize_t max_bus_speed_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t max_bus_speed_read_file(struct pci_slot *slot, char *buf)
{
char *speed_string;
int retval;
enum pci_bus_speed value;

- retval = get_max_bus_speed (slot, &value);
+ retval = get_max_bus_speed(slot->hotplug, &value);
if (retval)
goto exit;

@@ -340,18 +283,18 @@ exit:
return retval;
}

-static struct hotplug_slot_attribute hotplug_slot_attr_max_bus_speed = {
+static struct pci_slot_attribute hotplug_slot_attr_max_bus_speed = {
.attr = {.name = "max_bus_speed", .mode = S_IFREG | S_IRUGO},
.show = max_bus_speed_read_file,
};

-static ssize_t cur_bus_speed_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t cur_bus_speed_read_file(struct pci_slot *slot, char *buf)
{
char *speed_string;
int retval;
enum pci_bus_speed value;

- retval = get_cur_bus_speed (slot, &value);
+ retval = get_cur_bus_speed(slot->hotplug, &value);
if (retval)
goto exit;

@@ -366,14 +309,15 @@ exit:
return retval;
}

-static struct hotplug_slot_attribute hotplug_slot_attr_cur_bus_speed = {
+static struct pci_slot_attribute hotplug_slot_attr_cur_bus_speed = {
.attr = {.name = "cur_bus_speed", .mode = S_IFREG | S_IRUGO},
.show = cur_bus_speed_read_file,
};

-static ssize_t test_write_file (struct hotplug_slot *slot, const char *buf,
+static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,
size_t count)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
unsigned long ltest;
u32 test;
int retval = 0;
@@ -396,13 +340,14 @@ exit:
return count;
}

-static struct hotplug_slot_attribute hotplug_slot_attr_test = {
+static struct pci_slot_attribute hotplug_slot_attr_test = {
.attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},
.store = test_write_file
};

-static int has_power_file (struct hotplug_slot *slot)
+static int has_power_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if ((slot->ops->enable_slot) ||
@@ -412,8 +357,9 @@ static int has_power_file (struct hotplug_slot *slot)
return -ENOENT;
}

-static int has_attention_file (struct hotplug_slot *slot)
+static int has_attention_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if ((slot->ops->set_attention_status) ||
@@ -422,8 +368,9 @@ static int has_attention_file (struct hotplug_slot *slot)
return -ENOENT;
}

-static int has_latch_file (struct hotplug_slot *slot)
+static int has_latch_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->get_latch_status)
@@ -431,8 +378,9 @@ static int has_latch_file (struct hotplug_slot *slot)
return -ENOENT;
}

-static int has_adapter_file (struct hotplug_slot *slot)
+static int has_adapter_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->get_adapter_status)
@@ -440,17 +388,9 @@ static int has_adapter_file (struct hotplug_slot *slot)
return -ENOENT;
}

-static int has_address_file (struct hotplug_slot *slot)
-{
- if ((!slot) || (!slot->ops))
- return -ENODEV;
- if (slot->ops->get_address)
- return 0;
- return -ENOENT;
-}
-
-static int has_max_bus_speed_file (struct hotplug_slot *slot)
+static int has_max_bus_speed_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->get_max_bus_speed)
@@ -458,8 +398,9 @@ static int has_max_bus_speed_file (struct hotplug_slot *slot)
return -ENOENT;
}

-static int has_cur_bus_speed_file (struct hotplug_slot *slot)
+static int has_cur_bus_speed_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->get_cur_bus_speed)
@@ -467,8 +408,9 @@ static int has_cur_bus_speed_file (struct hotplug_slot *slot)
return -ENOENT;
}

-static int has_test_file (struct hotplug_slot *slot)
+static int has_test_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->hardware_test)
@@ -476,7 +418,7 @@ static int has_test_file (struct hotplug_slot *slot)
return -ENOENT;
}

-static int fs_add_slot (struct hotplug_slot *slot)
+static int fs_add_slot(struct pci_slot *slot)
{
int retval = 0;

@@ -507,13 +449,6 @@ static int fs_add_slot (struct hotplug_slot *slot)
goto exit_adapter;
}

- if (has_address_file(slot) == 0) {
- retval = sysfs_create_file(&slot->kobj,
- &hotplug_slot_attr_address.attr);
- if (retval)
- goto exit_address;
- }
-
if (has_max_bus_speed_file(slot) == 0) {
retval = sysfs_create_file(&slot->kobj,
&hotplug_slot_attr_max_bus_speed.attr);
@@ -546,10 +481,6 @@ exit_cur_speed:
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);

exit_max_speed:
- if (has_address_file(slot) == 0)
- sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr);
-
-exit_address:
if (has_adapter_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);

@@ -569,7 +500,7 @@ exit:
return retval;
}

-static void fs_remove_slot (struct hotplug_slot *slot)
+static void fs_remove_slot(struct pci_slot *slot)
{
if (has_power_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
@@ -583,9 +514,6 @@ static void fs_remove_slot (struct hotplug_slot *slot)
if (has_adapter_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);

- if (has_address_file(slot) == 0)
- sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr);
-
if (has_max_bus_speed_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);

@@ -609,6 +537,12 @@ static struct hotplug_slot *get_slot_from_name (const char *name)
return NULL;
}

+static void hotplug_release(struct pci_slot *slot)
+{
+ struct hotplug_slot *hotplug = slot->hotplug;
+ hotplug->release(hotplug);
+}
+
/**
* pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem
* @slot: pointer to the &struct hotplug_slot to register
@@ -618,9 +552,10 @@ static struct hotplug_slot *get_slot_from_name (const char *name)
*
* Returns 0 if successful, anything else for an error.
*/
-int pci_hp_register (struct hotplug_slot *slot)
+int pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, int slot_nr)
{
int result;
+ struct pci_slot *pci_slot;

if (slot == NULL)
return -ENODEV;
@@ -632,19 +567,23 @@ int pci_hp_register (struct hotplug_slot *slot)
return -EINVAL;
}

- kobject_set_name(&slot->kobj, "%s", slot->name);
- kobj_set_kset_s(slot, pci_hotplug_slots_subsys);
+ pci_slot = pci_create_slot(bus, slot_nr, slot->name);
+ if (IS_ERR(pci_slot))
+ return PTR_ERR(pci_slot);

- /* this can fail if we have already registered a slot with the same name */
- if (kobject_register(&slot->kobj)) {
- err("Unable to register kobject");
- return -EINVAL;
+ result = pci_slot_add_hotplug(bus, slot_nr, hotplug_release);
+ if (result) {
+ pci_destroy_slot(pci_slot);
+ return result;
}
-
- list_add (&slot->slot_list, &pci_hotplug_slot_list);

- result = fs_add_slot (slot);
- dbg ("Added slot %s to the list\n", slot->name);
+ slot->pci_slot = pci_slot;
+ pci_slot->hotplug = slot;
+
+ list_add(&slot->slot_list, &pci_hotplug_slot_list);
+
+ result = fs_add_slot(pci_slot);
+ dbg("Added slot %s to the list\n", slot->name);
return result;
}

@@ -657,22 +596,24 @@ int pci_hp_register (struct hotplug_slot *slot)
*
* Returns 0 if successful, anything else for an error.
*/
-int pci_hp_deregister (struct hotplug_slot *slot)
+int pci_hp_deregister(struct hotplug_slot *hotplug)
{
struct hotplug_slot *temp;
+ struct pci_slot *slot;

- if (slot == NULL)
+ if (!hotplug)
return -ENODEV;

- temp = get_slot_from_name (slot->name);
- if (temp != slot) {
+ temp = get_slot_from_name(hotplug->name);
+ if (temp != hotplug)
return -ENODEV;
- }
- list_del (&slot->slot_list);

- fs_remove_slot (slot);
- dbg ("Removed slot %s from the list\n", slot->name);
- kobject_unregister(&slot->kobj);
+ list_del(&hotplug->slot_list);
+
+ slot = hotplug->pci_slot;
+ fs_remove_slot(slot);
+ pci_destroy_slot(slot);
+ dbg("Removed slot %s from the list\n", hotplug->name);
return 0;
}

@@ -686,13 +627,15 @@ int pci_hp_deregister (struct hotplug_slot *slot)
*
* Returns 0 if successful, anything else for an error.
*/
-int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot,
+int __must_check pci_hp_change_slot_info(struct hotplug_slot *hotplug,
struct hotplug_slot_info *info)
{
- if ((slot == NULL) || (info == NULL))
+ struct pci_slot *slot;
+ if (!hotplug || !info)
return -ENODEV;
+ slot = hotplug->pci_slot;

- memcpy (slot->info, info, sizeof (struct hotplug_slot_info));
+ memcpy(hotplug->info, info, sizeof(struct hotplug_slot_info));

return 0;
}
@@ -701,31 +644,21 @@ static int __init pci_hotplug_init (void)
{
int result;

- kobj_set_kset_s(&pci_hotplug_slots_subsys, pci_bus_type.subsys);
- result = subsystem_register(&pci_hotplug_slots_subsys);
- if (result) {
- err("Register subsys with error %d\n", result);
- goto exit;
- }
result = cpci_hotplug_init(debug);
if (result) {
err ("cpci_hotplug_init with error %d\n", result);
- goto err_subsys;
+ goto err_cpci;
}

info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
- goto exit;
-
-err_subsys:
- subsystem_unregister(&pci_hotplug_slots_subsys);
-exit:
+
+err_cpci:
return result;
}

static void __exit pci_hotplug_exit (void)
{
cpci_hotplug_exit();
- subsystem_unregister(&pci_hotplug_slots_subsys);
}

module_init(pci_hotplug_init);
@@ -737,7 +670,6 @@ MODULE_LICENSE("GPL");
module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");

-EXPORT_SYMBOL_GPL(pci_hotplug_slots_subsys);
EXPORT_SYMBOL_GPL(pci_hp_register);
EXPORT_SYMBOL_GPL(pci_hp_deregister);
EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index 6462ac3..0e9eaf6 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -69,7 +69,6 @@ static int get_power_status (struct hotplug_slot *slot, u8 *value);
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
-static int get_address (struct hotplug_slot *slot, u32 *value);
static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);

@@ -82,7 +81,6 @@ static struct hotplug_slot_ops pciehp_hotplug_slot_ops = {
.get_attention_status = get_attention_status,
.get_latch_status = get_latch_status,
.get_adapter_status = get_adapter_status,
- .get_address = get_address,
.get_max_bus_speed = get_max_bus_speed,
.get_cur_bus_speed = get_cur_bus_speed,
};
@@ -245,14 +243,18 @@ static int init_slots(struct controller *ctrl)
dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x "
"slot_device_offset=%x\n", slot->bus, slot->device,
slot->hp_slot, slot->number, ctrl->slot_device_offset);
- retval = pci_hp_register(hotplug_slot);
+ retval = pci_hp_register(hotplug_slot,
+ ctrl->pci_dev->subordinate,
+ slot->device);
+ if (retval == -EBUSY)
+ goto error_info;
if (retval) {
err ("pci_hp_register failed with error %d\n", retval);
goto error_info;
}
/* create additional sysfs entries */
if (EMI(ctrl->ctrlcap)) {
- retval = sysfs_create_file(&hotplug_slot->kobj,
+ retval = sysfs_create_file(&hotplug_slot->pci_slot->kobj,
&hotplug_slot_attr_lock.attr);
if (retval) {
pci_hp_deregister(hotplug_slot);
@@ -285,7 +287,7 @@ static void cleanup_slots(struct controller *ctrl)
slot = list_entry(tmp, struct slot, slot_list);
list_del(&slot->slot_list);
if (EMI(ctrl->ctrlcap))
- sysfs_remove_file(&slot->hotplug_slot->kobj,
+ sysfs_remove_file(&slot->hotplug_slot->pci_slot->kobj,
&hotplug_slot_attr_lock.attr);
cancel_delayed_work(&slot->work);
flush_scheduled_work();
@@ -387,18 +389,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
return 0;
}

-static int get_address(struct hotplug_slot *hotplug_slot, u32 *value)
-{
- struct slot *slot = hotplug_slot->private;
- struct pci_bus *bus = slot->ctrl->pci_dev->subordinate;
-
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
-
- *value = (pci_domain_nr(bus) << 16) | (slot->bus << 8) | slot->device;
-
- return 0;
-}
-
static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
{
struct slot *slot = hotplug_slot->private;
@@ -464,7 +454,12 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_
/* Setup the slot information structures */
rc = init_slots(ctrl);
if (rc) {
- err("%s: slot initialization failed\n", PCIE_MODULE_NAME);
+ if (rc == -EBUSY)
+ warn("%s: slot already registered by another "
+ "hotplug driver\n", PCIE_MODULE_NAME);
+ else
+ err("%s: slot initialization failed\n",
+ PCIE_MODULE_NAME);
goto err_out_release_ctlr;
}

diff --git a/drivers/pci/hotplug/rpadlpar_sysfs.c b/drivers/pci/hotplug/rpadlpar_sysfs.c
index a080fed..3663507 100644
--- a/drivers/pci/hotplug/rpadlpar_sysfs.c
+++ b/drivers/pci/hotplug/rpadlpar_sysfs.c
@@ -23,6 +23,8 @@

#define MAX_DRC_NAME_LEN 64

+extern struct kset pci_slots_subsys;
+
/* Store return code of dlpar operation in attribute struct */
struct dlpar_io_attr {
int rc;
@@ -130,7 +132,7 @@ struct kobj_type ktype_dlpar_io = {

struct kset dlpar_io_kset = {
.kobj = {.ktype = &ktype_dlpar_io,
- .parent = &pci_hotplug_slots_subsys.kobj},
+ .parent = &pci_slots_subsys.kobj},
.ktype = &ktype_dlpar_io,
};

diff --git a/drivers/pci/hotplug/rpaphp_slot.c b/drivers/pci/hotplug/rpaphp_slot.c
index d4ee872..3e1f24b 100644
--- a/drivers/pci/hotplug/rpaphp_slot.c
+++ b/drivers/pci/hotplug/rpaphp_slot.c
@@ -160,7 +160,8 @@ int rpaphp_register_slot(struct slot *slot)
return -EAGAIN;
}

- retval = pci_hp_register(php_slot);
+ retval = pci_hp_register(php_slot, slot->bus,
+ PCI_SLOT(PCI_DN(slot->dn->child)->devfn));
if (retval) {
err("pci_hp_register failed with error %d\n", retval);
return retval;
diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c
index 693519e..cc74602 100644
--- a/drivers/pci/hotplug/sgi_hotplug.c
+++ b/drivers/pci/hotplug/sgi_hotplug.c
@@ -625,7 +625,7 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
bss_hotplug_slot->ops = &sn_hotplug_slot_ops;
bss_hotplug_slot->release = &sn_release_slot;

- rc = pci_hp_register(bss_hotplug_slot);
+ rc = pci_hp_register(bss_hotplug_slot, pci_bus, device);
if (rc)
goto register_err;
}
diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c
index 80dec97..22c4d2e 100644
--- a/drivers/pci/hotplug/shpchp_core.c
+++ b/drivers/pci/hotplug/shpchp_core.c
@@ -65,7 +65,6 @@ static int get_power_status (struct hotplug_slot *slot, u8 *value);
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
-static int get_address (struct hotplug_slot *slot, u32 *value);
static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);

@@ -78,7 +77,6 @@ static struct hotplug_slot_ops shpchp_hotplug_slot_ops = {
.get_attention_status = get_attention_status,
.get_latch_status = get_latch_status,
.get_adapter_status = get_adapter_status,
- .get_address = get_address,
.get_max_bus_speed = get_max_bus_speed,
.get_cur_bus_speed = get_cur_bus_speed,
};
@@ -152,7 +150,8 @@ static int init_slots(struct controller *ctrl)
dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x "
"slot_device_offset=%x\n", slot->bus, slot->device,
slot->hp_slot, slot->number, ctrl->slot_device_offset);
- retval = pci_hp_register(slot->hotplug_slot);
+ retval = pci_hp_register(slot->hotplug_slot,
+ ctrl->pci_dev->subordinate, slot->device);
if (retval) {
err("pci_hp_register failed with error %d\n", retval);
goto error_info;
@@ -277,18 +276,6 @@ static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value)
return 0;
}

-static int get_address (struct hotplug_slot *hotplug_slot, u32 *value)
-{
- struct slot *slot = get_slot(hotplug_slot);
- struct pci_bus *bus = slot->ctrl->pci_dev->subordinate;
-
- dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
-
- *value = (pci_domain_nr(bus) << 16) | (slot->bus << 8) | slot->device;
-
- return 0;
-}
-
static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
{
struct slot *slot = get_slot(hotplug_slot);
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index fc87e14..4bcfabf 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -91,3 +91,16 @@ pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev)
}

struct pci_dev *pci_find_upstream_pcie_bridge(struct pci_dev *pdev);
+
+/* PCI slot sysfs helper code */
+#define to_pci_slot(s) container_of(s, struct pci_slot, kobj)
+
+extern struct kset pci_slots_subsys;
+
+struct pci_slot_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct pci_slot *, char *);
+ ssize_t (*store)(struct pci_slot *, const char *, size_t);
+};
+#define to_pci_slot_attr(s) container_of(s, struct pci_slot_attribute, attr)
+
diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c
new file mode 100644
index 0000000..f16e0af
--- /dev/null
+++ b/drivers/pci/slot.c
@@ -0,0 +1,197 @@
+/*
+ * drivers/pci/slot.c
+ * Copyright (C) 2006 Matthew Wilcox <[email protected]>
+ * Copyright (C) 2006,2007 Hewlett-Packard Development Company, L.P.
+ * Copyright (C) 2007 Alex Chiang <[email protected]>
+ */
+
+#include <linux/kobject.h>
+#include <linux/pci.h>
+#include "pci.h"
+
+struct kset pci_slots_subsys;
+EXPORT_SYMBOL_GPL(pci_slots_subsys);
+
+static ssize_t pci_slot_attr_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct pci_slot *slot = to_pci_slot(kobj);
+ struct pci_slot_attribute *attribute = to_pci_slot_attr(attr);
+ return attribute->show ? attribute->show(slot, buf) : -EIO;
+}
+
+static ssize_t pci_slot_attr_store(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t len)
+{
+ struct pci_slot *slot = to_pci_slot(kobj);
+ struct pci_slot_attribute *attribute = to_pci_slot_attr(attr);
+ return attribute->store ? attribute->store(slot, buf, len) : -EIO;
+}
+
+static struct sysfs_ops pci_slot_sysfs_ops = {
+ .show = pci_slot_attr_show,
+ .store = pci_slot_attr_store,
+};
+
+static ssize_t address_read_file(struct pci_slot *slot, char *buf)
+{
+ return sprintf(buf, "%04x:%02x:%02x\n", pci_domain_nr(slot->bus),
+ slot->bus->number, slot->number);
+}
+
+static struct pci_slot_attribute pci_slot_attr_address = {
+ .attr = { .name = "address", .mode = S_IFREG | S_IRUGO },
+ .show = address_read_file,
+};
+
+static void remove_sysfs_files(struct pci_slot *slot)
+{
+ sysfs_remove_file(&slot->kobj, &pci_slot_attr_address.attr);
+}
+
+static int create_sysfs_files(struct pci_slot *slot)
+{
+ int result;
+
+ result = sysfs_create_file(&slot->kobj, &pci_slot_attr_address.attr);
+
+ return result;
+}
+
+static void pci_slot_release(struct kobject *kobj)
+{
+ struct pci_slot **pprev;
+ struct pci_slot *slot = to_pci_slot(kobj);
+
+ for (pprev = &slot->bus->slot; *pprev; pprev = &(*pprev)->next) {
+ if (*pprev == slot) {
+ *pprev = slot->next;
+ break;
+ }
+ }
+
+ if (slot->release)
+ slot->release(slot);
+
+ remove_sysfs_files(slot);
+ kfree(slot);
+}
+
+static struct kobj_type pci_slot_ktype = {
+ .sysfs_ops = &pci_slot_sysfs_ops,
+ .release = &pci_slot_release,
+};
+decl_subsys_name(pci_slots, slots, &pci_slot_ktype, NULL);
+
+int pci_slot_add_hotplug(struct pci_bus *parent, int slot_nr,
+ void (*release)(struct pci_slot *))
+{
+ struct pci_slot *slot;
+ int retval, found;
+
+ retval = found = 0;
+
+ down_write(&pci_bus_sem);
+
+ /* This slot should have already been created, so look for it. If
+ * we can't find it, return -EEXIST.
+ */
+ for (slot = parent->slot; slot; slot = slot->next)
+ if (slot->number == slot_nr) {
+ found = 1;
+ break;
+ }
+
+ if (!found) {
+ retval = -EEXIST;
+ goto out;
+ }
+
+ if (slot->release) {
+ retval = -EBUSY;
+ goto out;
+ }
+
+ slot->release = release;
+ out:
+ up_write(&pci_bus_sem);
+ return retval;
+}
+EXPORT_SYMBOL_GPL(pci_slot_add_hotplug);
+
+struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr,
+ const char *name)
+{
+ struct pci_slot *slot;
+ int err;
+
+ down_write(&pci_bus_sem);
+
+ /* If we've already created this slot, bump refcount and return. */
+ for (slot = parent->slot; slot; slot = slot->next) {
+ if (slot->number == slot_nr) {
+ kobject_get(&slot->kobj);
+ goto out;
+ }
+ }
+
+ slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+ if (!slot) {
+ slot = ERR_PTR(-ENOMEM);
+ goto out;
+ }
+
+ slot->bus = parent;
+ slot->number = slot_nr;
+
+ kobject_set_name(&slot->kobj, "%s", name);
+ kobj_set_kset_s(slot, pci_slots_subsys);
+ if (kobject_register(&slot->kobj)) {
+ printk(KERN_ERR "Unable to register kobject %s", name);
+ err = -EINVAL;
+ goto err;
+ }
+
+ err = create_sysfs_files(slot);
+ if (err)
+ goto unregister;
+
+ slot->next = parent->slot;
+ parent->slot = slot;
+
+ out:
+ up_write(&pci_bus_sem);
+ return slot;
+
+ unregister:
+ kobject_unregister(&slot->kobj);
+ err:
+ kfree(slot);
+ slot = ERR_PTR(err);
+ goto out;
+}
+EXPORT_SYMBOL_GPL(pci_create_slot);
+
+int pci_destroy_slot(struct pci_slot *slot)
+{
+ kobject_put(&slot->kobj);
+ if (atomic_read(&slot->kobj.kref.refcount) == 1)
+ kobject_unregister(&slot->kobj);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_destroy_slot);
+
+static int pci_slot_init(void)
+{
+ int result;
+
+ kobj_set_kset_s(&pci_slots_subsys, pci_bus_type.subsys);
+ result = subsystem_register(&pci_slots_subsys);
+ if (result)
+ printk(KERN_ERR "PCI: Slot initialisation failure (%d)",
+ result);
+ return result;
+}
+
+subsys_initcall(pci_slot_init);
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 0dd93bb..b70d7dc 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -129,6 +129,16 @@ struct pci_cap_saved_state {
u32 data[0];
};

+/* pci_slot represents a physical slot */
+struct pci_slot {
+ struct pci_bus *bus; /* The bus this slot is on */
+ struct pci_slot *next; /* Next slot on this bus */
+ struct hotplug_slot *hotplug; /* Hotplug info (migrate over time) */
+ unsigned char number; /* PCI_SLOT(pci_dev->devfn) */
+ struct kobject kobj;
+ void (*release)(struct pci_slot *);
+};
+
/*
* The pci_dev structure is used to describe PCI devices.
*/
@@ -140,6 +150,7 @@ struct pci_dev {

void *sysdata; /* hook for sys-specific extension */
struct proc_dir_entry *procent; /* device entry in /proc/bus/pci */
+ struct pci_slot *slot; /* Physical slot this device is in */

unsigned int devfn; /* encoded device & function index */
unsigned short vendor;
@@ -261,6 +272,7 @@ struct pci_bus {
struct list_head children; /* list of child buses */
struct list_head devices; /* list of devices on this bus */
struct pci_dev *self; /* bridge device as seen by parent */
+ struct pci_slot *slot; /* First physical slot on this bus */
struct resource *resource[PCI_BUS_NUM_RESOURCES];
/* address space routed to this bus */

@@ -470,6 +482,11 @@ static inline struct pci_bus *pci_scan_bus(int bus, struct pci_ops *ops, void *s
}
struct pci_bus *pci_create_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata);
struct pci_bus * pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int busnr);
+struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr,
+ const char *name);
+int pci_slot_add_hotplug(struct pci_bus *parent, int slot_nr,
+ void (*release)(struct pci_slot *));
+int pci_destroy_slot(struct pci_slot *slot);
int pci_scan_slot(struct pci_bus *bus, int devfn);
struct pci_dev * pci_scan_single_device(struct pci_bus *bus, int devfn);
void pci_device_add(struct pci_dev *dev, struct pci_bus *bus);
diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h
index ab4cb6e..bb36c59 100644
--- a/include/linux/pci_hotplug.h
+++ b/include/linux/pci_hotplug.h
@@ -95,9 +95,6 @@ struct hotplug_slot_attribute {
* @get_adapter_status: Called to get see if an adapter is present in the slot or not.
* If this field is NULL, the value passed in the struct hotplug_slot_info
* will be used when this value is requested by a user.
- * @get_address: Called to get pci address of a slot.
- * If this field is NULL, the value passed in the struct hotplug_slot_info
- * will be used when this value is requested by a user.
* @get_max_bus_speed: Called to get the max bus speed for a slot.
* If this field is NULL, the value passed in the struct hotplug_slot_info
* will be used when this value is requested by a user.
@@ -120,7 +117,6 @@ struct hotplug_slot_ops {
int (*get_attention_status) (struct hotplug_slot *slot, u8 *value);
int (*get_latch_status) (struct hotplug_slot *slot, u8 *value);
int (*get_adapter_status) (struct hotplug_slot *slot, u8 *value);
- int (*get_address) (struct hotplug_slot *slot, u32 *value);
int (*get_max_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value);
int (*get_cur_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value);
};
@@ -140,7 +136,6 @@ struct hotplug_slot_info {
u8 attention_status;
u8 latch_status;
u8 adapter_status;
- u32 address;
enum pci_bus_speed max_bus_speed;
enum pci_bus_speed cur_bus_speed;
};
@@ -166,15 +161,14 @@ struct hotplug_slot {

/* Variables below this are for use only by the hotplug pci core. */
struct list_head slot_list;
- struct kobject kobj;
+ struct pci_slot *pci_slot;
};
#define to_hotplug_slot(n) container_of(n, struct hotplug_slot, kobj)

-extern int pci_hp_register (struct hotplug_slot *slot);
-extern int pci_hp_deregister (struct hotplug_slot *slot);
+extern int pci_hp_register(struct hotplug_slot *, struct pci_bus *, int nr);
+extern int pci_hp_deregister(struct hotplug_slot *slot);
extern int __must_check pci_hp_change_slot_info (struct hotplug_slot *slot,
struct hotplug_slot_info *info);
-extern struct kset pci_hotplug_slots_subsys;

/* PCI Setting Record (Type 0) */
struct hpp_type0 {
--
1.5.3.1.g1e61

2007-11-26 22:28:32

by Alex Chiang

[permalink] [raw]
Subject: [PATCH 4/4, v5] ACPI, PCI: ACPI PCI slot detection driver

Detect all physical PCI slots as described by ACPI, and create
entries in /sys/bus/pci/slots/.

Not all physical slots are hotpluggable, and the acpiphp module
does not detect them. Now we know the physical PCI geography of
our system, without caring about hotplug.

v4 -> v5:
Convert to a tristate module.

Remove #ifdef CONFIG_ACPI_PCI_SLOT, as struct pci_slot
objects are properly refcounted, and multiple calls to
pci_create/destroy_slot work just fine.

Remove prior -EBUSY changes, as they have been folded
into the Introduce pci_slot patch.

v3 -> v4:
Always attempt to call pci_create_slot from pcihp_core to
cover cases of
a) user did not say Y to Kconfig option ACPI_PCI_SLOT
b) native PCIe hotplug driver registering on a
non-ACPI system.

Return -EBUSY if an hp driver attempts to register a slot
that is already registered to another driver. Do not
consider that to be an error condition in acpiphp and pciehp.

v2 -> v3:
Add Kconfig option to driver, allowing users to [de]config
this driver. If configured, take slightly different code
paths in pci_hp_register and pci_hp_deregister.

v1 -> v2:
Now recursively discovering p2p bridges and slots
underneath them. Hopefully, this will prevent us
from trying to register the same slot multiple times.

Signed-off-by: Alex Chiang <[email protected]>
---
drivers/acpi/Kconfig | 9 +
drivers/acpi/Makefile | 1 +
drivers/acpi/pci_slot.c | 294 ++++++++++++++++++++++++++++++++
drivers/pci/hotplug/pci_hotplug_core.c | 11 +-
drivers/pci/hotplug/pciehp_core.c | 2 +-
drivers/pci/hotplug/sgi_hotplug.c | 2 +-
6 files changed, 316 insertions(+), 3 deletions(-)
create mode 100644 drivers/acpi/pci_slot.c

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index ce9dead..f1eae4f 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -292,6 +292,15 @@ config ACPI_EC
the battery and thermal drivers. If you are compiling for a
mobile system, say Y.

+config ACPI_PCI_SLOT
+ tristate "PCI slot detection driver"
+ default n
+ help
+ This driver will attempt to discover all PCI slots in your system,
+ and creates entries in /sys/bus/pci/slots/. This feature can
+ help you correlate PCI bus addresses with the physical geography
+ of your slots. If you are unsure, say N.
+
config ACPI_POWER
bool
default y
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 54e3ab0..d89000e 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_ACPI_DOCK) += dock.o
obj-$(CONFIG_ACPI_BAY) += bay.o
obj-$(CONFIG_ACPI_VIDEO) += video.o
obj-y += pci_root.o pci_link.o pci_irq.o pci_bind.o
+obj-$(CONFIG_ACPI_PCI_SLOT) += pci_slot.o
obj-$(CONFIG_ACPI_POWER) += power.o
obj-$(CONFIG_ACPI_PROCESSOR) += processor.o
obj-$(CONFIG_ACPI_CONTAINER) += container.o
diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c
new file mode 100644
index 0000000..724f4f0
--- /dev/null
+++ b/drivers/acpi/pci_slot.c
@@ -0,0 +1,294 @@
+/*
+ * pci_slot.c - ACPI PCI Slot Driver
+ *
+ * The code here is heavily leveraged from the acpiphp module.
+ * Thanks to Matthew Wilcox <[email protected]> for much guidance.
+ *
+ * Copyright (C) 2007 Alex Chiang <[email protected]>
+ * Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+
+#define DRIVER_VERSION "0.1"
+#define DRIVER_AUTHOR "Alex Chiang <[email protected]>"
+#define DRIVER_DESC "ACPI PCI Slot Detection Driver"
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define _COMPONENT ACPI_PCI_COMPONENT
+ACPI_MODULE_NAME("pci_slot");
+
+#define MY_NAME "pci_slot"
+#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
+
+static int acpi_pci_slot_add(acpi_handle handle);
+static void acpi_pci_slot_remove(acpi_handle handle);
+
+static struct acpi_pci_driver acpi_pci_slot_driver = {
+ .add = acpi_pci_slot_add,
+ .remove = acpi_pci_slot_remove,
+};
+
+static int
+check_slot(acpi_handle handle, int *device, unsigned long *sun)
+{
+ unsigned long adr;
+ acpi_status status;
+
+ status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
+ if (ACPI_FAILURE(status))
+ return -1;
+ *device = (adr >> 16) & 0xffff;
+
+ /* No _SUN == not a slot == bail */
+ status = acpi_evaluate_integer(handle, "_SUN", NULL, sun);
+ if (ACPI_FAILURE(status))
+ return -1;
+
+ return 0;
+}
+
+/*
+ * unregister_slot
+ *
+ * Called once for each SxFy object in the namespace. Each call to
+ * pci_destroy_slot decrements the refcount on the pci_slot, and
+ * eventually calls kobject_unregister at the appropriate time.
+ */
+static acpi_status
+unregister_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+ int device;
+ unsigned long sun;
+ struct pci_slot *slot;
+ struct pci_bus *pci_bus = context;
+
+ if (check_slot(handle, &device, &sun))
+ return AE_OK;
+
+ for (slot = pci_bus->slot; slot; slot = slot->next) {
+ if (slot->number == device)
+ pci_destroy_slot(slot);
+ }
+
+ return AE_OK;
+}
+
+/*
+ * register_slot
+ *
+ * Called once for each SxFy object in the namespace. Don't worry about
+ * calling pci_create_slot multiple times for the same pci_bus:device,
+ * since each subsequent call simply bumps the refcount on the pci_slot.
+ *
+ * The number of calls to pci_destroy_slot from unregister_slot is
+ * symmetrical.
+ */
+static acpi_status
+register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+ int device;
+ unsigned long sun;
+ char name[KOBJ_NAME_LEN];
+
+ struct pci_slot *pci_slot;
+ struct pci_bus *pci_bus = context;
+
+ if (check_slot(handle, &device, &sun))
+ return AE_OK;
+
+ snprintf(name, sizeof(name), "%u", (u32)sun);
+ pci_slot = pci_create_slot(pci_bus, device, name);
+ if (IS_ERR(pci_slot))
+ err("pci_create_slot returned %ld\n", PTR_ERR(pci_slot));
+
+ return AE_OK;
+}
+
+struct p2p_bridge_context
+{
+ acpi_walk_callback user_function;
+ struct pci_bus *pci_bus;
+};
+
+/*
+ * walk_p2p_bridge - discover and walk p2p bridges
+ * @handle: points to an acpi_pci_root
+ * @context: p2p_bridge_context pointer
+ *
+ * Note that when we call ourselves recursively, we pass a different
+ * value of pci_bus in the child_context.
+ */
+static acpi_status
+walk_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+ int device, function;
+ unsigned long adr;
+ acpi_status status;
+ acpi_handle dummy_handle;
+ acpi_walk_callback user_function;
+
+ struct pci_dev *dev;
+ struct pci_bus *pci_bus;
+ struct p2p_bridge_context child_context;
+ struct p2p_bridge_context *parent_context = context;
+
+ pci_bus = parent_context->pci_bus;
+ user_function = parent_context->user_function;
+
+ status = acpi_get_handle(handle, "_ADR", &dummy_handle);
+ if (ACPI_FAILURE(status))
+ return AE_OK;
+
+ status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
+ if (ACPI_FAILURE(status))
+ return AE_OK;
+
+ device = (adr >> 16) & 0xffff;
+ function = adr & 0xffff;
+
+ dev = pci_get_slot(pci_bus, PCI_DEVFN(device, function));
+ if (!dev || !dev->subordinate)
+ goto out;
+
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
+ user_function, dev->subordinate, NULL);
+ if (ACPI_FAILURE(status))
+ goto out;
+
+ child_context.pci_bus = dev->subordinate;
+ child_context.user_function = user_function;
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
+ walk_p2p_bridge, &child_context, NULL);
+out:
+ pci_dev_put(dev);
+ return AE_OK;
+}
+
+#define ACPI_STA_FUNCTIONING (0x00000008)
+
+/*
+ * walk_root_bridge - generic root bridge walker
+ * @handle: points to an acpi_pci_root
+ * @user_function: user callback for slot objects
+ *
+ * Call user_function for all objects underneath this root bridge.
+ * Walk p2p bridges underneath us and call user_function on those too.
+ */
+static int
+walk_root_bridge(acpi_handle handle, acpi_walk_callback user_function)
+{
+ int seg, bus;
+ unsigned long tmp;
+ acpi_status status;
+ acpi_handle dummy_handle;
+ struct pci_bus *pci_bus;
+ struct p2p_bridge_context context;
+
+ /* If the bridge doesn't have _STA, we assume it is always there */
+ status = acpi_get_handle(handle, "_STA", &dummy_handle);
+ if (ACPI_SUCCESS(status)) {
+ status = acpi_evaluate_integer(handle, "_STA", NULL, &tmp);
+ if (ACPI_FAILURE(status)) {
+ info("%s: _STA evaluation failure\n", __FUNCTION__);
+ return 0;
+ }
+ if ((tmp & ACPI_STA_FUNCTIONING) == 0)
+ /* don't register this object */
+ return 0;
+ }
+
+ status = acpi_evaluate_integer(handle, "_SEG", NULL, &tmp);
+ seg = ACPI_SUCCESS(status) ? tmp : 0;
+
+ status = acpi_evaluate_integer(handle, "_BBN", NULL, &tmp);
+ bus = ACPI_SUCCESS(status) ? tmp : 0;
+
+ pci_bus = pci_find_bus(seg, bus);
+ if (!pci_bus)
+ return 0;
+
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
+ user_function, pci_bus, NULL);
+ if (ACPI_FAILURE(status))
+ return status;
+
+ context.pci_bus = pci_bus;
+ context.user_function = user_function;
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
+ walk_p2p_bridge, &context, NULL);
+ if (ACPI_FAILURE(status))
+ err("%s: walk_p2p_bridge failure - %d\n", __FUNCTION__, status);
+
+ return status;
+}
+
+/*
+ * acpi_pci_slot_add
+ * @handle: points to an acpi_pci_root
+ */
+static int
+acpi_pci_slot_add(acpi_handle handle)
+{
+ acpi_status status;
+
+ status = walk_root_bridge(handle, register_slot);
+ if (ACPI_FAILURE(status))
+ err("%s: register_slot failure - %d\n", __FUNCTION__, status);
+
+ return status;
+}
+
+/*
+ * acpi_pci_slot_remove
+ * @handle: points to an acpi_pci_root
+ */
+static void
+acpi_pci_slot_remove(acpi_handle handle)
+{
+ acpi_status status;
+
+ status = walk_root_bridge(handle, unregister_slot);
+ if (ACPI_FAILURE(status))
+ err("%s: unregister_slot failure - %d\n", __FUNCTION__, status);
+}
+
+static int __init
+acpi_pci_slot_init(void)
+{
+ acpi_pci_register_driver(&acpi_pci_slot_driver);
+ return 0;
+}
+
+
+static void __exit
+acpi_pci_slot_exit(void)
+{
+ acpi_pci_unregister_driver(&acpi_pci_slot_driver);
+}
+
+module_init(acpi_pci_slot_init);
+module_exit(acpi_pci_slot_exit);
diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c
index 9a259eb..5dd8e12 100644
--- a/drivers/pci/hotplug/pci_hotplug_core.c
+++ b/drivers/pci/hotplug/pci_hotplug_core.c
@@ -567,6 +567,11 @@ int pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, int slot_nr)
return -EINVAL;
}

+ /*
+ * No problems if we call this interface from both ACPI_PCI_SLOT
+ * driver and call it here again. If we've already created the
+ * pci_slot, the interface will simply bump the refcount.
+ */
pci_slot = pci_create_slot(bus, slot_nr, slot->name);
if (IS_ERR(pci_slot))
return PTR_ERR(pci_slot);
@@ -612,8 +617,12 @@ int pci_hp_deregister(struct hotplug_slot *hotplug)

slot = hotplug->pci_slot;
fs_remove_slot(slot);
- pci_destroy_slot(slot);
dbg("Removed slot %s from the list\n", hotplug->name);
+
+ hotplug_release(slot);
+ slot->release = NULL;
+ pci_destroy_slot(slot);
+
return 0;
}

diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index 0e9eaf6..299f6fa 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -249,7 +249,7 @@ static int init_slots(struct controller *ctrl)
if (retval == -EBUSY)
goto error_info;
if (retval) {
- err ("pci_hp_register failed with error %d\n", retval);
+ err("pci_hp_register failed with error %d\n", retval);
goto error_info;
}
/* create additional sysfs entries */
--
1.5.3.1.g1e61

2007-11-27 03:05:17

by Gary Hade

[permalink] [raw]
Subject: Re: [PATCH 0/4, v3] Physical PCI slot objects

On Mon, Nov 26, 2007 at 03:22:53PM -0700, Alex Chiang wrote:
> Hi Gary, Kenji-san, et. al,
>
> * Gary Hade <[email protected]>:
> >
> > Alex, What I was trying to suggest is a boot-time kernel
> > option, not a kernel configuration option. The basic idea is
> > to give the user (with a single binary kernel) the ability to
> > include your ACPI-PCI slot driver feature changes only when
> > they are really needed. In addition to reducing the number of
> > system/PCI hotplug driver combinations where your changes would
> > need to be validated, I believe would also help alleviate other
> > worries (e.g. Andi Kleen's memory consumption concern). I
> > believe this goal could also be achieved with the kernel config
> > option by making the pci_slot module runtime loadable with the
> > PCI hotplug drivers only visiting your new code when the
> > pci_slot driver is loaded, although I think this would be more
> > difficult to implement.
>
> I have modified my patch series so that the final patch that
> introduces my ACPI-PCI slot driver is a full-fledged module, that
> has a tristate Kconfig option.
>
> It can be modprobe'd/rmmod'ed in any combination, and in any
> order with other PCI hotplug modules. There is no ordering
> dependency, even at module unload time, so you can safely rmmod
> pci_slot, and safely continue using features provided by the PCI
> hotplug drivers (acpiphp, pciehp, etc.). The opposite works too.

Nice! I like the loadable module approach much better than my
boot-time kernel option suggestion.

>
> The one limitation is that two separate hotplug drivers cannot
> both claim the same device (2nd module loaded will get -EBUSY
> errors), but I do not believe that is a regression from current
> behavior.

I cannot confirm this since the systems I am using only support
a single hotplug driver (acpiphp).

>
> I have only tested with acpiphp and pciehp, as that's the only
> hardware I have, but I believe my code will play nicely with the
> other PCI hp drivers as well.

I have only tested your changes with acpiphp.

>
> The patch series is fully bisectable, and the correct behavior
> occurs no matter which patch you happen to have applied.

Based on my testing (see below) this appears to be true.

>
> I'll be sending v5 of patches 3 and 4 shortly (patches 1 and 2
> did not change). It is still based on 2.6.24-rc2, because I was
> too scared to do another git rebase while using stgit. :-/

I have been using 2.6.24-rc3 source for my testing.

>
> > Also, I notice that even with your current CONFIG_ACPI_PCI_SLOT
> > implementation your numerous PCI hotplug driver changes (except
> > for only two places in pci_hotplug_core.c where there is
> > `#ifndef CONFIG_ACPI_PCI_SLOT` and `#ifdef CONFIG_ACPI_PCI_SLOT`)
> > are _always_ exposed. So, even with CONFIG_ACPI_PCI_SLOT disabled
> > there is IMO a need for testing of the affected PCI hotplug drivers
> > on more than a small number of isolated systems.
>
> You are, of course, correct.
>
> In my opinion, though, I would say most of the changes to the PCI
> hotplug drivers themselves are pretty straightforward, as in
> removing the different ways of getting the PCI address.
>
> The scary part of the changes (aside from the ACPI-PCI slot
> driver) revolve around the new struct pci_slot, which is
> relatively self-contained, and only expose themselves via the
> pci_create_slot/pci_destroy_slot interfaces which only the PCI
> hotplug corecares about.

I think this sounds like a reasonable argument for not
doing what I was trying to suggest.

>
> Regardless, your point stands. How do you suggest I get more
> testing time?

I am only able to test with acpiphp. In addition to the
testing on the x3850 described below I would also like to
do some testing on an x3950 which has a mix of hotplug and
non-hotplug slots. If this testing which I hope to complete
this week goes well, I will be satisfied.

I will let others speak for the other hotplug drivers
and platforms.

> Is this patchset appropriate for the -mm tree yet?

I would defer to our illustrious maintainers on this one. :)

> Or do you think it still needs more work?

I am now much more comfortable with your changes with respect
to acpiphp on the systems I worry about but others may have
concerns with respect to the other hotplug drivers, or even
acpiphp, on other systems.

>
> > The good news is that I was able to test your v3 changes
> > (w/2.6.24-rc3 source) on our x3850 today with 'acpiphp' and,
> > except for the above mentioned inability to run-time
> > include/exclude them, they seemed to work fine. The previous
> > boot-time ACPI error messages are gone and I was able to
> > successfully hot-remove and hot-add both PCI-X and PCIe
> > adapters.
>
> Thanks for testing. Please let me know how v5 works for you too.

I just tried your v5 (1/4 v3, 2/4 v3, 3/4 v5, 4/4 v5) applied
to 2.6.24-rc3 source with acpiphp on the x3850 and found
nothing to complain about. About time, eh? :)

Following boot, 'pci_slot' was already loaded and slot directories
(each containing an address file) for all 6 (2 PCI-X, 4 PCIe)
slots were present. I then successfully modprobed acpiphp and
successfully hot-removed the PCI-X and PCIe cards that were present
during boot. I then successfully hot-added the same cards to
the slots they occupied during boot and to slots that were vacant
during boot.

I then unloaded acpiphp and pci_slot and reloaded both in
the opposite order (acpiphp 1st, pci_slot 2nd) and
successfully repeated the above hot-remove/hot-add operations.

I then unloaded both drivers again, reloaded only acpiphp,
and successfully repeated the same hot-remove/hot-add operations.

I will let you know how it goes on the x3950.

Thanks for all your hard work on this.

Gary

--
Gary Hade
System x Enablement
IBM Linux Technology Center
503-578-4503 IBM T/L: 775-4503
[email protected]
http://www.ibm.com/linux/ltc

2007-11-27 19:13:40

by Kristen Carlson Accardi

[permalink] [raw]
Subject: Re: [PATCH 0/4, v3] Physical PCI slot objects

On Mon, 26 Nov 2007 19:04:54 -0800
Gary Hade <[email protected]> wrote:

> > Is this patchset appropriate for the -mm tree yet?
>
> I would defer to our illustrious maintainers on this one. :)
>
> > Or do you think it still needs more work?
>
> I am now much more comfortable with your changes with respect
> to acpiphp on the systems I worry about but others may have
> concerns with respect to the other hotplug drivers, or even
> acpiphp, on other systems.

I'm ok with adding it to my quilt tree (and into -mm) after Gary gets
back to us later this week.

2007-11-28 21:32:37

by Gary Hade

[permalink] [raw]
Subject: Re: [PATCH 0/4, v3] Physical PCI slot objects

On Tue, Nov 27, 2007 at 11:11:36AM -0800, Kristen Carlson Accardi wrote:
> On Mon, 26 Nov 2007 19:04:54 -0800
> Gary Hade <[email protected]> wrote:
>
> > > Is this patchset appropriate for the -mm tree yet?
> >
> > I would defer to our illustrious maintainers on this one. :)
> >
> > > Or do you think it still needs more work?
> >
> > I am now much more comfortable with your changes with respect
> > to acpiphp on the systems I worry about but others may have
> > concerns with respect to the other hotplug drivers, or even
> > acpiphp, on other systems.
>
> I'm ok with adding it to my quilt tree (and into -mm) after Gary gets
> back to us later this week.

I'm getting back to you but unfortunately with not so good
news. Sorry Alex.

On the x3950 (configured single node) I encountered the below
problem when attempting to hotplug a PCIe adapter when 'pci_slot'
was loaded prior to 'acpiphp'. I did not see the problem when
the drivers were loaded in the opposite order.

After the messages appeared, the hotplug operation did not
complete and I was unable to recover hotplug functionality
without a reboot. I attempted recovery by unloading both
drivers and then reloading only 'acpiphp' but even though
an LED for the slot that had received the problem triggering
adapter indicated that the slot was receiving power, the
sysfs slot power file indicated otherwise (contained 0).
So, I was unable to power off the slot to allow the removal
and reinsertion of the adapter.

FYI, the node contains 2 hotpluggable PCIe slots and 5
non-hotpluggable PCIe slots but 'pci_slot' only exposed
the 2 hotpluggable slots. This does not appear to be due
to a 'pci_slot' driver problem since I looked at the DSDT
and SSDT and found that there are currently no _SUN methods
for the non-hotpluggable slots.

Gary

--
Gary Hade
System x Enablement
IBM Linux Technology Center
503-578-4503 IBM T/L: 775-4503
[email protected]
http://www.ibm.com/linux/ltc

invalid opcode: 0000 [1] SMP
CPU 1
Modules linked in: acpiphp pci_slot e1000 aic79xx scsi_transport_spi shpchp dock pci_hotplug ipt_LOG xt_limit xt_pkttype button battery ac power_supply ip6t_REJECT xt_tcpudp ipt_REJECT iptable_mangle iptable_filter ip6table_mangle ip_tables ip6table_filter ip6_tables x_tables ipv6 usbhid ff_memless ext3 jbd loop dm_mod ehci_hcd uhci_hcd usbcore ide_cd bnx2 cdrom rng_core reiserfs ata_piix ahci libata thermal processor piix sg megaraid_sas fan edd sd_mod scsi_mod ide_disk ide_core
Pid: 121, comm: kacpi_notify Not tainted 2.6.24-rc3-gh-smp #1
RIP: 0010:[<ffffffff882c2344>] [<ffffffff882c2344>] :pci_slot:__this_module+0x21c4/0xfffffffffffff204
RSP: 0018:ffff81103fa43ea8 EFLAGS: 00010216
RAX: ffff81103f944a18 RBX: ffff81103d4fe910 RCX: 000000000000000f
RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff8110400d13d0
RBP: ffffffff8032d97b R08: ffff8110400fc7e0 R09: 0000000000000002
R10: 0000000000000000 R11: ffffffff8021d193 R12: ffff811040105cf0
R13: ffffffffffffffff R14: ffffffff80635820 R15: 0000000000000000
FS: 0000000000000000(0000) GS:ffff8110400ed8c0(0000) knlGS:0000000000000000
CS: 0010 DS: 0018 ES: 0018 CR0: 000000008005003b
CR2: 00002b266d876471 CR3: 000000103c825000 CR4: 00000000000006e0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
Process kacpi_notify (pid: 121, threadinfo ffff81103fa42000, task ffff81103f9f8040)
Stack: ffffffff8033339c ffff81103d119a00 ffffffff8032d99e ffff81103f9fc540
ffffffff8024618d ffff81103f9fc540 ffff81103f9fc540 ffffffff8024696c
ffffffff80246a46 0000000000000000 ffff81103f9f8040 ffffffff80249ada
Call Trace:
[<ffffffff8033339c>] acpi_ev_notify_dispatch+0x57/0x60
[<ffffffff8032d99e>] acpi_os_execute_notify+0x23/0x2c
[<ffffffff8024618d>] run_workqueue+0x7f/0x10b
[<ffffffff8024696c>] worker_thread+0x0/0xe4
[<ffffffff80246a46>] worker_thread+0xda/0xe4
[<ffffffff80249ada>] autoremove_wake_function+0x0/0x2e
[<ffffffff802499bc>] kthread+0x47/0x73
[<ffffffff8020cc98>] child_rip+0xa/0x12
[<ffffffff80249975>] kthread+0x0/0x73
[<ffffffff8020cc8e>] child_rip+0x0/0x12


Code: ff ff ff ff 40 23 2c 88 ff ff ff ff 00 c8 c6 3b 10 81 ff ff
RIP [<ffffffff882c2344>] :pci_slot:__this_module+0x21c4/0xfffffffffffff204
RSP <ffff81103fa43ea8>

2007-11-29 00:08:32

by Kristen Carlson Accardi

[permalink] [raw]
Subject: Re: [PATCH 0/4, v3] Physical PCI slot objects

On Wed, 28 Nov 2007 13:31:47 -0800
Gary Hade <[email protected]> wrote:

> FYI, the node contains 2 hotpluggable PCIe slots and 5
> non-hotpluggable PCIe slots but 'pci_slot' only exposed
> the 2 hotpluggable slots. This does not appear to be due
> to a 'pci_slot' driver problem since I looked at the DSDT
> and SSDT and found that there are currently no _SUN methods
> for the non-hotpluggable slots.

Thanks for testing Gary. I would think this situation would be the
common case, since I doubt most firmware writers would bother to
implement _SUN for non-hotpluggable slots -- at least on other DSDT
I've seen this has been the case as well.

2007-11-29 01:10:00

by Gary Hade

[permalink] [raw]
Subject: Re: [PATCH 0/4, v3] Physical PCI slot objects

On Wed, Nov 28, 2007 at 04:02:38PM -0800, Kristen Carlson Accardi wrote:
> On Wed, 28 Nov 2007 13:31:47 -0800
> Gary Hade <[email protected]> wrote:
>
> > FYI, the node contains 2 hotpluggable PCIe slots and 5
> > non-hotpluggable PCIe slots but 'pci_slot' only exposed
> > the 2 hotpluggable slots. This does not appear to be due
> > to a 'pci_slot' driver problem since I looked at the DSDT
> > and SSDT and found that there are currently no _SUN methods
> > for the non-hotpluggable slots.
>
> Thanks for testing Gary. I would think this situation would be the
> common case, since I doubt most firmware writers would bother to
> implement _SUN for non-hotpluggable slots -- at least on other DSDT
> I've seen this has been the case as well.

Yea, I was also not surprised although features such as
Alex working on may provide some motivation to change that.

Gary

--
Gary Hade
System x Enablement
IBM Linux Technology Center
503-578-4503 IBM T/L: 775-4503
[email protected]
http://www.ibm.com/linux/ltc

2007-11-29 07:52:58

by Kenji Kaneshige

[permalink] [raw]
Subject: Re: [PATCH 0/4, v3] Physical PCI slot objects

> Hi Gary, Kenji-san, et. al,
>
> * Gary Hade <[email protected]>:
>> Alex, What I was trying to suggest is a boot-time kernel
>> option, not a kernel configuration option. The basic idea is
>> to give the user (with a single binary kernel) the ability to
>> include your ACPI-PCI slot driver feature changes only when
>> they are really needed. In addition to reducing the number of
>> system/PCI hotplug driver combinations where your changes would
>> need to be validated, I believe would also help alleviate other
>> worries (e.g. Andi Kleen's memory consumption concern). I
>> believe this goal could also be achieved with the kernel config
>> option by making the pci_slot module runtime loadable with the
>> PCI hotplug drivers only visiting your new code when the
>> pci_slot driver is loaded, although I think this would be more
>> difficult to implement.
>
> I have modified my patch series so that the final patch that
> introduces my ACPI-PCI slot driver is a full-fledged module, that
> has a tristate Kconfig option.
>

Thank you for your good job.

I tested shpchp and pciehp both with and without pci_slot module. There
seems no regression from shpchp and pciehp's point of view.
(I had a little concern about the hotplug slots' name that vary depending
on whether pci_slot functionality is enabled or disabled. But, now that we
can build pci_slot driver as a kernel module, I don't think it is a big
problem).

Only the problems is that I got Call Traces with the following error
messages when pci_slot driver was loaded, and one strange slot named
'1023' was registered (other slots are fine). This is the same problem
I reported before.

sysfs: duplicate filename '1023' can not be created
WARNING: at fs/sysfs/dir.c:424 sysfs_add_one()

kobject_add failed for 1023 with -EEXIST, don't try to register
things with the same name in the same directory.

On my system, hotplug slots themselves can be added, removed and replaced
with the ohter type of I/O box. The ACPI firmware tells OS the presence of
those slots using _STA method (That is, it doesn't use 'LoadTable()' AML
operator). On the other hand, current pci_slot driver doesn't check _STA.
As a result, pci_slot driver tryied to register the invalid (non-existing)
slots. The ACPI firmware of my system returns '1023' if the invalid slot's
_SUN is evaluated. This is the cause of Call Traces mentioned above. To
fix this problem, pci_slot driver need to check _STA when scanning ACPI
Namespace.

I'm sorry for reporting this so late. I'm attaching the patch to fix the
problem. This is against 2.6.24-rc3 with your patches applied. Could you
try it?

BTW, acpiphp also seems to have the same problem...

Thanks,
Kenji Kaneshige

---
drivers/acpi/pci_slot.c | 13 +++++++++++++
1 file changed, 13 insertions(+)

Index: linux-2.6.24-rc3/drivers/acpi/pci_slot.c
===================================================================
--- linux-2.6.24-rc3.orig/drivers/acpi/pci_slot.c
+++ linux-2.6.24-rc3/drivers/acpi/pci_slot.c
@@ -113,10 +113,17 @@ register_slot(acpi_handle handle, u32 lv
int device;
unsigned long sun;
char name[KOBJ_NAME_LEN];
+ acpi_status status;
+ struct acpi_device *dummy_device;

struct pci_slot *pci_slot;
struct pci_bus *pci_bus = context;

+ /* Skip non-existing device object. */
+ status = acpi_bus_get_device(handle, &dummy_device);
+ if (ACPI_FAILURE(status))
+ return AE_OK;
+
if (check_slot(handle, &device, &sun))
return AE_OK;

@@ -150,12 +157,18 @@ walk_p2p_bridge(acpi_handle handle, u32
acpi_status status;
acpi_handle dummy_handle;
acpi_walk_callback user_function;
+ struct acpi_device *dummy_device;

struct pci_dev *dev;
struct pci_bus *pci_bus;
struct p2p_bridge_context child_context;
struct p2p_bridge_context *parent_context = context;

+ /* Skip non-existing device object. */
+ status = acpi_bus_get_device(handle, &dummy_device);
+ if (ACPI_FAILURE(status))
+ return AE_OK;
+
pci_bus = parent_context->pci_bus;
user_function = parent_context->user_function;


2007-11-30 01:19:59

by Alex Chiang

[permalink] [raw]
Subject: Re: [PATCH 0/4, v3] Physical PCI slot objects

Hi Gary,

First, thanks for all the help and testing -- I really appreciate
it.

* Gary Hade <[email protected]>:
>
> I'm getting back to you but unfortunately with not so good
> news. Sorry Alex.

:-/

> On the x3950 (configured single node) I encountered the below
> problem when attempting to hotplug a PCIe adapter when 'pci_slot'
> was loaded prior to 'acpiphp'. I did not see the problem when
> the drivers were loaded in the opposite order.

Very bizarre, especially given the stack trace below, which
doesn't really make any sense to me at all.

> FYI, the node contains 2 hotpluggable PCIe slots and 5
> non-hotpluggable PCIe slots but 'pci_slot' only exposed
> the 2 hotpluggable slots. This does not appear to be due
> to a 'pci_slot' driver problem since I looked at the DSDT
> and SSDT and found that there are currently no _SUN methods
> for the non-hotpluggable slots.

Ok, this is not too surprising, but it's a different can o'
worms. ;) Let's save this for another day...

> invalid opcode: 0000 [1] SMP
> CPU 1
> Modules linked in: acpiphp pci_slot e1000 aic79xx scsi_transport_spi shpchp dock pci_hotplug ipt_LOG xt_limit xt_pkttype button battery ac power_supply ip6t_REJECT xt_tcpudp ipt_REJECT iptable_mangle iptable_filter ip6table_mangle ip_tables ip6table_filter ip6_tables x_tables ipv6 usbhid ff_memless ext3 jbd loop dm_mod ehci_hcd uhci_hcd usbcore ide_cd bnx2 cdrom rng_core reiserfs ata_piix ahci libata thermal processor piix sg megaraid_sas fan edd sd_mod scsi_mod ide_disk ide_core
> Pid: 121, comm: kacpi_notify Not tainted 2.6.24-rc3-gh-smp #1
> RIP: 0010:[<ffffffff882c2344>] [<ffffffff882c2344>] :pci_slot:__this_module+0x21c4/0xfffffffffffff204
> RSP: 0018:ffff81103fa43ea8 EFLAGS: 00010216
> RAX: ffff81103f944a18 RBX: ffff81103d4fe910 RCX: 000000000000000f
> RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff8110400d13d0
> RBP: ffffffff8032d97b R08: ffff8110400fc7e0 R09: 0000000000000002
> R10: 0000000000000000 R11: ffffffff8021d193 R12: ffff811040105cf0
> R13: ffffffffffffffff R14: ffffffff80635820 R15: 0000000000000000
> FS: 0000000000000000(0000) GS:ffff8110400ed8c0(0000) knlGS:0000000000000000
> CS: 0010 DS: 0018 ES: 0018 CR0: 000000008005003b
> CR2: 00002b266d876471 CR3: 000000103c825000 CR4: 00000000000006e0
> DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
> DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
> Process kacpi_notify (pid: 121, threadinfo ffff81103fa42000, task ffff81103f9f8040)
> Stack: ffffffff8033339c ffff81103d119a00 ffffffff8032d99e ffff81103f9fc540
> ffffffff8024618d ffff81103f9fc540 ffff81103f9fc540 ffffffff8024696c
> ffffffff80246a46 0000000000000000 ffff81103f9f8040 ffffffff80249ada
> Call Trace:
> [<ffffffff8033339c>] acpi_ev_notify_dispatch+0x57/0x60
> [<ffffffff8032d99e>] acpi_os_execute_notify+0x23/0x2c
> [<ffffffff8024618d>] run_workqueue+0x7f/0x10b
> [<ffffffff8024696c>] worker_thread+0x0/0xe4
> [<ffffffff80246a46>] worker_thread+0xda/0xe4
> [<ffffffff80249ada>] autoremove_wake_function+0x0/0x2e
> [<ffffffff802499bc>] kthread+0x47/0x73
> [<ffffffff8020cc98>] child_rip+0xa/0x12
> [<ffffffff80249975>] kthread+0x0/0x73
> [<ffffffff8020cc8e>] child_rip+0x0/0x12

Maybe we're trying to kick off a hotplug event on the wrong slot?
I really have no idea...

> Code: ff ff ff ff 40 23 2c 88 ff ff ff ff 00 c8 c6 3b 10 81 ff ff
> RIP [<ffffffff882c2344>] :pci_slot:__this_module+0x21c4/0xfffffffffffff204
> RSP <ffff81103fa43ea8>

Can you apply this debug patch on top of your tree, and send me
the output?

I'd be curious to see the output for your failure case:

# modprobe pci_slot debug=1
# modprobe acpiphp debug=1

Thanks.

/ac

diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c
index 724f4f0..5a62def 100644
--- a/drivers/acpi/pci_slot.c
+++ b/drivers/acpi/pci_slot.c
@@ -30,12 +30,16 @@
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>

+static int debug;
+
#define DRIVER_VERSION "0.1"
#define DRIVER_AUTHOR "Alex Chiang <[email protected]>"
#define DRIVER_DESC "ACPI PCI Slot Detection Driver"
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
+MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
+module_param(debug, bool, 0644);

#define _COMPONENT ACPI_PCI_COMPONENT
ACPI_MODULE_NAME("pci_slot");
@@ -43,6 +47,12 @@ ACPI_MODULE_NAME("pci_slot");
#define MY_NAME "pci_slot"
#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
+#define dbg(format, arg...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG "%s: " format, \
+ MY_NAME , ## arg); \
+ } while (0)

static int acpi_pci_slot_add(acpi_handle handle);
static void acpi_pci_slot_remove(acpi_handle handle);
@@ -125,6 +135,9 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
if (IS_ERR(pci_slot))
err("pci_create_slot returned %ld\n", PTR_ERR(pci_slot));

+ dbg("pci_slot: %#lx, pci_bus: %x, device: %d, name: %s\n",
+ (uint64_t)pci_slot, pci_bus->number, device, name);
+
return AE_OK;
}

@@ -174,6 +187,7 @@ walk_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
if (!dev || !dev->subordinate)
goto out;

+ dbg("p2p bridge walk, pci_bus = %x\n", dev->subordinate->number);
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
user_function, dev->subordinate, NULL);
if (ACPI_FAILURE(status))
@@ -231,6 +245,7 @@ walk_root_bridge(acpi_handle handle, acpi_walk_callback user_function)
if (!pci_bus)
return 0;

+ dbg("root bridge walk, pci_bus = %x\n", pci_bus->number);
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
user_function, pci_bus, NULL);
if (ACPI_FAILURE(status))
@@ -283,7 +298,6 @@ acpi_pci_slot_init(void)
return 0;
}

-
static void __exit
acpi_pci_slot_exit(void)
{
diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c
index f16e0af..80c4928 100644
--- a/drivers/pci/slot.c
+++ b/drivers/pci/slot.c
@@ -9,6 +9,15 @@
#include <linux/pci.h>
#include "pci.h"

+static int pci_slot_debug;
+#define MY_NAME "slot"
+#define dbg(format, arg...) \
+ do { \
+ if (pci_slot_debug) \
+ printk(KERN_DEBUG "%s: " format, \
+ MY_NAME , ## arg); \
+ } while (0)
+
struct kset pci_slots_subsys;
EXPORT_SYMBOL_GPL(pci_slots_subsys);

@@ -63,6 +72,9 @@ static void pci_slot_release(struct kobject *kobj)
struct pci_slot **pprev;
struct pci_slot *slot = to_pci_slot(kobj);

+ dbg("%s: releasing pci_slot on %x:%d\n", __FUNCTION__,
+ slot->bus->number, slot->number);
+
for (pprev = &slot->bus->slot; *pprev; pprev = &(*pprev)->next) {
if (*pprev == slot) {
*pprev = slot->next;
@@ -103,15 +115,19 @@ int pci_slot_add_hotplug(struct pci_bus *parent, int slot_nr,
}

if (!found) {
+ dbg("%s: slot not found\n", __FUNCTION__);
retval = -EEXIST;
goto out;
}

if (slot->release) {
+ dbg("%s: already claimed\n", __FUNCTION__);
retval = -EBUSY;
goto out;
}

+ dbg("%s: adding release function to %x:%d\n",
+ __FUNCTION__, parent->number, slot_nr);
slot->release = release;
out:
up_write(&pci_bus_sem);
@@ -131,6 +147,9 @@ struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr,
for (slot = parent->slot; slot; slot = slot->next) {
if (slot->number == slot_nr) {
kobject_get(&slot->kobj);
+ dbg("%s: bumped refcount to %d on %x:%d\n",
+ __FUNCTION__, slot->kobj.kref.refcount,
+ parent->number, slot_nr);
goto out;
}
}
@@ -159,6 +178,9 @@ struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr,
slot->next = parent->slot;
parent->slot = slot;

+ dbg("%s: created pci_slot on %x:%d\n",
+ __FUNCTION__, parent->number, slot_nr);
+
out:
up_write(&pci_bus_sem);
return slot;
@@ -175,6 +197,10 @@ EXPORT_SYMBOL_GPL(pci_create_slot);
int pci_destroy_slot(struct pci_slot *slot)
{
kobject_put(&slot->kobj);
+
+ dbg("%s: decreased refcount to %d on %x:%d\n", __FUNCTION__,
+ slot->kobj.kref.refcount, slot->bus->number, slot->number);
+
if (atomic_read(&slot->kobj.kref.refcount) == 1)
kobject_unregister(&slot->kobj);

@@ -186,6 +212,8 @@ static int pci_slot_init(void)
{
int result;

+ pci_slot_debug = 1;
+
kobj_set_kset_s(&pci_slots_subsys, pci_bus_type.subsys);
result = subsystem_register(&pci_slots_subsys);
if (result)

2007-11-30 01:51:24

by Alex Chiang

[permalink] [raw]
Subject: Re: [PATCH 0/4, v3] Physical PCI slot objects

Hi Kenji-san,

* Kenji Kaneshige <[email protected]>:
> > Hi Gary, Kenji-san, et. al,
> >
> > * Gary Hade <[email protected]>:
> >> Alex, What I was trying to suggest is a boot-time kernel
> >> option, not a kernel configuration option. The basic idea is
> >> to give the user (with a single binary kernel) the ability to
> >> include your ACPI-PCI slot driver feature changes only when
> >> they are really needed. In addition to reducing the number of
> >> system/PCI hotplug driver combinations where your changes would
> >> need to be validated, I believe would also help alleviate other
> >> worries (e.g. Andi Kleen's memory consumption concern). I
> >> believe this goal could also be achieved with the kernel config
> >> option by making the pci_slot module runtime loadable with the
> >> PCI hotplug drivers only visiting your new code when the
> >> pci_slot driver is loaded, although I think this would be more
> >> difficult to implement.
> >
> > I have modified my patch series so that the final patch that
> > introduces my ACPI-PCI slot driver is a full-fledged module, that
> > has a tristate Kconfig option.
> >
>
> Thank you for your good job.

Thanks for testing. :)

> I tested shpchp and pciehp both with and without pci_slot
> module. There seems no regression from shpchp and pciehp's
> point of view. (I had a little concern about the hotplug
> slots' name that vary depending on whether pci_slot
> functionality is enabled or disabled. But, now that we can
> build pci_slot driver as a kernel module, I don't think it is a
> big problem).

Hm, you are right. On my machine, if I load pciehp first and
acpiphp second (even without loading pci_slot), I will see the
following:

[root@canola slots]# ls
0016_0006 0197_0005 10 3 4 7 8 9

[root@canola slots]# lsmod | grep pci_slot
[root@canola slots]# lsmod | grep hp
acpiphp 115984 0
pciehp 140616 0
pci_hotplug 123972 2 acpiphp,pciehp

On the other hand, if I do load pci_slot first, and then pciehp,
you are right, I will see something like this:

[root@canola slots]# ls
1 10 2 3 4 5 6 7 8 9

[root@canola slots]# lsmod | grep pci_slot
pci_slot 74436 0
[root@canola slots]# lsmod | grep hp
pciehp 140616 0
pci_hotplug 123972 1 pciehp

But I do agree, people don't need to load pci_slot at all if they
don't want it, and they won't be bothered.

> Only the problems is that I got Call Traces with the following
> error messages when pci_slot driver was loaded, and one strange
> slot named '1023' was registered (other slots are fine). This
> is the same problem I reported before.
>
> sysfs: duplicate filename '1023' can not be created
> WARNING: at fs/sysfs/dir.c:424 sysfs_add_one()
>
> kobject_add failed for 1023 with -EEXIST, don't try to
> register things with the same name in the same directory.
>
> On my system, hotplug slots themselves can be added, removed
> and replaced with the ohter type of I/O box. The ACPI firmware
> tells OS the presence of those slots using _STA method (That
> is, it doesn't use 'LoadTable()' AML operator). On the other
> hand, current pci_slot driver doesn't check _STA. As a result,
> pci_slot driver tryied to register the invalid (non-existing)
> slots. The ACPI firmware of my system returns '1023' if the
> invalid slot's _SUN is evaluated. This is the cause of Call
> Traces mentioned above. To fix this problem, pci_slot driver
> need to check _STA when scanning ACPI Namespace.

Now this is very curious. The relevant line in pci_slot is:

check_slot()
status = acpi_evaluate_integer(handle, "_SUN", NULL, sun);
if (ACPI_FAILURE(status))
return -1;

Why does your firmware return the error information inside sun,
instead of returning an error in status? That doesn't seem right
to me...

> I'm sorry for reporting this so late. I'm attaching the patch
> to fix the problem. This is against 2.6.24-rc3 with your
> patches applied. Could you try it?

Applying this patch causes me to only detect populated slots in
my system, which isn't what I want -- otherwise, I could have
just enumerated the PCI bus and found the devices that way. :)

Maybe on your machine, checking existence of _STA might do the
right thing, but I don't think we should actually be looking at
any of the actual bits returned.

If we check ACPI_STA_DEVICE_PRESENT, then we will not detect
empty slots on my system. Can you try this patch to see if at
least the first call to acpi_evaluate_integer helps? If that
doesn't help, maybe the second block will help you, but it breaks
my machine...

Thanks.

/ac


diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c
index 724f4f0..63a4dc8 100644
--- a/drivers/acpi/pci_slot.c
+++ b/drivers/acpi/pci_slot.c
@@ -55,9 +65,21 @@ static struct acpi_pci_driver acpi_pci_slot_driver = {
static int
check_slot(acpi_handle handle, int *device, unsigned long *sun)
{
- unsigned long adr;
+ unsigned long adr, sta;
acpi_status status;

+ /* Doesn't seem to hurt anything on hp systems */
+ status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
+ if (ACPI_FAILURE(status))
+ return -1;
+
+ /* This code causes us to fail to detect empty slots, so
+ * commented out for now.
+ *
+ if (!(sta & ACPI_STA_DEVICE_PRESENT))
+ return -1;
+ */
+
status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
if (ACPI_FAILURE(status))
return -1;

2007-11-30 19:10:48

by Gary Hade

[permalink] [raw]
Subject: Re: [PATCH 0/4, v3] Physical PCI slot objects

On Thu, Nov 29, 2007 at 06:19:41PM -0700, Alex Chiang wrote:

< snip >

> > [<ffffffff802499bc>] kthread+0x47/0x73
> > [<ffffffff8020cc98>] child_rip+0xa/0x12
> > [<ffffffff80249975>] kthread+0x0/0x73
> > [<ffffffff8020cc8e>] child_rip+0x0/0x12
>
> Maybe we're trying to kick off a hotplug event on the wrong slot?
> I really have no idea...

One hotplug event related difference that I believe I
noticed between the x3850 were I did not see the problem and
the x3950 is the hotplug event arrival location. On the
x3850 the handler receives the handle for the transparent
p2p bridge above the slot. On the x3950 I believe the handler
is receiving the handle for the root bridge (directly above
the transparent p2p bridge). acpiphp installs a handler for
each of these possible arrival locations. pci_slot installs
a handler for neither location which seems like it should be
okay.

I notice that acpi_pci_register_driver() was only being
used by acpiphp before pci_slot. Perhaps your changes are
flushing out some sort of ACPI core coexistence issue not
seen previously because there was only a single user?

My silly idea is probably no better than your no idea. :)

>
> > Code: ff ff ff ff 40 23 2c 88 ff ff ff ff 00 c8 c6 3b 10 81 ff ff
> > RIP [<ffffffff882c2344>] :pci_slot:__this_module+0x21c4/0xfffffffffffff204
> > RSP <ffff81103fa43ea8>
>
> Can you apply this debug patch on top of your tree, and send me
> the output?
>
> I'd be curious to see the output for your failure case:
>
> # modprobe pci_slot debug=1
> # modprobe acpiphp debug=1

Someone else is using the system right now so I may not
be able to do this until next week.

Gary

--
Gary Hade
System x Enablement
IBM Linux Technology Center
503-578-4503 IBM T/L: 775-4503
[email protected]
http://www.ibm.com/linux/ltc

2007-12-03 03:39:53

by Kenji Kaneshige

[permalink] [raw]
Subject: Re: [PATCH 0/4, v3] Physical PCI slot objects

Hi Alex-san,

>> On my system, hotplug slots themselves can be added, removed
>> and replaced with the ohter type of I/O box. The ACPI firmware
>> tells OS the presence of those slots using _STA method (That
>> is, it doesn't use 'LoadTable()' AML operator). On the other
>> hand, current pci_slot driver doesn't check _STA. As a result,
>> pci_slot driver tryied to register the invalid (non-existing)
>> slots. The ACPI firmware of my system returns '1023' if the
>> invalid slot's _SUN is evaluated. This is the cause of Call
>> Traces mentioned above. To fix this problem, pci_slot driver
>> need to check _STA when scanning ACPI Namespace.
>
> Now this is very curious. The relevant line in pci_slot is:
>
> check_slot()
> status = acpi_evaluate_integer(handle, "_SUN", NULL, sun);
> if (ACPI_FAILURE(status))
> return -1;
>
> Why does your firmware return the error information inside sun,
> instead of returning an error in status? That doesn't seem right
> to me...

Because ACPI spec doesn't provide any way for firmware (AML)
to return as error.

In addtion, I think we should not trust the _SUN value of
non-existing device because the ACPI spec says in "6.5.1 _INI
(Init)" that _INI method is run before _ADR, _CID, _HID, _SUN, and
_UID are run. It means _SUN could be initialized in _INI method
implecitely. And it also says that "If the _STA method indicates
that the device is not present, OSPM will not run the _INI and will
not examine the children of the device for _INI methods.". After all,
_SUN for non-existing device is not reliable because it might not
initialized by _INI method.

>
>> I'm sorry for reporting this so late. I'm attaching the patch
>> to fix the problem. This is against 2.6.24-rc3 with your
>> patches applied. Could you try it?
>
> Applying this patch causes me to only detect populated slots in
> my system, which isn't what I want -- otherwise, I could have
> just enumerated the PCI bus and found the devices that way. :)
>
> Maybe on your machine, checking existence of _STA might do the
> right thing, but I don't think we should actually be looking at
> any of the actual bits returned.
>
> If we check ACPI_STA_DEVICE_PRESENT, then we will not detect
> empty slots on my system. Can you try this patch to see if at
> least the first call to acpi_evaluate_integer helps? If that
> doesn't help, maybe the second block will help you, but it breaks
> my machine...

Maybe the result is as you guess.
The first block doesn't help me (with the first block, all of the
slot disappeared. Please see the bottom of this mail for details).
The second block helps me.

There seems a difference of the interpretation about _STA for PCI
hotplug slot between your firmware and my firmware. The difference
is:

- Your firmware provides the _STA method to represent the presence
of PCI adapter card on the slot.

- My firmware provides the _STA method to represent the presence
of the slot.

Providing _STA method to represent the presence of PCI adpater card
on the slot (as your firmware does) doesn't seem right to me because
of the following reasons.

- ACPI spec says "After calling _EJ0, OSPM verifies the device no
longer exists to determine if the eject succeeded. For _HID devices,
OSPM evaluates the _STA method. For _ADR devices, OSPM checks with
the bus driver for that device." in "6.3.3 _EJx (Eject)". Because
PCI adapter card on the slot is _ADR device, the presence of the
card must be checked with bus driver, not _STA.

- ACPI spec provides the example AML code which uses _STA to
represent Docking Station (See 6.3.2 _EJD (Ejection Dependent
Device)". The usage of this is same as my firmware.

What do you think about that?

P.S. None of the slots except the strange slot named '1023' were
detected with your patch. It would happen on other machines
(might including hp machine) too. The reason is _STA evaluation
fails on the hotplug slot which doesn't have _STA method. If the
device object doesn't have a _STA method, we need to handle it as
if it is present. I believe firmware normally doesn't provide
_STA method for PCI hotplug slots.

Thanks,
Kenji Kaneshige

2007-12-03 22:44:34

by Alex Chiang

[permalink] [raw]
Subject: Re: [PATCH 0/4, v3] Physical PCI slot objects

Hi Kenji-san,

* Kenji Kaneshige <[email protected]>:
> Hi Alex-san,
>
>>> On my system, hotplug slots themselves can be added, removed
>>> and replaced with the ohter type of I/O box.

Are you talking about some sort of I/O cabinet/chassis that you
can attach to the actual computer? Can the I/O expander unit be
hotplugged? Or do you need to power your machine down to attach
it?

If you can hotplug it, I'm guessing that is why your firmware
presents SxFy objects in the namespace with "weird" _SUN values,
and it's why you have to check _STA to see if the slots are valid
or not. That means the value returned by _SUN will change too,
right? What will it turn into?

Is that right? Or am I completely wrong? :)

>>> The ACPI firmware tells OS the presence of those slots using
>>> _STA method (That is, it doesn't use 'LoadTable()' AML
>>> operator). On the other hand, current pci_slot driver doesn't
>>> check _STA. As a result, pci_slot driver tryied to register
>>> the invalid (non-existing) slots. The ACPI firmware of my
>>> system returns '1023' if the invalid slot's _SUN is
>>> evaluated. This is the cause of Call Traces mentioned above.
>>> To fix this problem, pci_slot driver need to check _STA when
>>> scanning ACPI Namespace.
>>
>> Now this is very curious. The relevant line in pci_slot is:
>> check_slot()
>> status = acpi_evaluate_integer(handle, "_SUN", NULL, sun);
>> if (ACPI_FAILURE(status))
>> return -1;
>> Why does your firmware return the error information inside sun,
>> instead of returning an error in status? That doesn't seem right
>> to me...
>
> Because ACPI spec doesn't provide any way for firmware (AML)
> to return as error.

You are right -- I got confused about the interpreter returning
AE_NOT_FOUND vs the actual firmware returning an error value.
Thank you for this clarification.

> In addtion, I think we should not trust the _SUN value of
> non-existing device because the ACPI spec says in "6.5.1 _INI
> (Init)" that _INI method is run before _ADR, _CID, _HID, _SUN, and
> _UID are run. It means _SUN could be initialized in _INI method
> implecitely. And it also says that "If the _STA method indicates
> that the device is not present, OSPM will not run the _INI and will
> not examine the children of the device for _INI methods.". After all,
> _SUN for non-existing device is not reliable because it might not
> initialized by _INI method.

This is true, but HP platforms provide _INI at the root
device/host bridge level, not on SxFy objects, so it doesn't seem
that we would need to call _STA before calling _SUN for SxFy.

Does your firmware provide _INI on SxFy objects?

>>> I'm sorry for reporting this so late. I'm attaching the patch
>>> to fix the problem. This is against 2.6.24-rc3 with your
>>> patches applied. Could you try it?
>> Applying this patch causes me to only detect populated slots in
>> my system, which isn't what I want -- otherwise, I could have
>> just enumerated the PCI bus and found the devices that way. :)
>> Maybe on your machine, checking existence of _STA might do the
>> right thing, but I don't think we should actually be looking at
>> any of the actual bits returned. If we check ACPI_STA_DEVICE_PRESENT, then
>> we will not detect
>> empty slots on my system. Can you try this patch to see if at
>> least the first call to acpi_evaluate_integer helps? If that
>> doesn't help, maybe the second block will help you, but it breaks
>> my machine...
>
> Maybe the result is as you guess.
> The first block doesn't help me (with the first block, all of the
> slot disappeared. Please see the bottom of this mail for details).
> The second block helps me.
>
> There seems a difference of the interpretation about _STA for PCI
> hotplug slot between your firmware and my firmware. The difference
> is:
>
> - Your firmware provides the _STA method to represent the presence
> of PCI adapter card on the slot.
>
> - My firmware provides the _STA method to represent the presence
> of the slot.

Yes, that sounds right...

> Providing _STA method to represent the presence of PCI adpater card
> on the slot (as your firmware does) doesn't seem right to me because
> of the following reasons.
>
> - ACPI spec says "After calling _EJ0, OSPM verifies the device no
> longer exists to determine if the eject succeeded. For _HID devices,
> OSPM evaluates the _STA method. For _ADR devices, OSPM checks with
> the bus driver for that device." in "6.3.3 _EJx (Eject)". Because
> PCI adapter card on the slot is _ADR device, the presence of the
> card must be checked with bus driver, not _STA.
>
> - ACPI spec provides the example AML code which uses _STA to
> represent Docking Station (See 6.3.2 _EJD (Ejection Dependent
> Device)". The usage of this is same as my firmware.
>
> What do you think about that?

Our firmware teams seem to think that _STA should give the status
of the card for hotplug support and general functional state.
They claim that it doesn't makes much sense to support _STA on
the slot itself unless you can physically change the slot
topology on the machine at runtime, which we can't do (although
maybe you can).

The section of the spec you quoted is correct as long as we are
talking ACPI 2.0 or later. My platforms implement ACPI 1.0b for
legacy reasons. :-/

In ACPI 1.0b, _EJx definition says (section 6.3.2):

For hot removal, the device must be immediately ejected
when the OS calls the _EJ0 control method. The _EJ0
control method does not return until ejection is
complete. After calling _EJ0, the OS will call _STA to
determine whether or not the eject succeeded.

So your firmware implementation does not seem backward compatible
with the 1.0b spec. The different versions of ACPI is part of the
reason why my patch is breaking on your machine.

But as long as we are quoting the spec... :)

_SUN evaluates to a DWORD that is the number to be used
in the user interface. This number is required to be
unique among the slots of the same type. It is also
recommended that this number match the slot number
printed on the physical slot whenever possible.

section 6.1.6 of ACPI 2.0c

My question is, why is your firmware returning multiple values of
1023 then? This seems to be the real reason why my patch is
breaking on your machine.

While depending on ACPI 1.0b behavior might be somewhat risky,
returning the same value for _SUN multiple times, for slots of
the same type, definitely seems non-compliant.

What is your opinion?

> P.S. None of the slots except the strange slot named '1023'
> were detected with your patch. It would happen on other
> machines (might including hp machine) too. The reason is _STA
> evaluation fails on the hotplug slot which doesn't have _STA
> method. If the device object doesn't have a _STA method, we
> need to handle it as if it is present.

Thanks for this explanation about treating non-present _STA as
present. It makes sense.

> I believe firmware normally doesn't provide _STA method for PCI
> hotplug slots.

I somewhat disagree that firmware doesn't normally provide _STA
for PCI hotplug slots, but I think that is a side-issue. :)

Thanks.

/ac

2007-12-04 13:03:25

by Kenji Kaneshige

[permalink] [raw]
Subject: Re: [PATCH 0/4, v3] Physical PCI slot objects

Hi Alex-san,

> Hi Kenji-san,
>
> * Kenji Kaneshige <[email protected]>:
>> Hi Alex-san,
>>
>>>> On my system, hotplug slots themselves can be added, removed
>>>> and replaced with the ohter type of I/O box.
>
> Are you talking about some sort of I/O cabinet/chassis that you
> can attach to the actual computer? Can the I/O expander unit be
> hotplugged? Or do you need to power your machine down to attach
> it?
>
> If you can hotplug it, I'm guessing that is why your firmware
> presents SxFy objects in the namespace with "weird" _SUN values,
> and it's why you have to check _STA to see if the slots are valid
> or not. That means the value returned by _SUN will change too,
> right? What will it turn into?
>

Currently, it's not hotpluggable (will be hotpluggable in the future).
Here is a sample AML code to explain what my firmware is doing.

Device (PCI0) {
Device (P2PA) {
Device (P2PB) { // for I/O unit (A)
Name (_ADR, ...)
Method (_STA) { ... }
}
Device (S0F0) { // for I/O unit (B)
Name (_ADR, ...)
Method (_STA) { ... }
Method (_EJx) { ... }
Method (_SUN) { ... }
}
...
}
...
}

If the I/O unit (A) is connected, _STA of P2PB returns as present
and _STA of S0F0 returns as not present.
If the I/O unit (B) is connected, _STA of P2PB returns as not
present and _STA of S0F0 returns as present.

>> In addtion, I think we should not trust the _SUN value of
>> non-existing device because the ACPI spec says in "6.5.1 _INI
>> (Init)" that _INI method is run before _ADR, _CID, _HID, _SUN, and
>> _UID are run. It means _SUN could be initialized in _INI method
>> implecitely. And it also says that "If the _STA method indicates
>> that the device is not present, OSPM will not run the _INI and will
>> not examine the children of the device for _INI methods.". After all,
>> _SUN for non-existing device is not reliable because it might not
>> initialized by _INI method.
>
> This is true, but HP platforms provide _INI at the root
> device/host bridge level, not on SxFy objects, so it doesn't seem
> that we would need to call _STA before calling _SUN for SxFy.
>
> Does your firmware provide _INI on SxFy objects?

No, it doesn't. But what I wanted to say was we should not use _SUN
value of non-existing device object.

>
> Our firmware teams seem to think that _STA should give the status
> of the card for hotplug support and general functional state.
> They claim that it doesn't makes much sense to support _STA on
> the slot itself unless you can physically change the slot
> topology on the machine at runtime, which we can't do (although
> maybe you can).
>
> The section of the spec you quoted is correct as long as we are
> talking ACPI 2.0 or later. My platforms implement ACPI 1.0b for
> legacy reasons. :-/
>
> In ACPI 1.0b, _EJx definition says (section 6.3.2):
>
> For hot removal, the device must be immediately ejected
> when the OS calls the _EJ0 control method. The _EJ0
> control method does not return until ejection is
> complete. After calling _EJ0, the OS will call _STA to
> determine whether or not the eject succeeded.
>
> So your firmware implementation does not seem backward compatible
> with the 1.0b spec. The different versions of ACPI is part of the
> reason why my patch is breaking on your machine.
>

I think this is the real reason. My platform implements ACPI 2.0 or
later. I didn't notice the chage to_EJx definition. Maybe we need to
check ACPI version in pci_slot driver.

> But as long as we are quoting the spec... :)
>
> _SUN evaluates to a DWORD that is the number to be used
> in the user interface. This number is required to be
> unique among the slots of the same type. It is also
> recommended that this number match the slot number
> printed on the physical slot whenever possible.
>
> section 6.1.6 of ACPI 2.0c
>
> My question is, why is your firmware returning multiple values of
> 1023 then? This seems to be the real reason why my patch is
> breaking on your machine.
>
> While depending on ACPI 1.0b behavior might be somewhat risky,
> returning the same value for _SUN multiple times, for slots of
> the same type, definitely seems non-compliant.
>

The reason is very simple. The reason is your patch is evaluating
invalid _SUN method. We must skip non-existing device object. This
is what your patch is already doing for pci root bridges.
In addition, even if those _SUN method were changed to return unique
number, none of the problems would be solved. Maybe pci_slot driver
would detect many unknown slots.

Thanks,
Kenji Kaneshige

2007-12-10 23:02:37

by Alex Chiang

[permalink] [raw]
Subject: Re: [PATCH 0/4, v3] Physical PCI slot objects

Hi Kenji-san,

I have been thinking about this problem for quite a bit, and
think that there are no good solutions...

* Kenji Kaneshige <[email protected]>:
>>>>> On my system, hotplug slots themselves can be added, removed
>>>>> and replaced with the ohter type of I/O box.
>> Are you talking about some sort of I/O cabinet/chassis that you
>> can attach to the actual computer? Can the I/O expander unit be
>> hotplugged? Or do you need to power your machine down to attach
>> it?
>> If you can hotplug it, I'm guessing that is why your firmware
>> presents SxFy objects in the namespace with "weird" _SUN values,
>> and it's why you have to check _STA to see if the slots are valid
>> or not. That means the value returned by _SUN will change too,
>> right? What will it turn into?
>>
>
> Currently, it's not hotpluggable (will be hotpluggable in the future).
> Here is a sample AML code to explain what my firmware is doing.
>
> Device (PCI0) {
> Device (P2PA) {
> Device (P2PB) { // for I/O unit (A)
> Name (_ADR, ...)
> Method (_STA) { ... }
> }
> Device (S0F0) { // for I/O unit (B)
> Name (_ADR, ...)
> Method (_STA) { ... }
> Method (_EJx) { ... }
> Method (_SUN) { ... }
> }
> ...
> }
> ...
> }
>
> If the I/O unit (A) is connected, _STA of P2PB returns as present
> and _STA of S0F0 returns as not present.
> If the I/O unit (B) is connected, _STA of P2PB returns as not
> present and _STA of S0F0 returns as present.

If I/O unit A or B can never appear while the system is turned on
(aka not hotpluggable), then it is incorrect to present them in
the current namespace.

>>> In addtion, I think we should not trust the _SUN value of
>>> non-existing device because the ACPI spec says in "6.5.1 _INI
>>> (Init)" that _INI method is run before _ADR, _CID, _HID, _SUN, and
>>> _UID are run. It means _SUN could be initialized in _INI method
>>> implecitely. And it also says that "If the _STA method indicates
>>> that the device is not present, OSPM will not run the _INI and will
>>> not examine the children of the device for _INI methods.". After all,
>>> _SUN for non-existing device is not reliable because it might not
>>> initialized by _INI method.
>> This is true, but HP platforms provide _INI at the root
>> device/host bridge level, not on SxFy objects, so it doesn't seem
>> that we would need to call _STA before calling _SUN for SxFy.
>> Does your firmware provide _INI on SxFy objects?
>
> No, it doesn't. But what I wanted to say was we should not use _SUN
> value of non-existing device object.

There is nothing illegal about evaluating _SUN for an object that
returns 0x0 for _STA.

Also, when you say "non-existing", I think of the ACPI CA
exception code AE_NOT_EXIST which means "absent from
the namespace", and is the reason why my code works on both HP
and IBM machines. It does not mean "_STA == 0x0".

>> Our firmware teams seem to think that _STA should give the status
>> of the card for hotplug support and general functional state.
>> They claim that it doesn't makes much sense to support _STA on
>> the slot itself unless you can physically change the slot
>> topology on the machine at runtime, which we can't do (although
>> maybe you can).
>> The section of the spec you quoted is correct as long as we are
>> talking ACPI 2.0 or later. My platforms implement ACPI 1.0b for
>> legacy reasons. :-/
>> In ACPI 1.0b, _EJx definition says (section 6.3.2):
>> For hot removal, the device must be immediately ejected
>> when the OS calls the _EJ0 control method. The _EJ0
>> control method does not return until ejection is
>> complete. After calling _EJ0, the OS will call _STA to
>> determine whether or not the eject succeeded.
>> So your firmware implementation does not seem backward compatible
>> with the 1.0b spec. The different versions of ACPI is part of the
>> reason why my patch is breaking on your machine.
>
> I think this is the real reason. My platform implements ACPI 2.0 or
> later. I didn't notice the chage to_EJx definition. Maybe we need to
> check ACPI version in pci_slot driver.

I did some experiments on HP low-end ia64 (ACPI 1.0b only) and
our mid-range and high-end ia64 platforms (ACPI 2.0c). Checking
for _STA before evaluating _SUN leads to the same result for me:
we only detect populated slots.

I think that the real issue is not 1.0 vs 2.0, but the semantics
that our different firmware teams have placed on _STA. Again,

- HP firmware thinks _STA should give status of the card
- Fujitsu firmware thinks _STA should give status of the slot

So we are at an impasse. :(

>> But as long as we are quoting the spec... :)
>> _SUN evaluates to a DWORD that is the number to be used
>> in the user interface. This number is required to be
>> unique among the slots of the same type. It is also
>> recommended that this number match the slot number
>> printed on the physical slot whenever possible.
>> section 6.1.6 of ACPI 2.0c
>> My question is, why is your firmware returning multiple values of
>> 1023 then? This seems to be the real reason why my patch is
>> breaking on your machine.
>> While depending on ACPI 1.0b behavior might be somewhat risky,
>> returning the same value for _SUN multiple times, for slots of
>> the same type, definitely seems non-compliant.
>
> The reason is very simple. The reason is your patch is evaluating
> invalid _SUN method. We must skip non-existing device object. This
> is what your patch is already doing for pci root bridges.
> In addition, even if those _SUN method were changed to return unique
> number, none of the problems would be solved. Maybe pci_slot driver
> would detect many unknown slots.

I did some experiments to see if I could write a quick hack, like

- do not register a slot if we've already seen this _SUN

But it broke my reference counting, and I don't think it would be
robust enough if users wanted to modprobe/rmmod pci_slot and
acpiphp/pciehp in random order.

I do not agree that _SUN should return a repeating, non-compliant
value even if _STA says the device is not present. A truly
non-existent device is one that does not appear in the namespace.
If you cannot hotplug an I/O unit on your machine, then your
firmware shouldn't be presenting those devices in the namespace.

I think the best option would be for your firmware to return the
actual value of _SUN if I/O unit A and/or B was plugged in. That
way, nothing special has to happen when the units are hotplugged
-- they'll already have correct entries in sysfs. If they are
never hotplugged, it shouldn't be confusing for the user, since
they will never actually get any devices in those slots anyway.

What happens if you try to load acpiphp on your system today?
Does it work, or do you get the same messages about trying to
create multiple entries in sysfs with the same name?

Do you have any better ideas for detecting all physical slots in
a system, regardless of whether they are populated?

Thanks.

/ac

2008-03-11 19:20:18

by Kristen Carlson Accardi

[permalink] [raw]
Subject: Re: [PATCH 0/4, v3] Physical PCI slot objects

On Thu, 29 Nov 2007 16:47:14 +0900
Kenji Kaneshige <[email protected]> wrote:

> > Hi Gary, Kenji-san, et. al,
> >
> > * Gary Hade <[email protected]>:
> >> Alex, What I was trying to suggest is a boot-time kernel
> >> option, not a kernel configuration option. The basic idea is
> >> to give the user (with a single binary kernel) the ability to
> >> include your ACPI-PCI slot driver feature changes only when
> >> they are really needed. In addition to reducing the number of
> >> system/PCI hotplug driver combinations where your changes would
> >> need to be validated, I believe would also help alleviate other
> >> worries (e.g. Andi Kleen's memory consumption concern). I
> >> believe this goal could also be achieved with the kernel config
> >> option by making the pci_slot module runtime loadable with the
> >> PCI hotplug drivers only visiting your new code when the
> >> pci_slot driver is loaded, although I think this would be more
> >> difficult to implement.
> >
> > I have modified my patch series so that the final patch that
> > introduces my ACPI-PCI slot driver is a full-fledged module, that
> > has a tristate Kconfig option.
> >
>
> Thank you for your good job.
>
> I tested shpchp and pciehp both with and without pci_slot module.
> There seems no regression from shpchp and pciehp's point of view.
> (I had a little concern about the hotplug slots' name that vary
> depending on whether pci_slot functionality is enabled or disabled.
> But, now that we can build pci_slot driver as a kernel module, I
> don't think it is a big problem).
>
> Only the problems is that I got Call Traces with the following error
> messages when pci_slot driver was loaded, and one strange slot named
> '1023' was registered (other slots are fine). This is the same problem
> I reported before.
>
> sysfs: duplicate filename '1023' can not be created
> WARNING: at fs/sysfs/dir.c:424 sysfs_add_one()
>
> kobject_add failed for 1023 with -EEXIST, don't try to register
> things with the same name in the same directory.
>
> On my system, hotplug slots themselves can be added, removed and
> replaced with the ohter type of I/O box. The ACPI firmware tells OS
> the presence of those slots using _STA method (That is, it doesn't
> use 'LoadTable()' AML operator). On the other hand, current pci_slot
> driver doesn't check _STA. As a result, pci_slot driver tryied to
> register the invalid (non-existing) slots. The ACPI firmware of my
> system returns '1023' if the invalid slot's _SUN is evaluated. This
> is the cause of Call Traces mentioned above. To fix this problem,
> pci_slot driver need to check _STA when scanning ACPI Namespace.
>
> I'm sorry for reporting this so late. I'm attaching the patch to fix
> the problem. This is against 2.6.24-rc3 with your patches applied.
> Could you try it?
>
> BTW, acpiphp also seems to have the same problem...
>
> Thanks,
> Kenji Kaneshige

Hello - back to this mail for a moment. I wanted to talk about acpiphp
again. You say that acpiphp has this same problem as Alex's driver wrt
evalating _SUN on a non existant object. If we skip registering the
slot in acpiphp's register_slot function if it returns 0 for _STA, we
will not detect any hotplug slots, since leaf nodes will return 0 for
_STA if a device is not present. Should we only skip it if it's parent
is a p2p bridge that has _EJ0 and the parent's _STA says empty?

Your input is appreciated,
Kristen

>
> ---
> drivers/acpi/pci_slot.c | 13 +++++++++++++
> 1 file changed, 13 insertions(+)
>
> Index: linux-2.6.24-rc3/drivers/acpi/pci_slot.c
> ===================================================================
> --- linux-2.6.24-rc3.orig/drivers/acpi/pci_slot.c
> +++ linux-2.6.24-rc3/drivers/acpi/pci_slot.c
> @@ -113,10 +113,17 @@ register_slot(acpi_handle handle, u32 lv
> int device;
> unsigned long sun;
> char name[KOBJ_NAME_LEN];
> + acpi_status status;
> + struct acpi_device *dummy_device;
>
> struct pci_slot *pci_slot;
> struct pci_bus *pci_bus = context;
>
> + /* Skip non-existing device object. */
> + status = acpi_bus_get_device(handle, &dummy_device);
> + if (ACPI_FAILURE(status))
> + return AE_OK;
> +
> if (check_slot(handle, &device, &sun))
> return AE_OK;
>
> @@ -150,12 +157,18 @@ walk_p2p_bridge(acpi_handle handle, u32
> acpi_status status;
> acpi_handle dummy_handle;
> acpi_walk_callback user_function;
> + struct acpi_device *dummy_device;
>
> struct pci_dev *dev;
> struct pci_bus *pci_bus;
> struct p2p_bridge_context child_context;
> struct p2p_bridge_context *parent_context = context;
>
> + /* Skip non-existing device object. */
> + status = acpi_bus_get_device(handle, &dummy_device);
> + if (ACPI_FAILURE(status))
> + return AE_OK;
> +
> pci_bus = parent_context->pci_bus;
> user_function = parent_context->user_function;
>
>

2008-03-12 12:37:24

by Kenji Kaneshige

[permalink] [raw]
Subject: Re: [PATCH 0/4, v3] Physical PCI slot objects

Hi Kristen,

> Hello - back to this mail for a moment. I wanted to talk about acpiphp
> again. You say that acpiphp has this same problem as Alex's driver wrt
> evalating _SUN on a non existant object. If we skip registering the
> slot in acpiphp's register_slot function if it returns 0 for _STA, we
> will not detect any hotplug slots, since leaf nodes will return 0 for
> _STA if a device is not present. Should we only skip it if it's parent
> is a p2p bridge that has _EJ0 and the parent's _STA says empty?

I've been not looking at acpiphp code for a long time. So I need refresh
my memory before answering. Sorry.

But I want to say one thing. We can detect all the hotplug slots if ACPI
firmware doesn't provide _STA. We don't need _STA for PCI hotplug slots
and should not provide _STA for PCI hotplug slots if the system is based
on ACPI2.0 or later, because those are _ADR based devices (PCI root bridge,
which is _HID based device, is a exception). For _ADR based devices, we
should check the presence of the device using PCI bus drivers instead of
evaluating _STA. Please see the section of "_EJx (Eject)" in ACPI2.0b or
later spec. It says "For _HID devices, OSPM evaluates the _STA method. For
_ADR devices, OSPM checks with the bus driver for that device.".

BTW, acpiphp_glue.c says _STA is "optionally". I don't know what it means.

Thanks,
Kenji Kaneshige


Kristen Carlson Accardi wrote:
> On Thu, 29 Nov 2007 16:47:14 +0900
> Kenji Kaneshige <[email protected]> wrote:
>
>>> Hi Gary, Kenji-san, et. al,
>>>
>>> * Gary Hade <[email protected]>:
>>>> Alex, What I was trying to suggest is a boot-time kernel
>>>> option, not a kernel configuration option. The basic idea is
>>>> to give the user (with a single binary kernel) the ability to
>>>> include your ACPI-PCI slot driver feature changes only when
>>>> they are really needed. In addition to reducing the number of
>>>> system/PCI hotplug driver combinations where your changes would
>>>> need to be validated, I believe would also help alleviate other
>>>> worries (e.g. Andi Kleen's memory consumption concern). I
>>>> believe this goal could also be achieved with the kernel config
>>>> option by making the pci_slot module runtime loadable with the
>>>> PCI hotplug drivers only visiting your new code when the
>>>> pci_slot driver is loaded, although I think this would be more
>>>> difficult to implement.
>>> I have modified my patch series so that the final patch that
>>> introduces my ACPI-PCI slot driver is a full-fledged module, that
>>> has a tristate Kconfig option.
>>>
>> Thank you for your good job.
>>
>> I tested shpchp and pciehp both with and without pci_slot module.
>> There seems no regression from shpchp and pciehp's point of view.
>> (I had a little concern about the hotplug slots' name that vary
>> depending on whether pci_slot functionality is enabled or disabled.
>> But, now that we can build pci_slot driver as a kernel module, I
>> don't think it is a big problem).
>>
>> Only the problems is that I got Call Traces with the following error
>> messages when pci_slot driver was loaded, and one strange slot named
>> '1023' was registered (other slots are fine). This is the same problem
>> I reported before.
>>
>> sysfs: duplicate filename '1023' can not be created
>> WARNING: at fs/sysfs/dir.c:424 sysfs_add_one()
>>
>> kobject_add failed for 1023 with -EEXIST, don't try to register
>> things with the same name in the same directory.
>>
>> On my system, hotplug slots themselves can be added, removed and
>> replaced with the ohter type of I/O box. The ACPI firmware tells OS
>> the presence of those slots using _STA method (That is, it doesn't
>> use 'LoadTable()' AML operator). On the other hand, current pci_slot
>> driver doesn't check _STA. As a result, pci_slot driver tryied to
>> register the invalid (non-existing) slots. The ACPI firmware of my
>> system returns '1023' if the invalid slot's _SUN is evaluated. This
>> is the cause of Call Traces mentioned above. To fix this problem,
>> pci_slot driver need to check _STA when scanning ACPI Namespace.
>>
>> I'm sorry for reporting this so late. I'm attaching the patch to fix
>> the problem. This is against 2.6.24-rc3 with your patches applied.
>> Could you try it?
>>
>> BTW, acpiphp also seems to have the same problem...
>>
>> Thanks,
>> Kenji Kaneshige
>
> Hello - back to this mail for a moment. I wanted to talk about acpiphp
> again. You say that acpiphp has this same problem as Alex's driver wrt
> evalating _SUN on a non existant object. If we skip registering the
> slot in acpiphp's register_slot function if it returns 0 for _STA, we
> will not detect any hotplug slots, since leaf nodes will return 0 for
> _STA if a device is not present. Should we only skip it if it's parent
> is a p2p bridge that has _EJ0 and the parent's _STA says empty?
>
> Your input is appreciated,
> Kristen
>
>> ---
>> drivers/acpi/pci_slot.c | 13 +++++++++++++
>> 1 file changed, 13 insertions(+)
>>
>> Index: linux-2.6.24-rc3/drivers/acpi/pci_slot.c
>> ===================================================================
>> --- linux-2.6.24-rc3.orig/drivers/acpi/pci_slot.c
>> +++ linux-2.6.24-rc3/drivers/acpi/pci_slot.c
>> @@ -113,10 +113,17 @@ register_slot(acpi_handle handle, u32 lv
>> int device;
>> unsigned long sun;
>> char name[KOBJ_NAME_LEN];
>> + acpi_status status;
>> + struct acpi_device *dummy_device;
>>
>> struct pci_slot *pci_slot;
>> struct pci_bus *pci_bus = context;
>>
>> + /* Skip non-existing device object. */
>> + status = acpi_bus_get_device(handle, &dummy_device);
>> + if (ACPI_FAILURE(status))
>> + return AE_OK;
>> +
>> if (check_slot(handle, &device, &sun))
>> return AE_OK;
>>
>> @@ -150,12 +157,18 @@ walk_p2p_bridge(acpi_handle handle, u32
>> acpi_status status;
>> acpi_handle dummy_handle;
>> acpi_walk_callback user_function;
>> + struct acpi_device *dummy_device;
>>
>> struct pci_dev *dev;
>> struct pci_bus *pci_bus;
>> struct p2p_bridge_context child_context;
>> struct p2p_bridge_context *parent_context = context;
>>
>> + /* Skip non-existing device object. */
>> + status = acpi_bus_get_device(handle, &dummy_device);
>> + if (ACPI_FAILURE(status))
>> + return AE_OK;
>> +
>> pci_bus = parent_context->pci_bus;
>> user_function = parent_context->user_function;
>>
>>
>
>