diff options
author | Corey Minyard <minyard@acm.org> | 2006-10-01 02:27:56 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-10-01 03:39:23 -0400 |
commit | c69c31270c35a6b8421a8e4ba81de1247ac6df95 (patch) | |
tree | 5f46902faa683f32a69adbe53c8ec3ca4ed19466 | |
parent | 54f67f631dfc25ca7a8b19200e34013abc974337 (diff) |
[PATCH] IPMI: per-channel command registration
This patch adds the ability to register for a command per-channel in the
IPMI driver.
If your BMC supports multiple channels, incoming messages can be useful to
have the ability to register to receive commands on a specific channel
instead the current behaviour of all channels.
Signed-off-by: David Barksdale <amatus@ocgnet.org>
Signed-off-by: Corey Minyard <minyard@acm.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | Documentation/IPMI.txt | 9 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_devintf.c | 34 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_msghandler.c | 75 | ||||
-rw-r--r-- | include/linux/ipmi.h | 48 |
4 files changed, 138 insertions, 28 deletions
diff --git a/Documentation/IPMI.txt b/Documentation/IPMI.txt index 0256805b548f..7756e09ea759 100644 --- a/Documentation/IPMI.txt +++ b/Documentation/IPMI.txt | |||
@@ -326,9 +326,12 @@ for events, they will all receive all events that come in. | |||
326 | 326 | ||
327 | For receiving commands, you have to individually register commands you | 327 | For receiving commands, you have to individually register commands you |
328 | want to receive. Call ipmi_register_for_cmd() and supply the netfn | 328 | want to receive. Call ipmi_register_for_cmd() and supply the netfn |
329 | and command name for each command you want to receive. Only one user | 329 | and command name for each command you want to receive. You also |
330 | may be registered for each netfn/cmd, but different users may register | 330 | specify a bitmask of the channels you want to receive the command from |
331 | for different commands. | 331 | (or use IPMI_CHAN_ALL for all channels if you don't care). Only one |
332 | user may be registered for each netfn/cmd/channel, but different users | ||
333 | may register for different commands, or the same command if the | ||
334 | channel bitmasks do not overlap. | ||
332 | 335 | ||
333 | From userland, equivalent IOCTLs are provided to do these functions. | 336 | From userland, equivalent IOCTLs are provided to do these functions. |
334 | 337 | ||
diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c index 68d7c61a864e..81fcf0ce21d1 100644 --- a/drivers/char/ipmi/ipmi_devintf.c +++ b/drivers/char/ipmi/ipmi_devintf.c | |||
@@ -377,7 +377,8 @@ static int ipmi_ioctl(struct inode *inode, | |||
377 | break; | 377 | break; |
378 | } | 378 | } |
379 | 379 | ||
380 | rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd); | 380 | rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd, |
381 | IPMI_CHAN_ALL); | ||
381 | break; | 382 | break; |
382 | } | 383 | } |
383 | 384 | ||
@@ -390,7 +391,36 @@ static int ipmi_ioctl(struct inode *inode, | |||
390 | break; | 391 | break; |
391 | } | 392 | } |
392 | 393 | ||
393 | rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd); | 394 | rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd, |
395 | IPMI_CHAN_ALL); | ||
396 | break; | ||
397 | } | ||
398 | |||
399 | case IPMICTL_REGISTER_FOR_CMD_CHANS: | ||
400 | { | ||
401 | struct ipmi_cmdspec_chans val; | ||
402 | |||
403 | if (copy_from_user(&val, arg, sizeof(val))) { | ||
404 | rv = -EFAULT; | ||
405 | break; | ||
406 | } | ||
407 | |||
408 | rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd, | ||
409 | val.chans); | ||
410 | break; | ||
411 | } | ||
412 | |||
413 | case IPMICTL_UNREGISTER_FOR_CMD_CHANS: | ||
414 | { | ||
415 | struct ipmi_cmdspec_chans val; | ||
416 | |||
417 | if (copy_from_user(&val, arg, sizeof(val))) { | ||
418 | rv = -EFAULT; | ||
419 | break; | ||
420 | } | ||
421 | |||
422 | rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd, | ||
423 | val.chans); | ||
394 | break; | 424 | break; |
395 | } | 425 | } |
396 | 426 | ||
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 843d34c8627c..2455e8d478ac 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c | |||
@@ -96,6 +96,7 @@ struct cmd_rcvr | |||
96 | ipmi_user_t user; | 96 | ipmi_user_t user; |
97 | unsigned char netfn; | 97 | unsigned char netfn; |
98 | unsigned char cmd; | 98 | unsigned char cmd; |
99 | unsigned int chans; | ||
99 | 100 | ||
100 | /* | 101 | /* |
101 | * This is used to form a linked lised during mass deletion. | 102 | * This is used to form a linked lised during mass deletion. |
@@ -953,24 +954,41 @@ int ipmi_set_gets_events(ipmi_user_t user, int val) | |||
953 | 954 | ||
954 | static struct cmd_rcvr *find_cmd_rcvr(ipmi_smi_t intf, | 955 | static struct cmd_rcvr *find_cmd_rcvr(ipmi_smi_t intf, |
955 | unsigned char netfn, | 956 | unsigned char netfn, |
956 | unsigned char cmd) | 957 | unsigned char cmd, |
958 | unsigned char chan) | ||
957 | { | 959 | { |
958 | struct cmd_rcvr *rcvr; | 960 | struct cmd_rcvr *rcvr; |
959 | 961 | ||
960 | list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) { | 962 | list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) { |
961 | if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd)) | 963 | if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd) |
964 | && (rcvr->chans & (1 << chan))) | ||
962 | return rcvr; | 965 | return rcvr; |
963 | } | 966 | } |
964 | return NULL; | 967 | return NULL; |
965 | } | 968 | } |
966 | 969 | ||
970 | static int is_cmd_rcvr_exclusive(ipmi_smi_t intf, | ||
971 | unsigned char netfn, | ||
972 | unsigned char cmd, | ||
973 | unsigned int chans) | ||
974 | { | ||
975 | struct cmd_rcvr *rcvr; | ||
976 | |||
977 | list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) { | ||
978 | if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd) | ||
979 | && (rcvr->chans & chans)) | ||
980 | return 0; | ||
981 | } | ||
982 | return 1; | ||
983 | } | ||
984 | |||
967 | int ipmi_register_for_cmd(ipmi_user_t user, | 985 | int ipmi_register_for_cmd(ipmi_user_t user, |
968 | unsigned char netfn, | 986 | unsigned char netfn, |
969 | unsigned char cmd) | 987 | unsigned char cmd, |
988 | unsigned int chans) | ||
970 | { | 989 | { |
971 | ipmi_smi_t intf = user->intf; | 990 | ipmi_smi_t intf = user->intf; |
972 | struct cmd_rcvr *rcvr; | 991 | struct cmd_rcvr *rcvr; |
973 | struct cmd_rcvr *entry; | ||
974 | int rv = 0; | 992 | int rv = 0; |
975 | 993 | ||
976 | 994 | ||
@@ -979,12 +997,12 @@ int ipmi_register_for_cmd(ipmi_user_t user, | |||
979 | return -ENOMEM; | 997 | return -ENOMEM; |
980 | rcvr->cmd = cmd; | 998 | rcvr->cmd = cmd; |
981 | rcvr->netfn = netfn; | 999 | rcvr->netfn = netfn; |
1000 | rcvr->chans = chans; | ||
982 | rcvr->user = user; | 1001 | rcvr->user = user; |
983 | 1002 | ||
984 | mutex_lock(&intf->cmd_rcvrs_mutex); | 1003 | mutex_lock(&intf->cmd_rcvrs_mutex); |
985 | /* Make sure the command/netfn is not already registered. */ | 1004 | /* Make sure the command/netfn is not already registered. */ |
986 | entry = find_cmd_rcvr(intf, netfn, cmd); | 1005 | if (!is_cmd_rcvr_exclusive(intf, netfn, cmd, chans)) { |
987 | if (entry) { | ||
988 | rv = -EBUSY; | 1006 | rv = -EBUSY; |
989 | goto out_unlock; | 1007 | goto out_unlock; |
990 | } | 1008 | } |
@@ -1001,24 +1019,39 @@ int ipmi_register_for_cmd(ipmi_user_t user, | |||
1001 | 1019 | ||
1002 | int ipmi_unregister_for_cmd(ipmi_user_t user, | 1020 | int ipmi_unregister_for_cmd(ipmi_user_t user, |
1003 | unsigned char netfn, | 1021 | unsigned char netfn, |
1004 | unsigned char cmd) | 1022 | unsigned char cmd, |
1023 | unsigned int chans) | ||
1005 | { | 1024 | { |
1006 | ipmi_smi_t intf = user->intf; | 1025 | ipmi_smi_t intf = user->intf; |
1007 | struct cmd_rcvr *rcvr; | 1026 | struct cmd_rcvr *rcvr; |
1027 | struct cmd_rcvr *rcvrs = NULL; | ||
1028 | int i, rv = -ENOENT; | ||
1008 | 1029 | ||
1009 | mutex_lock(&intf->cmd_rcvrs_mutex); | 1030 | mutex_lock(&intf->cmd_rcvrs_mutex); |
1010 | /* Make sure the command/netfn is not already registered. */ | 1031 | for (i = 0; i < IPMI_NUM_CHANNELS; i++) { |
1011 | rcvr = find_cmd_rcvr(intf, netfn, cmd); | 1032 | if (((1 << i) & chans) == 0) |
1012 | if ((rcvr) && (rcvr->user == user)) { | 1033 | continue; |
1013 | list_del_rcu(&rcvr->link); | 1034 | rcvr = find_cmd_rcvr(intf, netfn, cmd, i); |
1014 | mutex_unlock(&intf->cmd_rcvrs_mutex); | 1035 | if (rcvr == NULL) |
1015 | synchronize_rcu(); | 1036 | continue; |
1037 | if (rcvr->user == user) { | ||
1038 | rv = 0; | ||
1039 | rcvr->chans &= ~chans; | ||
1040 | if (rcvr->chans == 0) { | ||
1041 | list_del_rcu(&rcvr->link); | ||
1042 | rcvr->next = rcvrs; | ||
1043 | rcvrs = rcvr; | ||
1044 | } | ||
1045 | } | ||
1046 | } | ||
1047 | mutex_unlock(&intf->cmd_rcvrs_mutex); | ||
1048 | synchronize_rcu(); | ||
1049 | while (rcvrs) { | ||
1050 | rcvr = rcvrs; | ||
1051 | rcvrs = rcvr->next; | ||
1016 | kfree(rcvr); | 1052 | kfree(rcvr); |
1017 | return 0; | ||
1018 | } else { | ||
1019 | mutex_unlock(&intf->cmd_rcvrs_mutex); | ||
1020 | return -ENOENT; | ||
1021 | } | 1053 | } |
1054 | return rv; | ||
1022 | } | 1055 | } |
1023 | 1056 | ||
1024 | void ipmi_user_set_run_to_completion(ipmi_user_t user, int val) | 1057 | void ipmi_user_set_run_to_completion(ipmi_user_t user, int val) |
@@ -2548,6 +2581,7 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, | |||
2548 | int rv = 0; | 2581 | int rv = 0; |
2549 | unsigned char netfn; | 2582 | unsigned char netfn; |
2550 | unsigned char cmd; | 2583 | unsigned char cmd; |
2584 | unsigned char chan; | ||
2551 | ipmi_user_t user = NULL; | 2585 | ipmi_user_t user = NULL; |
2552 | struct ipmi_ipmb_addr *ipmb_addr; | 2586 | struct ipmi_ipmb_addr *ipmb_addr; |
2553 | struct ipmi_recv_msg *recv_msg; | 2587 | struct ipmi_recv_msg *recv_msg; |
@@ -2568,9 +2602,10 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, | |||
2568 | 2602 | ||
2569 | netfn = msg->rsp[4] >> 2; | 2603 | netfn = msg->rsp[4] >> 2; |
2570 | cmd = msg->rsp[8]; | 2604 | cmd = msg->rsp[8]; |
2605 | chan = msg->rsp[3] & 0xf; | ||
2571 | 2606 | ||
2572 | rcu_read_lock(); | 2607 | rcu_read_lock(); |
2573 | rcvr = find_cmd_rcvr(intf, netfn, cmd); | 2608 | rcvr = find_cmd_rcvr(intf, netfn, cmd, chan); |
2574 | if (rcvr) { | 2609 | if (rcvr) { |
2575 | user = rcvr->user; | 2610 | user = rcvr->user; |
2576 | kref_get(&user->refcount); | 2611 | kref_get(&user->refcount); |
@@ -2728,6 +2763,7 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf, | |||
2728 | int rv = 0; | 2763 | int rv = 0; |
2729 | unsigned char netfn; | 2764 | unsigned char netfn; |
2730 | unsigned char cmd; | 2765 | unsigned char cmd; |
2766 | unsigned char chan; | ||
2731 | ipmi_user_t user = NULL; | 2767 | ipmi_user_t user = NULL; |
2732 | struct ipmi_lan_addr *lan_addr; | 2768 | struct ipmi_lan_addr *lan_addr; |
2733 | struct ipmi_recv_msg *recv_msg; | 2769 | struct ipmi_recv_msg *recv_msg; |
@@ -2748,9 +2784,10 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf, | |||
2748 | 2784 | ||
2749 | netfn = msg->rsp[6] >> 2; | 2785 | netfn = msg->rsp[6] >> 2; |
2750 | cmd = msg->rsp[10]; | 2786 | cmd = msg->rsp[10]; |
2787 | chan = msg->rsp[3] & 0xf; | ||
2751 | 2788 | ||
2752 | rcu_read_lock(); | 2789 | rcu_read_lock(); |
2753 | rcvr = find_cmd_rcvr(intf, netfn, cmd); | 2790 | rcvr = find_cmd_rcvr(intf, netfn, cmd, chan); |
2754 | if (rcvr) { | 2791 | if (rcvr) { |
2755 | user = rcvr->user; | 2792 | user = rcvr->user; |
2756 | kref_get(&user->refcount); | 2793 | kref_get(&user->refcount); |
diff --git a/include/linux/ipmi.h b/include/linux/ipmi.h index d09fbeabf1dc..796ca009fd46 100644 --- a/include/linux/ipmi.h +++ b/include/linux/ipmi.h | |||
@@ -148,6 +148,13 @@ struct ipmi_lan_addr | |||
148 | #define IPMI_BMC_CHANNEL 0xf | 148 | #define IPMI_BMC_CHANNEL 0xf |
149 | #define IPMI_NUM_CHANNELS 0x10 | 149 | #define IPMI_NUM_CHANNELS 0x10 |
150 | 150 | ||
151 | /* | ||
152 | * Used to signify an "all channel" bitmask. This is more than the | ||
153 | * actual number of channels because this is used in userland and | ||
154 | * will cover us if the number of channels is extended. | ||
155 | */ | ||
156 | #define IPMI_CHAN_ALL (~0) | ||
157 | |||
151 | 158 | ||
152 | /* | 159 | /* |
153 | * A raw IPMI message without any addressing. This covers both | 160 | * A raw IPMI message without any addressing. This covers both |
@@ -350,18 +357,21 @@ int ipmi_request_supply_msgs(ipmi_user_t user, | |||
350 | 357 | ||
351 | /* | 358 | /* |
352 | * When commands come in to the SMS, the user can register to receive | 359 | * When commands come in to the SMS, the user can register to receive |
353 | * them. Only one user can be listening on a specific netfn/cmd pair | 360 | * them. Only one user can be listening on a specific netfn/cmd/chan tuple |
354 | * at a time, you will get an EBUSY error if the command is already | 361 | * at a time, you will get an EBUSY error if the command is already |
355 | * registered. If a command is received that does not have a user | 362 | * registered. If a command is received that does not have a user |
356 | * registered, the driver will automatically return the proper | 363 | * registered, the driver will automatically return the proper |
357 | * error. | 364 | * error. Channels are specified as a bitfield, use IPMI_CHAN_ALL to |
365 | * mean all channels. | ||
358 | */ | 366 | */ |
359 | int ipmi_register_for_cmd(ipmi_user_t user, | 367 | int ipmi_register_for_cmd(ipmi_user_t user, |
360 | unsigned char netfn, | 368 | unsigned char netfn, |
361 | unsigned char cmd); | 369 | unsigned char cmd, |
370 | unsigned int chans); | ||
362 | int ipmi_unregister_for_cmd(ipmi_user_t user, | 371 | int ipmi_unregister_for_cmd(ipmi_user_t user, |
363 | unsigned char netfn, | 372 | unsigned char netfn, |
364 | unsigned char cmd); | 373 | unsigned char cmd, |
374 | unsigned int chans); | ||
365 | 375 | ||
366 | /* | 376 | /* |
367 | * Allow run-to-completion mode to be set for the interface of | 377 | * Allow run-to-completion mode to be set for the interface of |
@@ -571,6 +581,36 @@ struct ipmi_cmdspec | |||
571 | #define IPMICTL_UNREGISTER_FOR_CMD _IOR(IPMI_IOC_MAGIC, 15, \ | 581 | #define IPMICTL_UNREGISTER_FOR_CMD _IOR(IPMI_IOC_MAGIC, 15, \ |
572 | struct ipmi_cmdspec) | 582 | struct ipmi_cmdspec) |
573 | 583 | ||
584 | /* | ||
585 | * Register to get commands from other entities on specific channels. | ||
586 | * This way, you can only listen on specific channels, or have messages | ||
587 | * from some channels go to one place and other channels to someplace | ||
588 | * else. The chans field is a bitmask, (1 << channel) for each channel. | ||
589 | * It may be IPMI_CHAN_ALL for all channels. | ||
590 | */ | ||
591 | struct ipmi_cmdspec_chans | ||
592 | { | ||
593 | unsigned int netfn; | ||
594 | unsigned int cmd; | ||
595 | unsigned int chans; | ||
596 | }; | ||
597 | |||
598 | /* | ||
599 | * Register to receive a specific command on specific channels. error values: | ||
600 | * - EFAULT - an address supplied was invalid. | ||
601 | * - EBUSY - One of the netfn/cmd/chans supplied was already in use. | ||
602 | * - ENOMEM - could not allocate memory for the entry. | ||
603 | */ | ||
604 | #define IPMICTL_REGISTER_FOR_CMD_CHANS _IOR(IPMI_IOC_MAGIC, 28, \ | ||
605 | struct ipmi_cmdspec_chans) | ||
606 | /* | ||
607 | * Unregister some netfn/cmd/chans. error values: | ||
608 | * - EFAULT - an address supplied was invalid. | ||
609 | * - ENOENT - None of the netfn/cmd/chans were found registered for this user. | ||
610 | */ | ||
611 | #define IPMICTL_UNREGISTER_FOR_CMD_CHANS _IOR(IPMI_IOC_MAGIC, 29, \ | ||
612 | struct ipmi_cmdspec_chans) | ||
613 | |||
574 | /* | 614 | /* |
575 | * Set whether this interface receives events. Note that the first | 615 | * Set whether this interface receives events. Note that the first |
576 | * user registered for events will get all pending events for the | 616 | * user registered for events will get all pending events for the |