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 /drivers/char/ipmi | |
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>
Diffstat (limited to 'drivers/char/ipmi')
-rw-r--r-- | drivers/char/ipmi/ipmi_devintf.c | 34 | ||||
-rw-r--r-- | drivers/char/ipmi/ipmi_msghandler.c | 75 |
2 files changed, 88 insertions, 21 deletions
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); |