Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v4.15-rc1 543 lines 14 kB view raw
1/* 2 * PCI HotPlug Controller Core 3 * 4 * Copyright (C) 2001-2002 Greg Kroah-Hartman (greg@kroah.com) 5 * Copyright (C) 2001-2002 IBM Corp. 6 * 7 * All rights reserved. 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or (at 12 * your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, but 15 * WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 17 * NON INFRINGEMENT. See the GNU General Public License for more 18 * details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with this program; if not, write to the Free Software 22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 23 * 24 * Send feedback to <kristen.c.accardi@intel.com> 25 * 26 * Authors: 27 * Greg Kroah-Hartman <greg@kroah.com> 28 * Scott Murray <scottm@somanetworks.com> 29 */ 30 31#include <linux/module.h> /* try_module_get & module_put */ 32#include <linux/moduleparam.h> 33#include <linux/kernel.h> 34#include <linux/types.h> 35#include <linux/list.h> 36#include <linux/kobject.h> 37#include <linux/sysfs.h> 38#include <linux/pagemap.h> 39#include <linux/init.h> 40#include <linux/mount.h> 41#include <linux/namei.h> 42#include <linux/mutex.h> 43#include <linux/pci.h> 44#include <linux/pci_hotplug.h> 45#include <linux/uaccess.h> 46#include "../pci.h" 47#include "cpci_hotplug.h" 48 49#define MY_NAME "pci_hotplug" 50 51#define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt, MY_NAME, __func__, ## arg); } while (0) 52#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME, ## arg) 53#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME, ## arg) 54#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME, ## arg) 55 56/* local variables */ 57static bool debug; 58 59static LIST_HEAD(pci_hotplug_slot_list); 60static DEFINE_MUTEX(pci_hp_mutex); 61 62/* Weee, fun with macros... */ 63#define GET_STATUS(name, type) \ 64static int get_##name(struct hotplug_slot *slot, type *value) \ 65{ \ 66 struct hotplug_slot_ops *ops = slot->ops; \ 67 int retval = 0; \ 68 if (!try_module_get(ops->owner)) \ 69 return -ENODEV; \ 70 if (ops->get_##name) \ 71 retval = ops->get_##name(slot, value); \ 72 else \ 73 *value = slot->info->name; \ 74 module_put(ops->owner); \ 75 return retval; \ 76} 77 78GET_STATUS(power_status, u8) 79GET_STATUS(attention_status, u8) 80GET_STATUS(latch_status, u8) 81GET_STATUS(adapter_status, u8) 82 83static ssize_t power_read_file(struct pci_slot *pci_slot, char *buf) 84{ 85 int retval; 86 u8 value; 87 88 retval = get_power_status(pci_slot->hotplug, &value); 89 if (retval) 90 return retval; 91 92 return sprintf(buf, "%d\n", value); 93} 94 95static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf, 96 size_t count) 97{ 98 struct hotplug_slot *slot = pci_slot->hotplug; 99 unsigned long lpower; 100 u8 power; 101 int retval = 0; 102 103 lpower = simple_strtoul(buf, NULL, 10); 104 power = (u8)(lpower & 0xff); 105 dbg("power = %d\n", power); 106 107 if (!try_module_get(slot->ops->owner)) { 108 retval = -ENODEV; 109 goto exit; 110 } 111 switch (power) { 112 case 0: 113 if (slot->ops->disable_slot) 114 retval = slot->ops->disable_slot(slot); 115 break; 116 117 case 1: 118 if (slot->ops->enable_slot) 119 retval = slot->ops->enable_slot(slot); 120 break; 121 122 default: 123 err("Illegal value specified for power\n"); 124 retval = -EINVAL; 125 } 126 module_put(slot->ops->owner); 127 128exit: 129 if (retval) 130 return retval; 131 return count; 132} 133 134static struct pci_slot_attribute hotplug_slot_attr_power = { 135 .attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR}, 136 .show = power_read_file, 137 .store = power_write_file 138}; 139 140static ssize_t attention_read_file(struct pci_slot *pci_slot, char *buf) 141{ 142 int retval; 143 u8 value; 144 145 retval = get_attention_status(pci_slot->hotplug, &value); 146 if (retval) 147 return retval; 148 149 return sprintf(buf, "%d\n", value); 150} 151 152static ssize_t attention_write_file(struct pci_slot *pci_slot, const char *buf, 153 size_t count) 154{ 155 struct hotplug_slot_ops *ops = pci_slot->hotplug->ops; 156 unsigned long lattention; 157 u8 attention; 158 int retval = 0; 159 160 lattention = simple_strtoul(buf, NULL, 10); 161 attention = (u8)(lattention & 0xff); 162 dbg(" - attention = %d\n", attention); 163 164 if (!try_module_get(ops->owner)) { 165 retval = -ENODEV; 166 goto exit; 167 } 168 if (ops->set_attention_status) 169 retval = ops->set_attention_status(pci_slot->hotplug, attention); 170 module_put(ops->owner); 171 172exit: 173 if (retval) 174 return retval; 175 return count; 176} 177 178static struct pci_slot_attribute hotplug_slot_attr_attention = { 179 .attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR}, 180 .show = attention_read_file, 181 .store = attention_write_file 182}; 183 184static ssize_t latch_read_file(struct pci_slot *pci_slot, char *buf) 185{ 186 int retval; 187 u8 value; 188 189 retval = get_latch_status(pci_slot->hotplug, &value); 190 if (retval) 191 return retval; 192 193 return sprintf(buf, "%d\n", value); 194} 195 196static struct pci_slot_attribute hotplug_slot_attr_latch = { 197 .attr = {.name = "latch", .mode = S_IFREG | S_IRUGO}, 198 .show = latch_read_file, 199}; 200 201static ssize_t presence_read_file(struct pci_slot *pci_slot, char *buf) 202{ 203 int retval; 204 u8 value; 205 206 retval = get_adapter_status(pci_slot->hotplug, &value); 207 if (retval) 208 return retval; 209 210 return sprintf(buf, "%d\n", value); 211} 212 213static struct pci_slot_attribute hotplug_slot_attr_presence = { 214 .attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO}, 215 .show = presence_read_file, 216}; 217 218static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf, 219 size_t count) 220{ 221 struct hotplug_slot *slot = pci_slot->hotplug; 222 unsigned long ltest; 223 u32 test; 224 int retval = 0; 225 226 ltest = simple_strtoul(buf, NULL, 10); 227 test = (u32)(ltest & 0xffffffff); 228 dbg("test = %d\n", test); 229 230 if (!try_module_get(slot->ops->owner)) { 231 retval = -ENODEV; 232 goto exit; 233 } 234 if (slot->ops->hardware_test) 235 retval = slot->ops->hardware_test(slot, test); 236 module_put(slot->ops->owner); 237 238exit: 239 if (retval) 240 return retval; 241 return count; 242} 243 244static struct pci_slot_attribute hotplug_slot_attr_test = { 245 .attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR}, 246 .store = test_write_file 247}; 248 249static bool has_power_file(struct pci_slot *pci_slot) 250{ 251 struct hotplug_slot *slot = pci_slot->hotplug; 252 253 if ((!slot) || (!slot->ops)) 254 return false; 255 if ((slot->ops->enable_slot) || 256 (slot->ops->disable_slot) || 257 (slot->ops->get_power_status)) 258 return true; 259 return false; 260} 261 262static bool has_attention_file(struct pci_slot *pci_slot) 263{ 264 struct hotplug_slot *slot = pci_slot->hotplug; 265 266 if ((!slot) || (!slot->ops)) 267 return false; 268 if ((slot->ops->set_attention_status) || 269 (slot->ops->get_attention_status)) 270 return true; 271 return false; 272} 273 274static bool has_latch_file(struct pci_slot *pci_slot) 275{ 276 struct hotplug_slot *slot = pci_slot->hotplug; 277 278 if ((!slot) || (!slot->ops)) 279 return false; 280 if (slot->ops->get_latch_status) 281 return true; 282 return false; 283} 284 285static bool has_adapter_file(struct pci_slot *pci_slot) 286{ 287 struct hotplug_slot *slot = pci_slot->hotplug; 288 289 if ((!slot) || (!slot->ops)) 290 return false; 291 if (slot->ops->get_adapter_status) 292 return true; 293 return false; 294} 295 296static bool has_test_file(struct pci_slot *pci_slot) 297{ 298 struct hotplug_slot *slot = pci_slot->hotplug; 299 300 if ((!slot) || (!slot->ops)) 301 return false; 302 if (slot->ops->hardware_test) 303 return true; 304 return false; 305} 306 307static int fs_add_slot(struct pci_slot *pci_slot) 308{ 309 int retval = 0; 310 311 /* Create symbolic link to the hotplug driver module */ 312 pci_hp_create_module_link(pci_slot); 313 314 if (has_power_file(pci_slot)) { 315 retval = sysfs_create_file(&pci_slot->kobj, 316 &hotplug_slot_attr_power.attr); 317 if (retval) 318 goto exit_power; 319 } 320 321 if (has_attention_file(pci_slot)) { 322 retval = sysfs_create_file(&pci_slot->kobj, 323 &hotplug_slot_attr_attention.attr); 324 if (retval) 325 goto exit_attention; 326 } 327 328 if (has_latch_file(pci_slot)) { 329 retval = sysfs_create_file(&pci_slot->kobj, 330 &hotplug_slot_attr_latch.attr); 331 if (retval) 332 goto exit_latch; 333 } 334 335 if (has_adapter_file(pci_slot)) { 336 retval = sysfs_create_file(&pci_slot->kobj, 337 &hotplug_slot_attr_presence.attr); 338 if (retval) 339 goto exit_adapter; 340 } 341 342 if (has_test_file(pci_slot)) { 343 retval = sysfs_create_file(&pci_slot->kobj, 344 &hotplug_slot_attr_test.attr); 345 if (retval) 346 goto exit_test; 347 } 348 349 goto exit; 350 351exit_test: 352 if (has_adapter_file(pci_slot)) 353 sysfs_remove_file(&pci_slot->kobj, 354 &hotplug_slot_attr_presence.attr); 355exit_adapter: 356 if (has_latch_file(pci_slot)) 357 sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr); 358exit_latch: 359 if (has_attention_file(pci_slot)) 360 sysfs_remove_file(&pci_slot->kobj, 361 &hotplug_slot_attr_attention.attr); 362exit_attention: 363 if (has_power_file(pci_slot)) 364 sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr); 365exit_power: 366 pci_hp_remove_module_link(pci_slot); 367exit: 368 return retval; 369} 370 371static void fs_remove_slot(struct pci_slot *pci_slot) 372{ 373 if (has_power_file(pci_slot)) 374 sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr); 375 376 if (has_attention_file(pci_slot)) 377 sysfs_remove_file(&pci_slot->kobj, 378 &hotplug_slot_attr_attention.attr); 379 380 if (has_latch_file(pci_slot)) 381 sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr); 382 383 if (has_adapter_file(pci_slot)) 384 sysfs_remove_file(&pci_slot->kobj, 385 &hotplug_slot_attr_presence.attr); 386 387 if (has_test_file(pci_slot)) 388 sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_test.attr); 389 390 pci_hp_remove_module_link(pci_slot); 391} 392 393static struct hotplug_slot *get_slot_from_name(const char *name) 394{ 395 struct hotplug_slot *slot; 396 397 list_for_each_entry(slot, &pci_hotplug_slot_list, slot_list) { 398 if (strcmp(hotplug_slot_name(slot), name) == 0) 399 return slot; 400 } 401 return NULL; 402} 403 404/** 405 * __pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem 406 * @bus: bus this slot is on 407 * @slot: pointer to the &struct hotplug_slot to register 408 * @devnr: device number 409 * @name: name registered with kobject core 410 * @owner: caller module owner 411 * @mod_name: caller module name 412 * 413 * Registers a hotplug slot with the pci hotplug subsystem, which will allow 414 * userspace interaction to the slot. 415 * 416 * Returns 0 if successful, anything else for an error. 417 */ 418int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, 419 int devnr, const char *name, 420 struct module *owner, const char *mod_name) 421{ 422 int result; 423 struct pci_slot *pci_slot; 424 425 if (slot == NULL) 426 return -ENODEV; 427 if ((slot->info == NULL) || (slot->ops == NULL)) 428 return -EINVAL; 429 if (slot->release == NULL) { 430 dbg("Why are you trying to register a hotplug slot without a proper release function?\n"); 431 return -EINVAL; 432 } 433 434 slot->ops->owner = owner; 435 slot->ops->mod_name = mod_name; 436 437 mutex_lock(&pci_hp_mutex); 438 /* 439 * No problems if we call this interface from both ACPI_PCI_SLOT 440 * driver and call it here again. If we've already created the 441 * pci_slot, the interface will simply bump the refcount. 442 */ 443 pci_slot = pci_create_slot(bus, devnr, name, slot); 444 if (IS_ERR(pci_slot)) { 445 result = PTR_ERR(pci_slot); 446 goto out; 447 } 448 449 slot->pci_slot = pci_slot; 450 pci_slot->hotplug = slot; 451 452 list_add(&slot->slot_list, &pci_hotplug_slot_list); 453 454 result = fs_add_slot(pci_slot); 455 kobject_uevent(&pci_slot->kobj, KOBJ_ADD); 456 dbg("Added slot %s to the list\n", name); 457out: 458 mutex_unlock(&pci_hp_mutex); 459 return result; 460} 461EXPORT_SYMBOL_GPL(__pci_hp_register); 462 463/** 464 * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem 465 * @slot: pointer to the &struct hotplug_slot to deregister 466 * 467 * The @slot must have been registered with the pci hotplug subsystem 468 * previously with a call to pci_hp_register(). 469 * 470 * Returns 0 if successful, anything else for an error. 471 */ 472int pci_hp_deregister(struct hotplug_slot *slot) 473{ 474 struct hotplug_slot *temp; 475 struct pci_slot *pci_slot; 476 477 if (!slot) 478 return -ENODEV; 479 480 mutex_lock(&pci_hp_mutex); 481 temp = get_slot_from_name(hotplug_slot_name(slot)); 482 if (temp != slot) { 483 mutex_unlock(&pci_hp_mutex); 484 return -ENODEV; 485 } 486 487 list_del(&slot->slot_list); 488 489 pci_slot = slot->pci_slot; 490 fs_remove_slot(pci_slot); 491 dbg("Removed slot %s from the list\n", hotplug_slot_name(slot)); 492 493 slot->release(slot); 494 pci_slot->hotplug = NULL; 495 pci_destroy_slot(pci_slot); 496 mutex_unlock(&pci_hp_mutex); 497 498 return 0; 499} 500EXPORT_SYMBOL_GPL(pci_hp_deregister); 501 502/** 503 * pci_hp_change_slot_info - changes the slot's information structure in the core 504 * @slot: pointer to the slot whose info has changed 505 * @info: pointer to the info copy into the slot's info structure 506 * 507 * @slot must have been registered with the pci 508 * hotplug subsystem previously with a call to pci_hp_register(). 509 * 510 * Returns 0 if successful, anything else for an error. 511 */ 512int pci_hp_change_slot_info(struct hotplug_slot *slot, 513 struct hotplug_slot_info *info) 514{ 515 if (!slot || !info) 516 return -ENODEV; 517 518 memcpy(slot->info, info, sizeof(struct hotplug_slot_info)); 519 520 return 0; 521} 522EXPORT_SYMBOL_GPL(pci_hp_change_slot_info); 523 524static int __init pci_hotplug_init(void) 525{ 526 int result; 527 528 result = cpci_hotplug_init(debug); 529 if (result) { 530 err("cpci_hotplug_init with error %d\n", result); 531 return result; 532 } 533 534 return result; 535} 536device_initcall(pci_hotplug_init); 537 538/* 539 * not really modular, but the easiest way to keep compat with existing 540 * bootargs behaviour is to continue using module_param here. 541 */ 542module_param(debug, bool, 0644); 543MODULE_PARM_DESC(debug, "Debugging mode enabled or not");