mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 15:50:59 +07:00
ipmi: Rework locking and shutdown for hot remove
To handle hot remove of interfaces, a lot of rework had to be done to the locking. Several things were switched over to srcu and shutdown for users and interfaces was added for cleaner shutdown. Signed-off-by: Corey Minyard <cminyard@mvista.com>
This commit is contained in:
parent
ac93bd0c9e
commit
e86ee2d44b
@ -197,8 +197,12 @@ MODULE_PARM_DESC(default_max_retries,
|
||||
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;
|
||||
|
||||
@ -213,6 +217,23 @@ struct ipmi_user {
|
||||
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;
|
||||
|
||||
@ -444,10 +465,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;
|
||||
@ -467,12 +489,6 @@ 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;
|
||||
|
||||
@ -615,6 +631,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.
|
||||
@ -715,58 +732,32 @@ static void intf_free(struct kref *ref)
|
||||
|
||||
struct watcher_entry {
|
||||
int intf_num;
|
||||
struct ipmi_smi *intf;
|
||||
struct ipmi_smi *intf;
|
||||
struct list_head link;
|
||||
};
|
||||
|
||||
int ipmi_smi_watcher_register(struct ipmi_smi_watcher *watcher)
|
||||
{
|
||||
struct ipmi_smi *intf;
|
||||
LIST_HEAD(to_deliver);
|
||||
struct watcher_entry *e, *e2;
|
||||
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);
|
||||
|
||||
@ -787,12 +778,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
|
||||
@ -905,9 +898,17 @@ static int deliver_response(struct ipmi_smi *intf, 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);
|
||||
|
||||
struct ipmi_user *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;
|
||||
@ -1094,7 +1095,7 @@ int ipmi_create_user(unsigned int if_num,
|
||||
{
|
||||
unsigned long flags;
|
||||
struct ipmi_user *new_user;
|
||||
int rv = 0;
|
||||
int rv = 0, index;
|
||||
struct ipmi_smi *intf;
|
||||
|
||||
/*
|
||||
@ -1129,7 +1130,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;
|
||||
@ -1139,6 +1140,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);
|
||||
|
||||
@ -1148,26 +1153,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);
|
||||
@ -1176,13 +1162,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;
|
||||
}
|
||||
@ -1190,26 +1175,25 @@ EXPORT_SYMBOL(ipmi_create_user);
|
||||
|
||||
int ipmi_get_smi_info(int if_num, struct ipmi_smi_info *data)
|
||||
{
|
||||
int rv = 0;
|
||||
int rv, index;
|
||||
struct ipmi_smi *intf;
|
||||
const struct ipmi_smi_handlers *handlers;
|
||||
|
||||
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;
|
||||
}
|
||||
@ -1221,7 +1205,7 @@ static void free_user(struct kref *ref)
|
||||
kfree(user);
|
||||
}
|
||||
|
||||
int ipmi_destroy_user(struct ipmi_user *user)
|
||||
static void _ipmi_destroy_user(struct ipmi_user *user)
|
||||
{
|
||||
struct ipmi_smi *intf = user->intf;
|
||||
int i;
|
||||
@ -1229,7 +1213,22 @@ int ipmi_destroy_user(struct ipmi_user *user)
|
||||
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);
|
||||
@ -1254,7 +1253,7 @@ int ipmi_destroy_user(struct ipmi_user *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) {
|
||||
@ -1272,16 +1271,14 @@ int ipmi_destroy_user(struct ipmi_user *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;
|
||||
@ -1293,16 +1290,20 @@ int ipmi_get_version(struct ipmi_user *user,
|
||||
unsigned char *minor)
|
||||
{
|
||||
struct ipmi_device_id id;
|
||||
int rv;
|
||||
int rv, index;
|
||||
|
||||
user = acquire_ipmi_user(user, &index);
|
||||
if (!user)
|
||||
return -ENODEV;
|
||||
|
||||
rv = bmc_get_device_id(user->intf, NULL, &id, NULL, NULL);
|
||||
if (rv)
|
||||
return rv;
|
||||
if (!rv) {
|
||||
*major = ipmi_version_major(&id);
|
||||
*minor = ipmi_version_minor(&id);
|
||||
}
|
||||
release_ipmi_user(user, index);
|
||||
|
||||
*major = ipmi_version_major(&id);
|
||||
*minor = ipmi_version_minor(&id);
|
||||
|
||||
return 0;
|
||||
return rv;
|
||||
}
|
||||
EXPORT_SYMBOL(ipmi_get_version);
|
||||
|
||||
@ -1310,9 +1311,17 @@ int ipmi_set_my_address(struct ipmi_user *user,
|
||||
unsigned int channel,
|
||||
unsigned char address)
|
||||
{
|
||||
int index;
|
||||
|
||||
user = acquire_ipmi_user(user, &index);
|
||||
if (!user)
|
||||
return -ENODEV;
|
||||
|
||||
if (channel >= IPMI_MAX_CHANNELS)
|
||||
return -EINVAL;
|
||||
user->intf->addrinfo[channel].address = address;
|
||||
release_ipmi_user(user, index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ipmi_set_my_address);
|
||||
@ -1321,9 +1330,17 @@ int ipmi_get_my_address(struct ipmi_user *user,
|
||||
unsigned int channel,
|
||||
unsigned char *address)
|
||||
{
|
||||
int index;
|
||||
|
||||
user = acquire_ipmi_user(user, &index);
|
||||
if (!user)
|
||||
return -ENODEV;
|
||||
|
||||
if (channel >= IPMI_MAX_CHANNELS)
|
||||
return -EINVAL;
|
||||
*address = user->intf->addrinfo[channel].address;
|
||||
release_ipmi_user(user, index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ipmi_get_my_address);
|
||||
@ -1332,9 +1349,17 @@ int ipmi_set_my_LUN(struct ipmi_user *user,
|
||||
unsigned int channel,
|
||||
unsigned char LUN)
|
||||
{
|
||||
int index;
|
||||
|
||||
user = acquire_ipmi_user(user, &index);
|
||||
if (!user)
|
||||
return -ENODEV;
|
||||
|
||||
if (channel >= IPMI_MAX_CHANNELS)
|
||||
return -EINVAL;
|
||||
user->intf->addrinfo[channel].lun = LUN & 0x3;
|
||||
release_ipmi_user(user, index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ipmi_set_my_LUN);
|
||||
@ -1343,21 +1368,34 @@ int ipmi_get_my_LUN(struct ipmi_user *user,
|
||||
unsigned int channel,
|
||||
unsigned char *address)
|
||||
{
|
||||
int index;
|
||||
|
||||
user = acquire_ipmi_user(user, &index);
|
||||
if (!user)
|
||||
return -ENODEV;
|
||||
|
||||
if (channel >= IPMI_MAX_CHANNELS)
|
||||
return -EINVAL;
|
||||
*address = user->intf->addrinfo[channel].lun;
|
||||
release_ipmi_user(user, index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ipmi_get_my_LUN);
|
||||
|
||||
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;
|
||||
}
|
||||
@ -1372,10 +1410,14 @@ static void maintenance_mode_update(struct ipmi_smi *intf)
|
||||
|
||||
int ipmi_set_maintenance_mode(struct ipmi_user *user, int mode)
|
||||
{
|
||||
int rv = 0;
|
||||
int rv = 0, index;
|
||||
unsigned long flags;
|
||||
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) {
|
||||
switch (mode) {
|
||||
@ -1402,6 +1444,7 @@ int ipmi_set_maintenance_mode(struct ipmi_user *user, int mode)
|
||||
}
|
||||
out_unlock:
|
||||
spin_unlock_irqrestore(&intf->maintenance_mode_lock, flags);
|
||||
release_ipmi_user(user, index);
|
||||
|
||||
return rv;
|
||||
}
|
||||
@ -1413,6 +1456,11 @@ int ipmi_set_gets_events(struct ipmi_user *user, bool val)
|
||||
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);
|
||||
|
||||
@ -1462,6 +1510,7 @@ int ipmi_set_gets_events(struct ipmi_user *user, bool val)
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&intf->events_lock, flags);
|
||||
release_ipmi_user(user, index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1504,8 +1553,11 @@ int ipmi_register_for_cmd(struct ipmi_user *user,
|
||||
{
|
||||
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)
|
||||
@ -1531,6 +1583,7 @@ int ipmi_register_for_cmd(struct ipmi_user *user,
|
||||
mutex_unlock(&intf->cmd_rcvrs_mutex);
|
||||
if (rv)
|
||||
kfree(rcvr);
|
||||
release_ipmi_user(user, index);
|
||||
|
||||
return rv;
|
||||
}
|
||||
@ -1544,7 +1597,11 @@ int ipmi_unregister_for_cmd(struct ipmi_user *user,
|
||||
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++) {
|
||||
@ -1565,12 +1622,14 @@ int ipmi_unregister_for_cmd(struct ipmi_user *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);
|
||||
@ -2065,8 +2124,10 @@ static int i_ipmi_request(struct ipmi_user *user,
|
||||
recv_msg = supplied_recv;
|
||||
else {
|
||||
recv_msg = ipmi_alloc_recv_msg();
|
||||
if (recv_msg == NULL)
|
||||
return -ENOMEM;
|
||||
if (recv_msg == NULL) {
|
||||
rv = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
recv_msg->user_msg_data = user_msg_data;
|
||||
|
||||
@ -2076,7 +2137,8 @@ static int i_ipmi_request(struct ipmi_user *user,
|
||||
smi_msg = ipmi_alloc_smi_msg();
|
||||
if (smi_msg == NULL) {
|
||||
ipmi_free_recv_msg(recv_msg);
|
||||
return -ENOMEM;
|
||||
rv = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2088,6 +2150,7 @@ static int i_ipmi_request(struct ipmi_user *user,
|
||||
|
||||
recv_msg->user = user;
|
||||
if (user)
|
||||
/* The put happens when the message is freed. */
|
||||
kref_get(&user->refcount);
|
||||
recv_msg->msgid = msgid;
|
||||
/*
|
||||
@ -2123,6 +2186,7 @@ static int i_ipmi_request(struct ipmi_user *user,
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
out:
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -2148,25 +2212,32 @@ int ipmi_request_settime(struct ipmi_user *user,
|
||||
unsigned int retry_time_ms)
|
||||
{
|
||||
unsigned char saddr = 0, lun = 0;
|
||||
int rv;
|
||||
int rv, index;
|
||||
|
||||
if (!user)
|
||||
return -EINVAL;
|
||||
|
||||
user = acquire_ipmi_user(user, &index);
|
||||
if (!user)
|
||||
return -ENODEV;
|
||||
|
||||
rv = check_addr(user->intf, addr, &saddr, &lun);
|
||||
if (rv)
|
||||
return rv;
|
||||
return i_ipmi_request(user,
|
||||
user->intf,
|
||||
addr,
|
||||
msgid,
|
||||
msg,
|
||||
user_msg_data,
|
||||
NULL, NULL,
|
||||
priority,
|
||||
saddr,
|
||||
lun,
|
||||
retries,
|
||||
retry_time_ms);
|
||||
if (!rv)
|
||||
rv = i_ipmi_request(user,
|
||||
user->intf,
|
||||
addr,
|
||||
msgid,
|
||||
msg,
|
||||
user_msg_data,
|
||||
NULL, NULL,
|
||||
priority,
|
||||
saddr,
|
||||
lun,
|
||||
retries,
|
||||
retry_time_ms);
|
||||
|
||||
release_ipmi_user(user, index);
|
||||
return rv;
|
||||
}
|
||||
EXPORT_SYMBOL(ipmi_request_settime);
|
||||
|
||||
@ -2180,25 +2251,32 @@ int ipmi_request_supply_msgs(struct ipmi_user *user,
|
||||
int priority)
|
||||
{
|
||||
unsigned char saddr = 0, lun = 0;
|
||||
int rv;
|
||||
int rv, index;
|
||||
|
||||
if (!user)
|
||||
return -EINVAL;
|
||||
|
||||
user = acquire_ipmi_user(user, &index);
|
||||
if (!user)
|
||||
return -ENODEV;
|
||||
|
||||
rv = check_addr(user->intf, addr, &saddr, &lun);
|
||||
if (rv)
|
||||
return rv;
|
||||
return i_ipmi_request(user,
|
||||
user->intf,
|
||||
addr,
|
||||
msgid,
|
||||
msg,
|
||||
user_msg_data,
|
||||
supplied_smi,
|
||||
supplied_recv,
|
||||
priority,
|
||||
saddr,
|
||||
lun,
|
||||
-1, 0);
|
||||
if (!rv)
|
||||
rv = i_ipmi_request(user,
|
||||
user->intf,
|
||||
addr,
|
||||
msgid,
|
||||
msg,
|
||||
user_msg_data,
|
||||
supplied_smi,
|
||||
supplied_recv,
|
||||
priority,
|
||||
saddr,
|
||||
lun,
|
||||
-1, 0);
|
||||
|
||||
release_ipmi_user(user, index);
|
||||
return rv;
|
||||
}
|
||||
EXPORT_SYMBOL(ipmi_request_supply_msgs);
|
||||
|
||||
@ -3455,6 +3533,13 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
|
||||
if (!intf)
|
||||
return -ENOMEM;
|
||||
|
||||
rv = init_srcu_struct(&intf->users_srcu);
|
||||
if (rv) {
|
||||
kfree(intf);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
intf->bmc = &intf->tmp_bmc;
|
||||
INIT_LIST_HEAD(&intf->bmc->intfs);
|
||||
mutex_init(&intf->bmc->dyn_mutex);
|
||||
@ -3507,7 +3592,6 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
|
||||
intf->proc_dir = NULL;
|
||||
#endif
|
||||
|
||||
mutex_lock(&smi_watchers_mutex);
|
||||
mutex_lock(&ipmi_interfaces_mutex);
|
||||
/* Look for a hole in the numbers. */
|
||||
i = 0;
|
||||
@ -3552,11 +3636,10 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
|
||||
if (intf->proc_dir)
|
||||
remove_proc_entries(intf);
|
||||
#endif
|
||||
intf->handlers = NULL;
|
||||
list_del_rcu(&intf->link);
|
||||
mutex_unlock(&ipmi_interfaces_mutex);
|
||||
mutex_unlock(&smi_watchers_mutex);
|
||||
synchronize_rcu();
|
||||
synchronize_srcu(&ipmi_interfaces_srcu);
|
||||
cleanup_srcu_struct(&intf->users_srcu);
|
||||
kref_put(&intf->refcount, intf_free);
|
||||
} else {
|
||||
/*
|
||||
@ -3567,9 +3650,9 @@ int ipmi_register_smi(const struct ipmi_smi_handlers *handlers,
|
||||
smp_wmb();
|
||||
intf->intf_num = i;
|
||||
mutex_unlock(&ipmi_interfaces_mutex);
|
||||
|
||||
/* After this point the interface is legal to use. */
|
||||
call_smi_watchers(i, intf->si_dev);
|
||||
mutex_unlock(&smi_watchers_mutex);
|
||||
}
|
||||
|
||||
return rv;
|
||||
@ -3631,45 +3714,49 @@ static void cleanup_smi_msgs(struct ipmi_smi *intf)
|
||||
int ipmi_unregister_smi(struct ipmi_smi *intf)
|
||||
{
|
||||
struct ipmi_smi_watcher *w;
|
||||
int intf_num = intf->intf_num;
|
||||
struct ipmi_user *user;
|
||||
int intf_num = intf->intf_num, index;
|
||||
|
||||
mutex_lock(&smi_watchers_mutex);
|
||||
mutex_lock(&ipmi_interfaces_mutex);
|
||||
intf->intf_num = -1;
|
||||
intf->in_shutdown = true;
|
||||
list_del_rcu(&intf->link);
|
||||
mutex_unlock(&ipmi_interfaces_mutex);
|
||||
synchronize_rcu();
|
||||
synchronize_srcu(&ipmi_interfaces_srcu);
|
||||
|
||||
/* At this point no users can be added to the interface. */
|
||||
|
||||
/*
|
||||
* Call all the watcher interfaces to tell them that
|
||||
* an interface is going away.
|
||||
*/
|
||||
mutex_lock(&smi_watchers_mutex);
|
||||
list_for_each_entry(w, &smi_watchers, link)
|
||||
w->smi_gone(intf_num);
|
||||
mutex_unlock(&smi_watchers_mutex);
|
||||
|
||||
index = srcu_read_lock(&intf->users_srcu);
|
||||
while (!list_empty(&intf->users)) {
|
||||
struct ipmi_user *user =
|
||||
container_of(list_next_rcu(&intf->users),
|
||||
struct ipmi_user, link);
|
||||
|
||||
_ipmi_destroy_user(user);
|
||||
}
|
||||
srcu_read_unlock(&intf->users_srcu, index);
|
||||
|
||||
if (intf->handlers->shutdown)
|
||||
intf->handlers->shutdown(intf->send_info);
|
||||
|
||||
cleanup_smi_msgs(intf);
|
||||
|
||||
/* Clean up the effects of users on the lower-level software. */
|
||||
mutex_lock(&ipmi_interfaces_mutex);
|
||||
rcu_read_lock();
|
||||
list_for_each_entry_rcu(user, &intf->users, link) {
|
||||
module_put(intf->handlers->owner);
|
||||
if (intf->handlers->dec_usecount)
|
||||
intf->handlers->dec_usecount(intf->send_info);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
intf->handlers = NULL;
|
||||
mutex_unlock(&ipmi_interfaces_mutex);
|
||||
|
||||
#ifdef CONFIG_IPMI_PROC_INTERFACE
|
||||
remove_proc_entries(intf);
|
||||
#endif
|
||||
ipmi_bmc_unregister(intf);
|
||||
|
||||
/*
|
||||
* Call all the watcher interfaces to tell them that
|
||||
* an interface is gone.
|
||||
*/
|
||||
list_for_each_entry(w, &smi_watchers, link)
|
||||
w->smi_gone(intf_num);
|
||||
mutex_unlock(&smi_watchers_mutex);
|
||||
|
||||
cleanup_srcu_struct(&intf->users_srcu);
|
||||
kref_put(&intf->refcount, intf_free);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(ipmi_unregister_smi);
|
||||
@ -4141,8 +4228,7 @@ static int handle_read_event_rsp(struct ipmi_smi *intf,
|
||||
struct ipmi_recv_msg *recv_msg, *recv_msg2;
|
||||
struct list_head msgs;
|
||||
struct ipmi_user *user;
|
||||
int rv = 0;
|
||||
int deliver_count = 0;
|
||||
int rv = 0, deliver_count = 0, index;
|
||||
unsigned long flags;
|
||||
|
||||
if (msg->rsp_size < 19) {
|
||||
@ -4166,7 +4252,7 @@ static int handle_read_event_rsp(struct ipmi_smi *intf,
|
||||
* Allocate and fill in one message for every user that is
|
||||
* getting events.
|
||||
*/
|
||||
rcu_read_lock();
|
||||
index = srcu_read_lock(&intf->users_srcu);
|
||||
list_for_each_entry_rcu(user, &intf->users, link) {
|
||||
if (!user->gets_events)
|
||||
continue;
|
||||
@ -4195,7 +4281,7 @@ static int handle_read_event_rsp(struct ipmi_smi *intf,
|
||||
kref_get(&user->refcount);
|
||||
list_add_tail(&recv_msg->link, &msgs);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
srcu_read_unlock(&intf->users_srcu, index);
|
||||
|
||||
if (deliver_count) {
|
||||
/* Now deliver all the messages. */
|
||||
@ -4242,7 +4328,7 @@ static int handle_bmc_rsp(struct ipmi_smi *intf,
|
||||
struct ipmi_smi_msg *msg)
|
||||
{
|
||||
struct ipmi_recv_msg *recv_msg;
|
||||
struct ipmi_user *user;
|
||||
struct ipmi_system_interface_addr *smi_addr;
|
||||
|
||||
recv_msg = (struct ipmi_recv_msg *) msg->user_data;
|
||||
if (recv_msg == NULL) {
|
||||
@ -4251,30 +4337,19 @@ static int handle_bmc_rsp(struct ipmi_smi *intf,
|
||||
return 0;
|
||||
}
|
||||
|
||||
user = recv_msg->user;
|
||||
/* Make sure the user still exists. */
|
||||
if (user && !user->valid) {
|
||||
/* The user for the message went away, so give up. */
|
||||
ipmi_inc_stat(intf, unhandled_local_responses);
|
||||
ipmi_free_recv_msg(recv_msg);
|
||||
} else {
|
||||
struct ipmi_system_interface_addr *smi_addr;
|
||||
|
||||
ipmi_inc_stat(intf, handled_local_responses);
|
||||
recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE;
|
||||
recv_msg->msgid = msg->msgid;
|
||||
smi_addr = ((struct ipmi_system_interface_addr *)
|
||||
&recv_msg->addr);
|
||||
smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
|
||||
smi_addr->channel = IPMI_BMC_CHANNEL;
|
||||
smi_addr->lun = msg->rsp[0] & 3;
|
||||
recv_msg->msg.netfn = msg->rsp[0] >> 2;
|
||||
recv_msg->msg.cmd = msg->rsp[1];
|
||||
memcpy(recv_msg->msg_data, &msg->rsp[2], msg->rsp_size - 2);
|
||||
recv_msg->msg.data = recv_msg->msg_data;
|
||||
recv_msg->msg.data_len = msg->rsp_size - 2;
|
||||
deliver_local_response(intf, recv_msg);
|
||||
}
|
||||
recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE;
|
||||
recv_msg->msgid = msg->msgid;
|
||||
smi_addr = ((struct ipmi_system_interface_addr *)
|
||||
&recv_msg->addr);
|
||||
smi_addr->addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE;
|
||||
smi_addr->channel = IPMI_BMC_CHANNEL;
|
||||
smi_addr->lun = msg->rsp[0] & 3;
|
||||
recv_msg->msg.netfn = msg->rsp[0] >> 2;
|
||||
recv_msg->msg.cmd = msg->rsp[1];
|
||||
memcpy(recv_msg->msg_data, &msg->rsp[2], msg->rsp_size - 2);
|
||||
recv_msg->msg.data = recv_msg->msg_data;
|
||||
recv_msg->msg.data_len = msg->rsp_size - 2;
|
||||
deliver_local_response(intf, recv_msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -4327,7 +4402,7 @@ static int handle_one_recv_msg(struct ipmi_smi *intf,
|
||||
* It's a response to a response we sent. For this we
|
||||
* deliver a send message response to the user.
|
||||
*/
|
||||
struct ipmi_recv_msg *recv_msg = msg->user_data;
|
||||
struct ipmi_recv_msg *recv_msg = msg->user_data;
|
||||
|
||||
requeue = 0;
|
||||
if (msg->rsp_size < 2)
|
||||
@ -4342,10 +4417,6 @@ static int handle_one_recv_msg(struct ipmi_smi *intf,
|
||||
if (!recv_msg)
|
||||
goto out;
|
||||
|
||||
/* Make sure the user still exists. */
|
||||
if (!recv_msg->user || !recv_msg->user->valid)
|
||||
goto out;
|
||||
|
||||
recv_msg->recv_type = IPMI_RESPONSE_RESPONSE_TYPE;
|
||||
recv_msg->msg.data = recv_msg->msg_data;
|
||||
recv_msg->msg.data_len = 1;
|
||||
@ -4488,14 +4559,15 @@ static void handle_new_recv_msgs(struct ipmi_smi *intf)
|
||||
*/
|
||||
if (atomic_add_unless(&intf->watchdog_pretimeouts_to_deliver, -1, 0)) {
|
||||
struct ipmi_user *user;
|
||||
int index;
|
||||
|
||||
rcu_read_lock();
|
||||
index = srcu_read_lock(&intf->users_srcu);
|
||||
list_for_each_entry_rcu(user, &intf->users, link) {
|
||||
if (user->handler->ipmi_watchdog_pretimeout)
|
||||
user->handler->ipmi_watchdog_pretimeout(
|
||||
user->handler_data);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
srcu_read_unlock(&intf->users_srcu, index);
|
||||
}
|
||||
}
|
||||
|
||||
@ -4662,8 +4734,7 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent,
|
||||
int slot, unsigned long *flags,
|
||||
unsigned int *waiting_msgs)
|
||||
{
|
||||
struct ipmi_recv_msg *msg;
|
||||
const struct ipmi_smi_handlers *handlers;
|
||||
struct ipmi_recv_msg *msg;
|
||||
|
||||
if (intf->in_shutdown)
|
||||
return;
|
||||
@ -4721,8 +4792,7 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent,
|
||||
* only for messages to the local MC, which don't get
|
||||
* resent.
|
||||
*/
|
||||
handlers = intf->handlers;
|
||||
if (handlers) {
|
||||
if (intf->handlers) {
|
||||
if (is_lan_addr(&ent->recv_msg->addr))
|
||||
ipmi_inc_stat(intf,
|
||||
retransmitted_lan_commands);
|
||||
@ -4730,7 +4800,7 @@ static void check_msg_timeout(struct ipmi_smi *intf, struct seq_table *ent,
|
||||
ipmi_inc_stat(intf,
|
||||
retransmitted_ipmb_commands);
|
||||
|
||||
smi_send(intf, handlers, smi_msg, 0);
|
||||
smi_send(intf, intf->handlers, smi_msg, 0);
|
||||
} else
|
||||
ipmi_free_smi_msg(smi_msg);
|
||||
|
||||
@ -4822,12 +4892,12 @@ static atomic_t stop_operation;
|
||||
static void ipmi_timeout(struct timer_list *unused)
|
||||
{
|
||||
struct ipmi_smi *intf;
|
||||
int nt = 0;
|
||||
int nt = 0, index;
|
||||
|
||||
if (atomic_read(&stop_operation))
|
||||
return;
|
||||
|
||||
rcu_read_lock();
|
||||
index = srcu_read_lock(&ipmi_interfaces_srcu);
|
||||
list_for_each_entry_rcu(intf, &ipmi_interfaces, link) {
|
||||
int lnt = 0;
|
||||
|
||||
@ -4850,7 +4920,7 @@ static void ipmi_timeout(struct timer_list *unused)
|
||||
|
||||
nt += lnt;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
srcu_read_unlock(&ipmi_interfaces_srcu, index);
|
||||
|
||||
if (nt)
|
||||
mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES);
|
||||
|
Loading…
Reference in New Issue
Block a user