With lockdep enabled, we see a bug from the BMC dynamic-id support:
BUG: sleeping function called from invalid context at
../kernel/locking/mutex.c:747
in_atomic(): 1, irqs_disabled(): 0, pid: 0, name: swapper/0
no locks held by swapper/0/0.
CPU: 0 PID: 0 Comm: swapper/0 Not tainted 4.13.0-rc7-next-20170829 #2
Call Trace:
[c000003fff74f9b0] [c000000000a8d9f8] dump_stack+0xb0/0xf8 (unreliable)
[c000003fff74f9f0] [c000000000102820] ___might_sleep+0x150/0x180
[c000003fff74fa50] [c000000000aa98d0] __mutex_lock+0x80/0xa20
[c000003fff74fb60] [c0000000005f0280] bmc_device_id_handler+0x50/0x1e0
[c000003fff74fbe0] [c0000000005f0cfc] deliver_response+0xac/0x100
[c000003fff74fc10] [c0000000005f24e4] handle_one_recv_msg+0x164/0x10e0
[c000003fff74fd10] [c0000000005f369c] handle_new_recv_msgs+0x23c/0x2b0
[c000003fff74fd70] [c0000000000cfdf8] tasklet_action+0xc8/0x1b0
[c000003fff74fdd0] [c000000000ab00d8] __do_softirq+0x1d8/0x4cc
[c000003fff74fef0] [c0000000000cf608] irq_exit+0xe8/0x120
[c000003fff74ff10] [c000000000016568] __do_irq+0x108/0x2e0
[c000003fff74ff90] [c000000000029c10] call_do_irq+0x14/0x24
[c000000000ff7a70] [c0000000000167d4] do_IRQ+0x94/0x140
[c000000000ff7ac0] [c000000000008b28] hardware_interrupt_common+0x128/0x130
--- interrupt: 501 at replay_interrupt_return+0x0/0x4
LR = arch_local_irq_restore+0x74/0x90
[c000000000ff7db0] [0000000010004c20] 0x10004c20 (unreliable)
[c000000000ff7dd0] [c00000000001e3e8] arch_cpu_idle+0xd8/0x130
[c000000000ff7e00] [c000000000aaef34] default_idle_call+0x44/0x7c
[c000000000ff7e20] [c00000000012a884] do_idle+0x294/0x2b0
[c000000000ff7e90] [c00000000012aac0] cpu_startup_entry+0x30/0x50
[c000000000ff7ec0] [c00000000000d0f8] rest_init+0x1b8/0x1e0
[c000000000ff7f00] [c000000000dc0d28] start_kernel+0x53c/0x558
[c000000000ff7f90] [c00000000000ac70] start_here_common+0x1c/0x4ac
ipmi-powernv ibm,opal:ipmi: Found new BMC (man_id: 0x000000, prod_id: 0xaabb,
dev_id: 0x20)
- the IPMI response from the device ID query is being invoked at IRQ
context, so using a mutex isn't suitable for dyn_id_lock.
This change makes dyn_id_lock a spinlock instead. The lock is only
protecting from concurrent accesses to the id & dyn_id_expiry members of
struct bmc_device, so we don't need to sleep while it's held.
Reported-by: Michael Ellerman <[email protected]>
Signed-off-by: Jeremy Kerr <[email protected]>
---
drivers/char/ipmi/ipmi_msghandler.c | 26 ++++++++++++++------------
1 file changed, 14 insertions(+), 12 deletions(-)
diff --git a/drivers/char/ipmi/ipmi_msghandler.c
b/drivers/char/ipmi/ipmi_msghandler.c
index 9c82d55..7edb482 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -277,7 +277,7 @@ struct bmc_device {
ipmi_smi_t intf;
enum bmc_dyn_device_id_state dyn_id_state;
unsigned long dyn_id_expiry;
- struct mutex dyn_id_lock; /* protects id & dyn_id* fields */
+ spinlock_t dyn_id_lock; /* protects id & dyn_id* fields */
unsigned char guid[16];
int guid_set;
char name[16];
@@ -2087,6 +2087,7 @@ EXPORT_SYMBOL(ipmi_request_supply_msgs);
static void bmc_device_id_handler(ipmi_smi_t intf, struct ipmi_recv_msg *msg)
{
struct ipmi_device_id id;
+ unsigned long flags;
int rc;
if ((msg->addr.addr_type != IPMI_SYSTEM_INTERFACE_ADDR_TYPE)
@@ -2098,7 +2099,7 @@ static void bmc_device_id_handler(ipmi_smi_t intf, struct
ipmi_recv_msg *msg)
* the mutex over the entire transaction), as we may be called after
* bmc_get_device_id has timed-out (and so released its mutex).
*/
- mutex_lock(&intf->bmc->dyn_id_lock);
+ spin_lock_irqsave(&intf->bmc->dyn_id_lock, flags);
rc = ipmi_demangle_device_id(msg->msg.netfn, msg->msg.cmd,
msg->msg.data, msg->msg.data_len, &id);
@@ -2117,7 +2118,7 @@ static void bmc_device_id_handler(ipmi_smi_t intf, struct
ipmi_recv_msg *msg)
intf->bmc->dyn_id_state = BMC_DEVICE_DYN_ID_STATE_VALID;
intf->bmc->dyn_id_expiry = jiffies + IPMI_DYN_DEV_ID_EXPIRY;
out:
- mutex_unlock(&intf->bmc->dyn_id_lock);
+ spin_unlock_irqrestore(&intf->bmc->dyn_id_lock, flags);
wake_up(&intf->waitq);
}
@@ -2127,17 +2128,18 @@ static int bmc_get_device_id(struct bmc_device *bmc,
struct ipmi_device_id *id)
struct ipmi_system_interface_addr si;
ipmi_smi_t intf = bmc->intf;
struct kernel_ipmi_msg msg;
+ unsigned long flags;
bool query = false;
int tmp, rc = 0;
- mutex_lock(&bmc->dyn_id_lock);
+ spin_lock_irqsave(&bmc->dyn_id_lock, flags);
/* if we have a valid and current ID, just return that. */
if (bmc->dyn_id_state == BMC_DEVICE_DYN_ID_STATE_VALID &&
time_is_after_jiffies(bmc->dyn_id_expiry)) {
if (id)
*id = bmc->id;
- mutex_unlock(&bmc->dyn_id_lock);
+ spin_unlock_irqrestore(&bmc->dyn_id_lock, flags);
return 0;
}
@@ -2153,7 +2155,7 @@ static int bmc_get_device_id(struct bmc_device *bmc,
struct ipmi_device_id *id)
/* We unlock here, as bmc_device_id_handler will need to re-acquire
* the mutex
*/
- mutex_unlock(&bmc->dyn_id_lock);
+ spin_unlock_irqrestore(&bmc->dyn_id_lock, flags);
/* Send Get Device ID request. */
if (query) {
@@ -2182,9 +2184,9 @@ static int bmc_get_device_id(struct bmc_device *bmc,
struct ipmi_device_id *id)
-1, 0);
if (rc) {
- mutex_lock(&bmc->dyn_id_lock);
+ spin_lock_irqsave(&bmc->dyn_id_lock, flags);
bmc->dyn_id_state = BMC_DEVICE_DYN_ID_STATE_INVALID;
- mutex_unlock(&bmc->dyn_id_lock);
+ spin_unlock_irqrestore(&bmc->dyn_id_lock, flags);
return rc;
}
}
@@ -2200,7 +2202,7 @@ static int bmc_get_device_id(struct bmc_device *bmc,
struct ipmi_device_id *id)
if (query)
intf->null_user_handler = NULL;
- mutex_lock(&bmc->dyn_id_lock);
+ spin_lock_irqsave(&bmc->dyn_id_lock, flags);
if (!tmp) {
/* timeout */
bmc->dyn_id_state = BMC_DEVICE_DYN_ID_STATE_INVALID;
@@ -2209,7 +2211,7 @@ static int bmc_get_device_id(struct bmc_device *bmc,
struct ipmi_device_id *id)
/* We didn't timeout, but another thread has started a new
* query after we'd dropped the lock. Wait for that thread
* instead. */
- mutex_unlock(&bmc->dyn_id_lock);
+ spin_unlock_irqrestore(&bmc->dyn_id_lock, flags);
goto retry;
} else if (bmc->dyn_id_state != BMC_DEVICE_DYN_ID_STATE_VALID) {
@@ -2219,7 +2221,7 @@ static int bmc_get_device_id(struct bmc_device *bmc,
struct ipmi_device_id *id)
*id = bmc->id;
}
- mutex_unlock(&intf->bmc->dyn_id_lock);
+ spin_unlock_irqrestore(&intf->bmc->dyn_id_lock, flags);
return rc;
}
@@ -2730,7 +2732,7 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum)
struct bmc_device *bmc = intf->bmc;
struct bmc_device *old_bmc;
- mutex_init(&bmc->dyn_id_lock);
+ spin_lock_init(&bmc->dyn_id_lock);
bmc_get_device_id(bmc, NULL);
mutex_lock(&ipmidriver_mutex);
--
2.7.4
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Openipmi-developer mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openipmi-developer