Commit c69c3127 authored by Corey Minyard's avatar Corey Minyard Committed by Linus Torvalds

[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: default avatarDavid Barksdale <amatus@ocgnet.org>
Signed-off-by: default avatarCorey Minyard <minyard@acm.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 54f67f63
...@@ -326,9 +326,12 @@ for events, they will all receive all events that come in. ...@@ -326,9 +326,12 @@ for events, they will all receive all events that come in.
For receiving commands, you have to individually register commands you For receiving commands, you have to individually register commands you
want to receive. Call ipmi_register_for_cmd() and supply the netfn want to receive. Call ipmi_register_for_cmd() and supply the netfn
and command name for each command you want to receive. Only one user and command name for each command you want to receive. You also
may be registered for each netfn/cmd, but different users may register specify a bitmask of the channels you want to receive the command from
for different commands. (or use IPMI_CHAN_ALL for all channels if you don't care). Only one
user may be registered for each netfn/cmd/channel, but different users
may register for different commands, or the same command if the
channel bitmasks do not overlap.
From userland, equivalent IOCTLs are provided to do these functions. From userland, equivalent IOCTLs are provided to do these functions.
......
...@@ -377,7 +377,8 @@ static int ipmi_ioctl(struct inode *inode, ...@@ -377,7 +377,8 @@ static int ipmi_ioctl(struct inode *inode,
break; break;
} }
rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd); rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd,
IPMI_CHAN_ALL);
break; break;
} }
...@@ -390,7 +391,36 @@ static int ipmi_ioctl(struct inode *inode, ...@@ -390,7 +391,36 @@ static int ipmi_ioctl(struct inode *inode,
break; break;
} }
rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd); rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd,
IPMI_CHAN_ALL);
break;
}
case IPMICTL_REGISTER_FOR_CMD_CHANS:
{
struct ipmi_cmdspec_chans val;
if (copy_from_user(&val, arg, sizeof(val))) {
rv = -EFAULT;
break;
}
rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd,
val.chans);
break;
}
case IPMICTL_UNREGISTER_FOR_CMD_CHANS:
{
struct ipmi_cmdspec_chans val;
if (copy_from_user(&val, arg, sizeof(val))) {
rv = -EFAULT;
break;
}
rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd,
val.chans);
break; break;
} }
......
...@@ -96,6 +96,7 @@ struct cmd_rcvr ...@@ -96,6 +96,7 @@ struct cmd_rcvr
ipmi_user_t user; ipmi_user_t user;
unsigned char netfn; unsigned char netfn;
unsigned char cmd; unsigned char cmd;
unsigned int chans;
/* /*
* This is used to form a linked lised during mass deletion. * 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,24 +954,41 @@ int ipmi_set_gets_events(ipmi_user_t user, int val)
static struct cmd_rcvr *find_cmd_rcvr(ipmi_smi_t intf, static struct cmd_rcvr *find_cmd_rcvr(ipmi_smi_t intf,
unsigned char netfn, unsigned char netfn,
unsigned char cmd) unsigned char cmd,
unsigned char chan)
{ {
struct cmd_rcvr *rcvr; struct cmd_rcvr *rcvr;
list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) { list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) {
if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd)) if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd)
&& (rcvr->chans & (1 << chan)))
return rcvr; return rcvr;
} }
return NULL; return NULL;
} }
static int is_cmd_rcvr_exclusive(ipmi_smi_t intf,
unsigned char netfn,
unsigned char cmd,
unsigned int chans)
{
struct cmd_rcvr *rcvr;
list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) {
if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd)
&& (rcvr->chans & chans))
return 0;
}
return 1;
}
int ipmi_register_for_cmd(ipmi_user_t user, int ipmi_register_for_cmd(ipmi_user_t user,
unsigned char netfn, unsigned char netfn,
unsigned char cmd) unsigned char cmd,
unsigned int chans)
{ {
ipmi_smi_t intf = user->intf; ipmi_smi_t intf = user->intf;
struct cmd_rcvr *rcvr; struct cmd_rcvr *rcvr;
struct cmd_rcvr *entry;
int rv = 0; int rv = 0;
...@@ -979,12 +997,12 @@ int ipmi_register_for_cmd(ipmi_user_t user, ...@@ -979,12 +997,12 @@ int ipmi_register_for_cmd(ipmi_user_t user,
return -ENOMEM; return -ENOMEM;
rcvr->cmd = cmd; rcvr->cmd = cmd;
rcvr->netfn = netfn; rcvr->netfn = netfn;
rcvr->chans = chans;
rcvr->user = user; rcvr->user = user;
mutex_lock(&intf->cmd_rcvrs_mutex); mutex_lock(&intf->cmd_rcvrs_mutex);
/* Make sure the command/netfn is not already registered. */ /* Make sure the command/netfn is not already registered. */
entry = find_cmd_rcvr(intf, netfn, cmd); if (!is_cmd_rcvr_exclusive(intf, netfn, cmd, chans)) {
if (entry) {
rv = -EBUSY; rv = -EBUSY;
goto out_unlock; goto out_unlock;
} }
...@@ -1001,24 +1019,39 @@ int ipmi_register_for_cmd(ipmi_user_t user, ...@@ -1001,24 +1019,39 @@ int ipmi_register_for_cmd(ipmi_user_t user,
int ipmi_unregister_for_cmd(ipmi_user_t user, int ipmi_unregister_for_cmd(ipmi_user_t user,
unsigned char netfn, unsigned char netfn,
unsigned char cmd) unsigned char cmd,
unsigned int chans)
{ {
ipmi_smi_t intf = user->intf; ipmi_smi_t intf = user->intf;
struct cmd_rcvr *rcvr; struct cmd_rcvr *rcvr;
struct cmd_rcvr *rcvrs = NULL;
int i, rv = -ENOENT;
mutex_lock(&intf->cmd_rcvrs_mutex); mutex_lock(&intf->cmd_rcvrs_mutex);
/* Make sure the command/netfn is not already registered. */ for (i = 0; i < IPMI_NUM_CHANNELS; i++) {
rcvr = find_cmd_rcvr(intf, netfn, cmd); if (((1 << i) & chans) == 0)
if ((rcvr) && (rcvr->user == user)) { continue;
list_del_rcu(&rcvr->link); rcvr = find_cmd_rcvr(intf, netfn, cmd, i);
mutex_unlock(&intf->cmd_rcvrs_mutex); if (rcvr == NULL)
synchronize_rcu(); continue;
if (rcvr->user == user) {
rv = 0;
rcvr->chans &= ~chans;
if (rcvr->chans == 0) {
list_del_rcu(&rcvr->link);
rcvr->next = rcvrs;
rcvrs = rcvr;
}
}
}
mutex_unlock(&intf->cmd_rcvrs_mutex);
synchronize_rcu();
while (rcvrs) {
rcvr = rcvrs;
rcvrs = rcvr->next;
kfree(rcvr); kfree(rcvr);
return 0;
} else {
mutex_unlock(&intf->cmd_rcvrs_mutex);
return -ENOENT;
} }
return rv;
} }
void ipmi_user_set_run_to_completion(ipmi_user_t user, int val) 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,6 +2581,7 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf,
int rv = 0; int rv = 0;
unsigned char netfn; unsigned char netfn;
unsigned char cmd; unsigned char cmd;
unsigned char chan;
ipmi_user_t user = NULL; ipmi_user_t user = NULL;
struct ipmi_ipmb_addr *ipmb_addr; struct ipmi_ipmb_addr *ipmb_addr;
struct ipmi_recv_msg *recv_msg; struct ipmi_recv_msg *recv_msg;
...@@ -2568,9 +2602,10 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf, ...@@ -2568,9 +2602,10 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf,
netfn = msg->rsp[4] >> 2; netfn = msg->rsp[4] >> 2;
cmd = msg->rsp[8]; cmd = msg->rsp[8];
chan = msg->rsp[3] & 0xf;
rcu_read_lock(); rcu_read_lock();
rcvr = find_cmd_rcvr(intf, netfn, cmd); rcvr = find_cmd_rcvr(intf, netfn, cmd, chan);
if (rcvr) { if (rcvr) {
user = rcvr->user; user = rcvr->user;
kref_get(&user->refcount); kref_get(&user->refcount);
...@@ -2728,6 +2763,7 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf, ...@@ -2728,6 +2763,7 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf,
int rv = 0; int rv = 0;
unsigned char netfn; unsigned char netfn;
unsigned char cmd; unsigned char cmd;
unsigned char chan;
ipmi_user_t user = NULL; ipmi_user_t user = NULL;
struct ipmi_lan_addr *lan_addr; struct ipmi_lan_addr *lan_addr;
struct ipmi_recv_msg *recv_msg; struct ipmi_recv_msg *recv_msg;
...@@ -2748,9 +2784,10 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf, ...@@ -2748,9 +2784,10 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf,
netfn = msg->rsp[6] >> 2; netfn = msg->rsp[6] >> 2;
cmd = msg->rsp[10]; cmd = msg->rsp[10];
chan = msg->rsp[3] & 0xf;
rcu_read_lock(); rcu_read_lock();
rcvr = find_cmd_rcvr(intf, netfn, cmd); rcvr = find_cmd_rcvr(intf, netfn, cmd, chan);
if (rcvr) { if (rcvr) {
user = rcvr->user; user = rcvr->user;
kref_get(&user->refcount); kref_get(&user->refcount);
......
...@@ -148,6 +148,13 @@ struct ipmi_lan_addr ...@@ -148,6 +148,13 @@ struct ipmi_lan_addr
#define IPMI_BMC_CHANNEL 0xf #define IPMI_BMC_CHANNEL 0xf
#define IPMI_NUM_CHANNELS 0x10 #define IPMI_NUM_CHANNELS 0x10
/*
* Used to signify an "all channel" bitmask. This is more than the
* actual number of channels because this is used in userland and
* will cover us if the number of channels is extended.
*/
#define IPMI_CHAN_ALL (~0)
/* /*
* A raw IPMI message without any addressing. This covers both * A raw IPMI message without any addressing. This covers both
...@@ -350,18 +357,21 @@ int ipmi_request_supply_msgs(ipmi_user_t user, ...@@ -350,18 +357,21 @@ int ipmi_request_supply_msgs(ipmi_user_t user,
/* /*
* When commands come in to the SMS, the user can register to receive * When commands come in to the SMS, the user can register to receive
* them. Only one user can be listening on a specific netfn/cmd pair * them. Only one user can be listening on a specific netfn/cmd/chan tuple
* at a time, you will get an EBUSY error if the command is already * at a time, you will get an EBUSY error if the command is already
* registered. If a command is received that does not have a user * registered. If a command is received that does not have a user
* registered, the driver will automatically return the proper * registered, the driver will automatically return the proper
* error. * error. Channels are specified as a bitfield, use IPMI_CHAN_ALL to
* mean all channels.
*/ */
int ipmi_register_for_cmd(ipmi_user_t user, int ipmi_register_for_cmd(ipmi_user_t user,
unsigned char netfn, unsigned char netfn,
unsigned char cmd); unsigned char cmd,
unsigned int chans);
int ipmi_unregister_for_cmd(ipmi_user_t user, int ipmi_unregister_for_cmd(ipmi_user_t user,
unsigned char netfn, unsigned char netfn,
unsigned char cmd); unsigned char cmd,
unsigned int chans);
/* /*
* Allow run-to-completion mode to be set for the interface of * Allow run-to-completion mode to be set for the interface of
...@@ -571,6 +581,36 @@ struct ipmi_cmdspec ...@@ -571,6 +581,36 @@ struct ipmi_cmdspec
#define IPMICTL_UNREGISTER_FOR_CMD _IOR(IPMI_IOC_MAGIC, 15, \ #define IPMICTL_UNREGISTER_FOR_CMD _IOR(IPMI_IOC_MAGIC, 15, \
struct ipmi_cmdspec) struct ipmi_cmdspec)
/*
* Register to get commands from other entities on specific channels.
* This way, you can only listen on specific channels, or have messages
* from some channels go to one place and other channels to someplace
* else. The chans field is a bitmask, (1 << channel) for each channel.
* It may be IPMI_CHAN_ALL for all channels.
*/
struct ipmi_cmdspec_chans
{
unsigned int netfn;
unsigned int cmd;
unsigned int chans;
};
/*
* Register to receive a specific command on specific channels. error values:
* - EFAULT - an address supplied was invalid.
* - EBUSY - One of the netfn/cmd/chans supplied was already in use.
* - ENOMEM - could not allocate memory for the entry.
*/
#define IPMICTL_REGISTER_FOR_CMD_CHANS _IOR(IPMI_IOC_MAGIC, 28, \
struct ipmi_cmdspec_chans)
/*
* Unregister some netfn/cmd/chans. error values:
* - EFAULT - an address supplied was invalid.
* - ENOENT - None of the netfn/cmd/chans were found registered for this user.
*/
#define IPMICTL_UNREGISTER_FOR_CMD_CHANS _IOR(IPMI_IOC_MAGIC, 29, \
struct ipmi_cmdspec_chans)
/* /*
* Set whether this interface receives events. Note that the first * Set whether this interface receives events. Note that the first
* user registered for events will get all pending events for the * user registered for events will get all pending events for the
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment