2018-01-27 04:22:04 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* PCI HotPlug Controller Core
|
|
|
|
*
|
|
|
|
* Copyright (C) 2001-2002 Greg Kroah-Hartman (greg@kroah.com)
|
|
|
|
* Copyright (C) 2001-2002 IBM Corp.
|
|
|
|
*
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
2006-09-30 01:30:27 +08:00
|
|
|
* Send feedback to <kristen.c.accardi@intel.com>
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
2016-11-15 21:55:51 +08:00
|
|
|
* Authors:
|
|
|
|
* Greg Kroah-Hartman <greg@kroah.com>
|
|
|
|
* Scott Murray <scottm@somanetworks.com>
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
|
2016-08-25 04:57:51 +08:00
|
|
|
#include <linux/module.h> /* try_module_get & module_put */
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/moduleparam.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/list.h>
|
2006-10-14 11:05:19 +08:00
|
|
|
#include <linux/kobject.h>
|
|
|
|
#include <linux/sysfs.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/pagemap.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/mount.h>
|
|
|
|
#include <linux/namei.h>
|
2008-10-21 07:40:57 +08:00
|
|
|
#include <linux/mutex.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/pci.h>
|
2006-10-14 11:05:19 +08:00
|
|
|
#include <linux/pci_hotplug.h>
|
2016-12-25 03:46:01 +08:00
|
|
|
#include <linux/uaccess.h>
|
2008-06-11 05:28:50 +08:00
|
|
|
#include "../pci.h"
|
2013-04-16 00:44:18 +08:00
|
|
|
#include "cpci_hotplug.h"
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#define MY_NAME "pci_hotplug"
|
|
|
|
|
2015-12-28 05:21:11 +08:00
|
|
|
#define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt, MY_NAME, __func__, ## arg); } while (0)
|
|
|
|
#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 warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME, ## arg)
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* local variables */
|
2012-01-13 07:02:20 +08:00
|
|
|
static bool debug;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
static LIST_HEAD(pci_hotplug_slot_list);
|
2008-10-21 07:40:57 +08:00
|
|
|
static DEFINE_MUTEX(pci_hp_mutex);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* Weee, fun with macros... */
|
2014-04-19 08:13:49 +08:00
|
|
|
#define GET_STATUS(name, type) \
|
|
|
|
static int get_##name(struct hotplug_slot *slot, type *value) \
|
2005-04-17 06:20:36 +08:00
|
|
|
{ \
|
2018-09-08 15:59:01 +08:00
|
|
|
const struct hotplug_slot_ops *ops = slot->ops; \
|
2005-04-17 06:20:36 +08:00
|
|
|
int retval = 0; \
|
2018-09-08 15:59:01 +08:00
|
|
|
if (!try_module_get(slot->owner)) \
|
2008-09-22 14:26:05 +08:00
|
|
|
return -ENODEV; \
|
|
|
|
if (ops->get_##name) \
|
|
|
|
retval = ops->get_##name(slot, value); \
|
2018-09-08 15:59:01 +08:00
|
|
|
module_put(slot->owner); \
|
2005-04-17 06:20:36 +08:00
|
|
|
return retval; \
|
|
|
|
}
|
|
|
|
|
|
|
|
GET_STATUS(power_status, u8)
|
|
|
|
GET_STATUS(attention_status, u8)
|
|
|
|
GET_STATUS(latch_status, u8)
|
|
|
|
GET_STATUS(adapter_status, u8)
|
|
|
|
|
2015-06-19 15:57:44 +08:00
|
|
|
static ssize_t power_read_file(struct pci_slot *pci_slot, char *buf)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int retval;
|
|
|
|
u8 value;
|
|
|
|
|
2015-06-19 15:57:44 +08:00
|
|
|
retval = get_power_status(pci_slot->hotplug, &value);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (retval)
|
2014-04-19 08:13:49 +08:00
|
|
|
return retval;
|
|
|
|
|
|
|
|
return sprintf(buf, "%d\n", value);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-06-11 05:28:50 +08:00
|
|
|
static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,
|
2014-04-19 08:13:49 +08:00
|
|
|
size_t count)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-06-11 05:28:50 +08:00
|
|
|
struct hotplug_slot *slot = pci_slot->hotplug;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned long lpower;
|
|
|
|
u8 power;
|
|
|
|
int retval = 0;
|
|
|
|
|
2014-04-19 08:13:49 +08:00
|
|
|
lpower = simple_strtoul(buf, NULL, 10);
|
2005-04-17 06:20:36 +08:00
|
|
|
power = (u8)(lpower & 0xff);
|
2014-04-19 08:13:49 +08:00
|
|
|
dbg("power = %d\n", power);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2018-09-08 15:59:01 +08:00
|
|
|
if (!try_module_get(slot->owner)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
retval = -ENODEV;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
switch (power) {
|
2014-04-19 08:13:49 +08:00
|
|
|
case 0:
|
|
|
|
if (slot->ops->disable_slot)
|
|
|
|
retval = slot->ops->disable_slot(slot);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
if (slot->ops->enable_slot)
|
|
|
|
retval = slot->ops->enable_slot(slot);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
err("Illegal value specified for power\n");
|
|
|
|
retval = -EINVAL;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2018-09-08 15:59:01 +08:00
|
|
|
module_put(slot->owner);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-11-15 02:28:18 +08:00
|
|
|
exit:
|
2005-04-17 06:20:36 +08:00
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2008-06-11 05:28:50 +08:00
|
|
|
static struct pci_slot_attribute hotplug_slot_attr_power = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR},
|
|
|
|
.show = power_read_file,
|
|
|
|
.store = power_write_file
|
|
|
|
};
|
|
|
|
|
2015-06-19 15:57:44 +08:00
|
|
|
static ssize_t attention_read_file(struct pci_slot *pci_slot, char *buf)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int retval;
|
|
|
|
u8 value;
|
|
|
|
|
2015-06-19 15:57:44 +08:00
|
|
|
retval = get_attention_status(pci_slot->hotplug, &value);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (retval)
|
2014-04-19 08:13:49 +08:00
|
|
|
return retval;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-04-19 08:13:49 +08:00
|
|
|
return sprintf(buf, "%d\n", value);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2015-06-19 15:57:44 +08:00
|
|
|
static ssize_t attention_write_file(struct pci_slot *pci_slot, const char *buf,
|
2014-04-19 08:13:49 +08:00
|
|
|
size_t count)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2018-09-08 15:59:01 +08:00
|
|
|
struct hotplug_slot *slot = pci_slot->hotplug;
|
|
|
|
const struct hotplug_slot_ops *ops = slot->ops;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned long lattention;
|
|
|
|
u8 attention;
|
|
|
|
int retval = 0;
|
|
|
|
|
2014-04-19 08:13:49 +08:00
|
|
|
lattention = simple_strtoul(buf, NULL, 10);
|
2005-04-17 06:20:36 +08:00
|
|
|
attention = (u8)(lattention & 0xff);
|
2014-04-19 08:13:49 +08:00
|
|
|
dbg(" - attention = %d\n", attention);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2018-09-08 15:59:01 +08:00
|
|
|
if (!try_module_get(slot->owner)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
retval = -ENODEV;
|
|
|
|
goto exit;
|
|
|
|
}
|
2008-06-11 05:28:50 +08:00
|
|
|
if (ops->set_attention_status)
|
2018-09-08 15:59:01 +08:00
|
|
|
retval = ops->set_attention_status(slot, attention);
|
|
|
|
module_put(slot->owner);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-11-15 02:28:18 +08:00
|
|
|
exit:
|
2005-04-17 06:20:36 +08:00
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2008-06-11 05:28:50 +08:00
|
|
|
static struct pci_slot_attribute hotplug_slot_attr_attention = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR},
|
|
|
|
.show = attention_read_file,
|
|
|
|
.store = attention_write_file
|
|
|
|
};
|
|
|
|
|
2015-06-19 15:57:44 +08:00
|
|
|
static ssize_t latch_read_file(struct pci_slot *pci_slot, char *buf)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int retval;
|
|
|
|
u8 value;
|
|
|
|
|
2015-06-19 15:57:44 +08:00
|
|
|
retval = get_latch_status(pci_slot->hotplug, &value);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (retval)
|
2014-04-19 08:13:49 +08:00
|
|
|
return retval;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-04-19 08:13:49 +08:00
|
|
|
return sprintf(buf, "%d\n", value);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-06-11 05:28:50 +08:00
|
|
|
static struct pci_slot_attribute hotplug_slot_attr_latch = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},
|
|
|
|
.show = latch_read_file,
|
|
|
|
};
|
|
|
|
|
2015-06-19 15:57:44 +08:00
|
|
|
static ssize_t presence_read_file(struct pci_slot *pci_slot, char *buf)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int retval;
|
|
|
|
u8 value;
|
|
|
|
|
2015-06-19 15:57:44 +08:00
|
|
|
retval = get_adapter_status(pci_slot->hotplug, &value);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (retval)
|
2014-04-19 08:13:49 +08:00
|
|
|
return retval;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-04-19 08:13:49 +08:00
|
|
|
return sprintf(buf, "%d\n", value);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-06-11 05:28:50 +08:00
|
|
|
static struct pci_slot_attribute hotplug_slot_attr_presence = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},
|
|
|
|
.show = presence_read_file,
|
|
|
|
};
|
|
|
|
|
2008-06-11 05:28:50 +08:00
|
|
|
static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,
|
2014-04-19 08:13:49 +08:00
|
|
|
size_t count)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-06-11 05:28:50 +08:00
|
|
|
struct hotplug_slot *slot = pci_slot->hotplug;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned long ltest;
|
|
|
|
u32 test;
|
|
|
|
int retval = 0;
|
|
|
|
|
2015-12-28 05:21:11 +08:00
|
|
|
ltest = simple_strtoul(buf, NULL, 10);
|
2005-04-17 06:20:36 +08:00
|
|
|
test = (u32)(ltest & 0xffffffff);
|
2014-04-19 08:13:49 +08:00
|
|
|
dbg("test = %d\n", test);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2018-09-08 15:59:01 +08:00
|
|
|
if (!try_module_get(slot->owner)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
retval = -ENODEV;
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
if (slot->ops->hardware_test)
|
|
|
|
retval = slot->ops->hardware_test(slot, test);
|
2018-09-08 15:59:01 +08:00
|
|
|
module_put(slot->owner);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-11-15 02:28:18 +08:00
|
|
|
exit:
|
2005-04-17 06:20:36 +08:00
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2008-06-11 05:28:50 +08:00
|
|
|
static struct pci_slot_attribute hotplug_slot_attr_test = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},
|
|
|
|
.store = test_write_file
|
|
|
|
};
|
|
|
|
|
2009-06-16 10:00:47 +08:00
|
|
|
static bool has_power_file(struct pci_slot *pci_slot)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-06-11 05:28:50 +08:00
|
|
|
struct hotplug_slot *slot = pci_slot->hotplug;
|
2014-04-19 08:13:49 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if ((!slot) || (!slot->ops))
|
2009-06-16 10:00:47 +08:00
|
|
|
return false;
|
2005-04-17 06:20:36 +08:00
|
|
|
if ((slot->ops->enable_slot) ||
|
|
|
|
(slot->ops->disable_slot) ||
|
|
|
|
(slot->ops->get_power_status))
|
2009-06-16 10:00:47 +08:00
|
|
|
return true;
|
|
|
|
return false;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2009-06-16 10:00:47 +08:00
|
|
|
static bool has_attention_file(struct pci_slot *pci_slot)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-06-11 05:28:50 +08:00
|
|
|
struct hotplug_slot *slot = pci_slot->hotplug;
|
2014-04-19 08:13:49 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if ((!slot) || (!slot->ops))
|
2009-06-16 10:00:47 +08:00
|
|
|
return false;
|
2005-04-17 06:20:36 +08:00
|
|
|
if ((slot->ops->set_attention_status) ||
|
|
|
|
(slot->ops->get_attention_status))
|
2009-06-16 10:00:47 +08:00
|
|
|
return true;
|
|
|
|
return false;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2009-06-16 10:00:47 +08:00
|
|
|
static bool has_latch_file(struct pci_slot *pci_slot)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-06-11 05:28:50 +08:00
|
|
|
struct hotplug_slot *slot = pci_slot->hotplug;
|
2014-04-19 08:13:49 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if ((!slot) || (!slot->ops))
|
2009-06-16 10:00:47 +08:00
|
|
|
return false;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (slot->ops->get_latch_status)
|
2009-06-16 10:00:47 +08:00
|
|
|
return true;
|
|
|
|
return false;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2009-06-16 10:00:47 +08:00
|
|
|
static bool has_adapter_file(struct pci_slot *pci_slot)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-06-11 05:28:50 +08:00
|
|
|
struct hotplug_slot *slot = pci_slot->hotplug;
|
2014-04-19 08:13:49 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if ((!slot) || (!slot->ops))
|
2009-06-16 10:00:47 +08:00
|
|
|
return false;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (slot->ops->get_adapter_status)
|
2009-06-16 10:00:47 +08:00
|
|
|
return true;
|
|
|
|
return false;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2009-06-16 10:00:47 +08:00
|
|
|
static bool has_test_file(struct pci_slot *pci_slot)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-06-11 05:28:50 +08:00
|
|
|
struct hotplug_slot *slot = pci_slot->hotplug;
|
2014-04-19 08:13:49 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
if ((!slot) || (!slot->ops))
|
2009-06-16 10:00:47 +08:00
|
|
|
return false;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (slot->ops->hardware_test)
|
2009-06-16 10:00:47 +08:00
|
|
|
return true;
|
|
|
|
return false;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2015-06-19 15:57:44 +08:00
|
|
|
static int fs_add_slot(struct pci_slot *pci_slot)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2006-08-29 02:43:25 +08:00
|
|
|
int retval = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-06-16 10:01:25 +08:00
|
|
|
/* Create symbolic link to the hotplug driver module */
|
2015-06-19 15:57:44 +08:00
|
|
|
pci_hp_create_module_link(pci_slot);
|
2009-06-16 10:01:25 +08:00
|
|
|
|
2015-06-19 15:57:44 +08:00
|
|
|
if (has_power_file(pci_slot)) {
|
|
|
|
retval = sysfs_create_file(&pci_slot->kobj,
|
2009-06-16 10:00:47 +08:00
|
|
|
&hotplug_slot_attr_power.attr);
|
2006-08-29 02:43:25 +08:00
|
|
|
if (retval)
|
|
|
|
goto exit_power;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-06-19 15:57:44 +08:00
|
|
|
if (has_attention_file(pci_slot)) {
|
|
|
|
retval = sysfs_create_file(&pci_slot->kobj,
|
2006-08-29 02:43:25 +08:00
|
|
|
&hotplug_slot_attr_attention.attr);
|
|
|
|
if (retval)
|
|
|
|
goto exit_attention;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-06-19 15:57:44 +08:00
|
|
|
if (has_latch_file(pci_slot)) {
|
|
|
|
retval = sysfs_create_file(&pci_slot->kobj,
|
2006-08-29 02:43:25 +08:00
|
|
|
&hotplug_slot_attr_latch.attr);
|
|
|
|
if (retval)
|
|
|
|
goto exit_latch;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-06-19 15:57:44 +08:00
|
|
|
if (has_adapter_file(pci_slot)) {
|
|
|
|
retval = sysfs_create_file(&pci_slot->kobj,
|
2006-08-29 02:43:25 +08:00
|
|
|
&hotplug_slot_attr_presence.attr);
|
|
|
|
if (retval)
|
|
|
|
goto exit_adapter;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-06-19 15:57:44 +08:00
|
|
|
if (has_test_file(pci_slot)) {
|
|
|
|
retval = sysfs_create_file(&pci_slot->kobj,
|
2006-08-29 02:43:25 +08:00
|
|
|
&hotplug_slot_attr_test.attr);
|
|
|
|
if (retval)
|
|
|
|
goto exit_test;
|
|
|
|
}
|
|
|
|
|
|
|
|
goto exit;
|
|
|
|
|
|
|
|
exit_test:
|
2015-06-19 15:57:44 +08:00
|
|
|
if (has_adapter_file(pci_slot))
|
|
|
|
sysfs_remove_file(&pci_slot->kobj,
|
2009-06-16 10:00:47 +08:00
|
|
|
&hotplug_slot_attr_presence.attr);
|
2006-08-29 02:43:25 +08:00
|
|
|
exit_adapter:
|
2015-06-19 15:57:44 +08:00
|
|
|
if (has_latch_file(pci_slot))
|
|
|
|
sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr);
|
2006-08-29 02:43:25 +08:00
|
|
|
exit_latch:
|
2015-06-19 15:57:44 +08:00
|
|
|
if (has_attention_file(pci_slot))
|
|
|
|
sysfs_remove_file(&pci_slot->kobj,
|
2009-06-16 10:00:47 +08:00
|
|
|
&hotplug_slot_attr_attention.attr);
|
2006-08-29 02:43:25 +08:00
|
|
|
exit_attention:
|
2015-06-19 15:57:44 +08:00
|
|
|
if (has_power_file(pci_slot))
|
|
|
|
sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr);
|
2006-08-29 02:43:25 +08:00
|
|
|
exit_power:
|
2015-06-19 15:57:44 +08:00
|
|
|
pci_hp_remove_module_link(pci_slot);
|
2006-08-29 02:43:25 +08:00
|
|
|
exit:
|
|
|
|
return retval;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2015-06-19 15:57:44 +08:00
|
|
|
static void fs_remove_slot(struct pci_slot *pci_slot)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2015-06-19 15:57:44 +08:00
|
|
|
if (has_power_file(pci_slot))
|
|
|
|
sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-06-19 15:57:44 +08:00
|
|
|
if (has_attention_file(pci_slot))
|
|
|
|
sysfs_remove_file(&pci_slot->kobj,
|
2009-06-16 10:00:47 +08:00
|
|
|
&hotplug_slot_attr_attention.attr);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-06-19 15:57:44 +08:00
|
|
|
if (has_latch_file(pci_slot))
|
|
|
|
sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-06-19 15:57:44 +08:00
|
|
|
if (has_adapter_file(pci_slot))
|
|
|
|
sysfs_remove_file(&pci_slot->kobj,
|
2009-06-16 10:00:47 +08:00
|
|
|
&hotplug_slot_attr_presence.attr);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-06-19 15:57:44 +08:00
|
|
|
if (has_test_file(pci_slot))
|
|
|
|
sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_test.attr);
|
2009-06-16 10:01:25 +08:00
|
|
|
|
2015-06-19 15:57:44 +08:00
|
|
|
pci_hp_remove_module_link(pci_slot);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2014-04-19 08:13:49 +08:00
|
|
|
static struct hotplug_slot *get_slot_from_name(const char *name)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct hotplug_slot *slot;
|
|
|
|
|
2015-12-12 21:36:57 +08:00
|
|
|
list_for_each_entry(slot, &pci_hotplug_slot_list, slot_list) {
|
2008-10-21 07:41:58 +08:00
|
|
|
if (strcmp(hotplug_slot_name(slot), name) == 0)
|
2008-10-21 07:40:57 +08:00
|
|
|
return slot;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2008-10-21 07:40:57 +08:00
|
|
|
return NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-06-16 10:01:25 +08:00
|
|
|
* __pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem
|
2008-06-26 06:27:34 +08:00
|
|
|
* @bus: bus this slot is on
|
2005-04-17 06:20:36 +08:00
|
|
|
* @slot: pointer to the &struct hotplug_slot to register
|
2009-06-16 10:01:25 +08:00
|
|
|
* @devnr: device number
|
2008-10-21 07:40:42 +08:00
|
|
|
* @name: name registered with kobject core
|
2009-06-25 00:18:14 +08:00
|
|
|
* @owner: caller module owner
|
|
|
|
* @mod_name: caller module name
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
PCI: hotplug: Demidlayer registration with the core
When a hotplug driver calls pci_hp_register(), all steps necessary for
registration are carried out in one go, including creation of a kobject
and addition to sysfs. That's a problem for pciehp once it's converted
to enable/disable the slot exclusively from the IRQ thread: The thread
needs to be spawned after creation of the kobject (because it uses the
kobject's name), but before addition to sysfs (because it will handle
enable/disable requests submitted via sysfs).
pci_hp_deregister() does offer a ->release callback that's invoked
after deletion from sysfs and before destruction of the kobject. But
because pci_hp_register() doesn't offer a counterpart, hotplug drivers'
->probe and ->remove code becomes asymmetric, which is error prone
as recently discovered use-after-free bugs in pciehp's ->remove hook
have shown.
In a sense, this appears to be a case of the midlayer antipattern:
"The core thesis of the "midlayer mistake" is that midlayers are
bad and should not exist. That common functionality which it is
so tempting to put in a midlayer should instead be provided as
library routines which can [be] used, augmented, or ignored by
each bottom level driver independently. Thus every subsystem
that supports multiple implementations (or drivers) should
provide a very thin top layer which calls directly into the
bottom layer drivers, and a rich library of support code that
eases the implementation of those drivers. This library is
available to, but not forced upon, those drivers."
-- Neil Brown (2009), https://lwn.net/Articles/336262/
The presence of midlayer traits in the PCI hotplug core might be ascribed
to its age: When it was introduced in February 2002, the blessings of a
library approach might not have been well known:
https://git.kernel.org/tglx/history/c/a8a2069f432c
For comparison, the driver core does offer split functions for creating
a kobject (device_initialize()) and addition to sysfs (device_add()) as
an alternative to carrying out everything at once (device_register()).
This was introduced in October 2002:
https://git.kernel.org/tglx/history/c/8b290eb19962
The odd ->release callback in the PCI hotplug core was added in 2003:
https://git.kernel.org/tglx/history/c/69f8d663b595
Clearly, a library approach would not force every hotplug driver to
implement a ->release callback, but rather allow the driver to remove
the sysfs files, release its data structures and finally destroy the
kobject. Alternatively, a driver may choose to remove everything with
pci_hp_deregister(), then release its data structures.
To this end, offer drivers pci_hp_initialize() and pci_hp_add() as a
split-up version of pci_hp_register(). Likewise, offer pci_hp_del()
and pci_hp_destroy() as a split-up version of pci_hp_deregister().
Eliminate the ->release callback and move its code into each driver's
teardown routine.
Declare pci_hp_deregister() void, in keeping with the usual kernel
pattern that enablement can fail, but disablement cannot. It only
returned an error if the caller passed in a NULL pointer or a slot which
has never or is no longer registered or is sharing its name with another
slot. Those would be bugs, so WARN about them. Few hotplug drivers
actually checked the return value and those that did only printed a
useless error message to dmesg. Remove that.
For most drivers the conversion was straightforward since it doesn't
matter whether the code in the ->release callback is executed before or
after destruction of the kobject. But in the case of ibmphp, it was
unclear to me whether setting slot_cur->ctrl and slot_cur->bus_on to
NULL needs to happen before the kobject is destroyed, so I erred on
the side of caution and ensured that the order stays the same. Another
nontrivial case is pnv_php, I've found the list and kref logic difficult
to understand, however my impression was that it is safe to delete the
list element and drop the references until after the kobject is
destroyed.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Andy Shevchenko <andy.shevchenko@gmail.com> # drivers/platform/x86
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Len Brown <lenb@kernel.org>
Cc: Scott Murray <scott@spiteful.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Gavin Shan <gwshan@linux.vnet.ibm.com>
Cc: Sebastian Ott <sebott@linux.vnet.ibm.com>
Cc: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Cc: Corentin Chary <corentin.chary@gmail.com>
Cc: Darren Hart <dvhart@infradead.org>
Cc: Andy Shevchenko <andy@infradead.org>
2018-07-20 06:27:43 +08:00
|
|
|
* Prepares a hotplug slot for in-kernel use and immediately publishes it to
|
|
|
|
* user space in one go. Drivers may alternatively carry out the two steps
|
|
|
|
* separately by invoking pci_hp_initialize() and pci_hp_add().
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* Returns 0 if successful, anything else for an error.
|
|
|
|
*/
|
2009-06-16 10:01:25 +08:00
|
|
|
int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus,
|
|
|
|
int devnr, const char *name,
|
|
|
|
struct module *owner, const char *mod_name)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int result;
|
PCI: hotplug: Demidlayer registration with the core
When a hotplug driver calls pci_hp_register(), all steps necessary for
registration are carried out in one go, including creation of a kobject
and addition to sysfs. That's a problem for pciehp once it's converted
to enable/disable the slot exclusively from the IRQ thread: The thread
needs to be spawned after creation of the kobject (because it uses the
kobject's name), but before addition to sysfs (because it will handle
enable/disable requests submitted via sysfs).
pci_hp_deregister() does offer a ->release callback that's invoked
after deletion from sysfs and before destruction of the kobject. But
because pci_hp_register() doesn't offer a counterpart, hotplug drivers'
->probe and ->remove code becomes asymmetric, which is error prone
as recently discovered use-after-free bugs in pciehp's ->remove hook
have shown.
In a sense, this appears to be a case of the midlayer antipattern:
"The core thesis of the "midlayer mistake" is that midlayers are
bad and should not exist. That common functionality which it is
so tempting to put in a midlayer should instead be provided as
library routines which can [be] used, augmented, or ignored by
each bottom level driver independently. Thus every subsystem
that supports multiple implementations (or drivers) should
provide a very thin top layer which calls directly into the
bottom layer drivers, and a rich library of support code that
eases the implementation of those drivers. This library is
available to, but not forced upon, those drivers."
-- Neil Brown (2009), https://lwn.net/Articles/336262/
The presence of midlayer traits in the PCI hotplug core might be ascribed
to its age: When it was introduced in February 2002, the blessings of a
library approach might not have been well known:
https://git.kernel.org/tglx/history/c/a8a2069f432c
For comparison, the driver core does offer split functions for creating
a kobject (device_initialize()) and addition to sysfs (device_add()) as
an alternative to carrying out everything at once (device_register()).
This was introduced in October 2002:
https://git.kernel.org/tglx/history/c/8b290eb19962
The odd ->release callback in the PCI hotplug core was added in 2003:
https://git.kernel.org/tglx/history/c/69f8d663b595
Clearly, a library approach would not force every hotplug driver to
implement a ->release callback, but rather allow the driver to remove
the sysfs files, release its data structures and finally destroy the
kobject. Alternatively, a driver may choose to remove everything with
pci_hp_deregister(), then release its data structures.
To this end, offer drivers pci_hp_initialize() and pci_hp_add() as a
split-up version of pci_hp_register(). Likewise, offer pci_hp_del()
and pci_hp_destroy() as a split-up version of pci_hp_deregister().
Eliminate the ->release callback and move its code into each driver's
teardown routine.
Declare pci_hp_deregister() void, in keeping with the usual kernel
pattern that enablement can fail, but disablement cannot. It only
returned an error if the caller passed in a NULL pointer or a slot which
has never or is no longer registered or is sharing its name with another
slot. Those would be bugs, so WARN about them. Few hotplug drivers
actually checked the return value and those that did only printed a
useless error message to dmesg. Remove that.
For most drivers the conversion was straightforward since it doesn't
matter whether the code in the ->release callback is executed before or
after destruction of the kobject. But in the case of ibmphp, it was
unclear to me whether setting slot_cur->ctrl and slot_cur->bus_on to
NULL needs to happen before the kobject is destroyed, so I erred on
the side of caution and ensured that the order stays the same. Another
nontrivial case is pnv_php, I've found the list and kref logic difficult
to understand, however my impression was that it is safe to delete the
list element and drop the references until after the kobject is
destroyed.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Andy Shevchenko <andy.shevchenko@gmail.com> # drivers/platform/x86
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Len Brown <lenb@kernel.org>
Cc: Scott Murray <scott@spiteful.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Gavin Shan <gwshan@linux.vnet.ibm.com>
Cc: Sebastian Ott <sebott@linux.vnet.ibm.com>
Cc: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Cc: Corentin Chary <corentin.chary@gmail.com>
Cc: Darren Hart <dvhart@infradead.org>
Cc: Andy Shevchenko <andy@infradead.org>
2018-07-20 06:27:43 +08:00
|
|
|
|
|
|
|
result = __pci_hp_initialize(slot, bus, devnr, name, owner, mod_name);
|
|
|
|
if (result)
|
|
|
|
return result;
|
|
|
|
|
|
|
|
result = pci_hp_add(slot);
|
|
|
|
if (result)
|
|
|
|
pci_hp_destroy(slot);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(__pci_hp_register);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* __pci_hp_initialize - prepare hotplug slot for in-kernel use
|
|
|
|
* @slot: pointer to the &struct hotplug_slot to initialize
|
|
|
|
* @bus: bus this slot is on
|
|
|
|
* @devnr: slot number
|
|
|
|
* @name: name registered with kobject core
|
|
|
|
* @owner: caller module owner
|
|
|
|
* @mod_name: caller module name
|
|
|
|
*
|
|
|
|
* Allocate and fill in a PCI slot for use by a hotplug driver. Once this has
|
|
|
|
* been called, the driver may invoke hotplug_slot_name() to get the slot's
|
|
|
|
* unique name. The driver must be prepared to handle a ->reset_slot callback
|
|
|
|
* from this point on.
|
|
|
|
*
|
|
|
|
* Returns 0 on success or a negative int on error.
|
|
|
|
*/
|
|
|
|
int __pci_hp_initialize(struct hotplug_slot *slot, struct pci_bus *bus,
|
|
|
|
int devnr, const char *name, struct module *owner,
|
|
|
|
const char *mod_name)
|
|
|
|
{
|
2008-06-11 05:28:50 +08:00
|
|
|
struct pci_slot *pci_slot;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (slot == NULL)
|
|
|
|
return -ENODEV;
|
PCI: hotplug: Drop hotplug_slot_info
Ever since the PCI hotplug core was introduced in 2002, drivers had to
allocate and register a struct hotplug_slot_info for every slot:
https://git.kernel.org/tglx/history/c/a8a2069f432c
Apparently the idea was that drivers furnish the hotplug core with an
up-to-date card presence status, power status, latch status and
attention indicator status as well as notify the hotplug core of changes
thereof. However only 4 out of 12 hotplug drivers bother to notify the
hotplug core with pci_hp_change_slot_info() and the hotplug core never
made any use of the information: There is just a single macro in
pci_hotplug_core.c, GET_STATUS(), which uses the hotplug_slot_info if
the driver lacks the corresponding callback in hotplug_slot_ops. The
macro is called when the user reads the attribute via sysfs.
Now, if the callback isn't defined, the attribute isn't exposed in sysfs
in the first place (see e.g. has_power_file()). There are only two
situations when the hotplug_slot_info would actually be accessed:
* If the driver defines ->enable_slot or ->disable_slot but not
->get_power_status.
* If the driver defines ->set_attention_status but not
->get_attention_status.
There is no driver doing the former and just a single driver doing the
latter, namely pnv_php.c. Amend it with a ->get_attention_status
callback. With that, the hotplug_slot_info becomes completely unused by
the PCI hotplug core. But a few drivers use it internally as a cache:
cpcihp uses it to cache the latch_status and adapter_status.
cpqhp uses it to cache the adapter_status.
pnv_php and rpaphp use it to cache the attention_status.
shpchp uses it to cache all four values.
Amend these drivers to cache the information in their private slot
struct. shpchp's slot struct already contains members to cache the
power_status and adapter_status, so additional members are only needed
for the other two values. In the case of cpqphp, the cached value is
only accessed in a single place, so instead of caching it, read the
current value from the hardware.
Caution: acpiphp, cpci, cpqhp, shpchp, asus-wmi and eeepc-laptop
populate the hotplug_slot_info with initial values on probe. That code
is herewith removed. There is a theoretical chance that the code has
side effects without which the driver fails to function, e.g. if the
ACPI method to read the adapter status needs to be executed at least
once on probe. That seems unlikely to me, still maintainers should
review the changes carefully for this possibility.
Rafael adds: "I'm not aware of any case in which it will break anything,
[...] but if that happens, it may be necessary to add the execution of
the control methods in question directly to the initialization part."
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Tyrel Datwyler <tyreld@linux.vnet.ibm.com> # drivers/pci/hotplug/rpa*
Acked-by: Sebastian Ott <sebott@linux.ibm.com> # drivers/pci/hotplug/s390*
Acked-by: Andy Shevchenko <andy.shevchenko@gmail.com> # drivers/platform/x86
Cc: Len Brown <lenb@kernel.org>
Cc: Scott Murray <scott@spiteful.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Oliver OHalloran <oliveroh@au1.ibm.com>
Cc: Gavin Shan <gwshan@linux.vnet.ibm.com>
Cc: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Cc: Corentin Chary <corentin.chary@gmail.com>
Cc: Darren Hart <dvhart@infradead.org>
2018-09-08 15:59:01 +08:00
|
|
|
if (slot->ops == NULL)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2018-09-08 15:59:01 +08:00
|
|
|
slot->owner = owner;
|
|
|
|
slot->mod_name = mod_name;
|
2008-10-21 07:40:57 +08:00
|
|
|
|
2008-06-11 05:30:42 +08:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2009-06-16 10:01:25 +08:00
|
|
|
pci_slot = pci_create_slot(bus, devnr, name, slot);
|
PCI: hotplug: Demidlayer registration with the core
When a hotplug driver calls pci_hp_register(), all steps necessary for
registration are carried out in one go, including creation of a kobject
and addition to sysfs. That's a problem for pciehp once it's converted
to enable/disable the slot exclusively from the IRQ thread: The thread
needs to be spawned after creation of the kobject (because it uses the
kobject's name), but before addition to sysfs (because it will handle
enable/disable requests submitted via sysfs).
pci_hp_deregister() does offer a ->release callback that's invoked
after deletion from sysfs and before destruction of the kobject. But
because pci_hp_register() doesn't offer a counterpart, hotplug drivers'
->probe and ->remove code becomes asymmetric, which is error prone
as recently discovered use-after-free bugs in pciehp's ->remove hook
have shown.
In a sense, this appears to be a case of the midlayer antipattern:
"The core thesis of the "midlayer mistake" is that midlayers are
bad and should not exist. That common functionality which it is
so tempting to put in a midlayer should instead be provided as
library routines which can [be] used, augmented, or ignored by
each bottom level driver independently. Thus every subsystem
that supports multiple implementations (or drivers) should
provide a very thin top layer which calls directly into the
bottom layer drivers, and a rich library of support code that
eases the implementation of those drivers. This library is
available to, but not forced upon, those drivers."
-- Neil Brown (2009), https://lwn.net/Articles/336262/
The presence of midlayer traits in the PCI hotplug core might be ascribed
to its age: When it was introduced in February 2002, the blessings of a
library approach might not have been well known:
https://git.kernel.org/tglx/history/c/a8a2069f432c
For comparison, the driver core does offer split functions for creating
a kobject (device_initialize()) and addition to sysfs (device_add()) as
an alternative to carrying out everything at once (device_register()).
This was introduced in October 2002:
https://git.kernel.org/tglx/history/c/8b290eb19962
The odd ->release callback in the PCI hotplug core was added in 2003:
https://git.kernel.org/tglx/history/c/69f8d663b595
Clearly, a library approach would not force every hotplug driver to
implement a ->release callback, but rather allow the driver to remove
the sysfs files, release its data structures and finally destroy the
kobject. Alternatively, a driver may choose to remove everything with
pci_hp_deregister(), then release its data structures.
To this end, offer drivers pci_hp_initialize() and pci_hp_add() as a
split-up version of pci_hp_register(). Likewise, offer pci_hp_del()
and pci_hp_destroy() as a split-up version of pci_hp_deregister().
Eliminate the ->release callback and move its code into each driver's
teardown routine.
Declare pci_hp_deregister() void, in keeping with the usual kernel
pattern that enablement can fail, but disablement cannot. It only
returned an error if the caller passed in a NULL pointer or a slot which
has never or is no longer registered or is sharing its name with another
slot. Those would be bugs, so WARN about them. Few hotplug drivers
actually checked the return value and those that did only printed a
useless error message to dmesg. Remove that.
For most drivers the conversion was straightforward since it doesn't
matter whether the code in the ->release callback is executed before or
after destruction of the kobject. But in the case of ibmphp, it was
unclear to me whether setting slot_cur->ctrl and slot_cur->bus_on to
NULL needs to happen before the kobject is destroyed, so I erred on
the side of caution and ensured that the order stays the same. Another
nontrivial case is pnv_php, I've found the list and kref logic difficult
to understand, however my impression was that it is safe to delete the
list element and drop the references until after the kobject is
destroyed.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Andy Shevchenko <andy.shevchenko@gmail.com> # drivers/platform/x86
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Len Brown <lenb@kernel.org>
Cc: Scott Murray <scott@spiteful.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Gavin Shan <gwshan@linux.vnet.ibm.com>
Cc: Sebastian Ott <sebott@linux.vnet.ibm.com>
Cc: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Cc: Corentin Chary <corentin.chary@gmail.com>
Cc: Darren Hart <dvhart@infradead.org>
Cc: Andy Shevchenko <andy@infradead.org>
2018-07-20 06:27:43 +08:00
|
|
|
if (IS_ERR(pci_slot))
|
|
|
|
return PTR_ERR(pci_slot);
|
2007-12-18 03:54:39 +08:00
|
|
|
|
2008-06-11 05:28:50 +08:00
|
|
|
slot->pci_slot = pci_slot;
|
|
|
|
pci_slot->hotplug = slot;
|
PCI: hotplug: Demidlayer registration with the core
When a hotplug driver calls pci_hp_register(), all steps necessary for
registration are carried out in one go, including creation of a kobject
and addition to sysfs. That's a problem for pciehp once it's converted
to enable/disable the slot exclusively from the IRQ thread: The thread
needs to be spawned after creation of the kobject (because it uses the
kobject's name), but before addition to sysfs (because it will handle
enable/disable requests submitted via sysfs).
pci_hp_deregister() does offer a ->release callback that's invoked
after deletion from sysfs and before destruction of the kobject. But
because pci_hp_register() doesn't offer a counterpart, hotplug drivers'
->probe and ->remove code becomes asymmetric, which is error prone
as recently discovered use-after-free bugs in pciehp's ->remove hook
have shown.
In a sense, this appears to be a case of the midlayer antipattern:
"The core thesis of the "midlayer mistake" is that midlayers are
bad and should not exist. That common functionality which it is
so tempting to put in a midlayer should instead be provided as
library routines which can [be] used, augmented, or ignored by
each bottom level driver independently. Thus every subsystem
that supports multiple implementations (or drivers) should
provide a very thin top layer which calls directly into the
bottom layer drivers, and a rich library of support code that
eases the implementation of those drivers. This library is
available to, but not forced upon, those drivers."
-- Neil Brown (2009), https://lwn.net/Articles/336262/
The presence of midlayer traits in the PCI hotplug core might be ascribed
to its age: When it was introduced in February 2002, the blessings of a
library approach might not have been well known:
https://git.kernel.org/tglx/history/c/a8a2069f432c
For comparison, the driver core does offer split functions for creating
a kobject (device_initialize()) and addition to sysfs (device_add()) as
an alternative to carrying out everything at once (device_register()).
This was introduced in October 2002:
https://git.kernel.org/tglx/history/c/8b290eb19962
The odd ->release callback in the PCI hotplug core was added in 2003:
https://git.kernel.org/tglx/history/c/69f8d663b595
Clearly, a library approach would not force every hotplug driver to
implement a ->release callback, but rather allow the driver to remove
the sysfs files, release its data structures and finally destroy the
kobject. Alternatively, a driver may choose to remove everything with
pci_hp_deregister(), then release its data structures.
To this end, offer drivers pci_hp_initialize() and pci_hp_add() as a
split-up version of pci_hp_register(). Likewise, offer pci_hp_del()
and pci_hp_destroy() as a split-up version of pci_hp_deregister().
Eliminate the ->release callback and move its code into each driver's
teardown routine.
Declare pci_hp_deregister() void, in keeping with the usual kernel
pattern that enablement can fail, but disablement cannot. It only
returned an error if the caller passed in a NULL pointer or a slot which
has never or is no longer registered or is sharing its name with another
slot. Those would be bugs, so WARN about them. Few hotplug drivers
actually checked the return value and those that did only printed a
useless error message to dmesg. Remove that.
For most drivers the conversion was straightforward since it doesn't
matter whether the code in the ->release callback is executed before or
after destruction of the kobject. But in the case of ibmphp, it was
unclear to me whether setting slot_cur->ctrl and slot_cur->bus_on to
NULL needs to happen before the kobject is destroyed, so I erred on
the side of caution and ensured that the order stays the same. Another
nontrivial case is pnv_php, I've found the list and kref logic difficult
to understand, however my impression was that it is safe to delete the
list element and drop the references until after the kobject is
destroyed.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Andy Shevchenko <andy.shevchenko@gmail.com> # drivers/platform/x86
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Len Brown <lenb@kernel.org>
Cc: Scott Murray <scott@spiteful.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Gavin Shan <gwshan@linux.vnet.ibm.com>
Cc: Sebastian Ott <sebott@linux.vnet.ibm.com>
Cc: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Cc: Corentin Chary <corentin.chary@gmail.com>
Cc: Darren Hart <dvhart@infradead.org>
Cc: Andy Shevchenko <andy@infradead.org>
2018-07-20 06:27:43 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(__pci_hp_initialize);
|
2008-06-11 05:28:50 +08:00
|
|
|
|
PCI: hotplug: Demidlayer registration with the core
When a hotplug driver calls pci_hp_register(), all steps necessary for
registration are carried out in one go, including creation of a kobject
and addition to sysfs. That's a problem for pciehp once it's converted
to enable/disable the slot exclusively from the IRQ thread: The thread
needs to be spawned after creation of the kobject (because it uses the
kobject's name), but before addition to sysfs (because it will handle
enable/disable requests submitted via sysfs).
pci_hp_deregister() does offer a ->release callback that's invoked
after deletion from sysfs and before destruction of the kobject. But
because pci_hp_register() doesn't offer a counterpart, hotplug drivers'
->probe and ->remove code becomes asymmetric, which is error prone
as recently discovered use-after-free bugs in pciehp's ->remove hook
have shown.
In a sense, this appears to be a case of the midlayer antipattern:
"The core thesis of the "midlayer mistake" is that midlayers are
bad and should not exist. That common functionality which it is
so tempting to put in a midlayer should instead be provided as
library routines which can [be] used, augmented, or ignored by
each bottom level driver independently. Thus every subsystem
that supports multiple implementations (or drivers) should
provide a very thin top layer which calls directly into the
bottom layer drivers, and a rich library of support code that
eases the implementation of those drivers. This library is
available to, but not forced upon, those drivers."
-- Neil Brown (2009), https://lwn.net/Articles/336262/
The presence of midlayer traits in the PCI hotplug core might be ascribed
to its age: When it was introduced in February 2002, the blessings of a
library approach might not have been well known:
https://git.kernel.org/tglx/history/c/a8a2069f432c
For comparison, the driver core does offer split functions for creating
a kobject (device_initialize()) and addition to sysfs (device_add()) as
an alternative to carrying out everything at once (device_register()).
This was introduced in October 2002:
https://git.kernel.org/tglx/history/c/8b290eb19962
The odd ->release callback in the PCI hotplug core was added in 2003:
https://git.kernel.org/tglx/history/c/69f8d663b595
Clearly, a library approach would not force every hotplug driver to
implement a ->release callback, but rather allow the driver to remove
the sysfs files, release its data structures and finally destroy the
kobject. Alternatively, a driver may choose to remove everything with
pci_hp_deregister(), then release its data structures.
To this end, offer drivers pci_hp_initialize() and pci_hp_add() as a
split-up version of pci_hp_register(). Likewise, offer pci_hp_del()
and pci_hp_destroy() as a split-up version of pci_hp_deregister().
Eliminate the ->release callback and move its code into each driver's
teardown routine.
Declare pci_hp_deregister() void, in keeping with the usual kernel
pattern that enablement can fail, but disablement cannot. It only
returned an error if the caller passed in a NULL pointer or a slot which
has never or is no longer registered or is sharing its name with another
slot. Those would be bugs, so WARN about them. Few hotplug drivers
actually checked the return value and those that did only printed a
useless error message to dmesg. Remove that.
For most drivers the conversion was straightforward since it doesn't
matter whether the code in the ->release callback is executed before or
after destruction of the kobject. But in the case of ibmphp, it was
unclear to me whether setting slot_cur->ctrl and slot_cur->bus_on to
NULL needs to happen before the kobject is destroyed, so I erred on
the side of caution and ensured that the order stays the same. Another
nontrivial case is pnv_php, I've found the list and kref logic difficult
to understand, however my impression was that it is safe to delete the
list element and drop the references until after the kobject is
destroyed.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Andy Shevchenko <andy.shevchenko@gmail.com> # drivers/platform/x86
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Len Brown <lenb@kernel.org>
Cc: Scott Murray <scott@spiteful.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Gavin Shan <gwshan@linux.vnet.ibm.com>
Cc: Sebastian Ott <sebott@linux.vnet.ibm.com>
Cc: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Cc: Corentin Chary <corentin.chary@gmail.com>
Cc: Darren Hart <dvhart@infradead.org>
Cc: Andy Shevchenko <andy@infradead.org>
2018-07-20 06:27:43 +08:00
|
|
|
/**
|
|
|
|
* pci_hp_add - publish hotplug slot to user space
|
|
|
|
* @slot: pointer to the &struct hotplug_slot to publish
|
|
|
|
*
|
|
|
|
* Make a hotplug slot's sysfs interface available and inform user space of its
|
|
|
|
* addition by sending a uevent. The hotplug driver must be prepared to handle
|
|
|
|
* all &struct hotplug_slot_ops callbacks from this point on.
|
|
|
|
*
|
|
|
|
* Returns 0 on success or a negative int on error.
|
|
|
|
*/
|
|
|
|
int pci_hp_add(struct hotplug_slot *slot)
|
|
|
|
{
|
|
|
|
struct pci_slot *pci_slot = slot->pci_slot;
|
|
|
|
int result;
|
2008-06-11 05:28:50 +08:00
|
|
|
|
|
|
|
result = fs_add_slot(pci_slot);
|
2018-07-20 06:27:31 +08:00
|
|
|
if (result)
|
PCI: hotplug: Demidlayer registration with the core
When a hotplug driver calls pci_hp_register(), all steps necessary for
registration are carried out in one go, including creation of a kobject
and addition to sysfs. That's a problem for pciehp once it's converted
to enable/disable the slot exclusively from the IRQ thread: The thread
needs to be spawned after creation of the kobject (because it uses the
kobject's name), but before addition to sysfs (because it will handle
enable/disable requests submitted via sysfs).
pci_hp_deregister() does offer a ->release callback that's invoked
after deletion from sysfs and before destruction of the kobject. But
because pci_hp_register() doesn't offer a counterpart, hotplug drivers'
->probe and ->remove code becomes asymmetric, which is error prone
as recently discovered use-after-free bugs in pciehp's ->remove hook
have shown.
In a sense, this appears to be a case of the midlayer antipattern:
"The core thesis of the "midlayer mistake" is that midlayers are
bad and should not exist. That common functionality which it is
so tempting to put in a midlayer should instead be provided as
library routines which can [be] used, augmented, or ignored by
each bottom level driver independently. Thus every subsystem
that supports multiple implementations (or drivers) should
provide a very thin top layer which calls directly into the
bottom layer drivers, and a rich library of support code that
eases the implementation of those drivers. This library is
available to, but not forced upon, those drivers."
-- Neil Brown (2009), https://lwn.net/Articles/336262/
The presence of midlayer traits in the PCI hotplug core might be ascribed
to its age: When it was introduced in February 2002, the blessings of a
library approach might not have been well known:
https://git.kernel.org/tglx/history/c/a8a2069f432c
For comparison, the driver core does offer split functions for creating
a kobject (device_initialize()) and addition to sysfs (device_add()) as
an alternative to carrying out everything at once (device_register()).
This was introduced in October 2002:
https://git.kernel.org/tglx/history/c/8b290eb19962
The odd ->release callback in the PCI hotplug core was added in 2003:
https://git.kernel.org/tglx/history/c/69f8d663b595
Clearly, a library approach would not force every hotplug driver to
implement a ->release callback, but rather allow the driver to remove
the sysfs files, release its data structures and finally destroy the
kobject. Alternatively, a driver may choose to remove everything with
pci_hp_deregister(), then release its data structures.
To this end, offer drivers pci_hp_initialize() and pci_hp_add() as a
split-up version of pci_hp_register(). Likewise, offer pci_hp_del()
and pci_hp_destroy() as a split-up version of pci_hp_deregister().
Eliminate the ->release callback and move its code into each driver's
teardown routine.
Declare pci_hp_deregister() void, in keeping with the usual kernel
pattern that enablement can fail, but disablement cannot. It only
returned an error if the caller passed in a NULL pointer or a slot which
has never or is no longer registered or is sharing its name with another
slot. Those would be bugs, so WARN about them. Few hotplug drivers
actually checked the return value and those that did only printed a
useless error message to dmesg. Remove that.
For most drivers the conversion was straightforward since it doesn't
matter whether the code in the ->release callback is executed before or
after destruction of the kobject. But in the case of ibmphp, it was
unclear to me whether setting slot_cur->ctrl and slot_cur->bus_on to
NULL needs to happen before the kobject is destroyed, so I erred on
the side of caution and ensured that the order stays the same. Another
nontrivial case is pnv_php, I've found the list and kref logic difficult
to understand, however my impression was that it is safe to delete the
list element and drop the references until after the kobject is
destroyed.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Andy Shevchenko <andy.shevchenko@gmail.com> # drivers/platform/x86
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Len Brown <lenb@kernel.org>
Cc: Scott Murray <scott@spiteful.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Gavin Shan <gwshan@linux.vnet.ibm.com>
Cc: Sebastian Ott <sebott@linux.vnet.ibm.com>
Cc: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Cc: Corentin Chary <corentin.chary@gmail.com>
Cc: Darren Hart <dvhart@infradead.org>
Cc: Andy Shevchenko <andy@infradead.org>
2018-07-20 06:27:43 +08:00
|
|
|
return result;
|
2018-07-20 06:27:31 +08:00
|
|
|
|
2008-06-11 05:28:50 +08:00
|
|
|
kobject_uevent(&pci_slot->kobj, KOBJ_ADD);
|
PCI: hotplug: Demidlayer registration with the core
When a hotplug driver calls pci_hp_register(), all steps necessary for
registration are carried out in one go, including creation of a kobject
and addition to sysfs. That's a problem for pciehp once it's converted
to enable/disable the slot exclusively from the IRQ thread: The thread
needs to be spawned after creation of the kobject (because it uses the
kobject's name), but before addition to sysfs (because it will handle
enable/disable requests submitted via sysfs).
pci_hp_deregister() does offer a ->release callback that's invoked
after deletion from sysfs and before destruction of the kobject. But
because pci_hp_register() doesn't offer a counterpart, hotplug drivers'
->probe and ->remove code becomes asymmetric, which is error prone
as recently discovered use-after-free bugs in pciehp's ->remove hook
have shown.
In a sense, this appears to be a case of the midlayer antipattern:
"The core thesis of the "midlayer mistake" is that midlayers are
bad and should not exist. That common functionality which it is
so tempting to put in a midlayer should instead be provided as
library routines which can [be] used, augmented, or ignored by
each bottom level driver independently. Thus every subsystem
that supports multiple implementations (or drivers) should
provide a very thin top layer which calls directly into the
bottom layer drivers, and a rich library of support code that
eases the implementation of those drivers. This library is
available to, but not forced upon, those drivers."
-- Neil Brown (2009), https://lwn.net/Articles/336262/
The presence of midlayer traits in the PCI hotplug core might be ascribed
to its age: When it was introduced in February 2002, the blessings of a
library approach might not have been well known:
https://git.kernel.org/tglx/history/c/a8a2069f432c
For comparison, the driver core does offer split functions for creating
a kobject (device_initialize()) and addition to sysfs (device_add()) as
an alternative to carrying out everything at once (device_register()).
This was introduced in October 2002:
https://git.kernel.org/tglx/history/c/8b290eb19962
The odd ->release callback in the PCI hotplug core was added in 2003:
https://git.kernel.org/tglx/history/c/69f8d663b595
Clearly, a library approach would not force every hotplug driver to
implement a ->release callback, but rather allow the driver to remove
the sysfs files, release its data structures and finally destroy the
kobject. Alternatively, a driver may choose to remove everything with
pci_hp_deregister(), then release its data structures.
To this end, offer drivers pci_hp_initialize() and pci_hp_add() as a
split-up version of pci_hp_register(). Likewise, offer pci_hp_del()
and pci_hp_destroy() as a split-up version of pci_hp_deregister().
Eliminate the ->release callback and move its code into each driver's
teardown routine.
Declare pci_hp_deregister() void, in keeping with the usual kernel
pattern that enablement can fail, but disablement cannot. It only
returned an error if the caller passed in a NULL pointer or a slot which
has never or is no longer registered or is sharing its name with another
slot. Those would be bugs, so WARN about them. Few hotplug drivers
actually checked the return value and those that did only printed a
useless error message to dmesg. Remove that.
For most drivers the conversion was straightforward since it doesn't
matter whether the code in the ->release callback is executed before or
after destruction of the kobject. But in the case of ibmphp, it was
unclear to me whether setting slot_cur->ctrl and slot_cur->bus_on to
NULL needs to happen before the kobject is destroyed, so I erred on
the side of caution and ensured that the order stays the same. Another
nontrivial case is pnv_php, I've found the list and kref logic difficult
to understand, however my impression was that it is safe to delete the
list element and drop the references until after the kobject is
destroyed.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Andy Shevchenko <andy.shevchenko@gmail.com> # drivers/platform/x86
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Len Brown <lenb@kernel.org>
Cc: Scott Murray <scott@spiteful.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Gavin Shan <gwshan@linux.vnet.ibm.com>
Cc: Sebastian Ott <sebott@linux.vnet.ibm.com>
Cc: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Cc: Corentin Chary <corentin.chary@gmail.com>
Cc: Darren Hart <dvhart@infradead.org>
Cc: Andy Shevchenko <andy@infradead.org>
2018-07-20 06:27:43 +08:00
|
|
|
mutex_lock(&pci_hp_mutex);
|
|
|
|
list_add(&slot->slot_list, &pci_hotplug_slot_list);
|
2008-10-21 07:40:57 +08:00
|
|
|
mutex_unlock(&pci_hp_mutex);
|
PCI: hotplug: Demidlayer registration with the core
When a hotplug driver calls pci_hp_register(), all steps necessary for
registration are carried out in one go, including creation of a kobject
and addition to sysfs. That's a problem for pciehp once it's converted
to enable/disable the slot exclusively from the IRQ thread: The thread
needs to be spawned after creation of the kobject (because it uses the
kobject's name), but before addition to sysfs (because it will handle
enable/disable requests submitted via sysfs).
pci_hp_deregister() does offer a ->release callback that's invoked
after deletion from sysfs and before destruction of the kobject. But
because pci_hp_register() doesn't offer a counterpart, hotplug drivers'
->probe and ->remove code becomes asymmetric, which is error prone
as recently discovered use-after-free bugs in pciehp's ->remove hook
have shown.
In a sense, this appears to be a case of the midlayer antipattern:
"The core thesis of the "midlayer mistake" is that midlayers are
bad and should not exist. That common functionality which it is
so tempting to put in a midlayer should instead be provided as
library routines which can [be] used, augmented, or ignored by
each bottom level driver independently. Thus every subsystem
that supports multiple implementations (or drivers) should
provide a very thin top layer which calls directly into the
bottom layer drivers, and a rich library of support code that
eases the implementation of those drivers. This library is
available to, but not forced upon, those drivers."
-- Neil Brown (2009), https://lwn.net/Articles/336262/
The presence of midlayer traits in the PCI hotplug core might be ascribed
to its age: When it was introduced in February 2002, the blessings of a
library approach might not have been well known:
https://git.kernel.org/tglx/history/c/a8a2069f432c
For comparison, the driver core does offer split functions for creating
a kobject (device_initialize()) and addition to sysfs (device_add()) as
an alternative to carrying out everything at once (device_register()).
This was introduced in October 2002:
https://git.kernel.org/tglx/history/c/8b290eb19962
The odd ->release callback in the PCI hotplug core was added in 2003:
https://git.kernel.org/tglx/history/c/69f8d663b595
Clearly, a library approach would not force every hotplug driver to
implement a ->release callback, but rather allow the driver to remove
the sysfs files, release its data structures and finally destroy the
kobject. Alternatively, a driver may choose to remove everything with
pci_hp_deregister(), then release its data structures.
To this end, offer drivers pci_hp_initialize() and pci_hp_add() as a
split-up version of pci_hp_register(). Likewise, offer pci_hp_del()
and pci_hp_destroy() as a split-up version of pci_hp_deregister().
Eliminate the ->release callback and move its code into each driver's
teardown routine.
Declare pci_hp_deregister() void, in keeping with the usual kernel
pattern that enablement can fail, but disablement cannot. It only
returned an error if the caller passed in a NULL pointer or a slot which
has never or is no longer registered or is sharing its name with another
slot. Those would be bugs, so WARN about them. Few hotplug drivers
actually checked the return value and those that did only printed a
useless error message to dmesg. Remove that.
For most drivers the conversion was straightforward since it doesn't
matter whether the code in the ->release callback is executed before or
after destruction of the kobject. But in the case of ibmphp, it was
unclear to me whether setting slot_cur->ctrl and slot_cur->bus_on to
NULL needs to happen before the kobject is destroyed, so I erred on
the side of caution and ensured that the order stays the same. Another
nontrivial case is pnv_php, I've found the list and kref logic difficult
to understand, however my impression was that it is safe to delete the
list element and drop the references until after the kobject is
destroyed.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Andy Shevchenko <andy.shevchenko@gmail.com> # drivers/platform/x86
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Len Brown <lenb@kernel.org>
Cc: Scott Murray <scott@spiteful.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Gavin Shan <gwshan@linux.vnet.ibm.com>
Cc: Sebastian Ott <sebott@linux.vnet.ibm.com>
Cc: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Cc: Corentin Chary <corentin.chary@gmail.com>
Cc: Darren Hart <dvhart@infradead.org>
Cc: Andy Shevchenko <andy@infradead.org>
2018-07-20 06:27:43 +08:00
|
|
|
dbg("Added slot %s to the list\n", hotplug_slot_name(slot));
|
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
PCI: hotplug: Demidlayer registration with the core
When a hotplug driver calls pci_hp_register(), all steps necessary for
registration are carried out in one go, including creation of a kobject
and addition to sysfs. That's a problem for pciehp once it's converted
to enable/disable the slot exclusively from the IRQ thread: The thread
needs to be spawned after creation of the kobject (because it uses the
kobject's name), but before addition to sysfs (because it will handle
enable/disable requests submitted via sysfs).
pci_hp_deregister() does offer a ->release callback that's invoked
after deletion from sysfs and before destruction of the kobject. But
because pci_hp_register() doesn't offer a counterpart, hotplug drivers'
->probe and ->remove code becomes asymmetric, which is error prone
as recently discovered use-after-free bugs in pciehp's ->remove hook
have shown.
In a sense, this appears to be a case of the midlayer antipattern:
"The core thesis of the "midlayer mistake" is that midlayers are
bad and should not exist. That common functionality which it is
so tempting to put in a midlayer should instead be provided as
library routines which can [be] used, augmented, or ignored by
each bottom level driver independently. Thus every subsystem
that supports multiple implementations (or drivers) should
provide a very thin top layer which calls directly into the
bottom layer drivers, and a rich library of support code that
eases the implementation of those drivers. This library is
available to, but not forced upon, those drivers."
-- Neil Brown (2009), https://lwn.net/Articles/336262/
The presence of midlayer traits in the PCI hotplug core might be ascribed
to its age: When it was introduced in February 2002, the blessings of a
library approach might not have been well known:
https://git.kernel.org/tglx/history/c/a8a2069f432c
For comparison, the driver core does offer split functions for creating
a kobject (device_initialize()) and addition to sysfs (device_add()) as
an alternative to carrying out everything at once (device_register()).
This was introduced in October 2002:
https://git.kernel.org/tglx/history/c/8b290eb19962
The odd ->release callback in the PCI hotplug core was added in 2003:
https://git.kernel.org/tglx/history/c/69f8d663b595
Clearly, a library approach would not force every hotplug driver to
implement a ->release callback, but rather allow the driver to remove
the sysfs files, release its data structures and finally destroy the
kobject. Alternatively, a driver may choose to remove everything with
pci_hp_deregister(), then release its data structures.
To this end, offer drivers pci_hp_initialize() and pci_hp_add() as a
split-up version of pci_hp_register(). Likewise, offer pci_hp_del()
and pci_hp_destroy() as a split-up version of pci_hp_deregister().
Eliminate the ->release callback and move its code into each driver's
teardown routine.
Declare pci_hp_deregister() void, in keeping with the usual kernel
pattern that enablement can fail, but disablement cannot. It only
returned an error if the caller passed in a NULL pointer or a slot which
has never or is no longer registered or is sharing its name with another
slot. Those would be bugs, so WARN about them. Few hotplug drivers
actually checked the return value and those that did only printed a
useless error message to dmesg. Remove that.
For most drivers the conversion was straightforward since it doesn't
matter whether the code in the ->release callback is executed before or
after destruction of the kobject. But in the case of ibmphp, it was
unclear to me whether setting slot_cur->ctrl and slot_cur->bus_on to
NULL needs to happen before the kobject is destroyed, so I erred on
the side of caution and ensured that the order stays the same. Another
nontrivial case is pnv_php, I've found the list and kref logic difficult
to understand, however my impression was that it is safe to delete the
list element and drop the references until after the kobject is
destroyed.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Andy Shevchenko <andy.shevchenko@gmail.com> # drivers/platform/x86
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Len Brown <lenb@kernel.org>
Cc: Scott Murray <scott@spiteful.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Gavin Shan <gwshan@linux.vnet.ibm.com>
Cc: Sebastian Ott <sebott@linux.vnet.ibm.com>
Cc: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Cc: Corentin Chary <corentin.chary@gmail.com>
Cc: Darren Hart <dvhart@infradead.org>
Cc: Andy Shevchenko <andy@infradead.org>
2018-07-20 06:27:43 +08:00
|
|
|
EXPORT_SYMBOL_GPL(pci_hp_add);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem
|
2015-06-19 15:57:44 +08:00
|
|
|
* @slot: pointer to the &struct hotplug_slot to deregister
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* The @slot must have been registered with the pci hotplug subsystem
|
|
|
|
* previously with a call to pci_hp_register().
|
|
|
|
*
|
|
|
|
* Returns 0 if successful, anything else for an error.
|
|
|
|
*/
|
PCI: hotplug: Demidlayer registration with the core
When a hotplug driver calls pci_hp_register(), all steps necessary for
registration are carried out in one go, including creation of a kobject
and addition to sysfs. That's a problem for pciehp once it's converted
to enable/disable the slot exclusively from the IRQ thread: The thread
needs to be spawned after creation of the kobject (because it uses the
kobject's name), but before addition to sysfs (because it will handle
enable/disable requests submitted via sysfs).
pci_hp_deregister() does offer a ->release callback that's invoked
after deletion from sysfs and before destruction of the kobject. But
because pci_hp_register() doesn't offer a counterpart, hotplug drivers'
->probe and ->remove code becomes asymmetric, which is error prone
as recently discovered use-after-free bugs in pciehp's ->remove hook
have shown.
In a sense, this appears to be a case of the midlayer antipattern:
"The core thesis of the "midlayer mistake" is that midlayers are
bad and should not exist. That common functionality which it is
so tempting to put in a midlayer should instead be provided as
library routines which can [be] used, augmented, or ignored by
each bottom level driver independently. Thus every subsystem
that supports multiple implementations (or drivers) should
provide a very thin top layer which calls directly into the
bottom layer drivers, and a rich library of support code that
eases the implementation of those drivers. This library is
available to, but not forced upon, those drivers."
-- Neil Brown (2009), https://lwn.net/Articles/336262/
The presence of midlayer traits in the PCI hotplug core might be ascribed
to its age: When it was introduced in February 2002, the blessings of a
library approach might not have been well known:
https://git.kernel.org/tglx/history/c/a8a2069f432c
For comparison, the driver core does offer split functions for creating
a kobject (device_initialize()) and addition to sysfs (device_add()) as
an alternative to carrying out everything at once (device_register()).
This was introduced in October 2002:
https://git.kernel.org/tglx/history/c/8b290eb19962
The odd ->release callback in the PCI hotplug core was added in 2003:
https://git.kernel.org/tglx/history/c/69f8d663b595
Clearly, a library approach would not force every hotplug driver to
implement a ->release callback, but rather allow the driver to remove
the sysfs files, release its data structures and finally destroy the
kobject. Alternatively, a driver may choose to remove everything with
pci_hp_deregister(), then release its data structures.
To this end, offer drivers pci_hp_initialize() and pci_hp_add() as a
split-up version of pci_hp_register(). Likewise, offer pci_hp_del()
and pci_hp_destroy() as a split-up version of pci_hp_deregister().
Eliminate the ->release callback and move its code into each driver's
teardown routine.
Declare pci_hp_deregister() void, in keeping with the usual kernel
pattern that enablement can fail, but disablement cannot. It only
returned an error if the caller passed in a NULL pointer or a slot which
has never or is no longer registered or is sharing its name with another
slot. Those would be bugs, so WARN about them. Few hotplug drivers
actually checked the return value and those that did only printed a
useless error message to dmesg. Remove that.
For most drivers the conversion was straightforward since it doesn't
matter whether the code in the ->release callback is executed before or
after destruction of the kobject. But in the case of ibmphp, it was
unclear to me whether setting slot_cur->ctrl and slot_cur->bus_on to
NULL needs to happen before the kobject is destroyed, so I erred on
the side of caution and ensured that the order stays the same. Another
nontrivial case is pnv_php, I've found the list and kref logic difficult
to understand, however my impression was that it is safe to delete the
list element and drop the references until after the kobject is
destroyed.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Andy Shevchenko <andy.shevchenko@gmail.com> # drivers/platform/x86
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Len Brown <lenb@kernel.org>
Cc: Scott Murray <scott@spiteful.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Gavin Shan <gwshan@linux.vnet.ibm.com>
Cc: Sebastian Ott <sebott@linux.vnet.ibm.com>
Cc: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Cc: Corentin Chary <corentin.chary@gmail.com>
Cc: Darren Hart <dvhart@infradead.org>
Cc: Andy Shevchenko <andy@infradead.org>
2018-07-20 06:27:43 +08:00
|
|
|
void pci_hp_deregister(struct hotplug_slot *slot)
|
|
|
|
{
|
|
|
|
pci_hp_del(slot);
|
|
|
|
pci_hp_destroy(slot);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(pci_hp_deregister);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* pci_hp_del - unpublish hotplug slot from user space
|
|
|
|
* @slot: pointer to the &struct hotplug_slot to unpublish
|
|
|
|
*
|
|
|
|
* Remove a hotplug slot's sysfs interface.
|
|
|
|
*
|
|
|
|
* Returns 0 on success or a negative int on error.
|
|
|
|
*/
|
|
|
|
void pci_hp_del(struct hotplug_slot *slot)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct hotplug_slot *temp;
|
|
|
|
|
PCI: hotplug: Demidlayer registration with the core
When a hotplug driver calls pci_hp_register(), all steps necessary for
registration are carried out in one go, including creation of a kobject
and addition to sysfs. That's a problem for pciehp once it's converted
to enable/disable the slot exclusively from the IRQ thread: The thread
needs to be spawned after creation of the kobject (because it uses the
kobject's name), but before addition to sysfs (because it will handle
enable/disable requests submitted via sysfs).
pci_hp_deregister() does offer a ->release callback that's invoked
after deletion from sysfs and before destruction of the kobject. But
because pci_hp_register() doesn't offer a counterpart, hotplug drivers'
->probe and ->remove code becomes asymmetric, which is error prone
as recently discovered use-after-free bugs in pciehp's ->remove hook
have shown.
In a sense, this appears to be a case of the midlayer antipattern:
"The core thesis of the "midlayer mistake" is that midlayers are
bad and should not exist. That common functionality which it is
so tempting to put in a midlayer should instead be provided as
library routines which can [be] used, augmented, or ignored by
each bottom level driver independently. Thus every subsystem
that supports multiple implementations (or drivers) should
provide a very thin top layer which calls directly into the
bottom layer drivers, and a rich library of support code that
eases the implementation of those drivers. This library is
available to, but not forced upon, those drivers."
-- Neil Brown (2009), https://lwn.net/Articles/336262/
The presence of midlayer traits in the PCI hotplug core might be ascribed
to its age: When it was introduced in February 2002, the blessings of a
library approach might not have been well known:
https://git.kernel.org/tglx/history/c/a8a2069f432c
For comparison, the driver core does offer split functions for creating
a kobject (device_initialize()) and addition to sysfs (device_add()) as
an alternative to carrying out everything at once (device_register()).
This was introduced in October 2002:
https://git.kernel.org/tglx/history/c/8b290eb19962
The odd ->release callback in the PCI hotplug core was added in 2003:
https://git.kernel.org/tglx/history/c/69f8d663b595
Clearly, a library approach would not force every hotplug driver to
implement a ->release callback, but rather allow the driver to remove
the sysfs files, release its data structures and finally destroy the
kobject. Alternatively, a driver may choose to remove everything with
pci_hp_deregister(), then release its data structures.
To this end, offer drivers pci_hp_initialize() and pci_hp_add() as a
split-up version of pci_hp_register(). Likewise, offer pci_hp_del()
and pci_hp_destroy() as a split-up version of pci_hp_deregister().
Eliminate the ->release callback and move its code into each driver's
teardown routine.
Declare pci_hp_deregister() void, in keeping with the usual kernel
pattern that enablement can fail, but disablement cannot. It only
returned an error if the caller passed in a NULL pointer or a slot which
has never or is no longer registered or is sharing its name with another
slot. Those would be bugs, so WARN about them. Few hotplug drivers
actually checked the return value and those that did only printed a
useless error message to dmesg. Remove that.
For most drivers the conversion was straightforward since it doesn't
matter whether the code in the ->release callback is executed before or
after destruction of the kobject. But in the case of ibmphp, it was
unclear to me whether setting slot_cur->ctrl and slot_cur->bus_on to
NULL needs to happen before the kobject is destroyed, so I erred on
the side of caution and ensured that the order stays the same. Another
nontrivial case is pnv_php, I've found the list and kref logic difficult
to understand, however my impression was that it is safe to delete the
list element and drop the references until after the kobject is
destroyed.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Andy Shevchenko <andy.shevchenko@gmail.com> # drivers/platform/x86
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Len Brown <lenb@kernel.org>
Cc: Scott Murray <scott@spiteful.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Gavin Shan <gwshan@linux.vnet.ibm.com>
Cc: Sebastian Ott <sebott@linux.vnet.ibm.com>
Cc: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Cc: Corentin Chary <corentin.chary@gmail.com>
Cc: Darren Hart <dvhart@infradead.org>
Cc: Andy Shevchenko <andy@infradead.org>
2018-07-20 06:27:43 +08:00
|
|
|
if (WARN_ON(!slot))
|
|
|
|
return;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-10-21 07:40:57 +08:00
|
|
|
mutex_lock(&pci_hp_mutex);
|
2015-06-19 15:57:44 +08:00
|
|
|
temp = get_slot_from_name(hotplug_slot_name(slot));
|
PCI: hotplug: Demidlayer registration with the core
When a hotplug driver calls pci_hp_register(), all steps necessary for
registration are carried out in one go, including creation of a kobject
and addition to sysfs. That's a problem for pciehp once it's converted
to enable/disable the slot exclusively from the IRQ thread: The thread
needs to be spawned after creation of the kobject (because it uses the
kobject's name), but before addition to sysfs (because it will handle
enable/disable requests submitted via sysfs).
pci_hp_deregister() does offer a ->release callback that's invoked
after deletion from sysfs and before destruction of the kobject. But
because pci_hp_register() doesn't offer a counterpart, hotplug drivers'
->probe and ->remove code becomes asymmetric, which is error prone
as recently discovered use-after-free bugs in pciehp's ->remove hook
have shown.
In a sense, this appears to be a case of the midlayer antipattern:
"The core thesis of the "midlayer mistake" is that midlayers are
bad and should not exist. That common functionality which it is
so tempting to put in a midlayer should instead be provided as
library routines which can [be] used, augmented, or ignored by
each bottom level driver independently. Thus every subsystem
that supports multiple implementations (or drivers) should
provide a very thin top layer which calls directly into the
bottom layer drivers, and a rich library of support code that
eases the implementation of those drivers. This library is
available to, but not forced upon, those drivers."
-- Neil Brown (2009), https://lwn.net/Articles/336262/
The presence of midlayer traits in the PCI hotplug core might be ascribed
to its age: When it was introduced in February 2002, the blessings of a
library approach might not have been well known:
https://git.kernel.org/tglx/history/c/a8a2069f432c
For comparison, the driver core does offer split functions for creating
a kobject (device_initialize()) and addition to sysfs (device_add()) as
an alternative to carrying out everything at once (device_register()).
This was introduced in October 2002:
https://git.kernel.org/tglx/history/c/8b290eb19962
The odd ->release callback in the PCI hotplug core was added in 2003:
https://git.kernel.org/tglx/history/c/69f8d663b595
Clearly, a library approach would not force every hotplug driver to
implement a ->release callback, but rather allow the driver to remove
the sysfs files, release its data structures and finally destroy the
kobject. Alternatively, a driver may choose to remove everything with
pci_hp_deregister(), then release its data structures.
To this end, offer drivers pci_hp_initialize() and pci_hp_add() as a
split-up version of pci_hp_register(). Likewise, offer pci_hp_del()
and pci_hp_destroy() as a split-up version of pci_hp_deregister().
Eliminate the ->release callback and move its code into each driver's
teardown routine.
Declare pci_hp_deregister() void, in keeping with the usual kernel
pattern that enablement can fail, but disablement cannot. It only
returned an error if the caller passed in a NULL pointer or a slot which
has never or is no longer registered or is sharing its name with another
slot. Those would be bugs, so WARN about them. Few hotplug drivers
actually checked the return value and those that did only printed a
useless error message to dmesg. Remove that.
For most drivers the conversion was straightforward since it doesn't
matter whether the code in the ->release callback is executed before or
after destruction of the kobject. But in the case of ibmphp, it was
unclear to me whether setting slot_cur->ctrl and slot_cur->bus_on to
NULL needs to happen before the kobject is destroyed, so I erred on
the side of caution and ensured that the order stays the same. Another
nontrivial case is pnv_php, I've found the list and kref logic difficult
to understand, however my impression was that it is safe to delete the
list element and drop the references until after the kobject is
destroyed.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Andy Shevchenko <andy.shevchenko@gmail.com> # drivers/platform/x86
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Len Brown <lenb@kernel.org>
Cc: Scott Murray <scott@spiteful.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Gavin Shan <gwshan@linux.vnet.ibm.com>
Cc: Sebastian Ott <sebott@linux.vnet.ibm.com>
Cc: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Cc: Corentin Chary <corentin.chary@gmail.com>
Cc: Darren Hart <dvhart@infradead.org>
Cc: Andy Shevchenko <andy@infradead.org>
2018-07-20 06:27:43 +08:00
|
|
|
if (WARN_ON(temp != slot)) {
|
2008-10-21 07:40:57 +08:00
|
|
|
mutex_unlock(&pci_hp_mutex);
|
PCI: hotplug: Demidlayer registration with the core
When a hotplug driver calls pci_hp_register(), all steps necessary for
registration are carried out in one go, including creation of a kobject
and addition to sysfs. That's a problem for pciehp once it's converted
to enable/disable the slot exclusively from the IRQ thread: The thread
needs to be spawned after creation of the kobject (because it uses the
kobject's name), but before addition to sysfs (because it will handle
enable/disable requests submitted via sysfs).
pci_hp_deregister() does offer a ->release callback that's invoked
after deletion from sysfs and before destruction of the kobject. But
because pci_hp_register() doesn't offer a counterpart, hotplug drivers'
->probe and ->remove code becomes asymmetric, which is error prone
as recently discovered use-after-free bugs in pciehp's ->remove hook
have shown.
In a sense, this appears to be a case of the midlayer antipattern:
"The core thesis of the "midlayer mistake" is that midlayers are
bad and should not exist. That common functionality which it is
so tempting to put in a midlayer should instead be provided as
library routines which can [be] used, augmented, or ignored by
each bottom level driver independently. Thus every subsystem
that supports multiple implementations (or drivers) should
provide a very thin top layer which calls directly into the
bottom layer drivers, and a rich library of support code that
eases the implementation of those drivers. This library is
available to, but not forced upon, those drivers."
-- Neil Brown (2009), https://lwn.net/Articles/336262/
The presence of midlayer traits in the PCI hotplug core might be ascribed
to its age: When it was introduced in February 2002, the blessings of a
library approach might not have been well known:
https://git.kernel.org/tglx/history/c/a8a2069f432c
For comparison, the driver core does offer split functions for creating
a kobject (device_initialize()) and addition to sysfs (device_add()) as
an alternative to carrying out everything at once (device_register()).
This was introduced in October 2002:
https://git.kernel.org/tglx/history/c/8b290eb19962
The odd ->release callback in the PCI hotplug core was added in 2003:
https://git.kernel.org/tglx/history/c/69f8d663b595
Clearly, a library approach would not force every hotplug driver to
implement a ->release callback, but rather allow the driver to remove
the sysfs files, release its data structures and finally destroy the
kobject. Alternatively, a driver may choose to remove everything with
pci_hp_deregister(), then release its data structures.
To this end, offer drivers pci_hp_initialize() and pci_hp_add() as a
split-up version of pci_hp_register(). Likewise, offer pci_hp_del()
and pci_hp_destroy() as a split-up version of pci_hp_deregister().
Eliminate the ->release callback and move its code into each driver's
teardown routine.
Declare pci_hp_deregister() void, in keeping with the usual kernel
pattern that enablement can fail, but disablement cannot. It only
returned an error if the caller passed in a NULL pointer or a slot which
has never or is no longer registered or is sharing its name with another
slot. Those would be bugs, so WARN about them. Few hotplug drivers
actually checked the return value and those that did only printed a
useless error message to dmesg. Remove that.
For most drivers the conversion was straightforward since it doesn't
matter whether the code in the ->release callback is executed before or
after destruction of the kobject. But in the case of ibmphp, it was
unclear to me whether setting slot_cur->ctrl and slot_cur->bus_on to
NULL needs to happen before the kobject is destroyed, so I erred on
the side of caution and ensured that the order stays the same. Another
nontrivial case is pnv_php, I've found the list and kref logic difficult
to understand, however my impression was that it is safe to delete the
list element and drop the references until after the kobject is
destroyed.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Andy Shevchenko <andy.shevchenko@gmail.com> # drivers/platform/x86
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Len Brown <lenb@kernel.org>
Cc: Scott Murray <scott@spiteful.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Gavin Shan <gwshan@linux.vnet.ibm.com>
Cc: Sebastian Ott <sebott@linux.vnet.ibm.com>
Cc: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Cc: Corentin Chary <corentin.chary@gmail.com>
Cc: Darren Hart <dvhart@infradead.org>
Cc: Andy Shevchenko <andy@infradead.org>
2018-07-20 06:27:43 +08:00
|
|
|
return;
|
2008-10-21 07:40:57 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2015-06-19 15:57:44 +08:00
|
|
|
list_del(&slot->slot_list);
|
PCI: hotplug: Demidlayer registration with the core
When a hotplug driver calls pci_hp_register(), all steps necessary for
registration are carried out in one go, including creation of a kobject
and addition to sysfs. That's a problem for pciehp once it's converted
to enable/disable the slot exclusively from the IRQ thread: The thread
needs to be spawned after creation of the kobject (because it uses the
kobject's name), but before addition to sysfs (because it will handle
enable/disable requests submitted via sysfs).
pci_hp_deregister() does offer a ->release callback that's invoked
after deletion from sysfs and before destruction of the kobject. But
because pci_hp_register() doesn't offer a counterpart, hotplug drivers'
->probe and ->remove code becomes asymmetric, which is error prone
as recently discovered use-after-free bugs in pciehp's ->remove hook
have shown.
In a sense, this appears to be a case of the midlayer antipattern:
"The core thesis of the "midlayer mistake" is that midlayers are
bad and should not exist. That common functionality which it is
so tempting to put in a midlayer should instead be provided as
library routines which can [be] used, augmented, or ignored by
each bottom level driver independently. Thus every subsystem
that supports multiple implementations (or drivers) should
provide a very thin top layer which calls directly into the
bottom layer drivers, and a rich library of support code that
eases the implementation of those drivers. This library is
available to, but not forced upon, those drivers."
-- Neil Brown (2009), https://lwn.net/Articles/336262/
The presence of midlayer traits in the PCI hotplug core might be ascribed
to its age: When it was introduced in February 2002, the blessings of a
library approach might not have been well known:
https://git.kernel.org/tglx/history/c/a8a2069f432c
For comparison, the driver core does offer split functions for creating
a kobject (device_initialize()) and addition to sysfs (device_add()) as
an alternative to carrying out everything at once (device_register()).
This was introduced in October 2002:
https://git.kernel.org/tglx/history/c/8b290eb19962
The odd ->release callback in the PCI hotplug core was added in 2003:
https://git.kernel.org/tglx/history/c/69f8d663b595
Clearly, a library approach would not force every hotplug driver to
implement a ->release callback, but rather allow the driver to remove
the sysfs files, release its data structures and finally destroy the
kobject. Alternatively, a driver may choose to remove everything with
pci_hp_deregister(), then release its data structures.
To this end, offer drivers pci_hp_initialize() and pci_hp_add() as a
split-up version of pci_hp_register(). Likewise, offer pci_hp_del()
and pci_hp_destroy() as a split-up version of pci_hp_deregister().
Eliminate the ->release callback and move its code into each driver's
teardown routine.
Declare pci_hp_deregister() void, in keeping with the usual kernel
pattern that enablement can fail, but disablement cannot. It only
returned an error if the caller passed in a NULL pointer or a slot which
has never or is no longer registered or is sharing its name with another
slot. Those would be bugs, so WARN about them. Few hotplug drivers
actually checked the return value and those that did only printed a
useless error message to dmesg. Remove that.
For most drivers the conversion was straightforward since it doesn't
matter whether the code in the ->release callback is executed before or
after destruction of the kobject. But in the case of ibmphp, it was
unclear to me whether setting slot_cur->ctrl and slot_cur->bus_on to
NULL needs to happen before the kobject is destroyed, so I erred on
the side of caution and ensured that the order stays the same. Another
nontrivial case is pnv_php, I've found the list and kref logic difficult
to understand, however my impression was that it is safe to delete the
list element and drop the references until after the kobject is
destroyed.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Andy Shevchenko <andy.shevchenko@gmail.com> # drivers/platform/x86
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Len Brown <lenb@kernel.org>
Cc: Scott Murray <scott@spiteful.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Gavin Shan <gwshan@linux.vnet.ibm.com>
Cc: Sebastian Ott <sebott@linux.vnet.ibm.com>
Cc: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Cc: Corentin Chary <corentin.chary@gmail.com>
Cc: Darren Hart <dvhart@infradead.org>
Cc: Andy Shevchenko <andy@infradead.org>
2018-07-20 06:27:43 +08:00
|
|
|
mutex_unlock(&pci_hp_mutex);
|
2015-06-19 15:57:44 +08:00
|
|
|
dbg("Removed slot %s from the list\n", hotplug_slot_name(slot));
|
PCI: hotplug: Demidlayer registration with the core
When a hotplug driver calls pci_hp_register(), all steps necessary for
registration are carried out in one go, including creation of a kobject
and addition to sysfs. That's a problem for pciehp once it's converted
to enable/disable the slot exclusively from the IRQ thread: The thread
needs to be spawned after creation of the kobject (because it uses the
kobject's name), but before addition to sysfs (because it will handle
enable/disable requests submitted via sysfs).
pci_hp_deregister() does offer a ->release callback that's invoked
after deletion from sysfs and before destruction of the kobject. But
because pci_hp_register() doesn't offer a counterpart, hotplug drivers'
->probe and ->remove code becomes asymmetric, which is error prone
as recently discovered use-after-free bugs in pciehp's ->remove hook
have shown.
In a sense, this appears to be a case of the midlayer antipattern:
"The core thesis of the "midlayer mistake" is that midlayers are
bad and should not exist. That common functionality which it is
so tempting to put in a midlayer should instead be provided as
library routines which can [be] used, augmented, or ignored by
each bottom level driver independently. Thus every subsystem
that supports multiple implementations (or drivers) should
provide a very thin top layer which calls directly into the
bottom layer drivers, and a rich library of support code that
eases the implementation of those drivers. This library is
available to, but not forced upon, those drivers."
-- Neil Brown (2009), https://lwn.net/Articles/336262/
The presence of midlayer traits in the PCI hotplug core might be ascribed
to its age: When it was introduced in February 2002, the blessings of a
library approach might not have been well known:
https://git.kernel.org/tglx/history/c/a8a2069f432c
For comparison, the driver core does offer split functions for creating
a kobject (device_initialize()) and addition to sysfs (device_add()) as
an alternative to carrying out everything at once (device_register()).
This was introduced in October 2002:
https://git.kernel.org/tglx/history/c/8b290eb19962
The odd ->release callback in the PCI hotplug core was added in 2003:
https://git.kernel.org/tglx/history/c/69f8d663b595
Clearly, a library approach would not force every hotplug driver to
implement a ->release callback, but rather allow the driver to remove
the sysfs files, release its data structures and finally destroy the
kobject. Alternatively, a driver may choose to remove everything with
pci_hp_deregister(), then release its data structures.
To this end, offer drivers pci_hp_initialize() and pci_hp_add() as a
split-up version of pci_hp_register(). Likewise, offer pci_hp_del()
and pci_hp_destroy() as a split-up version of pci_hp_deregister().
Eliminate the ->release callback and move its code into each driver's
teardown routine.
Declare pci_hp_deregister() void, in keeping with the usual kernel
pattern that enablement can fail, but disablement cannot. It only
returned an error if the caller passed in a NULL pointer or a slot which
has never or is no longer registered or is sharing its name with another
slot. Those would be bugs, so WARN about them. Few hotplug drivers
actually checked the return value and those that did only printed a
useless error message to dmesg. Remove that.
For most drivers the conversion was straightforward since it doesn't
matter whether the code in the ->release callback is executed before or
after destruction of the kobject. But in the case of ibmphp, it was
unclear to me whether setting slot_cur->ctrl and slot_cur->bus_on to
NULL needs to happen before the kobject is destroyed, so I erred on
the side of caution and ensured that the order stays the same. Another
nontrivial case is pnv_php, I've found the list and kref logic difficult
to understand, however my impression was that it is safe to delete the
list element and drop the references until after the kobject is
destroyed.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Andy Shevchenko <andy.shevchenko@gmail.com> # drivers/platform/x86
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Len Brown <lenb@kernel.org>
Cc: Scott Murray <scott@spiteful.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Gavin Shan <gwshan@linux.vnet.ibm.com>
Cc: Sebastian Ott <sebott@linux.vnet.ibm.com>
Cc: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Cc: Corentin Chary <corentin.chary@gmail.com>
Cc: Darren Hart <dvhart@infradead.org>
Cc: Andy Shevchenko <andy@infradead.org>
2018-07-20 06:27:43 +08:00
|
|
|
fs_remove_slot(slot->pci_slot);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(pci_hp_del);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* pci_hp_destroy - remove hotplug slot from in-kernel use
|
|
|
|
* @slot: pointer to the &struct hotplug_slot to destroy
|
|
|
|
*
|
|
|
|
* Destroy a PCI slot used by a hotplug driver. Once this has been called,
|
|
|
|
* the driver may no longer invoke hotplug_slot_name() to get the slot's
|
|
|
|
* unique name. The driver no longer needs to handle a ->reset_slot callback
|
|
|
|
* from this point on.
|
|
|
|
*
|
|
|
|
* Returns 0 on success or a negative int on error.
|
|
|
|
*/
|
|
|
|
void pci_hp_destroy(struct hotplug_slot *slot)
|
|
|
|
{
|
|
|
|
struct pci_slot *pci_slot = slot->pci_slot;
|
2008-06-11 05:28:50 +08:00
|
|
|
|
PCI: hotplug: Demidlayer registration with the core
When a hotplug driver calls pci_hp_register(), all steps necessary for
registration are carried out in one go, including creation of a kobject
and addition to sysfs. That's a problem for pciehp once it's converted
to enable/disable the slot exclusively from the IRQ thread: The thread
needs to be spawned after creation of the kobject (because it uses the
kobject's name), but before addition to sysfs (because it will handle
enable/disable requests submitted via sysfs).
pci_hp_deregister() does offer a ->release callback that's invoked
after deletion from sysfs and before destruction of the kobject. But
because pci_hp_register() doesn't offer a counterpart, hotplug drivers'
->probe and ->remove code becomes asymmetric, which is error prone
as recently discovered use-after-free bugs in pciehp's ->remove hook
have shown.
In a sense, this appears to be a case of the midlayer antipattern:
"The core thesis of the "midlayer mistake" is that midlayers are
bad and should not exist. That common functionality which it is
so tempting to put in a midlayer should instead be provided as
library routines which can [be] used, augmented, or ignored by
each bottom level driver independently. Thus every subsystem
that supports multiple implementations (or drivers) should
provide a very thin top layer which calls directly into the
bottom layer drivers, and a rich library of support code that
eases the implementation of those drivers. This library is
available to, but not forced upon, those drivers."
-- Neil Brown (2009), https://lwn.net/Articles/336262/
The presence of midlayer traits in the PCI hotplug core might be ascribed
to its age: When it was introduced in February 2002, the blessings of a
library approach might not have been well known:
https://git.kernel.org/tglx/history/c/a8a2069f432c
For comparison, the driver core does offer split functions for creating
a kobject (device_initialize()) and addition to sysfs (device_add()) as
an alternative to carrying out everything at once (device_register()).
This was introduced in October 2002:
https://git.kernel.org/tglx/history/c/8b290eb19962
The odd ->release callback in the PCI hotplug core was added in 2003:
https://git.kernel.org/tglx/history/c/69f8d663b595
Clearly, a library approach would not force every hotplug driver to
implement a ->release callback, but rather allow the driver to remove
the sysfs files, release its data structures and finally destroy the
kobject. Alternatively, a driver may choose to remove everything with
pci_hp_deregister(), then release its data structures.
To this end, offer drivers pci_hp_initialize() and pci_hp_add() as a
split-up version of pci_hp_register(). Likewise, offer pci_hp_del()
and pci_hp_destroy() as a split-up version of pci_hp_deregister().
Eliminate the ->release callback and move its code into each driver's
teardown routine.
Declare pci_hp_deregister() void, in keeping with the usual kernel
pattern that enablement can fail, but disablement cannot. It only
returned an error if the caller passed in a NULL pointer or a slot which
has never or is no longer registered or is sharing its name with another
slot. Those would be bugs, so WARN about them. Few hotplug drivers
actually checked the return value and those that did only printed a
useless error message to dmesg. Remove that.
For most drivers the conversion was straightforward since it doesn't
matter whether the code in the ->release callback is executed before or
after destruction of the kobject. But in the case of ibmphp, it was
unclear to me whether setting slot_cur->ctrl and slot_cur->bus_on to
NULL needs to happen before the kobject is destroyed, so I erred on
the side of caution and ensured that the order stays the same. Another
nontrivial case is pnv_php, I've found the list and kref logic difficult
to understand, however my impression was that it is safe to delete the
list element and drop the references until after the kobject is
destroyed.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Andy Shevchenko <andy.shevchenko@gmail.com> # drivers/platform/x86
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Len Brown <lenb@kernel.org>
Cc: Scott Murray <scott@spiteful.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Gavin Shan <gwshan@linux.vnet.ibm.com>
Cc: Sebastian Ott <sebott@linux.vnet.ibm.com>
Cc: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Cc: Corentin Chary <corentin.chary@gmail.com>
Cc: Darren Hart <dvhart@infradead.org>
Cc: Andy Shevchenko <andy@infradead.org>
2018-07-20 06:27:43 +08:00
|
|
|
slot->pci_slot = NULL;
|
2015-06-19 15:57:44 +08:00
|
|
|
pci_slot->hotplug = NULL;
|
|
|
|
pci_destroy_slot(pci_slot);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
PCI: hotplug: Demidlayer registration with the core
When a hotplug driver calls pci_hp_register(), all steps necessary for
registration are carried out in one go, including creation of a kobject
and addition to sysfs. That's a problem for pciehp once it's converted
to enable/disable the slot exclusively from the IRQ thread: The thread
needs to be spawned after creation of the kobject (because it uses the
kobject's name), but before addition to sysfs (because it will handle
enable/disable requests submitted via sysfs).
pci_hp_deregister() does offer a ->release callback that's invoked
after deletion from sysfs and before destruction of the kobject. But
because pci_hp_register() doesn't offer a counterpart, hotplug drivers'
->probe and ->remove code becomes asymmetric, which is error prone
as recently discovered use-after-free bugs in pciehp's ->remove hook
have shown.
In a sense, this appears to be a case of the midlayer antipattern:
"The core thesis of the "midlayer mistake" is that midlayers are
bad and should not exist. That common functionality which it is
so tempting to put in a midlayer should instead be provided as
library routines which can [be] used, augmented, or ignored by
each bottom level driver independently. Thus every subsystem
that supports multiple implementations (or drivers) should
provide a very thin top layer which calls directly into the
bottom layer drivers, and a rich library of support code that
eases the implementation of those drivers. This library is
available to, but not forced upon, those drivers."
-- Neil Brown (2009), https://lwn.net/Articles/336262/
The presence of midlayer traits in the PCI hotplug core might be ascribed
to its age: When it was introduced in February 2002, the blessings of a
library approach might not have been well known:
https://git.kernel.org/tglx/history/c/a8a2069f432c
For comparison, the driver core does offer split functions for creating
a kobject (device_initialize()) and addition to sysfs (device_add()) as
an alternative to carrying out everything at once (device_register()).
This was introduced in October 2002:
https://git.kernel.org/tglx/history/c/8b290eb19962
The odd ->release callback in the PCI hotplug core was added in 2003:
https://git.kernel.org/tglx/history/c/69f8d663b595
Clearly, a library approach would not force every hotplug driver to
implement a ->release callback, but rather allow the driver to remove
the sysfs files, release its data structures and finally destroy the
kobject. Alternatively, a driver may choose to remove everything with
pci_hp_deregister(), then release its data structures.
To this end, offer drivers pci_hp_initialize() and pci_hp_add() as a
split-up version of pci_hp_register(). Likewise, offer pci_hp_del()
and pci_hp_destroy() as a split-up version of pci_hp_deregister().
Eliminate the ->release callback and move its code into each driver's
teardown routine.
Declare pci_hp_deregister() void, in keeping with the usual kernel
pattern that enablement can fail, but disablement cannot. It only
returned an error if the caller passed in a NULL pointer or a slot which
has never or is no longer registered or is sharing its name with another
slot. Those would be bugs, so WARN about them. Few hotplug drivers
actually checked the return value and those that did only printed a
useless error message to dmesg. Remove that.
For most drivers the conversion was straightforward since it doesn't
matter whether the code in the ->release callback is executed before or
after destruction of the kobject. But in the case of ibmphp, it was
unclear to me whether setting slot_cur->ctrl and slot_cur->bus_on to
NULL needs to happen before the kobject is destroyed, so I erred on
the side of caution and ensured that the order stays the same. Another
nontrivial case is pnv_php, I've found the list and kref logic difficult
to understand, however my impression was that it is safe to delete the
list element and drop the references until after the kobject is
destroyed.
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Andy Shevchenko <andy.shevchenko@gmail.com> # drivers/platform/x86
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Len Brown <lenb@kernel.org>
Cc: Scott Murray <scott@spiteful.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Gavin Shan <gwshan@linux.vnet.ibm.com>
Cc: Sebastian Ott <sebott@linux.vnet.ibm.com>
Cc: Gerald Schaefer <gerald.schaefer@de.ibm.com>
Cc: Corentin Chary <corentin.chary@gmail.com>
Cc: Darren Hart <dvhart@infradead.org>
Cc: Andy Shevchenko <andy@infradead.org>
2018-07-20 06:27:43 +08:00
|
|
|
EXPORT_SYMBOL_GPL(pci_hp_destroy);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-04-19 08:13:49 +08:00
|
|
|
static int __init pci_hotplug_init(void)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int result;
|
2007-11-02 10:41:16 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
result = cpci_hotplug_init(debug);
|
|
|
|
if (result) {
|
2014-04-19 08:13:49 +08:00
|
|
|
err("cpci_hotplug_init with error %d\n", result);
|
|
|
|
return result;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2016-08-25 04:57:51 +08:00
|
|
|
device_initcall(pci_hotplug_init);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2016-08-25 04:57:51 +08:00
|
|
|
/*
|
|
|
|
* not really modular, but the easiest way to keep compat with existing
|
|
|
|
* bootargs behaviour is to continue using module_param here.
|
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
module_param(debug, bool, 0644);
|
|
|
|
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
|