Kernel panic when unloading libertas_sdio driver.
dinuliang at sina.com
dinuliang at sina.com
Wed Jun 2 23:03:53 EDT 2010
Dear All
I am porting wifi support on Android to the s3c6410 developement board, and my kernel version is 2.6.32.
The problem i'm dealing with is that sometimes when user unload the libertas_sdio driver,the kernel panic at
lbs_stop_card.
I track the problem, and find out it is cause by the kill system call.I tried to fix this, but i'm not
sure if i did it right.
My analysis of this problem is as follows:
In Android, when user want to disable the driver,the java service will ask the init process to stop the
wpa_supplicant process,and then unload the wifi driver.The init process will call kill system call to do
stop wpa_supplicant. This may cause a problem:
When kernel send a kill signal,it has to wake up the target process,and wpa_supplicant process is probably
sleeping in __lbs_cmd while waiting for a command to complete,and at the mean time the command may still in the
priv->cmdpendingq, not submitted yet.
int __lbs_cmd(struct lbs_private *priv, uint16_t command,
struct cmd_header *in_cmd, int in_cmd_size,
int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *),
unsigned long callback_arg)
{
struct cmd_ctrl_node *cmdnode;
unsigned long flags;
int ret = 0;
lbs_deb_enter(LBS_DEB_HOST);
cmdnode = __lbs_cmd_async(priv, command, in_cmd, in_cmd_size,
callback, callback_arg);
if (IS_ERR(cmdnode)) {
ret = PTR_ERR(cmdnode);
goto done;
}
might_sleep();
ret = wait_event_interruptible(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken);
spin_lock_irqsave(&priv->driver_lock, flags);
ret = cmdnode->result;
if (ret)
lbs_pr_info("PREP_CMD: command 0x%04x failed: %d\n",
command, ret);
__lbs_cleanup_and_insert_cmd(priv, cmdnode);
spin_unlock_irqrestore(&priv->driver_lock, flags);
done:
lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
return ret;
}
So after kernel wake up the process from wait_event_interruptible and clean up the node and
add it to the free list, the other pending list node or head is still keep link to it.
When user unload the driver,the kernel will crash at __wake_up_common in lbs_stop_card.*/
/* Flush pending command nodes */
spin_lock_irqsave(&priv->driver_lock, flags);
lbs_deb_main("clearing pending commands\n");
list_for_each_entry(cmdnode, &priv->cmdpendingq, list) {
cmdnode->result = -ENOENT;
cmdnode->cmdwaitqwoken = 1;
wake_up_interruptible(&cmdnode->cmdwait_q);
}
This is because the command node still linked in the priv->cmdpendingq,and it self link to the
priv->cmdfreeq.The list_for_each_entry will step to the list head of priv->cmdfreeq,which is doesn't
have a command node structure,and this will cause kernel penic when doing wake_up to it. The dump
log is as follows, i dump the command node structure befor the wake up:
libertas enter: lbs_stop_card()
libertas main: clearing pending commands
lbs_stop_card:has pending command node.
Dump command node:address of command node =c7ef00d8,list->prev =c7f38d98,list->next=c7f38d90,
result = 0, address of callback=0, callback_arg = 0,address of cmdbuf = c1d8d000,
cmdwaitqwoken = 1, address of cmdwait_q = c7ef00f4.
task state= 0, PID= 0.
Dump command node:address of command node =c7f38d90,list->prev =c7ef00d8,list->next=c7ef0000,
result = -940637992, address of callback=c7ef0120, callback_arg = c7f38da0,address of cmdbuf = c7f38da0,
cmdwaitqwoken = 8449, address of cmdwait_q = c7f38dac.
task state= 4001b508, PID= 65686361.
Unable to handle kernel NULL pointer dereference at virtual address 00000000
pgd = c7eb4000
[00000000] *pgd=57ead031, *pte=00000000, *ppte=00000000
Internal error: Oops: 80000007 [#1]
last sysfs file: /sys/android_power/request_state
Modules linked in: wlan(-) [last unloaded: wlan]
CPU: 0 Tainted: G W (2.6.32-00003-g930aecc-dirty #46)
PC is at 0x0
LR is at __wake_up_common+0x34/0x78
pc : [<00000000>] lr : [<c0043d28>] psr: a00000b3
sp : c7fbbdc8 ip : 00015000 fp : c7fbbdf4
r10: 00000000 r9 : 00000001 r8 : c7f38dac
r7 : 00000001 r6 : 00000001 r5 : 001027fc r4 : 00014ff4
r3 : 00000000 r2 : 00000000 r1 : 00000001 r0 : 00014ff4
Flags: NzCv IRQs off FIQs on Mode SVC_32 ISA Thumb Segment user
Control: 00c5387d Table: 57eb4008 DAC: 00000015
Process WifiService (pid: 1731, stack limit = 0xc7fba268)
Stack: (0xc7fbbdc8 to 0xc7fbc000)
bdc0: 00000000 60000093 c7f38d90 c7f382c0 c7f38d98 c7f38000
bde0: c7fba000 00000013 c7fbbe14 c7fbbdf8 c0043d94 c0043d00 00000000 c7fbbe18
be00: c020239c c7f38dac c7fbbe54 c7fbbe18 c02023b0 c0043d78 c7ef00d8 c7ef0120
be20: c7f38da0 c7f38da0 00002101 c7f38dac c7cef3e0 c7fe0000 c7cef3e0 00000880
be40: c002c028 00000000 c7fbbe84 c7fbbe58 bf03015c c02021fc 00000000 c7cef3f0
be60: bf032a30 c7cef3e8 bf032a30 c7cef3e0 bf032a30 bf032a30 c7fbbe9c c7fbbe88
be80: c024bbac bf03000c c7cef3e8 bf032a30 c7fbbeb4 c7fbbea0 c01d11f4 c024bb98
bea0: c7cef3e8 c7cef41c c7fbbed4 c7fbbeb8 c01d129c c01d1194 bf032a30 bf032a30
bec0: c041d93c 00000880 c7fbbef4 c7fbbed8 c01d04a4 c01d1240 00000000 bf032a30
bee0: 00000000 00000880 c7fbbf14 c7fbbef8 c01d1800 c01d0418 00000000 bf032a64
bf00: c7fbbf3c 00000880 c7fbbf24 c7fbbf18 c024bd9c c01d17a4 c7fbbf34 c7fbbf28
bf20: bf03208c c024bd88 c7fbbfa4 c7fbbf38 c006ccbc bf032024 c1c4f1e0 6e616c77
bf40: c7892200 00000006 c002c028 00000000 c7fbbf6c c7fbbf60 c009d3c0 c009d1d4
bf60: c7fbbf8c c7fbbf70 c009a00c c009d39c c7892240 00c4f1e0 bf032a64 00000880
bf80: c7fbbf84 00000000 a9c07658 ffffffff 00000009 00000081 00000000 c7fbbfa8
bfa0: c002be80 c006caf4 a9c07658 ffffffff a9c062e4 00000880 42747f3c 425f327c
bfc0: a9c07658 ffffffff 00000009 00000081 46364d88 42747ee4 42747ed0 002d90c8
bfe0: a9c07680 46364d50 a9c05c69 afe0d8ac 00000010 a9c062e4 00000000 00000000
Backtrace:
[<c0043cf4>] (__wake_up_common+0x0/0x78) from [<c0043d94>] (__wake_up+0x28/0x34)
The command node at the address 0xc7f38d90 is the node we discussed,it link
to the head of free list.
To fix that, i do some change in __lbs_cmd, and the problem seems go
away.
ret = wait_event_interruptible(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken);
if (ret == -ERESTARTSYS || cmdnode->result == -ENOENT) {
lbs_pr_info("Remove command node from pending list,have pending signal,
cmdnode address %lx.\n", cmdnode);
if (cmdnode != priv->cur_cmd)
list_del(&cmdnode->list);
}
Am i do it right?Thanks very much and Best Regards Liang Yifei
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.infradead.org/pipermail/libertas-dev/attachments/20100603/e1f79b22/attachment-0001.htm>
More information about the libertas-dev
mailing list