diff options
author | Corey Minyard <minyard@acm.org> | 2006-12-06 23:41:07 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.osdl.org> | 2006-12-07 11:39:47 -0500 |
commit | b361e27bba261ba59c73df464fa640f7c0fe3553 (patch) | |
tree | 4c457c471eb42be1fbcc9e76989e275fa8e023df /drivers/char/ipmi | |
parent | 15c62e10bb49eebf3da2b010a9196f5095947b0b (diff) |
[PATCH] IPMI: system interface hotplug
Add the ability to hot add and remove interfaces in the ipmi_si driver. Any
users who have the device open will get errors if they try to send a message.
Signed-off-by: Corey Minyard <minyard@acm.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/char/ipmi')
-rw-r--r-- | drivers/char/ipmi/ipmi_si_intf.c | 320 |
1 files changed, 297 insertions, 23 deletions
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index bedd76310045..8b36b5036391 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c | |||
@@ -61,6 +61,10 @@ | |||
61 | #include "ipmi_si_sm.h" | 61 | #include "ipmi_si_sm.h" |
62 | #include <linux/init.h> | 62 | #include <linux/init.h> |
63 | #include <linux/dmi.h> | 63 | #include <linux/dmi.h> |
64 | #include <linux/string.h> | ||
65 | #include <linux/ctype.h> | ||
66 | |||
67 | #define PFX "ipmi_si: " | ||
64 | 68 | ||
65 | /* Measure times between events in the driver. */ | 69 | /* Measure times between events in the driver. */ |
66 | #undef DEBUG_TIMING | 70 | #undef DEBUG_TIMING |
@@ -92,7 +96,7 @@ enum si_intf_state { | |||
92 | enum si_type { | 96 | enum si_type { |
93 | SI_KCS, SI_SMIC, SI_BT | 97 | SI_KCS, SI_SMIC, SI_BT |
94 | }; | 98 | }; |
95 | static char *si_to_str[] = { "KCS", "SMIC", "BT" }; | 99 | static char *si_to_str[] = { "kcs", "smic", "bt" }; |
96 | 100 | ||
97 | #define DEVICE_NAME "ipmi_si" | 101 | #define DEVICE_NAME "ipmi_si" |
98 | 102 | ||
@@ -222,7 +226,10 @@ struct smi_info | |||
222 | static int force_kipmid[SI_MAX_PARMS]; | 226 | static int force_kipmid[SI_MAX_PARMS]; |
223 | static int num_force_kipmid; | 227 | static int num_force_kipmid; |
224 | 228 | ||
229 | static int unload_when_empty = 1; | ||
230 | |||
225 | static int try_smi_init(struct smi_info *smi); | 231 | static int try_smi_init(struct smi_info *smi); |
232 | static void cleanup_one_si(struct smi_info *to_clean); | ||
226 | 233 | ||
227 | static ATOMIC_NOTIFIER_HEAD(xaction_notifier_list); | 234 | static ATOMIC_NOTIFIER_HEAD(xaction_notifier_list); |
228 | static int register_xaction_notifier(struct notifier_block * nb) | 235 | static int register_xaction_notifier(struct notifier_block * nb) |
@@ -247,7 +254,7 @@ static void return_hosed_msg(struct smi_info *smi_info) | |||
247 | /* Make it a reponse */ | 254 | /* Make it a reponse */ |
248 | msg->rsp[0] = msg->data[0] | 4; | 255 | msg->rsp[0] = msg->data[0] | 4; |
249 | msg->rsp[1] = msg->data[1]; | 256 | msg->rsp[1] = msg->data[1]; |
250 | msg->rsp[2] = 0xFF; /* Unknown error. */ | 257 | msg->rsp[2] = IPMI_ERR_UNSPECIFIED; |
251 | msg->rsp_size = 3; | 258 | msg->rsp_size = 3; |
252 | 259 | ||
253 | smi_info->curr_msg = NULL; | 260 | smi_info->curr_msg = NULL; |
@@ -716,6 +723,15 @@ static void sender(void *send_info, | |||
716 | struct timeval t; | 723 | struct timeval t; |
717 | #endif | 724 | #endif |
718 | 725 | ||
726 | if (atomic_read(&smi_info->stop_operation)) { | ||
727 | msg->rsp[0] = msg->data[0] | 4; | ||
728 | msg->rsp[1] = msg->data[1]; | ||
729 | msg->rsp[2] = IPMI_ERR_UNSPECIFIED; | ||
730 | msg->rsp_size = 3; | ||
731 | deliver_recv_msg(smi_info, msg); | ||
732 | return; | ||
733 | } | ||
734 | |||
719 | spin_lock_irqsave(&(smi_info->msg_lock), flags); | 735 | spin_lock_irqsave(&(smi_info->msg_lock), flags); |
720 | #ifdef DEBUG_TIMING | 736 | #ifdef DEBUG_TIMING |
721 | do_gettimeofday(&t); | 737 | do_gettimeofday(&t); |
@@ -819,6 +835,9 @@ static void request_events(void *send_info) | |||
819 | { | 835 | { |
820 | struct smi_info *smi_info = send_info; | 836 | struct smi_info *smi_info = send_info; |
821 | 837 | ||
838 | if (atomic_read(&smi_info->stop_operation)) | ||
839 | return; | ||
840 | |||
822 | atomic_set(&smi_info->req_events, 1); | 841 | atomic_set(&smi_info->req_events, 1); |
823 | } | 842 | } |
824 | 843 | ||
@@ -1003,6 +1022,16 @@ static int num_regshifts = 0; | |||
1003 | static int slave_addrs[SI_MAX_PARMS]; | 1022 | static int slave_addrs[SI_MAX_PARMS]; |
1004 | static int num_slave_addrs = 0; | 1023 | static int num_slave_addrs = 0; |
1005 | 1024 | ||
1025 | #define IPMI_IO_ADDR_SPACE 0 | ||
1026 | #define IPMI_MEM_ADDR_SPACE 1 | ||
1027 | static char *addr_space_to_str[] = { "I/O", "mem" }; | ||
1028 | |||
1029 | static int hotmod_handler(const char *val, struct kernel_param *kp); | ||
1030 | |||
1031 | module_param_call(hotmod, hotmod_handler, NULL, NULL, 0200); | ||
1032 | MODULE_PARM_DESC(hotmod, "Add and remove interfaces. See" | ||
1033 | " Documentation/IPMI.txt in the kernel sources for the" | ||
1034 | " gory details."); | ||
1006 | 1035 | ||
1007 | module_param_named(trydefaults, si_trydefaults, bool, 0); | 1036 | module_param_named(trydefaults, si_trydefaults, bool, 0); |
1008 | MODULE_PARM_DESC(trydefaults, "Setting this to 'false' will disable the" | 1037 | MODULE_PARM_DESC(trydefaults, "Setting this to 'false' will disable the" |
@@ -1054,12 +1083,12 @@ module_param_array(force_kipmid, int, &num_force_kipmid, 0); | |||
1054 | MODULE_PARM_DESC(force_kipmid, "Force the kipmi daemon to be enabled (1) or" | 1083 | MODULE_PARM_DESC(force_kipmid, "Force the kipmi daemon to be enabled (1) or" |
1055 | " disabled(0). Normally the IPMI driver auto-detects" | 1084 | " disabled(0). Normally the IPMI driver auto-detects" |
1056 | " this, but the value may be overridden by this parm."); | 1085 | " this, but the value may be overridden by this parm."); |
1086 | module_param(unload_when_empty, int, 0); | ||
1087 | MODULE_PARM_DESC(unload_when_empty, "Unload the module if no interfaces are" | ||
1088 | " specified or found, default is 1. Setting to 0" | ||
1089 | " is useful for hot add of devices using hotmod."); | ||
1057 | 1090 | ||
1058 | 1091 | ||
1059 | #define IPMI_IO_ADDR_SPACE 0 | ||
1060 | #define IPMI_MEM_ADDR_SPACE 1 | ||
1061 | static char *addr_space_to_str[] = { "I/O", "memory" }; | ||
1062 | |||
1063 | static void std_irq_cleanup(struct smi_info *info) | 1092 | static void std_irq_cleanup(struct smi_info *info) |
1064 | { | 1093 | { |
1065 | if (info->si_type == SI_BT) | 1094 | if (info->si_type == SI_BT) |
@@ -1333,6 +1362,234 @@ static int mem_setup(struct smi_info *info) | |||
1333 | return 0; | 1362 | return 0; |
1334 | } | 1363 | } |
1335 | 1364 | ||
1365 | /* | ||
1366 | * Parms come in as <op1>[:op2[:op3...]]. ops are: | ||
1367 | * add|remove,kcs|bt|smic,mem|i/o,<address>[,<opt1>[,<opt2>[,...]]] | ||
1368 | * Options are: | ||
1369 | * rsp=<regspacing> | ||
1370 | * rsi=<regsize> | ||
1371 | * rsh=<regshift> | ||
1372 | * irq=<irq> | ||
1373 | * ipmb=<ipmb addr> | ||
1374 | */ | ||
1375 | enum hotmod_op { HM_ADD, HM_REMOVE }; | ||
1376 | struct hotmod_vals { | ||
1377 | char *name; | ||
1378 | int val; | ||
1379 | }; | ||
1380 | static struct hotmod_vals hotmod_ops[] = { | ||
1381 | { "add", HM_ADD }, | ||
1382 | { "remove", HM_REMOVE }, | ||
1383 | { NULL } | ||
1384 | }; | ||
1385 | static struct hotmod_vals hotmod_si[] = { | ||
1386 | { "kcs", SI_KCS }, | ||
1387 | { "smic", SI_SMIC }, | ||
1388 | { "bt", SI_BT }, | ||
1389 | { NULL } | ||
1390 | }; | ||
1391 | static struct hotmod_vals hotmod_as[] = { | ||
1392 | { "mem", IPMI_MEM_ADDR_SPACE }, | ||
1393 | { "i/o", IPMI_IO_ADDR_SPACE }, | ||
1394 | { NULL } | ||
1395 | }; | ||
1396 | static int ipmi_strcasecmp(const char *s1, const char *s2) | ||
1397 | { | ||
1398 | while (*s1 || *s2) { | ||
1399 | if (!*s1) | ||
1400 | return -1; | ||
1401 | if (!*s2) | ||
1402 | return 1; | ||
1403 | if (*s1 != *s2) | ||
1404 | return *s1 - *s2; | ||
1405 | s1++; | ||
1406 | s2++; | ||
1407 | } | ||
1408 | return 0; | ||
1409 | } | ||
1410 | static int parse_str(struct hotmod_vals *v, int *val, char *name, char **curr) | ||
1411 | { | ||
1412 | char *s; | ||
1413 | int i; | ||
1414 | |||
1415 | s = strchr(*curr, ','); | ||
1416 | if (!s) { | ||
1417 | printk(KERN_WARNING PFX "No hotmod %s given.\n", name); | ||
1418 | return -EINVAL; | ||
1419 | } | ||
1420 | *s = '\0'; | ||
1421 | s++; | ||
1422 | for (i = 0; hotmod_ops[i].name; i++) { | ||
1423 | if (ipmi_strcasecmp(*curr, v[i].name) == 0) { | ||
1424 | *val = v[i].val; | ||
1425 | *curr = s; | ||
1426 | return 0; | ||
1427 | } | ||
1428 | } | ||
1429 | |||
1430 | printk(KERN_WARNING PFX "Invalid hotmod %s '%s'\n", name, *curr); | ||
1431 | return -EINVAL; | ||
1432 | } | ||
1433 | |||
1434 | static int hotmod_handler(const char *val, struct kernel_param *kp) | ||
1435 | { | ||
1436 | char *str = kstrdup(val, GFP_KERNEL); | ||
1437 | int rv = -EINVAL; | ||
1438 | char *next, *curr, *s, *n, *o; | ||
1439 | enum hotmod_op op; | ||
1440 | enum si_type si_type; | ||
1441 | int addr_space; | ||
1442 | unsigned long addr; | ||
1443 | int regspacing; | ||
1444 | int regsize; | ||
1445 | int regshift; | ||
1446 | int irq; | ||
1447 | int ipmb; | ||
1448 | int ival; | ||
1449 | struct smi_info *info; | ||
1450 | |||
1451 | if (!str) | ||
1452 | return -ENOMEM; | ||
1453 | |||
1454 | /* Kill any trailing spaces, as we can get a "\n" from echo. */ | ||
1455 | ival = strlen(str) - 1; | ||
1456 | while ((ival >= 0) && isspace(str[ival])) { | ||
1457 | str[ival] = '\0'; | ||
1458 | ival--; | ||
1459 | } | ||
1460 | |||
1461 | for (curr = str; curr; curr = next) { | ||
1462 | regspacing = 1; | ||
1463 | regsize = 1; | ||
1464 | regshift = 0; | ||
1465 | irq = 0; | ||
1466 | ipmb = 0x20; | ||
1467 | |||
1468 | next = strchr(curr, ':'); | ||
1469 | if (next) { | ||
1470 | *next = '\0'; | ||
1471 | next++; | ||
1472 | } | ||
1473 | |||
1474 | rv = parse_str(hotmod_ops, &ival, "operation", &curr); | ||
1475 | if (rv) | ||
1476 | break; | ||
1477 | op = ival; | ||
1478 | |||
1479 | rv = parse_str(hotmod_si, &ival, "interface type", &curr); | ||
1480 | if (rv) | ||
1481 | break; | ||
1482 | si_type = ival; | ||
1483 | |||
1484 | rv = parse_str(hotmod_as, &addr_space, "address space", &curr); | ||
1485 | if (rv) | ||
1486 | break; | ||
1487 | |||
1488 | s = strchr(curr, ','); | ||
1489 | if (s) { | ||
1490 | *s = '\0'; | ||
1491 | s++; | ||
1492 | } | ||
1493 | addr = simple_strtoul(curr, &n, 0); | ||
1494 | if ((*n != '\0') || (*curr == '\0')) { | ||
1495 | printk(KERN_WARNING PFX "Invalid hotmod address" | ||
1496 | " '%s'\n", curr); | ||
1497 | break; | ||
1498 | } | ||
1499 | |||
1500 | while (s) { | ||
1501 | curr = s; | ||
1502 | s = strchr(curr, ','); | ||
1503 | if (s) { | ||
1504 | *s = '\0'; | ||
1505 | s++; | ||
1506 | } | ||
1507 | o = strchr(curr, '='); | ||
1508 | if (o) { | ||
1509 | *o = '\0'; | ||
1510 | o++; | ||
1511 | } | ||
1512 | #define HOTMOD_INT_OPT(name, val) \ | ||
1513 | if (ipmi_strcasecmp(curr, name) == 0) { \ | ||
1514 | if (!o) { \ | ||
1515 | printk(KERN_WARNING PFX \ | ||
1516 | "No option given for '%s'\n", \ | ||
1517 | curr); \ | ||
1518 | goto out; \ | ||
1519 | } \ | ||
1520 | val = simple_strtoul(o, &n, 0); \ | ||
1521 | if ((*n != '\0') || (*o == '\0')) { \ | ||
1522 | printk(KERN_WARNING PFX \ | ||
1523 | "Bad option given for '%s'\n", \ | ||
1524 | curr); \ | ||
1525 | goto out; \ | ||
1526 | } \ | ||
1527 | } | ||
1528 | |||
1529 | HOTMOD_INT_OPT("rsp", regspacing) | ||
1530 | else HOTMOD_INT_OPT("rsi", regsize) | ||
1531 | else HOTMOD_INT_OPT("rsh", regshift) | ||
1532 | else HOTMOD_INT_OPT("irq", irq) | ||
1533 | else HOTMOD_INT_OPT("ipmb", ipmb) | ||
1534 | else { | ||
1535 | printk(KERN_WARNING PFX | ||
1536 | "Invalid hotmod option '%s'\n", | ||
1537 | curr); | ||
1538 | goto out; | ||
1539 | } | ||
1540 | #undef HOTMOD_INT_OPT | ||
1541 | } | ||
1542 | |||
1543 | if (op == HM_ADD) { | ||
1544 | info = kzalloc(sizeof(*info), GFP_KERNEL); | ||
1545 | if (!info) { | ||
1546 | rv = -ENOMEM; | ||
1547 | goto out; | ||
1548 | } | ||
1549 | |||
1550 | info->addr_source = "hotmod"; | ||
1551 | info->si_type = si_type; | ||
1552 | info->io.addr_data = addr; | ||
1553 | info->io.addr_type = addr_space; | ||
1554 | if (addr_space == IPMI_MEM_ADDR_SPACE) | ||
1555 | info->io_setup = mem_setup; | ||
1556 | else | ||
1557 | info->io_setup = port_setup; | ||
1558 | |||
1559 | info->io.addr = NULL; | ||
1560 | info->io.regspacing = regspacing; | ||
1561 | if (!info->io.regspacing) | ||
1562 | info->io.regspacing = DEFAULT_REGSPACING; | ||
1563 | info->io.regsize = regsize; | ||
1564 | if (!info->io.regsize) | ||
1565 | info->io.regsize = DEFAULT_REGSPACING; | ||
1566 | info->io.regshift = regshift; | ||
1567 | info->irq = irq; | ||
1568 | if (info->irq) | ||
1569 | info->irq_setup = std_irq_setup; | ||
1570 | info->slave_addr = ipmb; | ||
1571 | |||
1572 | try_smi_init(info); | ||
1573 | } else { | ||
1574 | /* remove */ | ||
1575 | struct smi_info *e, *tmp_e; | ||
1576 | |||
1577 | mutex_lock(&smi_infos_lock); | ||
1578 | list_for_each_entry_safe(e, tmp_e, &smi_infos, link) { | ||
1579 | if (e->io.addr_type != addr_space) | ||
1580 | continue; | ||
1581 | if (e->si_type != si_type) | ||
1582 | continue; | ||
1583 | if (e->io.addr_data == addr) | ||
1584 | cleanup_one_si(e); | ||
1585 | } | ||
1586 | mutex_unlock(&smi_infos_lock); | ||
1587 | } | ||
1588 | } | ||
1589 | out: | ||
1590 | kfree(str); | ||
1591 | return rv; | ||
1592 | } | ||
1336 | 1593 | ||
1337 | static __devinit void hardcode_find_bmc(void) | 1594 | static __devinit void hardcode_find_bmc(void) |
1338 | { | 1595 | { |
@@ -1349,11 +1606,11 @@ static __devinit void hardcode_find_bmc(void) | |||
1349 | 1606 | ||
1350 | info->addr_source = "hardcoded"; | 1607 | info->addr_source = "hardcoded"; |
1351 | 1608 | ||
1352 | if (!si_type[i] || strcmp(si_type[i], "kcs") == 0) { | 1609 | if (!si_type[i] || ipmi_strcasecmp(si_type[i], "kcs") == 0) { |
1353 | info->si_type = SI_KCS; | 1610 | info->si_type = SI_KCS; |
1354 | } else if (strcmp(si_type[i], "smic") == 0) { | 1611 | } else if (ipmi_strcasecmp(si_type[i], "smic") == 0) { |
1355 | info->si_type = SI_SMIC; | 1612 | info->si_type = SI_SMIC; |
1356 | } else if (strcmp(si_type[i], "bt") == 0) { | 1613 | } else if (ipmi_strcasecmp(si_type[i], "bt") == 0) { |
1357 | info->si_type = SI_BT; | 1614 | info->si_type = SI_BT; |
1358 | } else { | 1615 | } else { |
1359 | printk(KERN_WARNING | 1616 | printk(KERN_WARNING |
@@ -1968,19 +2225,9 @@ static int try_get_dev_id(struct smi_info *smi_info) | |||
1968 | static int type_file_read_proc(char *page, char **start, off_t off, | 2225 | static int type_file_read_proc(char *page, char **start, off_t off, |
1969 | int count, int *eof, void *data) | 2226 | int count, int *eof, void *data) |
1970 | { | 2227 | { |
1971 | char *out = (char *) page; | ||
1972 | struct smi_info *smi = data; | 2228 | struct smi_info *smi = data; |
1973 | 2229 | ||
1974 | switch (smi->si_type) { | 2230 | return sprintf(page, "%s\n", si_to_str[smi->si_type]); |
1975 | case SI_KCS: | ||
1976 | return sprintf(out, "kcs\n"); | ||
1977 | case SI_SMIC: | ||
1978 | return sprintf(out, "smic\n"); | ||
1979 | case SI_BT: | ||
1980 | return sprintf(out, "bt\n"); | ||
1981 | default: | ||
1982 | return 0; | ||
1983 | } | ||
1984 | } | 2231 | } |
1985 | 2232 | ||
1986 | static int stat_file_read_proc(char *page, char **start, off_t off, | 2233 | static int stat_file_read_proc(char *page, char **start, off_t off, |
@@ -2016,7 +2263,24 @@ static int stat_file_read_proc(char *page, char **start, off_t off, | |||
2016 | out += sprintf(out, "incoming_messages: %ld\n", | 2263 | out += sprintf(out, "incoming_messages: %ld\n", |
2017 | smi->incoming_messages); | 2264 | smi->incoming_messages); |
2018 | 2265 | ||
2019 | return (out - ((char *) page)); | 2266 | return out - page; |
2267 | } | ||
2268 | |||
2269 | static int param_read_proc(char *page, char **start, off_t off, | ||
2270 | int count, int *eof, void *data) | ||
2271 | { | ||
2272 | struct smi_info *smi = data; | ||
2273 | |||
2274 | return sprintf(page, | ||
2275 | "%s,%s,0x%lx,rsp=%d,rsi=%d,rsh=%d,irq=%d,ipmb=%d\n", | ||
2276 | si_to_str[smi->si_type], | ||
2277 | addr_space_to_str[smi->io.addr_type], | ||
2278 | smi->io.addr_data, | ||
2279 | smi->io.regspacing, | ||
2280 | smi->io.regsize, | ||
2281 | smi->io.regshift, | ||
2282 | smi->irq, | ||
2283 | smi->slave_addr); | ||
2020 | } | 2284 | } |
2021 | 2285 | ||
2022 | /* | 2286 | /* |
@@ -2407,6 +2671,16 @@ static int try_smi_init(struct smi_info *new_smi) | |||
2407 | goto out_err_stop_timer; | 2671 | goto out_err_stop_timer; |
2408 | } | 2672 | } |
2409 | 2673 | ||
2674 | rv = ipmi_smi_add_proc_entry(new_smi->intf, "params", | ||
2675 | param_read_proc, NULL, | ||
2676 | new_smi, THIS_MODULE); | ||
2677 | if (rv) { | ||
2678 | printk(KERN_ERR | ||
2679 | "ipmi_si: Unable to create proc entry: %d\n", | ||
2680 | rv); | ||
2681 | goto out_err_stop_timer; | ||
2682 | } | ||
2683 | |||
2410 | list_add_tail(&new_smi->link, &smi_infos); | 2684 | list_add_tail(&new_smi->link, &smi_infos); |
2411 | 2685 | ||
2412 | mutex_unlock(&smi_infos_lock); | 2686 | mutex_unlock(&smi_infos_lock); |
@@ -2515,7 +2789,7 @@ static __devinit int init_ipmi_si(void) | |||
2515 | } | 2789 | } |
2516 | 2790 | ||
2517 | mutex_lock(&smi_infos_lock); | 2791 | mutex_lock(&smi_infos_lock); |
2518 | if (list_empty(&smi_infos)) { | 2792 | if (unload_when_empty && list_empty(&smi_infos)) { |
2519 | mutex_unlock(&smi_infos_lock); | 2793 | mutex_unlock(&smi_infos_lock); |
2520 | #ifdef CONFIG_PCI | 2794 | #ifdef CONFIG_PCI |
2521 | pci_unregister_driver(&ipmi_pci_driver); | 2795 | pci_unregister_driver(&ipmi_pci_driver); |
@@ -2530,7 +2804,7 @@ static __devinit int init_ipmi_si(void) | |||
2530 | } | 2804 | } |
2531 | module_init(init_ipmi_si); | 2805 | module_init(init_ipmi_si); |
2532 | 2806 | ||
2533 | static void __devexit cleanup_one_si(struct smi_info *to_clean) | 2807 | static void cleanup_one_si(struct smi_info *to_clean) |
2534 | { | 2808 | { |
2535 | int rv; | 2809 | int rv; |
2536 | unsigned long flags; | 2810 | unsigned long flags; |