root/drivers/pci/hotplug/pci_hotplug_core.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. GET_STATUS
  2. power_write_file
  3. attention_read_file
  4. attention_write_file
  5. latch_read_file
  6. presence_read_file
  7. test_write_file
  8. has_power_file
  9. has_attention_file
  10. has_latch_file
  11. has_adapter_file
  12. has_test_file
  13. fs_add_slot
  14. fs_remove_slot
  15. get_slot_from_name
  16. __pci_hp_register
  17. __pci_hp_initialize
  18. pci_hp_add
  19. pci_hp_deregister
  20. pci_hp_del
  21. pci_hp_destroy
  22. pci_hotplug_init

   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>       /* 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 */
  43 static bool debug;
  44 
  45 static LIST_HEAD(pci_hotplug_slot_list);
  46 static DEFINE_MUTEX(pci_hp_mutex);
  47 
  48 /* Weee, fun with macros... */
  49 #define GET_STATUS(name, type)  \
  50 static 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 
  62 GET_STATUS(power_status, u8)
  63 GET_STATUS(attention_status, u8)
  64 GET_STATUS(latch_status, u8)
  65 GET_STATUS(adapter_status, u8)
  66 
  67 static 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 sprintf(buf, "%d\n", value);
  77 }
  78 
  79 static 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 
 112 exit:
 113         if (retval)
 114                 return retval;
 115         return count;
 116 }
 117 
 118 static 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_file
 122 };
 123 
 124 static 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 sprintf(buf, "%d\n", value);
 134 }
 135 
 136 static 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 
 157 exit:
 158         if (retval)
 159                 return retval;
 160         return count;
 161 }
 162 
 163 static 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_file
 167 };
 168 
 169 static 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 sprintf(buf, "%d\n", value);
 179 }
 180 
 181 static struct pci_slot_attribute hotplug_slot_attr_latch = {
 182         .attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},
 183         .show = latch_read_file,
 184 };
 185 
 186 static 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 sprintf(buf, "%d\n", value);
 196 }
 197 
 198 static struct pci_slot_attribute hotplug_slot_attr_presence = {
 199         .attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},
 200         .show = presence_read_file,
 201 };
 202 
 203 static 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 
 223 exit:
 224         if (retval)
 225                 return retval;
 226         return count;
 227 }
 228 
 229 static struct pci_slot_attribute hotplug_slot_attr_test = {
 230         .attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},
 231         .store = test_write_file
 232 };
 233 
 234 static 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 
 247 static 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 
 259 static 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 
 270 static 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 
 281 static 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 
 292 static 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 
 336 exit_test:
 337         if (has_adapter_file(pci_slot))
 338                 sysfs_remove_file(&pci_slot->kobj,
 339                                   &hotplug_slot_attr_presence.attr);
 340 exit_adapter:
 341         if (has_latch_file(pci_slot))
 342                 sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr);
 343 exit_latch:
 344         if (has_attention_file(pci_slot))
 345                 sysfs_remove_file(&pci_slot->kobj,
 346                                   &hotplug_slot_attr_attention.attr);
 347 exit_attention:
 348         if (has_power_file(pci_slot))
 349                 sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr);
 350 exit_power:
 351         pci_hp_remove_module_link(pci_slot);
 352 exit:
 353         return retval;
 354 }
 355 
 356 static 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 
 378 static 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 subsystem
 391  * @bus: bus this slot is on
 392  * @slot: pointer to the &struct hotplug_slot to register
 393  * @devnr: device number
 394  * @name: name registered with kobject core
 395  * @owner: caller module owner
 396  * @mod_name: caller module name
 397  *
 398  * Prepares a hotplug slot for in-kernel use and immediately publishes it to
 399  * user space in one go.  Drivers may alternatively carry out the two steps
 400  * separately by invoking pci_hp_initialize() and pci_hp_add().
 401  *
 402  * Returns 0 if successful, anything else for an error.
 403  */
 404 int __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 }
 420 EXPORT_SYMBOL_GPL(__pci_hp_register);
 421 
 422 /**
 423  * __pci_hp_initialize - prepare hotplug slot for in-kernel use
 424  * @slot: pointer to the &struct hotplug_slot to initialize
 425  * @bus: bus this slot is on
 426  * @devnr: slot number
 427  * @name: name registered with kobject core
 428  * @owner: caller module owner
 429  * @mod_name: caller module name
 430  *
 431  * Allocate and fill in a PCI slot for use by a hotplug driver.  Once this has
 432  * been called, the driver may invoke hotplug_slot_name() to get the slot's
 433  * unique name.  The driver must be prepared to handle a ->reset_slot callback
 434  * from this point on.
 435  *
 436  * Returns 0 on success or a negative int on error.
 437  */
 438 int __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_SLOT
 454          * driver and call it here again. If we've already created the
 455          * 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 }
 465 EXPORT_SYMBOL_GPL(__pci_hp_initialize);
 466 
 467 /**
 468  * pci_hp_add - publish hotplug slot to user space
 469  * @slot: pointer to the &struct hotplug_slot to publish
 470  *
 471  * Make a hotplug slot's sysfs interface available and inform user space of its
 472  * addition by sending a uevent.  The hotplug driver must be prepared to handle
 473  * all &struct hotplug_slot_ops callbacks from this point on.
 474  *
 475  * Returns 0 on success or a negative int on error.
 476  */
 477 int 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 }
 493 EXPORT_SYMBOL_GPL(pci_hp_add);
 494 
 495 /**
 496  * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem
 497  * @slot: pointer to the &struct hotplug_slot to deregister
 498  *
 499  * The @slot must have been registered with the pci hotplug subsystem
 500  * previously with a call to pci_hp_register().
 501  *
 502  * Returns 0 if successful, anything else for an error.
 503  */
 504 void pci_hp_deregister(struct hotplug_slot *slot)
 505 {
 506         pci_hp_del(slot);
 507         pci_hp_destroy(slot);
 508 }
 509 EXPORT_SYMBOL_GPL(pci_hp_deregister);
 510 
 511 /**
 512  * pci_hp_del - unpublish hotplug slot from user space
 513  * @slot: pointer to the &struct hotplug_slot to unpublish
 514  *
 515  * Remove a hotplug slot's sysfs interface.
 516  *
 517  * Returns 0 on success or a negative int on error.
 518  */
 519 void 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 }
 538 EXPORT_SYMBOL_GPL(pci_hp_del);
 539 
 540 /**
 541  * pci_hp_destroy - remove hotplug slot from in-kernel use
 542  * @slot: pointer to the &struct hotplug_slot to destroy
 543  *
 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's
 546  * unique name.  The driver no longer needs to handle a ->reset_slot callback
 547  * from this point on.
 548  *
 549  * Returns 0 on success or a negative int on error.
 550  */
 551 void 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 }
 559 EXPORT_SYMBOL_GPL(pci_hp_destroy);
 560 
 561 static 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 }
 573 device_initcall(pci_hotplug_init);
 574 
 575 /*
 576  * not really modular, but the easiest way to keep compat with existing
 577  * bootargs behaviour is to continue using module_param here.
 578  */
 579 module_param(debug, bool, 0644);
 580 MODULE_PARM_DESC(debug, "Debugging mode enabled or not");

/* [<][>][^][v][top][bottom][index][help] */