summaryrefslogtreecommitdiffstats
path: root/drivers/char/ipmi/ipmi_msghandler.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char/ipmi/ipmi_msghandler.c')
-rw-r--r--drivers/char/ipmi/ipmi_msghandler.c2121
1 files changed, 1000 insertions, 1121 deletions
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index 361148938801..51832b8a2c62 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -37,11 +37,30 @@
static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void);
static int ipmi_init_msghandler(void);
static void smi_recv_tasklet(unsigned long);
-static void handle_new_recv_msgs(ipmi_smi_t intf);
-static void need_waiter(ipmi_smi_t intf);
-static int handle_one_recv_msg(ipmi_smi_t intf,
+static void handle_new_recv_msgs(struct ipmi_smi *intf);
+static void need_waiter(struct ipmi_smi *intf);
+static int handle_one_recv_msg(struct ipmi_smi *intf,
struct ipmi_smi_msg *msg);
+#ifdef DEBUG
+static void ipmi_debug_msg(const char *title, unsigned char *data,
+ unsigned int len)
+{
+ int i, pos;
+ char buf[100];
+
+ pos = snprintf(buf, sizeof(buf), "%s: ", title);
+ for (i = 0; i < len; i++)
+ pos += snprintf(buf + pos, sizeof(buf) - pos,
+ " %2.2x", data[i]);
+ pr_debug("%s\n", buf);
+}
+#else
+static void ipmi_debug_msg(const char *title, unsigned char *data,
+ unsigned int len)
+{ }
+#endif
+
static int initialized;
enum ipmi_panic_event_op {
@@ -112,14 +131,13 @@ module_param_cb(panic_op, &panic_op_ops, NULL, 0600);
MODULE_PARM_DESC(panic_op, "Sets if the IPMI driver will attempt to store panic information in the event log in the event of a panic. Set to 'none' for no, 'event' for a single event, or 'string' for a generic event and the panic string in IPMI OEM events.");
-#ifdef CONFIG_IPMI_PROC_INTERFACE
-static struct proc_dir_entry *proc_ipmi_root;
-#endif /* CONFIG_IPMI_PROC_INTERFACE */
+#define MAX_EVENTS_IN_QUEUE 25
/* Remain in auto-maintenance mode for this amount of time (in ms). */
-#define IPMI_MAINTENANCE_MODE_TIMEOUT 30000
-
-#define MAX_EVENTS_IN_QUEUE 25
+static unsigned long maintenance_mode_timeout_ms = 30000;
+module_param(maintenance_mode_timeout_ms, ulong, 0644);
+MODULE_PARM_DESC(maintenance_mode_timeout_ms,
+ "The time (milliseconds) after the last maintenance message that the connection stays in maintenance mode.");
/*
* Don't let a message sit in a queue forever, always time it with at lest
@@ -127,6 +145,31 @@ static struct proc_dir_entry *proc_ipmi_root;
*/
#define MAX_MSG_TIMEOUT 60000
+/*
+ * Timeout times below are in milliseconds, and are done off a 1
+ * second timer. So setting the value to 1000 would mean anything
+ * between 0 and 1000ms. So really the only reasonable minimum
+ * setting it 2000ms, which is between 1 and 2 seconds.
+ */
+
+/* The default timeout for message retries. */
+static unsigned long default_retry_ms = 2000;
+module_param(default_retry_ms, ulong, 0644);
+MODULE_PARM_DESC(default_retry_ms,
+ "The time (milliseconds) between retry sends");
+
+/* The default timeout for maintenance mode message retries. */
+static unsigned long default_maintenance_retry_ms = 3000;
+module_param(default_maintenance_retry_ms, ulong, 0644);
+MODULE_PARM_DESC(default_maintenance_retry_ms,
+ "The time (milliseconds) between retry sends in maintenance mode");
+
+/* The default maximum number of retries */
+static unsigned int default_max_retries = 4;
+module_param(default_max_retries, uint, 0644);
+MODULE_PARM_DESC(default_max_retries,
+ "The time (milliseconds) between retry sends in maintenance mode");
+
/* Call every ~1000 ms. */
#define IPMI_TIMEOUT_TIME 1000
@@ -150,8 +193,12 @@ static struct proc_dir_entry *proc_ipmi_root;
struct ipmi_user {
struct list_head link;
- /* Set to false when the user is destroyed. */
- bool valid;
+ /*
+ * Set to NULL when the user is destroyed, a pointer to myself
+ * so srcu_dereference can be used on it.
+ */
+ struct ipmi_user *self;
+ struct srcu_struct release_barrier;
struct kref refcount;
@@ -160,16 +207,33 @@ struct ipmi_user {
void *handler_data;
/* The interface this user is bound to. */
- ipmi_smi_t intf;
+ struct ipmi_smi *intf;
/* Does this interface receive IPMI events? */
bool gets_events;
};
+static struct ipmi_user *acquire_ipmi_user(struct ipmi_user *user, int *index)
+ __acquires(user->release_barrier)
+{
+ struct ipmi_user *ruser;
+
+ *index = srcu_read_lock(&user->release_barrier);
+ ruser = srcu_dereference(user->self, &user->release_barrier);
+ if (!ruser)
+ srcu_read_unlock(&user->release_barrier, *index);
+ return ruser;
+}
+
+static void release_ipmi_user(struct ipmi_user *user, int index)
+{
+ srcu_read_unlock(&user->release_barrier, index);
+}
+
struct cmd_rcvr {
struct list_head link;
- ipmi_user_t user;
+ struct ipmi_user *user;
unsigned char netfn;
unsigned char cmd;
unsigned int chans;
@@ -247,13 +311,6 @@ struct ipmi_my_addrinfo {
unsigned char lun;
};
-#ifdef CONFIG_IPMI_PROC_INTERFACE
-struct ipmi_proc_entry {
- char *name;
- struct ipmi_proc_entry *next;
-};
-#endif
-
/*
* Note that the product id, manufacturer id, guid, and device id are
* immutable in this structure, so dyn_mutex is not required for
@@ -275,7 +332,7 @@ struct bmc_device {
};
#define to_bmc_device(x) container_of((x), struct bmc_device, pdev.dev)
-static int bmc_get_device_id(ipmi_smi_t intf, struct bmc_device *bmc,
+static int bmc_get_device_id(struct ipmi_smi *intf, struct bmc_device *bmc,
struct ipmi_device_id *id,
bool *guid_set, guid_t *guid);
@@ -397,10 +454,11 @@ struct ipmi_smi {
struct list_head link;
/*
- * The list of upper layers that are using me. seq_lock
- * protects this.
+ * The list of upper layers that are using me. seq_lock write
+ * protects this. Read protection is with srcu.
*/
struct list_head users;
+ struct srcu_struct users_srcu;
/* Used for wake ups at startup. */
wait_queue_head_t waitq;
@@ -420,24 +478,9 @@ struct ipmi_smi {
bool in_bmc_register; /* Handle recursive situations. Yuck. */
struct work_struct bmc_reg_work;
- /*
- * This is the lower-layer's sender routine. Note that you
- * must either be holding the ipmi_interfaces_mutex or be in
- * an umpreemptible region to use this. You must fetch the
- * value into a local variable and make sure it is not NULL.
- */
const struct ipmi_smi_handlers *handlers;
void *send_info;
-#ifdef CONFIG_IPMI_PROC_INTERFACE
- /* A list of proc entries for this interface. */
- struct mutex proc_entry_lock;
- struct ipmi_proc_entry *proc_entries;
-
- struct proc_dir_entry *proc_dir;
- char proc_dir_name[10];
-#endif
-
/* Driver-model device for the system interface. */
struct device *si_dev;
@@ -503,6 +546,13 @@ struct ipmi_smi {
spinlock_t maintenance_mode_lock; /* Used in a timer... */
/*
+ * If we are doing maintenance on something on IPMB, extend
+ * the timeout time to avoid timeouts writing firmware and
+ * such.
+ */
+ int ipmb_maintenance_mode_timeout;
+
+ /*
* A cheap hack, if this is non-null and a message to an
* interface comes in with a NULL user, call this routine with
* it. Note that the message will still be freed by the
@@ -510,7 +560,8 @@ struct ipmi_smi {
*
* Protected by bmc_reg_mutex.
*/
- void (*null_user_handler)(ipmi_smi_t intf, struct ipmi_recv_msg *msg);
+ void (*null_user_handler)(struct ipmi_smi *intf,
+ struct ipmi_recv_msg *msg);
/*
* When we are scanning the channels for an SMI, this will
@@ -536,12 +587,12 @@ struct ipmi_smi {
};
#define to_si_intf_from_dev(device) container_of(device, struct ipmi_smi, dev)
-static void __get_guid(ipmi_smi_t intf);
-static void __ipmi_bmc_unregister(ipmi_smi_t intf);
-static int __ipmi_bmc_register(ipmi_smi_t intf,
+static void __get_guid(struct ipmi_smi *intf);
+static void __ipmi_bmc_unregister(struct ipmi_smi *intf);
+static int __ipmi_bmc_register(struct ipmi_smi *intf,
struct ipmi_device_id *id,
bool guid_set, guid_t *guid, int intf_num);
-static int __scan_channels(ipmi_smi_t intf, struct ipmi_device_id *id);
+static int __scan_channels(struct ipmi_smi *intf, struct ipmi_device_id *id);
/**
@@ -560,6 +611,7 @@ static DEFINE_MUTEX(ipmidriver_mutex);
static LIST_HEAD(ipmi_interfaces);
static DEFINE_MUTEX(ipmi_interfaces_mutex);
+DEFINE_STATIC_SRCU(ipmi_interfaces_srcu);
/*
* List of watchers that want to know when smi's are added and deleted.
@@ -620,7 +672,7 @@ static void free_smi_msg_list(struct list_head *q)
}
}
-static void clean_up_interface_data(ipmi_smi_t intf)
+static void clean_up_interface_data(struct ipmi_smi *intf)
{
int i;
struct cmd_rcvr *rcvr, *rcvr2;
@@ -652,7 +704,7 @@ static void clean_up_interface_data(ipmi_smi_t intf)
static void intf_free(struct kref *ref)
{
- ipmi_smi_t intf = container_of(ref, struct ipmi_smi, refcount);
+ struct ipmi_smi *intf = container_of(ref, struct ipmi_smi, refcount);
clean_up_interface_data(intf);
kfree(intf);
@@ -660,65 +712,39 @@ static void intf_free(struct kref *ref)
struct watcher_entry {
int intf_num;
- ipmi_smi_t intf;
+ struct ipmi_smi *intf;
struct list_head link;
};
int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher)
{
- ipmi_smi_t intf;
- LIST_HEAD(to_deliver);
- struct watcher_entry *e, *e2;
+ struct ipmi_smi *intf;
+ int index;
mutex_lock(&smi_watchers_mutex);
- mutex_lock(&ipmi_interfaces_mutex);
-
- /* Build a list of things to deliver. */
- list_for_each_entry(intf, &ipmi_interfaces, link) {
- if (intf->intf_num == -1)
- continue;
- e = kmalloc(sizeof(*e), GFP_KERNEL);
- if (!e)
- goto out_err;
- kref_get(&intf->refcount);
- e->intf = intf;
- e->intf_num = intf->intf_num;
- list_add_tail(&e->link, &to_deliver);
- }
-
- /* We will succeed, so add it to the list. */
list_add(&watcher->link, &smi_watchers);
- mutex_unlock(&ipmi_interfaces_mutex);
+ index = srcu_read_lock(&ipmi_interfaces_srcu);
+ list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
+ int intf_num = READ_ONCE(intf->intf_num);
- list_for_each_entry_safe(e, e2, &to_deliver, link) {
- list_del(&e->link);
- watcher->new_smi(e->intf_num, e->intf->si_dev);
- kref_put(&e->intf->refcount, intf_free);
- kfree(e);
+ if (intf_num == -1)
+ continue;
+ watcher->new_smi(intf_num, intf->si_dev);
}
+ srcu_read_unlock(&ipmi_interfaces_srcu, index);
mutex_unlock(&smi_watchers_mutex);
return 0;
-
- out_err:
- mutex_unlock(&ipmi_interfaces_mutex);
- mutex_unlock(&smi_watchers_mutex);
- list_for_each_entry_safe(e, e2, &to_deliver, link) {
- list_del(&e->link);
- kref_put(&e->intf->refcount, intf_free);
- kfree(e);
- }
- return -ENOMEM;
}
EXPORT_SYMBOL(ipmi_smi_watcher_register);
int ipmi_smi_watcher_unregister(struct ipmi_smi_watcher *watcher)
{
mutex_lock(&smi_watchers_mutex);
- list_del(&(watcher->link));
+ list_del(&watcher->link);
mutex_unlock(&smi_watchers_mutex);
return 0;
}
@@ -732,12 +758,14 @@ call_smi_watchers(int i, struct device *dev)
{
struct ipmi_smi_watcher *w;
+ mutex_lock(&smi_watchers_mutex);
list_for_each_entry(w, &smi_watchers, link) {
if (try_module_get(w->owner)) {
w->new_smi(i, dev);
module_put(w->owner);
}
}
+ mutex_unlock(&smi_watchers_mutex);
}
static int
@@ -831,18 +859,17 @@ unsigned int ipmi_addr_length(int addr_type)
}
EXPORT_SYMBOL(ipmi_addr_length);
-static void deliver_response(struct ipmi_recv_msg *msg)
+static int deliver_response(struct ipmi_smi *intf, struct ipmi_recv_msg *msg)
{
- if (!msg->user) {
- ipmi_smi_t intf = msg->user_msg_data;
+ int rv = 0;
+ if (!msg->user) {
/* Special handling for NULL users. */
if (intf->null_user_handler) {
intf->null_user_handler(intf, msg);
- ipmi_inc_stat(intf, handled_local_responses);
} else {
/* No handler, so give up. */
- ipmi_inc_stat(intf, unhandled_local_responses);
+ rv = -EINVAL;
}
ipmi_free_recv_msg(msg);
} else if (!oops_in_progress) {
@@ -851,21 +878,40 @@ static void deliver_response(struct ipmi_recv_msg *msg)
* receive handler doesn't much meaning and has a deadlock
* risk. At this moment, simply skip it in that case.
*/
+ int index;
+ struct ipmi_user *user = acquire_ipmi_user(msg->user, &index);
- ipmi_user_t user = msg->user;
- user->handler->ipmi_recv_hndl(msg, user->handler_data);
+ if (user) {
+ user->handler->ipmi_recv_hndl(msg, user->handler_data);
+ release_ipmi_user(msg->user, index);
+ } else {
+ /* User went away, give up. */
+ ipmi_free_recv_msg(msg);
+ rv = -EINVAL;
+ }
}
+
+ return rv;
}
-static void
-deliver_err_response(struct ipmi_recv_msg *msg, int err)
+static void deliver_local_response(struct ipmi_smi *intf,
+ struct ipmi_recv_msg *msg)
+{
+ if (deliver_response(intf, msg))
+ ipmi_inc_stat(intf, unhandled_local_responses);
+ else
+ ipmi_inc_stat(intf, handled_local_responses);
+}
+
+static void deliver_err_response(struct ipmi_smi *intf,
+ struct ipmi_recv_msg *msg, int err)
{
msg->recv_type = IPMI_RESPONSE_RECV_TYPE;
msg->msg_data[0] = err;
msg->msg.netfn |= 1; /* Convert to a response. */
msg->msg.data_len = 1;
msg->msg.data = msg->msg_data;
- deliver_response(msg);
+ deliver_local_response(intf, msg);
}
/*
@@ -873,7 +919,7 @@ deliver_err_response(struct ipmi_recv_msg *msg, int err)
* message with the given timeout to the sequence table. This must be
* called with the interface's seq_lock held.
*/
-static int intf_next_seq(ipmi_smi_t intf,
+static int intf_next_seq(struct ipmi_smi *intf,
struct ipmi_recv_msg *recv_msg,
unsigned long timeout,
int retries,
@@ -884,6 +930,11 @@ static int intf_next_seq(ipmi_smi_t intf,
int rv = 0;
unsigned int i;
+ if (timeout == 0)
+ timeout = default_retry_ms;
+ if (retries < 0)
+ retries = default_max_retries;
+
for (i = intf->curr_seq; (i+1)%IPMI_IPMB_NUM_SEQ != intf->curr_seq;
i = (i+1)%IPMI_IPMB_NUM_SEQ) {
if (!intf->seq_table[i].inuse)
@@ -921,7 +972,7 @@ static int intf_next_seq(ipmi_smi_t intf,
* guard against message coming in after their timeout and the
* sequence number being reused).
*/
-static int intf_find_seq(ipmi_smi_t intf,
+static int intf_find_seq(struct ipmi_smi *intf,
unsigned char seq,
short channel,
unsigned char cmd,
@@ -935,26 +986,26 @@ static int intf_find_seq(ipmi_smi_t intf,
if (seq >= IPMI_IPMB_NUM_SEQ)
return -EINVAL;
- spin_lock_irqsave(&(intf->seq_lock), flags);
+ spin_lock_irqsave(&intf->seq_lock, flags);
if (intf->seq_table[seq].inuse) {
struct ipmi_recv_msg *msg = intf->seq_table[seq].recv_msg;
if ((msg->addr.channel == channel) && (msg->msg.cmd == cmd)
&& (msg->msg.netfn == netfn)
- && (ipmi_addr_equal(addr, &(msg->addr)))) {
+ && (ipmi_addr_equal(addr, &msg->addr))) {
*recv_msg = msg;
intf->seq_table[seq].inuse = 0;
rv = 0;
}
}
- spin_unlock_irqrestore(&(intf->seq_lock), flags);
+ spin_unlock_irqrestore(&intf->seq_lock, flags);
return rv;
}
/* Start the timer for a specific sequence table entry. */
-static int intf_start_seq_timer(ipmi_smi_t intf,
+static int intf_start_seq_timer(struct ipmi_smi *intf,
long msgid)
{
int rv = -ENODEV;
@@ -965,24 +1016,24 @@ static int intf_start_seq_timer(ipmi_smi_t intf,
GET_SEQ_FROM_MSGID(msgid, seq, seqid);
- spin_lock_irqsave(&(intf->seq_lock), flags);
+ spin_lock_irqsave(&intf->seq_lock, flags);
/*
* We do this verification because the user can be deleted
* while a message is outstanding.
*/
if ((intf->seq_table[seq].inuse)
&& (intf->seq_table[seq].seqid == seqid)) {
- struct seq_table *ent = &(intf->seq_table[seq]);
+ struct seq_table *ent = &intf->seq_table[seq];
ent->timeout = ent->orig_timeout;
rv = 0;
}
- spin_unlock_irqrestore(&(intf->seq_lock), flags);
+ spin_unlock_irqrestore(&intf->seq_lock, flags);
return rv;
}
/* Got an error for the send message for a specific sequence number. */
-static int intf_err_seq(ipmi_smi_t intf,
+static int intf_err_seq(struct ipmi_smi *intf,
long msgid,
unsigned int err)
{
@@ -995,23 +1046,23 @@ static int intf_err_seq(ipmi_smi_t intf,
GET_SEQ_FROM_MSGID(msgid, seq, seqid);
- spin_lock_irqsave(&(intf->seq_lock), flags);
+ spin_lock_irqsave(&intf->seq_lock, flags);
/*
* We do this verification because the user can be deleted
* while a message is outstanding.
*/
if ((intf->seq_table[seq].inuse)
&& (intf->seq_table[seq].seqid == seqid)) {
- struct seq_table *ent = &(intf->seq_table[seq]);
+ struct seq_table *ent = &intf->seq_table[seq];
ent->inuse = 0;
msg = ent->recv_msg;
rv = 0;
}
- spin_unlock_irqrestore(&(intf->seq_lock), flags);
+ spin_unlock_irqrestore(&intf->seq_lock, flags);
if (msg)
- deliver_err_response(msg, err);
+ deliver_err_response(intf, msg, err);
return rv;
}
@@ -1020,12 +1071,12 @@ static int intf_err_seq(ipmi_smi_t intf,
int ipmi_create_user(unsigned int if_num,
const struct ipmi_user_hndl *handler,
void *handler_data,
- ipmi_user_t *user)
+ struct ipmi_user **user)
{
unsigned long flags;
- ipmi_user_t new_user;
- int rv = 0;
- ipmi_smi_t intf;
+ struct ipmi_user *new_user;
+ int rv = 0, index;
+ struct ipmi_smi *intf;
/*
* There is no module usecount here, because it's not
@@ -1059,7 +1110,7 @@ int ipmi_create_user(unsigned int if_num,
if (!new_user)
return -ENOMEM;
- mutex_lock(&ipmi_interfaces_mutex);
+ index = srcu_read_lock(&ipmi_interfaces_srcu);
list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
if (intf->intf_num == if_num)
goto found;
@@ -1069,6 +1120,10 @@ int ipmi_create_user(unsigned int if_num,
goto out_kfree;
found:
+ rv = init_srcu_struct(&new_user->release_barrier);
+ if (rv)
+ goto out_kfree;
+
/* Note that each existing user holds a refcount to the interface. */
kref_get(&intf->refcount);
@@ -1078,26 +1133,7 @@ int ipmi_create_user(unsigned int if_num,
new_user->intf = intf;
new_user->gets_events = false;
- if (!try_module_get(intf->handlers->owner)) {
- rv = -ENODEV;
- goto out_kref;
- }
-
- if (intf->handlers->inc_usecount) {
- rv = intf->handlers->inc_usecount(intf->send_info);
- if (rv) {
- module_put(intf->handlers->owner);
- goto out_kref;
- }
- }
-
- /*
- * Hold the lock so intf->handlers is guaranteed to be good
- * until now
- */
- mutex_unlock(&ipmi_interfaces_mutex);
-
- new_user->valid = true;
+ rcu_assign_pointer(new_user->self, new_user);
spin_lock_irqsave(&intf->seq_lock, flags);
list_add_rcu(&new_user->link, &intf->users);
spin_unlock_irqrestore(&intf->seq_lock, flags);
@@ -1106,13 +1142,12 @@ int ipmi_create_user(unsigned int if_num,
if (atomic_inc_return(&intf->event_waiters) == 1)
need_waiter(intf);
}
+ srcu_read_unlock(&ipmi_interfaces_srcu, index);
*user = new_user;
return 0;
-out_kref:
- kref_put(&intf->refcount, intf_free);
out_kfree:
- mutex_unlock(&ipmi_interfaces_mutex);
+ srcu_read_unlock(&ipmi_interfaces_srcu, index);
kfree(new_user);
return rv;
}
@@ -1120,26 +1155,25 @@ EXPORT_SYMBOL(ipmi_create_user);
int ipmi_get_smi_info(int if_num, struct ipmi_smi_info *data)
{
- int rv = 0;
- ipmi_smi_t intf;
- const struct ipmi_smi_handlers *handlers;
+ int rv, index;
+ struct ipmi_smi *intf;
- mutex_lock(&ipmi_interfaces_mutex);
+ index = srcu_read_lock(&ipmi_interfaces_srcu);
list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
if (intf->intf_num == if_num)
goto found;
}
+ srcu_read_unlock(&ipmi_interfaces_srcu, index);
+
/* Not found, return an error */
- rv = -EINVAL;
- mutex_unlock(&ipmi_interfaces_mutex);
- return rv;
+ return -EINVAL;
found:
- handlers = intf->handlers;
- rv = -ENOSYS;
- if (handlers->get_smi_info)
- rv = handlers->get_smi_info(intf->send_info, data);
- mutex_unlock(&ipmi_interfaces_mutex);
+ if (!intf->handlers->get_smi_info)
+ rv = -ENOTTY;
+ else
+ rv = intf->handlers->get_smi_info(intf->send_info, data);
+ srcu_read_unlock(&ipmi_interfaces_srcu, index);
return rv;
}
@@ -1147,19 +1181,34 @@ EXPORT_SYMBOL(ipmi_get_smi_info);
static void free_user(struct kref *ref)
{
- ipmi_user_t user = container_of(ref, struct ipmi_user, refcount);
+ struct ipmi_user *user = container_of(ref, struct ipmi_user, refcount);
kfree(user);
}
-int ipmi_destroy_user(ipmi_user_t user)
+static void _ipmi_destroy_user(struct ipmi_user *user)
{
- ipmi_smi_t intf = user->intf;
+ struct ipmi_smi *intf = user->intf;
int i;
unsigned long flags;
struct cmd_rcvr *rcvr;
struct cmd_rcvr *rcvrs = NULL;
- user->valid = false;
+ if (!acquire_ipmi_user(user, &i)) {
+ /*
+ * The user has already been cleaned up, just make sure
+ * nothing is using it and return.
+ */
+ synchronize_srcu(&user->release_barrier);
+ return;
+ }
+
+ rcu_assign_pointer(user->self, NULL);
+ release_ipmi_user(user, i);
+
+ synchronize_srcu(&user->release_barrier);
+
+ if (user->handler->shutdown)
+ user->handler->shutdown(user->handler_data);
if (user->handler->ipmi_watchdog_pretimeout)
atomic_dec(&intf->event_waiters);
@@ -1184,7 +1233,7 @@ int ipmi_destroy_user(ipmi_user_t user)
* Remove the user from the command receiver's table. First
* we build a list of everything (not using the standard link,
* since other things may be using it till we do
- * synchronize_rcu()) then free everything in that list.
+ * synchronize_srcu()) then free everything in that list.
*/
mutex_lock(&intf->cmd_rcvrs_mutex);
list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) {
@@ -1202,109 +1251,156 @@ int ipmi_destroy_user(ipmi_user_t user)
kfree(rcvr);
}
- mutex_lock(&ipmi_interfaces_mutex);
- if (intf->handlers) {
- module_put(intf->handlers->owner);
- if (intf->handlers->dec_usecount)
- intf->handlers->dec_usecount(intf->send_info);
- }
- mutex_unlock(&ipmi_interfaces_mutex);
-
kref_put(&intf->refcount, intf_free);
+}
+int ipmi_destroy_user(struct ipmi_user *user)
+{
+ _ipmi_destroy_user(user);
+
+ cleanup_srcu_struct(&user->release_barrier);
kref_put(&user->refcount, free_user);
return 0;
}
EXPORT_SYMBOL(ipmi_destroy_user);
-int ipmi_get_version(ipmi_user_t user,
+int ipmi_get_version(struct ipmi_user *user,
unsigned char *major,
unsigned char *minor)
{
struct ipmi_device_id id;
- int rv;
+ int rv, index;
- rv = bmc_get_device_id(user->intf, NULL, &id, NULL, NULL);
- if (rv)
- return rv;
+ user = acquire_ipmi_user(user, &index);
+ if (!user)
+ return -ENODEV;
- *major = ipmi_version_major(&id);
- *minor = ipmi_version_minor(&id);
+ rv = bmc_get_device_id(user->intf, NULL, &id, NULL, NULL);
+ if (!rv) {
+ *major = ipmi_version_major(&id);
+ *minor = ipmi_version_minor(&id);
+ }
+ release_ipmi_user(user, index);
- return 0;
+ return rv;
}
EXPORT_SYMBOL(ipmi_get_version);
-int ipmi_set_my_address(ipmi_user_t user,
+int ipmi_set_my_address(struct ipmi_user *user,
unsigned int channel,
unsigned char address)
{
+ int index, rv = 0;
+
+ user = acquire_ipmi_user(user, &index);
+ if (!user)
+ return -ENODEV;
+
if (channel >= IPMI_MAX_CHANNELS)
- return -EINVAL;
- user->intf->addrinfo[channel].address = address;
- return 0;
+ rv = -EINVAL;
+ else
+ user->intf->addrinfo[channel].address = address;
+ release_ipmi_user(user, index);
+
+ return rv;
}
EXPORT_SYMBOL(ipmi_set_my_address);
-int ipmi_get_my_address(ipmi_user_t user,
+int ipmi_get_my_address(struct ipmi_user *user,
unsigned int channel,
unsigned char *address)
{
+ int index, rv = 0;
+
+ user = acquire_ipmi_user(user, &index);
+ if (!user)
+ return -ENODEV;
+
if (channel >= IPMI_MAX_CHANNELS)
- return -EINVAL;
- *address = user->intf->addrinfo[channel].address;
- return 0;
+ rv = -EINVAL;
+ else
+ *address = user->intf->addrinfo[channel].address;
+ release_ipmi_user(user, index);
+
+ return rv;
}
EXPORT_SYMBOL(ipmi_get_my_address);
-int ipmi_set_my_LUN(ipmi_user_t user,
+int ipmi_set_my_LUN(struct ipmi_user *user,
unsigned int channel,
unsigned char LUN)
{
+ int index, rv = 0;
+
+ user = acquire_ipmi_user(user, &index);
+ if (!user)
+ return -ENODEV;
+
if (channel >= IPMI_MAX_CHANNELS)
- return -EINVAL;
- user->intf->addrinfo[channel].lun = LUN & 0x3;
+ rv = -EINVAL;
+ else
+ user->intf->addrinfo[channel].lun = LUN & 0x3;
+ release_ipmi_user(user, index);
+
return 0;
}
EXPORT_SYMBOL(ipmi_set_my_LUN);
-int ipmi_get_my_LUN(ipmi_user_t user,
+int ipmi_get_my_LUN(struct ipmi_user *user,
unsigned int channel,
unsigned char *address)
{
+ int index, rv = 0;
+
+ user = acquire_ipmi_user(user, &index);
+ if (!user)
+ return -ENODEV;
+
if (channel >= IPMI_MAX_CHANNELS)
- return -EINVAL;
- *address = user->intf->addrinfo[channel].lun;
- return 0;
+ rv = -EINVAL;
+ else
+ *address = user->intf->addrinfo[channel].lun;
+ release_ipmi_user(user, index);
+
+ return rv;
}
EXPORT_SYMBOL(ipmi_get_my_LUN);
-int ipmi_get_maintenance_mode(ipmi_user_t user)
+int ipmi_get_maintenance_mode(struct ipmi_user *user)
{
- int mode;
+ int mode, index;
unsigned long flags;
+ user = acquire_ipmi_user(user, &index);
+ if (!user)
+ return -ENODEV;
+
spin_lock_irqsave(&user->intf->maintenance_mode_lock, flags);
mode = user->intf->maintenance_mode;
spin_unlock_irqrestore(&user->intf->maintenance_mode_lock, flags);
+ release_ipmi_user(user, index);
return mode;
}
EXPORT_SYMBOL(ipmi_get_maintenance_mode);
-static void maintenance_mode_update(ipmi_smi_t intf)
+static void maintenance_mode_update(struct ipmi_smi *intf)
{
if (intf->handlers->set_maintenance_mode)
intf->handlers->set_maintenance_mode(
intf->send_info, intf->maintenance_mode_enable);
}
-int ipmi_set_maintenance_mode(ipmi_user_t user, int mode)
+int ipmi_set_maintenance_mode(struct ipmi_user *user, int mode)
{
- int rv = 0;
+ int rv = 0, index;
unsigned long flags;
- ipmi_smi_t intf = user->intf;
+ struct ipmi_smi *intf = user->intf;
+
+ user = acquire_ipmi_user(user, &index);
+ if (!user)
+ return -ENODEV;
spin_lock_irqsave(&intf->maintenance_mode_lock, flags);
if (intf->maintenance_mode != mode) {
@@ -1332,17 +1428,23 @@ int ipmi_set_maintenance_mode(ipmi_user_t user, int mode)
}
out_unlock:
spin_unlock_irqrestore(&intf->maintenance_mode_lock, flags);
+ release_ipmi_user(user, index);
return rv;
}
EXPORT_SYMBOL(ipmi_set_maintenance_mode);
-int ipmi_set_gets_events(ipmi_user_t user, bool val)
+int ipmi_set_gets_events(struct ipmi_user *user, bool val)
{
unsigned long flags;
- ipmi_smi_t intf = user->intf;
+ struct ipmi_smi *intf = user->intf;
struct ipmi_recv_msg *msg, *msg2;
struct list_head msgs;
+ int index;
+
+ user = acquire_ipmi_user(user, &index);
+ if (!user)
+ return -ENODEV;
INIT_LIST_HEAD(&msgs);
@@ -1383,7 +1485,7 @@ int ipmi_set_gets_events(ipmi_user_t user, bool val)
list_for_each_entry_safe(msg, msg2, &msgs, link) {
msg->user = user;
kref_get(&user->refcount);
- deliver_response(msg);
+ deliver_local_response(intf, msg);
}
spin_lock_irqsave(&intf->events_lock, flags);
@@ -1392,12 +1494,13 @@ int ipmi_set_gets_events(ipmi_user_t user, bool val)
out:
spin_unlock_irqrestore(&intf->events_lock, flags);
+ release_ipmi_user(user, index);
return 0;
}
EXPORT_SYMBOL(ipmi_set_gets_events);
-static struct cmd_rcvr *find_cmd_rcvr(ipmi_smi_t intf,
+static struct cmd_rcvr *find_cmd_rcvr(struct ipmi_smi *intf,
unsigned char netfn,
unsigned char cmd,
unsigned char chan)
@@ -1412,7 +1515,7 @@ static struct cmd_rcvr *find_cmd_rcvr(ipmi_smi_t intf,
return NULL;
}
-static int is_cmd_rcvr_exclusive(ipmi_smi_t intf,
+static int is_cmd_rcvr_exclusive(struct ipmi_smi *intf,
unsigned char netfn,
unsigned char cmd,
unsigned int chans)
@@ -1427,19 +1530,24 @@ static int is_cmd_rcvr_exclusive(ipmi_smi_t intf,
return 1;
}
-int ipmi_register_for_cmd(ipmi_user_t user,
+int ipmi_register_for_cmd(struct ipmi_user *user,
unsigned char netfn,
unsigned char cmd,
unsigned int chans)
{
- ipmi_smi_t intf = user->intf;
+ struct ipmi_smi *intf = user->intf;
struct cmd_rcvr *rcvr;
- int rv = 0;
+ int rv = 0, index;
+ user = acquire_ipmi_user(user, &index);
+ if (!user)
+ return -ENODEV;
rcvr = kmalloc(sizeof(*rcvr), GFP_KERNEL);
- if (!rcvr)
- return -ENOMEM;
+ if (!rcvr) {
+ rv = -ENOMEM;
+ goto out_release;
+ }
rcvr->cmd = cmd;
rcvr->netfn = netfn;
rcvr->chans = chans;
@@ -1457,24 +1565,30 @@ int ipmi_register_for_cmd(ipmi_user_t user,
list_add_rcu(&rcvr->link, &intf->cmd_rcvrs);
- out_unlock:
+out_unlock:
mutex_unlock(&intf->cmd_rcvrs_mutex);
if (rv)
kfree(rcvr);
+out_release:
+ release_ipmi_user(user, index);
return rv;
}
EXPORT_SYMBOL(ipmi_register_for_cmd);
-int ipmi_unregister_for_cmd(ipmi_user_t user,
+int ipmi_unregister_for_cmd(struct ipmi_user *user,
unsigned char netfn,
unsigned char cmd,
unsigned int chans)
{
- ipmi_smi_t intf = user->intf;
+ struct ipmi_smi *intf = user->intf;
struct cmd_rcvr *rcvr;
struct cmd_rcvr *rcvrs = NULL;
- int i, rv = -ENOENT;
+ int i, rv = -ENOENT, index;
+
+ user = acquire_ipmi_user(user, &index);
+ if (!user)
+ return -ENODEV;
mutex_lock(&intf->cmd_rcvrs_mutex);
for (i = 0; i < IPMI_NUM_CHANNELS; i++) {
@@ -1495,12 +1609,14 @@ int ipmi_unregister_for_cmd(ipmi_user_t user,
}
mutex_unlock(&intf->cmd_rcvrs_mutex);
synchronize_rcu();
+ release_ipmi_user(user, index);
while (rcvrs) {
atomic_dec(&intf->event_waiters);
rcvr = rcvrs;
rcvrs = rcvr->next;
kfree(rcvr);
}
+
return rv;
}
EXPORT_SYMBOL(ipmi_unregister_for_cmd);
@@ -1535,21 +1651,19 @@ static inline void format_ipmb_msg(struct ipmi_smi_msg *smi_msg,
smi_msg->data[3] = 0;
smi_msg->data[i+3] = ipmb_addr->slave_addr;
smi_msg->data[i+4] = (msg->netfn << 2) | (ipmb_addr->lun & 0x3);
- smi_msg->data[i+5] = ipmb_checksum(&(smi_msg->data[i+3]), 2);
+ smi_msg->data[i+5] = ipmb_checksum(&smi_msg->data[i + 3], 2);
smi_msg->data[i+6] = source_address;
smi_msg->data[i+7] = (ipmb_seq << 2) | source_lun;
smi_msg->data[i+8] = msg->cmd;
/* Now tack on the data to the message. */
if (msg->data_len > 0)
- memcpy(&(smi_msg->data[i+9]), msg->data,
- msg->data_len);
+ memcpy(&smi_msg->data[i + 9], msg->data, msg->data_len);
smi_msg->data_size = msg->data_len + 9;
/* Now calculate the checksum and tack it on. */
smi_msg->data[i+smi_msg->data_size]
- = ipmb_checksum(&(smi_msg->data[i+6]),
- smi_msg->data_size-6);
+ = ipmb_checksum(&smi_msg->data[i + 6], smi_msg->data_size - 6);
/*
* Add on the checksum size and the offset from the
@@ -1574,21 +1688,19 @@ static inline void format_lan_msg(struct ipmi_smi_msg *smi_msg,
smi_msg->data[3] = lan_addr->session_handle;
smi_msg->data[4] = lan_addr->remote_SWID;
smi_msg->data[5] = (msg->netfn << 2) | (lan_addr->lun & 0x3);
- smi_msg->data[6] = ipmb_checksum(&(smi_msg->data[4]), 2);
+ smi_msg->data[6] = ipmb_checksum(&smi_msg->data[4], 2);
smi_msg->data[7] = lan_addr->local_SWID;
smi_msg->data[8] = (ipmb_seq << 2) | source_lun;
smi_msg->data[9] = msg->cmd;
/* Now tack on the data to the message. */
if (msg->data_len > 0)
- memcpy(&(smi_msg->data[10]), msg->data,
- msg->data_len);
+ memcpy(&smi_msg->data[10], msg->data, msg->data_len);
smi_msg->data_size = msg->data_len + 10;
/* Now calculate the checksum and tack it on. */
smi_msg->data[smi_msg->data_size]
- = ipmb_checksum(&(smi_msg->data[7]),
- smi_msg->data_size-7);
+ = ipmb_checksum(&smi_msg->data[7], smi_msg->data_size - 7);
/*
* Add on the checksum size and the offset from the
@@ -1599,7 +1711,7 @@ static inline void format_lan_msg(struct ipmi_smi_msg *smi_msg,
smi_msg->msgid = msgid;
}
-static struct ipmi_smi_msg *smi_add_send_msg(ipmi_smi_t intf,
+static struct ipmi_smi_msg *smi_add_send_msg(struct ipmi_smi *intf,
struct ipmi_smi_msg *smi_msg,
int priority)
{
@@ -1617,7 +1729,8 @@ static struct ipmi_smi_msg *smi_add_send_msg(ipmi_smi_t intf,
}
-static void smi_send(ipmi_smi_t intf, const struct ipmi_smi_handlers *handlers,
+static void smi_send(struct ipmi_smi *intf,
+ const struct ipmi_smi_handlers *handlers,
struct ipmi_smi_msg *smi_msg, int priority)
{
int run_to_completion = intf->run_to_completion;
@@ -1636,405 +1749,435 @@ static void smi_send(ipmi_smi_t intf, const struct ipmi_smi_handlers *handlers,
handlers->sender(intf->send_info, smi_msg);
}
-/*
- * Separate from ipmi_request so that the user does not have to be
- * supplied in certain circumstances (mainly at panic time). If
- * messages are supplied, they will be freed, even if an error
- * occurs.
- */
-static int i_ipmi_request(ipmi_user_t user,
- ipmi_smi_t intf,
- struct ipmi_addr *addr,
- long msgid,
- struct kernel_ipmi_msg *msg,
- void *user_msg_data,
- void *supplied_smi,
- struct ipmi_recv_msg *supplied_recv,