linux/drivers/pci/hotplug/pci_hotplug_core.c
<<
>>
Prefs
   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 */
  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 sprintf(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_file
 122};
 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 sprintf(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_file
 167};
 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 sprintf(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 sprintf(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_file
 232};
 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 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 */
 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 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 */
 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_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}
 465EXPORT_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 */
 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 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 */
 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 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 */
 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 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 */
 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 existing
 577 * 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