[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>hifive-unleashed-5.1
parent
54f67f631d
commit
c69c31270c
|
@ -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,
|
||||||
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,
|
||||||
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
|
||||||
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)
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
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;
|
||||||
|
rcvr = find_cmd_rcvr(intf, netfn, cmd, i);
|
||||||
|
if (rcvr == NULL)
|
||||||
|
continue;
|
||||||
|
if (rcvr->user == user) {
|
||||||
|
rv = 0;
|
||||||
|
rcvr->chans &= ~chans;
|
||||||
|
if (rcvr->chans == 0) {
|
||||||
list_del_rcu(&rcvr->link);
|
list_del_rcu(&rcvr->link);
|
||||||
|
rcvr->next = rcvrs;
|
||||||
|
rcvrs = rcvr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
mutex_unlock(&intf->cmd_rcvrs_mutex);
|
mutex_unlock(&intf->cmd_rcvrs_mutex);
|
||||||
synchronize_rcu();
|
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,
|
||||||
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,
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
|
|
||||||
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
|
||||||
#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,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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
|
||||||
#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
|
||||||
|
|
Loading…
Reference in New Issue