brintos

brintos / linux-shallow public Read only

0
0
Text · 14.2 KiB · 058d593 Raw
581 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * PCI HotPlug Controller Core4 *5 * Copyright (C) 2001-2002 Greg Kroah-Hartman (greg@kroah.com)6 * Copyright (C) 2001-2002 IBM Corp.7 *8 * All rights reserved.9 *10 * Send feedback to <kristen.c.accardi@intel.com>11 *12 * Authors:13 *   Greg Kroah-Hartman <greg@kroah.com>14 *   Scott Murray <scottm@somanetworks.com>15 */16 17#include <linux/module.h>	/* try_module_get & module_put */18#include <linux/moduleparam.h>19#include <linux/kernel.h>20#include <linux/types.h>21#include <linux/list.h>22#include <linux/kobject.h>23#include <linux/sysfs.h>24#include <linux/pagemap.h>25#include <linux/init.h>26#include <linux/mount.h>27#include <linux/namei.h>28#include <linux/mutex.h>29#include <linux/pci.h>30#include <linux/pci_hotplug.h>31#include <linux/uaccess.h>32#include "../pci.h"33#include "cpci_hotplug.h"34 35#define MY_NAME	"pci_hotplug"36 37#define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt, MY_NAME, __func__, ## arg); } while (0)38#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME, ## arg)39#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME, ## arg)40#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME, ## arg)41 42/* local variables */43static bool debug;44 45static LIST_HEAD(pci_hotplug_slot_list);46static DEFINE_MUTEX(pci_hp_mutex);47 48/* Weee, fun with macros... */49#define GET_STATUS(name, type)	\50static int get_##name(struct hotplug_slot *slot, type *value)		\51{									\52	const struct hotplug_slot_ops *ops = slot->ops;			\53	int retval = 0;							\54	if (!try_module_get(slot->owner))				\55		return -ENODEV;						\56	if (ops->get_##name)						\57		retval = ops->get_##name(slot, value);			\58	module_put(slot->owner);					\59	return retval;							\60}61 62GET_STATUS(power_status, u8)63GET_STATUS(attention_status, u8)64GET_STATUS(latch_status, u8)65GET_STATUS(adapter_status, u8)66 67static ssize_t power_read_file(struct pci_slot *pci_slot, char *buf)68{69	int retval;70	u8 value;71 72	retval = get_power_status(pci_slot->hotplug, &value);73	if (retval)74		return retval;75 76	return sysfs_emit(buf, "%d\n", value);77}78 79static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,80				size_t count)81{82	struct hotplug_slot *slot = pci_slot->hotplug;83	unsigned long lpower;84	u8 power;85	int retval = 0;86 87	lpower = simple_strtoul(buf, NULL, 10);88	power = (u8)(lpower & 0xff);89	dbg("power = %d\n", power);90 91	if (!try_module_get(slot->owner)) {92		retval = -ENODEV;93		goto exit;94	}95	switch (power) {96	case 0:97		if (slot->ops->disable_slot)98			retval = slot->ops->disable_slot(slot);99		break;100 101	case 1:102		if (slot->ops->enable_slot)103			retval = slot->ops->enable_slot(slot);104		break;105 106	default:107		err("Illegal value specified for power\n");108		retval = -EINVAL;109	}110	module_put(slot->owner);111 112exit:113	if (retval)114		return retval;115	return count;116}117 118static struct pci_slot_attribute hotplug_slot_attr_power = {119	.attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR},120	.show = power_read_file,121	.store = power_write_file122};123 124static ssize_t attention_read_file(struct pci_slot *pci_slot, char *buf)125{126	int retval;127	u8 value;128 129	retval = get_attention_status(pci_slot->hotplug, &value);130	if (retval)131		return retval;132 133	return sysfs_emit(buf, "%d\n", value);134}135 136static ssize_t attention_write_file(struct pci_slot *pci_slot, const char *buf,137				    size_t count)138{139	struct hotplug_slot *slot = pci_slot->hotplug;140	const struct hotplug_slot_ops *ops = slot->ops;141	unsigned long lattention;142	u8 attention;143	int retval = 0;144 145	lattention = simple_strtoul(buf, NULL, 10);146	attention = (u8)(lattention & 0xff);147	dbg(" - attention = %d\n", attention);148 149	if (!try_module_get(slot->owner)) {150		retval = -ENODEV;151		goto exit;152	}153	if (ops->set_attention_status)154		retval = ops->set_attention_status(slot, attention);155	module_put(slot->owner);156 157exit:158	if (retval)159		return retval;160	return count;161}162 163static struct pci_slot_attribute hotplug_slot_attr_attention = {164	.attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR},165	.show = attention_read_file,166	.store = attention_write_file167};168 169static ssize_t latch_read_file(struct pci_slot *pci_slot, char *buf)170{171	int retval;172	u8 value;173 174	retval = get_latch_status(pci_slot->hotplug, &value);175	if (retval)176		return retval;177 178	return sysfs_emit(buf, "%d\n", value);179}180 181static struct pci_slot_attribute hotplug_slot_attr_latch = {182	.attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},183	.show = latch_read_file,184};185 186static ssize_t presence_read_file(struct pci_slot *pci_slot, char *buf)187{188	int retval;189	u8 value;190 191	retval = get_adapter_status(pci_slot->hotplug, &value);192	if (retval)193		return retval;194 195	return sysfs_emit(buf, "%d\n", value);196}197 198static struct pci_slot_attribute hotplug_slot_attr_presence = {199	.attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},200	.show = presence_read_file,201};202 203static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,204			       size_t count)205{206	struct hotplug_slot *slot = pci_slot->hotplug;207	unsigned long ltest;208	u32 test;209	int retval = 0;210 211	ltest = simple_strtoul(buf, NULL, 10);212	test = (u32)(ltest & 0xffffffff);213	dbg("test = %d\n", test);214 215	if (!try_module_get(slot->owner)) {216		retval = -ENODEV;217		goto exit;218	}219	if (slot->ops->hardware_test)220		retval = slot->ops->hardware_test(slot, test);221	module_put(slot->owner);222 223exit:224	if (retval)225		return retval;226	return count;227}228 229static struct pci_slot_attribute hotplug_slot_attr_test = {230	.attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},231	.store = test_write_file232};233 234static bool has_power_file(struct pci_slot *pci_slot)235{236	struct hotplug_slot *slot = pci_slot->hotplug;237 238	if ((!slot) || (!slot->ops))239		return false;240	if ((slot->ops->enable_slot) ||241	    (slot->ops->disable_slot) ||242	    (slot->ops->get_power_status))243		return true;244	return false;245}246 247static bool has_attention_file(struct pci_slot *pci_slot)248{249	struct hotplug_slot *slot = pci_slot->hotplug;250 251	if ((!slot) || (!slot->ops))252		return false;253	if ((slot->ops->set_attention_status) ||254	    (slot->ops->get_attention_status))255		return true;256	return false;257}258 259static bool has_latch_file(struct pci_slot *pci_slot)260{261	struct hotplug_slot *slot = pci_slot->hotplug;262 263	if ((!slot) || (!slot->ops))264		return false;265	if (slot->ops->get_latch_status)266		return true;267	return false;268}269 270static bool has_adapter_file(struct pci_slot *pci_slot)271{272	struct hotplug_slot *slot = pci_slot->hotplug;273 274	if ((!slot) || (!slot->ops))275		return false;276	if (slot->ops->get_adapter_status)277		return true;278	return false;279}280 281static bool has_test_file(struct pci_slot *pci_slot)282{283	struct hotplug_slot *slot = pci_slot->hotplug;284 285	if ((!slot) || (!slot->ops))286		return false;287	if (slot->ops->hardware_test)288		return true;289	return false;290}291 292static int fs_add_slot(struct pci_slot *pci_slot)293{294	int retval = 0;295 296	/* Create symbolic link to the hotplug driver module */297	pci_hp_create_module_link(pci_slot);298 299	if (has_power_file(pci_slot)) {300		retval = sysfs_create_file(&pci_slot->kobj,301					   &hotplug_slot_attr_power.attr);302		if (retval)303			goto exit_power;304	}305 306	if (has_attention_file(pci_slot)) {307		retval = sysfs_create_file(&pci_slot->kobj,308					   &hotplug_slot_attr_attention.attr);309		if (retval)310			goto exit_attention;311	}312 313	if (has_latch_file(pci_slot)) {314		retval = sysfs_create_file(&pci_slot->kobj,315					   &hotplug_slot_attr_latch.attr);316		if (retval)317			goto exit_latch;318	}319 320	if (has_adapter_file(pci_slot)) {321		retval = sysfs_create_file(&pci_slot->kobj,322					   &hotplug_slot_attr_presence.attr);323		if (retval)324			goto exit_adapter;325	}326 327	if (has_test_file(pci_slot)) {328		retval = sysfs_create_file(&pci_slot->kobj,329					   &hotplug_slot_attr_test.attr);330		if (retval)331			goto exit_test;332	}333 334	goto exit;335 336exit_test:337	if (has_adapter_file(pci_slot))338		sysfs_remove_file(&pci_slot->kobj,339				  &hotplug_slot_attr_presence.attr);340exit_adapter:341	if (has_latch_file(pci_slot))342		sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr);343exit_latch:344	if (has_attention_file(pci_slot))345		sysfs_remove_file(&pci_slot->kobj,346				  &hotplug_slot_attr_attention.attr);347exit_attention:348	if (has_power_file(pci_slot))349		sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr);350exit_power:351	pci_hp_remove_module_link(pci_slot);352exit:353	return retval;354}355 356static void fs_remove_slot(struct pci_slot *pci_slot)357{358	if (has_power_file(pci_slot))359		sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr);360 361	if (has_attention_file(pci_slot))362		sysfs_remove_file(&pci_slot->kobj,363				  &hotplug_slot_attr_attention.attr);364 365	if (has_latch_file(pci_slot))366		sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr);367 368	if (has_adapter_file(pci_slot))369		sysfs_remove_file(&pci_slot->kobj,370				  &hotplug_slot_attr_presence.attr);371 372	if (has_test_file(pci_slot))373		sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_test.attr);374 375	pci_hp_remove_module_link(pci_slot);376}377 378static struct hotplug_slot *get_slot_from_name(const char *name)379{380	struct hotplug_slot *slot;381 382	list_for_each_entry(slot, &pci_hotplug_slot_list, slot_list) {383		if (strcmp(hotplug_slot_name(slot), name) == 0)384			return slot;385	}386	return NULL;387}388 389/**390 * __pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem391 * @bus: bus this slot is on392 * @slot: pointer to the &struct hotplug_slot to register393 * @devnr: device number394 * @name: name registered with kobject core395 * @owner: caller module owner396 * @mod_name: caller module name397 *398 * Prepares a hotplug slot for in-kernel use and immediately publishes it to399 * user space in one go.  Drivers may alternatively carry out the two steps400 * separately by invoking pci_hp_initialize() and pci_hp_add().401 *402 * Returns 0 if successful, anything else for an error.403 */404int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus,405		      int devnr, const char *name,406		      struct module *owner, const char *mod_name)407{408	int result;409 410	result = __pci_hp_initialize(slot, bus, devnr, name, owner, mod_name);411	if (result)412		return result;413 414	result = pci_hp_add(slot);415	if (result)416		pci_hp_destroy(slot);417 418	return result;419}420EXPORT_SYMBOL_GPL(__pci_hp_register);421 422/**423 * __pci_hp_initialize - prepare hotplug slot for in-kernel use424 * @slot: pointer to the &struct hotplug_slot to initialize425 * @bus: bus this slot is on426 * @devnr: slot number427 * @name: name registered with kobject core428 * @owner: caller module owner429 * @mod_name: caller module name430 *431 * Allocate and fill in a PCI slot for use by a hotplug driver.  Once this has432 * been called, the driver may invoke hotplug_slot_name() to get the slot's433 * unique name.  The driver must be prepared to handle a ->reset_slot callback434 * from this point on.435 *436 * Returns 0 on success or a negative int on error.437 */438int __pci_hp_initialize(struct hotplug_slot *slot, struct pci_bus *bus,439			int devnr, const char *name, struct module *owner,440			const char *mod_name)441{442	struct pci_slot *pci_slot;443 444	if (slot == NULL)445		return -ENODEV;446	if (slot->ops == NULL)447		return -EINVAL;448 449	slot->owner = owner;450	slot->mod_name = mod_name;451 452	/*453	 * No problems if we call this interface from both ACPI_PCI_SLOT454	 * driver and call it here again. If we've already created the455	 * pci_slot, the interface will simply bump the refcount.456	 */457	pci_slot = pci_create_slot(bus, devnr, name, slot);458	if (IS_ERR(pci_slot))459		return PTR_ERR(pci_slot);460 461	slot->pci_slot = pci_slot;462	pci_slot->hotplug = slot;463	return 0;464}465EXPORT_SYMBOL_GPL(__pci_hp_initialize);466 467/**468 * pci_hp_add - publish hotplug slot to user space469 * @slot: pointer to the &struct hotplug_slot to publish470 *471 * Make a hotplug slot's sysfs interface available and inform user space of its472 * addition by sending a uevent.  The hotplug driver must be prepared to handle473 * all &struct hotplug_slot_ops callbacks from this point on.474 *475 * Returns 0 on success or a negative int on error.476 */477int pci_hp_add(struct hotplug_slot *slot)478{479	struct pci_slot *pci_slot = slot->pci_slot;480	int result;481 482	result = fs_add_slot(pci_slot);483	if (result)484		return result;485 486	kobject_uevent(&pci_slot->kobj, KOBJ_ADD);487	mutex_lock(&pci_hp_mutex);488	list_add(&slot->slot_list, &pci_hotplug_slot_list);489	mutex_unlock(&pci_hp_mutex);490	dbg("Added slot %s to the list\n", hotplug_slot_name(slot));491	return 0;492}493EXPORT_SYMBOL_GPL(pci_hp_add);494 495/**496 * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem497 * @slot: pointer to the &struct hotplug_slot to deregister498 *499 * The @slot must have been registered with the pci hotplug subsystem500 * previously with a call to pci_hp_register().501 *502 * Returns 0 if successful, anything else for an error.503 */504void pci_hp_deregister(struct hotplug_slot *slot)505{506	pci_hp_del(slot);507	pci_hp_destroy(slot);508}509EXPORT_SYMBOL_GPL(pci_hp_deregister);510 511/**512 * pci_hp_del - unpublish hotplug slot from user space513 * @slot: pointer to the &struct hotplug_slot to unpublish514 *515 * Remove a hotplug slot's sysfs interface.516 *517 * Returns 0 on success or a negative int on error.518 */519void pci_hp_del(struct hotplug_slot *slot)520{521	struct hotplug_slot *temp;522 523	if (WARN_ON(!slot))524		return;525 526	mutex_lock(&pci_hp_mutex);527	temp = get_slot_from_name(hotplug_slot_name(slot));528	if (WARN_ON(temp != slot)) {529		mutex_unlock(&pci_hp_mutex);530		return;531	}532 533	list_del(&slot->slot_list);534	mutex_unlock(&pci_hp_mutex);535	dbg("Removed slot %s from the list\n", hotplug_slot_name(slot));536	fs_remove_slot(slot->pci_slot);537}538EXPORT_SYMBOL_GPL(pci_hp_del);539 540/**541 * pci_hp_destroy - remove hotplug slot from in-kernel use542 * @slot: pointer to the &struct hotplug_slot to destroy543 *544 * Destroy a PCI slot used by a hotplug driver.  Once this has been called,545 * the driver may no longer invoke hotplug_slot_name() to get the slot's546 * unique name.  The driver no longer needs to handle a ->reset_slot callback547 * from this point on.548 *549 * Returns 0 on success or a negative int on error.550 */551void pci_hp_destroy(struct hotplug_slot *slot)552{553	struct pci_slot *pci_slot = slot->pci_slot;554 555	slot->pci_slot = NULL;556	pci_slot->hotplug = NULL;557	pci_destroy_slot(pci_slot);558}559EXPORT_SYMBOL_GPL(pci_hp_destroy);560 561static int __init pci_hotplug_init(void)562{563	int result;564 565	result = cpci_hotplug_init(debug);566	if (result) {567		err("cpci_hotplug_init with error %d\n", result);568		return result;569	}570 571	return result;572}573device_initcall(pci_hotplug_init);574 575/*576 * not really modular, but the easiest way to keep compat with existing577 * bootargs behaviour is to continue using module_param here.578 */579module_param(debug, bool, 0644);580MODULE_PARM_DESC(debug, "Debugging mode enabled or not");581