File Index
Files and directories
Sign in
FILE

pci_hotplug_core.c

File preview with syntax highlighting.

linux-6.19.14/drivers/pci/hotplug/pci_hotplug_core.c C
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * PCI HotPlug Controller Core
  4 *
  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>
 18#include <linux/moduleparam.h>
 19#include <linux/kernel.h>
 20#include <linux/types.h>
 21#include <linux/kobject.h>
 22#include <linux/sysfs.h>
 23#include <linux/init.h>
 24#include <linux/pci.h>
 25#include <linux/pci_hotplug.h>
 26#include "../pci.h"
 27#include "cpci_hotplug.h"
 28
 29#define MY_NAME	"pci_hotplug"
 30
 31#define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt, MY_NAME, __func__, ## arg); } while (0)
 32#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME, ## arg)
 33#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME, ## arg)
 34#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME, ## arg)
 35
 36/* local variables */
 37static bool debug;
 38
 39/* Weee, fun with macros... */
 40#define GET_STATUS(name, type)	\
 41static int get_##name(struct hotplug_slot *slot, type *value)		\
 42{									\
 43	const struct hotplug_slot_ops *ops = slot->ops;			\
 44	int retval = 0;							\
 45	if (ops->get_##name)						\
 46		retval = ops->get_##name(slot, value);			\
 47	return retval;							\
 48}
 49
 50GET_STATUS(power_status, u8)
 51GET_STATUS(attention_status, u8)
 52GET_STATUS(latch_status, u8)
 53GET_STATUS(adapter_status, u8)
 54
 55static ssize_t power_read_file(struct pci_slot *pci_slot, char *buf)
 56{
 57	int retval;
 58	u8 value;
 59
 60	retval = get_power_status(pci_slot->hotplug, &value);
 61	if (retval)
 62		return retval;
 63
 64	return sysfs_emit(buf, "%d\n", value);
 65}
 66
 67static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,
 68				size_t count)
 69{
 70	struct hotplug_slot *slot = pci_slot->hotplug;
 71	unsigned long lpower;
 72	u8 power;
 73	int retval = 0;
 74
 75	lpower = simple_strtoul(buf, NULL, 10);
 76	power = (u8)(lpower & 0xff);
 77	dbg("power = %d\n", power);
 78
 79	switch (power) {
 80	case 0:
 81		if (slot->ops->disable_slot)
 82			retval = slot->ops->disable_slot(slot);
 83		break;
 84
 85	case 1:
 86		if (slot->ops->enable_slot)
 87			retval = slot->ops->enable_slot(slot);
 88		break;
 89
 90	default:
 91		err("Illegal value specified for power\n");
 92		retval = -EINVAL;
 93	}
 94
 95	if (retval)
 96		return retval;
 97	return count;
 98}
 99
100static struct pci_slot_attribute hotplug_slot_attr_power = {
101	.attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR},
102	.show = power_read_file,
103	.store = power_write_file
104};
105
106static ssize_t attention_read_file(struct pci_slot *pci_slot, char *buf)
107{
108	int retval;
109	u8 value;
110
111	retval = get_attention_status(pci_slot->hotplug, &value);
112	if (retval)
113		return retval;
114
115	return sysfs_emit(buf, "%d\n", value);
116}
117
118static ssize_t attention_write_file(struct pci_slot *pci_slot, const char *buf,
119				    size_t count)
120{
121	struct hotplug_slot *slot = pci_slot->hotplug;
122	const struct hotplug_slot_ops *ops = slot->ops;
123	unsigned long lattention;
124	u8 attention;
125	int retval = 0;
126
127	lattention = simple_strtoul(buf, NULL, 10);
128	attention = (u8)(lattention & 0xff);
129	dbg(" - attention = %d\n", attention);
130
131	if (ops->set_attention_status)
132		retval = ops->set_attention_status(slot, attention);
133
134	if (retval)
135		return retval;
136	return count;
137}
138
139static struct pci_slot_attribute hotplug_slot_attr_attention = {
140	.attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR},
141	.show = attention_read_file,
142	.store = attention_write_file
143};
144
145static ssize_t latch_read_file(struct pci_slot *pci_slot, char *buf)
146{
147	int retval;
148	u8 value;
149
150	retval = get_latch_status(pci_slot->hotplug, &value);
151	if (retval)
152		return retval;
153
154	return sysfs_emit(buf, "%d\n", value);
155}
156
157static struct pci_slot_attribute hotplug_slot_attr_latch = {
158	.attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},
159	.show = latch_read_file,
160};
161
162static ssize_t presence_read_file(struct pci_slot *pci_slot, char *buf)
163{
164	int retval;
165	u8 value;
166
167	retval = get_adapter_status(pci_slot->hotplug, &value);
168	if (retval)
169		return retval;
170
171	return sysfs_emit(buf, "%d\n", value);
172}
173
174static struct pci_slot_attribute hotplug_slot_attr_presence = {
175	.attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},
176	.show = presence_read_file,
177};
178
179static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,
180			       size_t count)
181{
182	struct hotplug_slot *slot = pci_slot->hotplug;
183	unsigned long ltest;
184	u32 test;
185	int retval = 0;
186
187	ltest = simple_strtoul(buf, NULL, 10);
188	test = (u32)(ltest & 0xffffffff);
189	dbg("test = %d\n", test);
190
191	if (slot->ops->hardware_test)
192		retval = slot->ops->hardware_test(slot, test);
193
194	if (retval)
195		return retval;
196	return count;
197}
198
199static struct pci_slot_attribute hotplug_slot_attr_test = {
200	.attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},
201	.store = test_write_file
202};
203
204static bool has_power_file(struct hotplug_slot *slot)
205{
206	if ((slot->ops->enable_slot) ||
207	    (slot->ops->disable_slot) ||
208	    (slot->ops->get_power_status))
209		return true;
210	return false;
211}
212
213static bool has_attention_file(struct hotplug_slot *slot)
214{
215	if ((slot->ops->set_attention_status) ||
216	    (slot->ops->get_attention_status))
217		return true;
218	return false;
219}
220
221static bool has_latch_file(struct hotplug_slot *slot)
222{
223	if (slot->ops->get_latch_status)
224		return true;
225	return false;
226}
227
228static bool has_adapter_file(struct hotplug_slot *slot)
229{
230	if (slot->ops->get_adapter_status)
231		return true;
232	return false;
233}
234
235static bool has_test_file(struct hotplug_slot *slot)
236{
237	if (slot->ops->hardware_test)
238		return true;
239	return false;
240}
241
242static int fs_add_slot(struct hotplug_slot *slot, struct pci_slot *pci_slot)
243{
244	struct kobject *kobj;
245	int retval = 0;
246
247	/* Create symbolic link to the hotplug driver module */
248	kobj = kset_find_obj(module_kset, slot->mod_name);
249	if (kobj) {
250		retval = sysfs_create_link(&pci_slot->kobj, kobj, "module");
251		if (retval)
252			dev_err(&pci_slot->bus->dev,
253				"Error creating sysfs link (%d)\n", retval);
254		kobject_put(kobj);
255	}
256
257	if (has_power_file(slot)) {
258		retval = sysfs_create_file(&pci_slot->kobj,
259					   &hotplug_slot_attr_power.attr);
260		if (retval)
261			goto exit_power;
262	}
263
264	if (has_attention_file(slot)) {
265		retval = sysfs_create_file(&pci_slot->kobj,
266					   &hotplug_slot_attr_attention.attr);
267		if (retval)
268			goto exit_attention;
269	}
270
271	if (has_latch_file(slot)) {
272		retval = sysfs_create_file(&pci_slot->kobj,
273					   &hotplug_slot_attr_latch.attr);
274		if (retval)
275			goto exit_latch;
276	}
277
278	if (has_adapter_file(slot)) {
279		retval = sysfs_create_file(&pci_slot->kobj,
280					   &hotplug_slot_attr_presence.attr);
281		if (retval)
282			goto exit_adapter;
283	}
284
285	if (has_test_file(slot)) {
286		retval = sysfs_create_file(&pci_slot->kobj,
287					   &hotplug_slot_attr_test.attr);
288		if (retval)
289			goto exit_test;
290	}
291
292	goto exit;
293
294exit_test:
295	if (has_adapter_file(slot))
296		sysfs_remove_file(&pci_slot->kobj,
297				  &hotplug_slot_attr_presence.attr);
298exit_adapter:
299	if (has_latch_file(slot))
300		sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr);
301exit_latch:
302	if (has_attention_file(slot))
303		sysfs_remove_file(&pci_slot->kobj,
304				  &hotplug_slot_attr_attention.attr);
305exit_attention:
306	if (has_power_file(slot))
307		sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr);
308exit_power:
309	sysfs_remove_link(&pci_slot->kobj, "module");
310exit:
311	return retval;
312}
313
314static void fs_remove_slot(struct hotplug_slot *slot, struct pci_slot *pci_slot)
315{
316	if (has_power_file(slot))
317		sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr);
318
319	if (has_attention_file(slot))
320		sysfs_remove_file(&pci_slot->kobj,
321				  &hotplug_slot_attr_attention.attr);
322
323	if (has_latch_file(slot))
324		sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr);
325
326	if (has_adapter_file(slot))
327		sysfs_remove_file(&pci_slot->kobj,
328				  &hotplug_slot_attr_presence.attr);
329
330	if (has_test_file(slot))
331		sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_test.attr);
332
333	sysfs_remove_link(&pci_slot->kobj, "module");
334}
335
336/**
337 * __pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem
338 * @slot: pointer to the &struct hotplug_slot to register
339 * @bus: bus this slot is on
340 * @devnr: device number
341 * @name: name registered with kobject core
342 * @owner: caller module owner
343 * @mod_name: caller module name
344 *
345 * Prepares a hotplug slot for in-kernel use and immediately publishes it to
346 * user space in one go.  Drivers may alternatively carry out the two steps
347 * separately by invoking pci_hp_initialize() and pci_hp_add().
348 *
349 * Returns 0 if successful, anything else for an error.
350 */
351int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus,
352		      int devnr, const char *name,
353		      struct module *owner, const char *mod_name)
354{
355	int result;
356
357	result = __pci_hp_initialize(slot, bus, devnr, name, owner, mod_name);
358	if (result)
359		return result;
360
361	result = pci_hp_add(slot);
362	if (result)
363		pci_hp_destroy(slot);
364
365	return result;
366}
367EXPORT_SYMBOL_GPL(__pci_hp_register);
368
369/**
370 * __pci_hp_initialize - prepare hotplug slot for in-kernel use
371 * @slot: pointer to the &struct hotplug_slot to initialize
372 * @bus: bus this slot is on
373 * @devnr: slot number
374 * @name: name registered with kobject core
375 * @owner: caller module owner
376 * @mod_name: caller module name
377 *
378 * Allocate and fill in a PCI slot for use by a hotplug driver.  Once this has
379 * been called, the driver may invoke hotplug_slot_name() to get the slot's
380 * unique name.  The driver must be prepared to handle a ->reset_slot callback
381 * from this point on.
382 *
383 * Returns 0 on success or a negative int on error.
384 */
385int __pci_hp_initialize(struct hotplug_slot *slot, struct pci_bus *bus,
386			int devnr, const char *name, struct module *owner,
387			const char *mod_name)
388{
389	struct pci_slot *pci_slot;
390
391	if (slot == NULL)
392		return -ENODEV;
393	if (slot->ops == NULL)
394		return -EINVAL;
395
396	slot->owner = owner;
397	slot->mod_name = mod_name;
398
399	/*
400	 * No problems if we call this interface from both ACPI_PCI_SLOT
401	 * driver and call it here again. If we've already created the
402	 * pci_slot, the interface will simply bump the refcount.
403	 */
404	pci_slot = pci_create_slot(bus, devnr, name, slot);
405	if (IS_ERR(pci_slot))
406		return PTR_ERR(pci_slot);
407
408	slot->pci_slot = pci_slot;
409	pci_slot->hotplug = slot;
410	return 0;
411}
412EXPORT_SYMBOL_GPL(__pci_hp_initialize);
413
414/**
415 * pci_hp_add - publish hotplug slot to user space
416 * @slot: pointer to the &struct hotplug_slot to publish
417 *
418 * Make a hotplug slot's sysfs interface available and inform user space of its
419 * addition by sending a uevent.  The hotplug driver must be prepared to handle
420 * all &struct hotplug_slot_ops callbacks from this point on.
421 *
422 * Returns 0 on success or a negative int on error.
423 */
424int pci_hp_add(struct hotplug_slot *slot)
425{
426	struct pci_slot *pci_slot;
427	int result;
428
429	if (WARN_ON(!slot))
430		return -EINVAL;
431
432	pci_slot = slot->pci_slot;
433
434	result = fs_add_slot(slot, pci_slot);
435	if (result)
436		return result;
437
438	kobject_uevent(&pci_slot->kobj, KOBJ_ADD);
439	return 0;
440}
441EXPORT_SYMBOL_GPL(pci_hp_add);
442
443/**
444 * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem
445 * @slot: pointer to the &struct hotplug_slot to deregister
446 *
447 * The @slot must have been registered with the pci hotplug subsystem
448 * previously with a call to pci_hp_register().
449 */
450void pci_hp_deregister(struct hotplug_slot *slot)
451{
452	pci_hp_del(slot);
453	pci_hp_destroy(slot);
454}
455EXPORT_SYMBOL_GPL(pci_hp_deregister);
456
457/**
458 * pci_hp_del - unpublish hotplug slot from user space
459 * @slot: pointer to the &struct hotplug_slot to unpublish
460 *
461 * Remove a hotplug slot's sysfs interface.
462 */
463void pci_hp_del(struct hotplug_slot *slot)
464{
465	if (WARN_ON(!slot))
466		return;
467
468	fs_remove_slot(slot, slot->pci_slot);
469}
470EXPORT_SYMBOL_GPL(pci_hp_del);
471
472/**
473 * pci_hp_destroy - remove hotplug slot from in-kernel use
474 * @slot: pointer to the &struct hotplug_slot to destroy
475 *
476 * Destroy a PCI slot used by a hotplug driver.  Once this has been called,
477 * the driver may no longer invoke hotplug_slot_name() to get the slot's
478 * unique name.  The driver no longer needs to handle a ->reset_slot callback
479 * from this point on.
480 */
481void pci_hp_destroy(struct hotplug_slot *slot)
482{
483	struct pci_slot *pci_slot = slot->pci_slot;
484
485	slot->pci_slot = NULL;
486	pci_slot->hotplug = NULL;
487	pci_destroy_slot(pci_slot);
488}
489EXPORT_SYMBOL_GPL(pci_hp_destroy);
490
491static DECLARE_WAIT_QUEUE_HEAD(pci_hp_link_change_wq);
492
493/**
494 * pci_hp_ignore_link_change - begin code section causing spurious link changes
495 * @pdev: PCI hotplug bridge
496 *
497 * Mark the beginning of a code section causing spurious link changes on the
498 * Secondary Bus of @pdev, e.g. as a side effect of a Secondary Bus Reset,
499 * D3cold transition, firmware update or FPGA reconfiguration.
500 *
501 * Hotplug drivers can thus check whether such a code section is executing
502 * concurrently, await it with pci_hp_spurious_link_change() and ignore the
503 * resulting link change events.
504 *
505 * Must be paired with pci_hp_unignore_link_change().  May be called both
506 * from the PCI core and from Endpoint drivers.  May be called for bridges
507 * which are not hotplug-capable, in which case it has no effect because
508 * no hotplug driver is bound to the bridge.
509 */
510void pci_hp_ignore_link_change(struct pci_dev *pdev)
511{
512	set_bit(PCI_LINK_CHANGING, &pdev->priv_flags);
513	smp_mb__after_atomic(); /* pairs with implied barrier of wait_event() */
514}
515
516/**
517 * pci_hp_unignore_link_change - end code section causing spurious link changes
518 * @pdev: PCI hotplug bridge
519 *
520 * Mark the end of a code section causing spurious link changes on the
521 * Secondary Bus of @pdev.  Must be paired with pci_hp_ignore_link_change().
522 */
523void pci_hp_unignore_link_change(struct pci_dev *pdev)
524{
525	set_bit(PCI_LINK_CHANGED, &pdev->priv_flags);
526	mb(); /* ensure pci_hp_spurious_link_change() sees either bit set */
527	clear_bit(PCI_LINK_CHANGING, &pdev->priv_flags);
528	wake_up_all(&pci_hp_link_change_wq);
529}
530
531/**
532 * pci_hp_spurious_link_change - check for spurious link changes
533 * @pdev: PCI hotplug bridge
534 *
535 * Check whether a code section is executing concurrently which is causing
536 * spurious link changes on the Secondary Bus of @pdev.  Await the end of the
537 * code section if so.
538 *
539 * May be called by hotplug drivers to check whether a link change is spurious
540 * and can be ignored.
541 *
542 * Because a genuine link change may have occurred in-between a spurious link
543 * change and the invocation of this function, hotplug drivers should perform
544 * sanity checks such as retrieving the current link state and bringing down
545 * the slot if the link is down.
546 *
547 * Return: %true if such a code section has been executing concurrently,
548 * otherwise %false.  Also return %true if such a code section has not been
549 * executing concurrently, but at least once since the last invocation of this
550 * function.
551 */
552bool pci_hp_spurious_link_change(struct pci_dev *pdev)
553{
554	wait_event(pci_hp_link_change_wq,
555		   !test_bit(PCI_LINK_CHANGING, &pdev->priv_flags));
556
557	return test_and_clear_bit(PCI_LINK_CHANGED, &pdev->priv_flags);
558}
559
560static int __init pci_hotplug_init(void)
561{
562	int result;
563
564	result = cpci_hotplug_init(debug);
565	if (result) {
566		err("cpci_hotplug_init with error %d\n", result);
567		return result;
568	}
569
570	return result;
571}
572device_initcall(pci_hotplug_init);
573
574/*
575 * not really modular, but the easiest way to keep compat with existing
576 * bootargs behaviour is to continue using module_param here.
577 */
578module_param(debug, bool, 0644);
579MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
~/ Read-only file preview.