2005-04-17 06:20:36 +08:00
|
|
|
/* $Id: capi.c,v 1.1.2.7 2004/04/28 09:48:59 armin Exp $
|
|
|
|
*
|
|
|
|
* CAPI 2.0 Interface for Linux
|
|
|
|
*
|
|
|
|
* Copyright 1996 by Carsten Paeth <calle@calle.de>
|
|
|
|
*
|
|
|
|
* This software may be used and distributed according to the terms
|
|
|
|
* of the GNU General Public License, incorporated herein by reference.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/major.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/fcntl.h>
|
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/signal.h>
|
2007-05-08 15:32:43 +08:00
|
|
|
#include <linux/mutex.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/mm.h>
|
2008-05-17 04:15:33 +08:00
|
|
|
#include <linux/smp_lock.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/timer.h>
|
|
|
|
#include <linux/wait.h>
|
|
|
|
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
|
|
|
|
#include <linux/tty.h>
|
|
|
|
#ifdef CONFIG_PPP
|
|
|
|
#include <linux/netdevice.h>
|
|
|
|
#include <linux/ppp_defs.h>
|
|
|
|
#include <linux/if_ppp.h>
|
|
|
|
#endif /* CONFIG_PPP */
|
|
|
|
#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
|
|
|
|
#include <linux/skbuff.h>
|
|
|
|
#include <linux/proc_fs.h>
|
|
|
|
#include <linux/poll.h>
|
|
|
|
#include <linux/capi.h>
|
|
|
|
#include <linux/kernelcapi.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/moduleparam.h>
|
|
|
|
#include <linux/isdn/capiutil.h>
|
|
|
|
#include <linux/isdn/capicmd.h>
|
|
|
|
#if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE)
|
|
|
|
#include "capifs.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static char *revision = "$Revision: 1.1.2.7 $";
|
|
|
|
|
|
|
|
MODULE_DESCRIPTION("CAPI4Linux: Userspace /dev/capi20 interface");
|
|
|
|
MODULE_AUTHOR("Carsten Paeth");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
|
|
|
#undef _DEBUG_REFCOUNT /* alloc/free and open/close debug */
|
|
|
|
#undef _DEBUG_TTYFUNCS /* call to tty_driver */
|
|
|
|
#undef _DEBUG_DATAFLOW /* data flow */
|
|
|
|
|
|
|
|
/* -------- driver information -------------------------------------- */
|
|
|
|
|
2005-03-24 02:01:41 +08:00
|
|
|
static struct class *capi_class;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-05-01 23:59:29 +08:00
|
|
|
static int capi_major = 68; /* allocated */
|
2005-04-17 06:20:36 +08:00
|
|
|
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
|
|
|
|
#define CAPINC_NR_PORTS 32
|
|
|
|
#define CAPINC_MAX_PORTS 256
|
2005-05-01 23:59:29 +08:00
|
|
|
static int capi_ttymajor = 191;
|
|
|
|
static int capi_ttyminors = CAPINC_NR_PORTS;
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
|
|
|
|
|
|
|
|
module_param_named(major, capi_major, uint, 0);
|
|
|
|
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
|
|
|
|
module_param_named(ttymajor, capi_ttymajor, uint, 0);
|
|
|
|
module_param_named(ttyminors, capi_ttyminors, uint, 0);
|
|
|
|
#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
|
|
|
|
|
|
|
|
/* -------- defines ------------------------------------------------- */
|
|
|
|
|
|
|
|
#define CAPINC_MAX_RECVQUEUE 10
|
|
|
|
#define CAPINC_MAX_SENDQUEUE 10
|
|
|
|
#define CAPI_MAX_BLKSIZE 2048
|
|
|
|
|
|
|
|
/* -------- data structures ----------------------------------------- */
|
|
|
|
|
|
|
|
struct capidev;
|
|
|
|
struct capincci;
|
|
|
|
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
|
|
|
|
struct capiminor;
|
|
|
|
|
[PATCH] CAPI crash / race condition
I am getting more or less reproducible crashes from the CAPI subsystem
using the fcdsl driver:
Unable to handle kernel NULL pointer dereference at virtual address 00000010
printing eip:
c39bbca4
*pde = 00000000
Oops: 0000 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bbca4>] Tainted: P VLI
EFLAGS: 00010202 (2.6.16.11 #3)
EIP is at handle_minor_send+0x17a/0x241 [capi]
eax: c24abbc0 ebx: c0b4c980 ecx: 00000010 edx: 00000010
esi: c1679140 edi: c2783016 ebp: 0000c28d esp: c0327e24
ds: 007b es: 007b ss: 0068
Process swapper (pid: 0, threadinfo=c0326000 task=c02e1300)
Stack: <0>000005b4 c1679180 00000000 c28d0000 c1ce04e0 c2f69654 c221604e c1679140
c39bc19a 00000038 c20c0400 c075c560 c1f2f800 00000000 c01dc9b5 c1e96a40
c075c560 c2ed64c0 c1e96a40 c01dcd3b c2fb94e8 c075c560 c0327f00 c1e96a40
Call Trace:
[<c39bc19a>] capinc_tty_write+0xda/0xf3 [capi]
[<c01dc9b5>] ppp_sync_push+0x52/0xfe
[<c01dcd3b>] ppp_sync_send+0x1f5/0x204
[<c01d9bc1>] ppp_push+0x3e/0x9c
[<c01dacd4>] ppp_xmit_process+0x422/0x4cc
[<c01daf3f>] ppp_start_xmit+0x1c1/0x1f6
[<c0213ea5>] qdisc_restart+0xa7/0x135
[<c020b112>] dev_queue_xmit+0xba/0x19e
[<c0223f69>] ip_output+0x1eb/0x236
[<c0220907>] ip_forward+0x1c1/0x21a
[<c021fa6c>] ip_rcv+0x38e/0x3ea
[<c020b4c2>] netif_receive_skb+0x166/0x195
[<c020b55e>] process_backlog+0x6d/0xd2
[<c020a30f>] net_rx_action+0x6a/0xff
[<c0112909>] __do_softirq+0x35/0x7d
[<c0112973>] do_softirq+0x22/0x26
[<c0103a9d>] do_IRQ+0x1e/0x25
[<c010255a>] common_interrupt+0x1a/0x20
[<c01013c5>] default_idle+0x2b/0x53
[<c0101426>] cpu_idle+0x39/0x4e
[<c0328386>] start_kernel+0x20b/0x20d
Code: c0 e8 b3 b6 77 fc 85 c0 75 10 68 d8 c8 9b c3 e8 82 3d 75 fc 8b 43 60 5a eb 50 8d 56 50 c7 00 00 00 00 00 66 89 68 04 eb 02 89
ca <8b> 0a 85 c9 75 f8 89 02 89 da ff 46 54 8b 46 10 e8 30 79 fd ff
<0>Kernel panic - not syncing: Fatal exception in interrupt
That oops took me to the "ackqueue" implementation in capi.c. The crash
occured in capincci_add_ack() (auto-inlined by the compiler).
I read the code a bit and finally decided to replace the custom linked list
implementation (struct capiminor->ackqueue) by a struct list_head. That
did not solve the crash, but produced the following interresting oops:
Unable to handle kernel paging request at virtual address 00200200
printing eip:
c39bb1f5
*pde = 00000000
Oops: 0002 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bb1f5>] Tainted: P VLI
EFLAGS: 00010246 (2.6.16.11 #3)
EIP is at capiminor_del_ack+0x18/0x49 [capi]
eax: 00200200 ebx: c18d41a0 ecx: c1385620 edx: 00100100
esi: 0000d147 edi: 00001103 ebp: 0000d147 esp: c1093f3c
ds: 007b es: 007b ss: 0068
Process events/0 (pid: 3, threadinfo=c1092000 task=c1089030)
Stack: <0>c2a17580 c18d41a0 c39bbd16 00000038 c18d41e0 00000000 d147c640 c29e0b68
c29e0b90 00000212 c29e0b68 c39932b2 c29e0bb0 c10736a0 c0119ef0 c399326c
c10736a8 c10736a0 c10736b0 c0119f93 c011a06e 00000001 00000000 00000000
Call Trace:
[<c39bbd16>] handle_minor_send+0x1af/0x241 [capi]
[<c39932b2>] recv_handler+0x46/0x5f [kernelcapi]
[<c0119ef0>] run_workqueue+0x5e/0x8d
[<c399326c>] recv_handler+0x0/0x5f [kernelcapi]
[<c0119f93>] worker_thread+0x0/0x10b
[<c011a06e>] worker_thread+0xdb/0x10b
[<c010c998>] default_wake_function+0x0/0xc
[<c011c399>] kthread+0x90/0xbc
[<c011c309>] kthread+0x0/0xbc
[<c0100a65>] kernel_thread_helper+0x5/0xb
Code: 7e 02 89 ee 89 f0 5a f7 d0 c1 f8 1f 5b 21 f0 5e 5f 5d c3 56 53 8b 48 50 89 d6 89 c3 8b 11 eb 2f 66 39 71 08 75 25 8b 41 04 8b 11 <89> 10 89 42 04 c7 01 00 01 10 00 89 c8 c7 41 04 00 02 20 00 e8
The interresting part of it is the "virtual address 00200200", which is
LIST_POISON2. I thought about some race condition, but as this is an UP
system, it leads to questions on how it can happen. If we look at EFLAGS:
00010202, we see that interrupts are enabled at the time of the crash
(eflags & 0x200).
Finally, I don't understand all the capi code, but I think that
handle_minor_send() is racing somehow against capi_recv_message(), which
call both capiminor_del_ack(). So if an IRQ occurs in the middle of
capiminor_del_ack() and another instance of it is invoked, it leads to
linked list corruption.
I came up with the following patch. With this, I could not reproduce the
crash anymore. Clearly, this is not the correct fix for the issue. As this
seems to be some locking issue, there might be more locking issues in that
code. For example, doesn't the whole struct capiminor have to be locked
somehow?
Cc: Carsten Paeth <calle@calle.de>
Cc: Kai Germaschewski <kai.germaschewski@gmx.de>
Cc: Karsten Keil <kkeil@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 15:25:30 +08:00
|
|
|
struct datahandle_queue {
|
|
|
|
struct list_head list;
|
|
|
|
u16 datahandle;
|
|
|
|
};
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
struct capiminor {
|
|
|
|
struct list_head list;
|
|
|
|
struct capincci *nccip;
|
|
|
|
unsigned int minor;
|
|
|
|
|
|
|
|
struct capi20_appl *ap;
|
|
|
|
u32 ncci;
|
|
|
|
u16 datahandle;
|
|
|
|
u16 msgid;
|
|
|
|
|
|
|
|
struct tty_struct *tty;
|
|
|
|
int ttyinstop;
|
|
|
|
int ttyoutstop;
|
|
|
|
struct sk_buff *ttyskb;
|
|
|
|
atomic_t ttyopencount;
|
|
|
|
|
|
|
|
struct sk_buff_head inqueue;
|
|
|
|
int inbytes;
|
|
|
|
struct sk_buff_head outqueue;
|
|
|
|
int outbytes;
|
|
|
|
|
|
|
|
/* transmit path */
|
[PATCH] CAPI crash / race condition
I am getting more or less reproducible crashes from the CAPI subsystem
using the fcdsl driver:
Unable to handle kernel NULL pointer dereference at virtual address 00000010
printing eip:
c39bbca4
*pde = 00000000
Oops: 0000 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bbca4>] Tainted: P VLI
EFLAGS: 00010202 (2.6.16.11 #3)
EIP is at handle_minor_send+0x17a/0x241 [capi]
eax: c24abbc0 ebx: c0b4c980 ecx: 00000010 edx: 00000010
esi: c1679140 edi: c2783016 ebp: 0000c28d esp: c0327e24
ds: 007b es: 007b ss: 0068
Process swapper (pid: 0, threadinfo=c0326000 task=c02e1300)
Stack: <0>000005b4 c1679180 00000000 c28d0000 c1ce04e0 c2f69654 c221604e c1679140
c39bc19a 00000038 c20c0400 c075c560 c1f2f800 00000000 c01dc9b5 c1e96a40
c075c560 c2ed64c0 c1e96a40 c01dcd3b c2fb94e8 c075c560 c0327f00 c1e96a40
Call Trace:
[<c39bc19a>] capinc_tty_write+0xda/0xf3 [capi]
[<c01dc9b5>] ppp_sync_push+0x52/0xfe
[<c01dcd3b>] ppp_sync_send+0x1f5/0x204
[<c01d9bc1>] ppp_push+0x3e/0x9c
[<c01dacd4>] ppp_xmit_process+0x422/0x4cc
[<c01daf3f>] ppp_start_xmit+0x1c1/0x1f6
[<c0213ea5>] qdisc_restart+0xa7/0x135
[<c020b112>] dev_queue_xmit+0xba/0x19e
[<c0223f69>] ip_output+0x1eb/0x236
[<c0220907>] ip_forward+0x1c1/0x21a
[<c021fa6c>] ip_rcv+0x38e/0x3ea
[<c020b4c2>] netif_receive_skb+0x166/0x195
[<c020b55e>] process_backlog+0x6d/0xd2
[<c020a30f>] net_rx_action+0x6a/0xff
[<c0112909>] __do_softirq+0x35/0x7d
[<c0112973>] do_softirq+0x22/0x26
[<c0103a9d>] do_IRQ+0x1e/0x25
[<c010255a>] common_interrupt+0x1a/0x20
[<c01013c5>] default_idle+0x2b/0x53
[<c0101426>] cpu_idle+0x39/0x4e
[<c0328386>] start_kernel+0x20b/0x20d
Code: c0 e8 b3 b6 77 fc 85 c0 75 10 68 d8 c8 9b c3 e8 82 3d 75 fc 8b 43 60 5a eb 50 8d 56 50 c7 00 00 00 00 00 66 89 68 04 eb 02 89
ca <8b> 0a 85 c9 75 f8 89 02 89 da ff 46 54 8b 46 10 e8 30 79 fd ff
<0>Kernel panic - not syncing: Fatal exception in interrupt
That oops took me to the "ackqueue" implementation in capi.c. The crash
occured in capincci_add_ack() (auto-inlined by the compiler).
I read the code a bit and finally decided to replace the custom linked list
implementation (struct capiminor->ackqueue) by a struct list_head. That
did not solve the crash, but produced the following interresting oops:
Unable to handle kernel paging request at virtual address 00200200
printing eip:
c39bb1f5
*pde = 00000000
Oops: 0002 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bb1f5>] Tainted: P VLI
EFLAGS: 00010246 (2.6.16.11 #3)
EIP is at capiminor_del_ack+0x18/0x49 [capi]
eax: 00200200 ebx: c18d41a0 ecx: c1385620 edx: 00100100
esi: 0000d147 edi: 00001103 ebp: 0000d147 esp: c1093f3c
ds: 007b es: 007b ss: 0068
Process events/0 (pid: 3, threadinfo=c1092000 task=c1089030)
Stack: <0>c2a17580 c18d41a0 c39bbd16 00000038 c18d41e0 00000000 d147c640 c29e0b68
c29e0b90 00000212 c29e0b68 c39932b2 c29e0bb0 c10736a0 c0119ef0 c399326c
c10736a8 c10736a0 c10736b0 c0119f93 c011a06e 00000001 00000000 00000000
Call Trace:
[<c39bbd16>] handle_minor_send+0x1af/0x241 [capi]
[<c39932b2>] recv_handler+0x46/0x5f [kernelcapi]
[<c0119ef0>] run_workqueue+0x5e/0x8d
[<c399326c>] recv_handler+0x0/0x5f [kernelcapi]
[<c0119f93>] worker_thread+0x0/0x10b
[<c011a06e>] worker_thread+0xdb/0x10b
[<c010c998>] default_wake_function+0x0/0xc
[<c011c399>] kthread+0x90/0xbc
[<c011c309>] kthread+0x0/0xbc
[<c0100a65>] kernel_thread_helper+0x5/0xb
Code: 7e 02 89 ee 89 f0 5a f7 d0 c1 f8 1f 5b 21 f0 5e 5f 5d c3 56 53 8b 48 50 89 d6 89 c3 8b 11 eb 2f 66 39 71 08 75 25 8b 41 04 8b 11 <89> 10 89 42 04 c7 01 00 01 10 00 89 c8 c7 41 04 00 02 20 00 e8
The interresting part of it is the "virtual address 00200200", which is
LIST_POISON2. I thought about some race condition, but as this is an UP
system, it leads to questions on how it can happen. If we look at EFLAGS:
00010202, we see that interrupts are enabled at the time of the crash
(eflags & 0x200).
Finally, I don't understand all the capi code, but I think that
handle_minor_send() is racing somehow against capi_recv_message(), which
call both capiminor_del_ack(). So if an IRQ occurs in the middle of
capiminor_del_ack() and another instance of it is invoked, it leads to
linked list corruption.
I came up with the following patch. With this, I could not reproduce the
crash anymore. Clearly, this is not the correct fix for the issue. As this
seems to be some locking issue, there might be more locking issues in that
code. For example, doesn't the whole struct capiminor have to be locked
somehow?
Cc: Carsten Paeth <calle@calle.de>
Cc: Kai Germaschewski <kai.germaschewski@gmx.de>
Cc: Karsten Keil <kkeil@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 15:25:30 +08:00
|
|
|
struct list_head ackqueue;
|
2005-04-17 06:20:36 +08:00
|
|
|
int nack;
|
[PATCH] CAPI crash / race condition
I am getting more or less reproducible crashes from the CAPI subsystem
using the fcdsl driver:
Unable to handle kernel NULL pointer dereference at virtual address 00000010
printing eip:
c39bbca4
*pde = 00000000
Oops: 0000 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bbca4>] Tainted: P VLI
EFLAGS: 00010202 (2.6.16.11 #3)
EIP is at handle_minor_send+0x17a/0x241 [capi]
eax: c24abbc0 ebx: c0b4c980 ecx: 00000010 edx: 00000010
esi: c1679140 edi: c2783016 ebp: 0000c28d esp: c0327e24
ds: 007b es: 007b ss: 0068
Process swapper (pid: 0, threadinfo=c0326000 task=c02e1300)
Stack: <0>000005b4 c1679180 00000000 c28d0000 c1ce04e0 c2f69654 c221604e c1679140
c39bc19a 00000038 c20c0400 c075c560 c1f2f800 00000000 c01dc9b5 c1e96a40
c075c560 c2ed64c0 c1e96a40 c01dcd3b c2fb94e8 c075c560 c0327f00 c1e96a40
Call Trace:
[<c39bc19a>] capinc_tty_write+0xda/0xf3 [capi]
[<c01dc9b5>] ppp_sync_push+0x52/0xfe
[<c01dcd3b>] ppp_sync_send+0x1f5/0x204
[<c01d9bc1>] ppp_push+0x3e/0x9c
[<c01dacd4>] ppp_xmit_process+0x422/0x4cc
[<c01daf3f>] ppp_start_xmit+0x1c1/0x1f6
[<c0213ea5>] qdisc_restart+0xa7/0x135
[<c020b112>] dev_queue_xmit+0xba/0x19e
[<c0223f69>] ip_output+0x1eb/0x236
[<c0220907>] ip_forward+0x1c1/0x21a
[<c021fa6c>] ip_rcv+0x38e/0x3ea
[<c020b4c2>] netif_receive_skb+0x166/0x195
[<c020b55e>] process_backlog+0x6d/0xd2
[<c020a30f>] net_rx_action+0x6a/0xff
[<c0112909>] __do_softirq+0x35/0x7d
[<c0112973>] do_softirq+0x22/0x26
[<c0103a9d>] do_IRQ+0x1e/0x25
[<c010255a>] common_interrupt+0x1a/0x20
[<c01013c5>] default_idle+0x2b/0x53
[<c0101426>] cpu_idle+0x39/0x4e
[<c0328386>] start_kernel+0x20b/0x20d
Code: c0 e8 b3 b6 77 fc 85 c0 75 10 68 d8 c8 9b c3 e8 82 3d 75 fc 8b 43 60 5a eb 50 8d 56 50 c7 00 00 00 00 00 66 89 68 04 eb 02 89
ca <8b> 0a 85 c9 75 f8 89 02 89 da ff 46 54 8b 46 10 e8 30 79 fd ff
<0>Kernel panic - not syncing: Fatal exception in interrupt
That oops took me to the "ackqueue" implementation in capi.c. The crash
occured in capincci_add_ack() (auto-inlined by the compiler).
I read the code a bit and finally decided to replace the custom linked list
implementation (struct capiminor->ackqueue) by a struct list_head. That
did not solve the crash, but produced the following interresting oops:
Unable to handle kernel paging request at virtual address 00200200
printing eip:
c39bb1f5
*pde = 00000000
Oops: 0002 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bb1f5>] Tainted: P VLI
EFLAGS: 00010246 (2.6.16.11 #3)
EIP is at capiminor_del_ack+0x18/0x49 [capi]
eax: 00200200 ebx: c18d41a0 ecx: c1385620 edx: 00100100
esi: 0000d147 edi: 00001103 ebp: 0000d147 esp: c1093f3c
ds: 007b es: 007b ss: 0068
Process events/0 (pid: 3, threadinfo=c1092000 task=c1089030)
Stack: <0>c2a17580 c18d41a0 c39bbd16 00000038 c18d41e0 00000000 d147c640 c29e0b68
c29e0b90 00000212 c29e0b68 c39932b2 c29e0bb0 c10736a0 c0119ef0 c399326c
c10736a8 c10736a0 c10736b0 c0119f93 c011a06e 00000001 00000000 00000000
Call Trace:
[<c39bbd16>] handle_minor_send+0x1af/0x241 [capi]
[<c39932b2>] recv_handler+0x46/0x5f [kernelcapi]
[<c0119ef0>] run_workqueue+0x5e/0x8d
[<c399326c>] recv_handler+0x0/0x5f [kernelcapi]
[<c0119f93>] worker_thread+0x0/0x10b
[<c011a06e>] worker_thread+0xdb/0x10b
[<c010c998>] default_wake_function+0x0/0xc
[<c011c399>] kthread+0x90/0xbc
[<c011c309>] kthread+0x0/0xbc
[<c0100a65>] kernel_thread_helper+0x5/0xb
Code: 7e 02 89 ee 89 f0 5a f7 d0 c1 f8 1f 5b 21 f0 5e 5f 5d c3 56 53 8b 48 50 89 d6 89 c3 8b 11 eb 2f 66 39 71 08 75 25 8b 41 04 8b 11 <89> 10 89 42 04 c7 01 00 01 10 00 89 c8 c7 41 04 00 02 20 00 e8
The interresting part of it is the "virtual address 00200200", which is
LIST_POISON2. I thought about some race condition, but as this is an UP
system, it leads to questions on how it can happen. If we look at EFLAGS:
00010202, we see that interrupts are enabled at the time of the crash
(eflags & 0x200).
Finally, I don't understand all the capi code, but I think that
handle_minor_send() is racing somehow against capi_recv_message(), which
call both capiminor_del_ack(). So if an IRQ occurs in the middle of
capiminor_del_ack() and another instance of it is invoked, it leads to
linked list corruption.
I came up with the following patch. With this, I could not reproduce the
crash anymore. Clearly, this is not the correct fix for the issue. As this
seems to be some locking issue, there might be more locking issues in that
code. For example, doesn't the whole struct capiminor have to be locked
somehow?
Cc: Carsten Paeth <calle@calle.de>
Cc: Kai Germaschewski <kai.germaschewski@gmx.de>
Cc: Karsten Keil <kkeil@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 15:25:30 +08:00
|
|
|
spinlock_t ackqlock;
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
|
|
|
|
|
2007-02-12 16:53:26 +08:00
|
|
|
/* FIXME: The following lock is a sledgehammer-workaround to a
|
|
|
|
* locking issue with the capiminor (and maybe other) data structure(s).
|
|
|
|
* Access to this data is done in a racy way and crashes the machine with
|
|
|
|
* a FritzCard DSL driver; sooner or later. This is a workaround
|
|
|
|
* which trades scalability vs stability, so it doesn't crash the kernel anymore.
|
|
|
|
* The correct (and scalable) fix for the issue seems to require
|
|
|
|
* an API change to the drivers... . */
|
|
|
|
static DEFINE_SPINLOCK(workaround_lock);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
struct capincci {
|
|
|
|
struct capincci *next;
|
|
|
|
u32 ncci;
|
|
|
|
struct capidev *cdev;
|
|
|
|
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
|
|
|
|
struct capiminor *minorp;
|
|
|
|
#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
|
|
|
|
};
|
|
|
|
|
|
|
|
struct capidev {
|
|
|
|
struct list_head list;
|
|
|
|
struct capi20_appl ap;
|
|
|
|
u16 errcode;
|
|
|
|
unsigned userflags;
|
|
|
|
|
|
|
|
struct sk_buff_head recvqueue;
|
|
|
|
wait_queue_head_t recvwait;
|
|
|
|
|
|
|
|
struct capincci *nccis;
|
|
|
|
|
2007-05-08 15:32:43 +08:00
|
|
|
struct mutex ncci_list_mtx;
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/* -------- global variables ---------------------------------------- */
|
|
|
|
|
|
|
|
static DEFINE_RWLOCK(capidev_list_lock);
|
|
|
|
static LIST_HEAD(capidev_list);
|
|
|
|
|
|
|
|
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
|
|
|
|
static DEFINE_RWLOCK(capiminor_list_lock);
|
|
|
|
static LIST_HEAD(capiminor_list);
|
|
|
|
#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
|
|
|
|
|
|
|
|
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
|
|
|
|
/* -------- datahandles --------------------------------------------- */
|
|
|
|
|
|
|
|
static int capincci_add_ack(struct capiminor *mp, u16 datahandle)
|
|
|
|
{
|
[PATCH] CAPI crash / race condition
I am getting more or less reproducible crashes from the CAPI subsystem
using the fcdsl driver:
Unable to handle kernel NULL pointer dereference at virtual address 00000010
printing eip:
c39bbca4
*pde = 00000000
Oops: 0000 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bbca4>] Tainted: P VLI
EFLAGS: 00010202 (2.6.16.11 #3)
EIP is at handle_minor_send+0x17a/0x241 [capi]
eax: c24abbc0 ebx: c0b4c980 ecx: 00000010 edx: 00000010
esi: c1679140 edi: c2783016 ebp: 0000c28d esp: c0327e24
ds: 007b es: 007b ss: 0068
Process swapper (pid: 0, threadinfo=c0326000 task=c02e1300)
Stack: <0>000005b4 c1679180 00000000 c28d0000 c1ce04e0 c2f69654 c221604e c1679140
c39bc19a 00000038 c20c0400 c075c560 c1f2f800 00000000 c01dc9b5 c1e96a40
c075c560 c2ed64c0 c1e96a40 c01dcd3b c2fb94e8 c075c560 c0327f00 c1e96a40
Call Trace:
[<c39bc19a>] capinc_tty_write+0xda/0xf3 [capi]
[<c01dc9b5>] ppp_sync_push+0x52/0xfe
[<c01dcd3b>] ppp_sync_send+0x1f5/0x204
[<c01d9bc1>] ppp_push+0x3e/0x9c
[<c01dacd4>] ppp_xmit_process+0x422/0x4cc
[<c01daf3f>] ppp_start_xmit+0x1c1/0x1f6
[<c0213ea5>] qdisc_restart+0xa7/0x135
[<c020b112>] dev_queue_xmit+0xba/0x19e
[<c0223f69>] ip_output+0x1eb/0x236
[<c0220907>] ip_forward+0x1c1/0x21a
[<c021fa6c>] ip_rcv+0x38e/0x3ea
[<c020b4c2>] netif_receive_skb+0x166/0x195
[<c020b55e>] process_backlog+0x6d/0xd2
[<c020a30f>] net_rx_action+0x6a/0xff
[<c0112909>] __do_softirq+0x35/0x7d
[<c0112973>] do_softirq+0x22/0x26
[<c0103a9d>] do_IRQ+0x1e/0x25
[<c010255a>] common_interrupt+0x1a/0x20
[<c01013c5>] default_idle+0x2b/0x53
[<c0101426>] cpu_idle+0x39/0x4e
[<c0328386>] start_kernel+0x20b/0x20d
Code: c0 e8 b3 b6 77 fc 85 c0 75 10 68 d8 c8 9b c3 e8 82 3d 75 fc 8b 43 60 5a eb 50 8d 56 50 c7 00 00 00 00 00 66 89 68 04 eb 02 89
ca <8b> 0a 85 c9 75 f8 89 02 89 da ff 46 54 8b 46 10 e8 30 79 fd ff
<0>Kernel panic - not syncing: Fatal exception in interrupt
That oops took me to the "ackqueue" implementation in capi.c. The crash
occured in capincci_add_ack() (auto-inlined by the compiler).
I read the code a bit and finally decided to replace the custom linked list
implementation (struct capiminor->ackqueue) by a struct list_head. That
did not solve the crash, but produced the following interresting oops:
Unable to handle kernel paging request at virtual address 00200200
printing eip:
c39bb1f5
*pde = 00000000
Oops: 0002 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bb1f5>] Tainted: P VLI
EFLAGS: 00010246 (2.6.16.11 #3)
EIP is at capiminor_del_ack+0x18/0x49 [capi]
eax: 00200200 ebx: c18d41a0 ecx: c1385620 edx: 00100100
esi: 0000d147 edi: 00001103 ebp: 0000d147 esp: c1093f3c
ds: 007b es: 007b ss: 0068
Process events/0 (pid: 3, threadinfo=c1092000 task=c1089030)
Stack: <0>c2a17580 c18d41a0 c39bbd16 00000038 c18d41e0 00000000 d147c640 c29e0b68
c29e0b90 00000212 c29e0b68 c39932b2 c29e0bb0 c10736a0 c0119ef0 c399326c
c10736a8 c10736a0 c10736b0 c0119f93 c011a06e 00000001 00000000 00000000
Call Trace:
[<c39bbd16>] handle_minor_send+0x1af/0x241 [capi]
[<c39932b2>] recv_handler+0x46/0x5f [kernelcapi]
[<c0119ef0>] run_workqueue+0x5e/0x8d
[<c399326c>] recv_handler+0x0/0x5f [kernelcapi]
[<c0119f93>] worker_thread+0x0/0x10b
[<c011a06e>] worker_thread+0xdb/0x10b
[<c010c998>] default_wake_function+0x0/0xc
[<c011c399>] kthread+0x90/0xbc
[<c011c309>] kthread+0x0/0xbc
[<c0100a65>] kernel_thread_helper+0x5/0xb
Code: 7e 02 89 ee 89 f0 5a f7 d0 c1 f8 1f 5b 21 f0 5e 5f 5d c3 56 53 8b 48 50 89 d6 89 c3 8b 11 eb 2f 66 39 71 08 75 25 8b 41 04 8b 11 <89> 10 89 42 04 c7 01 00 01 10 00 89 c8 c7 41 04 00 02 20 00 e8
The interresting part of it is the "virtual address 00200200", which is
LIST_POISON2. I thought about some race condition, but as this is an UP
system, it leads to questions on how it can happen. If we look at EFLAGS:
00010202, we see that interrupts are enabled at the time of the crash
(eflags & 0x200).
Finally, I don't understand all the capi code, but I think that
handle_minor_send() is racing somehow against capi_recv_message(), which
call both capiminor_del_ack(). So if an IRQ occurs in the middle of
capiminor_del_ack() and another instance of it is invoked, it leads to
linked list corruption.
I came up with the following patch. With this, I could not reproduce the
crash anymore. Clearly, this is not the correct fix for the issue. As this
seems to be some locking issue, there might be more locking issues in that
code. For example, doesn't the whole struct capiminor have to be locked
somehow?
Cc: Carsten Paeth <calle@calle.de>
Cc: Kai Germaschewski <kai.germaschewski@gmx.de>
Cc: Karsten Keil <kkeil@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 15:25:30 +08:00
|
|
|
struct datahandle_queue *n;
|
|
|
|
unsigned long flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
n = kmalloc(sizeof(*n), GFP_ATOMIC);
|
[PATCH] CAPI crash / race condition
I am getting more or less reproducible crashes from the CAPI subsystem
using the fcdsl driver:
Unable to handle kernel NULL pointer dereference at virtual address 00000010
printing eip:
c39bbca4
*pde = 00000000
Oops: 0000 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bbca4>] Tainted: P VLI
EFLAGS: 00010202 (2.6.16.11 #3)
EIP is at handle_minor_send+0x17a/0x241 [capi]
eax: c24abbc0 ebx: c0b4c980 ecx: 00000010 edx: 00000010
esi: c1679140 edi: c2783016 ebp: 0000c28d esp: c0327e24
ds: 007b es: 007b ss: 0068
Process swapper (pid: 0, threadinfo=c0326000 task=c02e1300)
Stack: <0>000005b4 c1679180 00000000 c28d0000 c1ce04e0 c2f69654 c221604e c1679140
c39bc19a 00000038 c20c0400 c075c560 c1f2f800 00000000 c01dc9b5 c1e96a40
c075c560 c2ed64c0 c1e96a40 c01dcd3b c2fb94e8 c075c560 c0327f00 c1e96a40
Call Trace:
[<c39bc19a>] capinc_tty_write+0xda/0xf3 [capi]
[<c01dc9b5>] ppp_sync_push+0x52/0xfe
[<c01dcd3b>] ppp_sync_send+0x1f5/0x204
[<c01d9bc1>] ppp_push+0x3e/0x9c
[<c01dacd4>] ppp_xmit_process+0x422/0x4cc
[<c01daf3f>] ppp_start_xmit+0x1c1/0x1f6
[<c0213ea5>] qdisc_restart+0xa7/0x135
[<c020b112>] dev_queue_xmit+0xba/0x19e
[<c0223f69>] ip_output+0x1eb/0x236
[<c0220907>] ip_forward+0x1c1/0x21a
[<c021fa6c>] ip_rcv+0x38e/0x3ea
[<c020b4c2>] netif_receive_skb+0x166/0x195
[<c020b55e>] process_backlog+0x6d/0xd2
[<c020a30f>] net_rx_action+0x6a/0xff
[<c0112909>] __do_softirq+0x35/0x7d
[<c0112973>] do_softirq+0x22/0x26
[<c0103a9d>] do_IRQ+0x1e/0x25
[<c010255a>] common_interrupt+0x1a/0x20
[<c01013c5>] default_idle+0x2b/0x53
[<c0101426>] cpu_idle+0x39/0x4e
[<c0328386>] start_kernel+0x20b/0x20d
Code: c0 e8 b3 b6 77 fc 85 c0 75 10 68 d8 c8 9b c3 e8 82 3d 75 fc 8b 43 60 5a eb 50 8d 56 50 c7 00 00 00 00 00 66 89 68 04 eb 02 89
ca <8b> 0a 85 c9 75 f8 89 02 89 da ff 46 54 8b 46 10 e8 30 79 fd ff
<0>Kernel panic - not syncing: Fatal exception in interrupt
That oops took me to the "ackqueue" implementation in capi.c. The crash
occured in capincci_add_ack() (auto-inlined by the compiler).
I read the code a bit and finally decided to replace the custom linked list
implementation (struct capiminor->ackqueue) by a struct list_head. That
did not solve the crash, but produced the following interresting oops:
Unable to handle kernel paging request at virtual address 00200200
printing eip:
c39bb1f5
*pde = 00000000
Oops: 0002 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bb1f5>] Tainted: P VLI
EFLAGS: 00010246 (2.6.16.11 #3)
EIP is at capiminor_del_ack+0x18/0x49 [capi]
eax: 00200200 ebx: c18d41a0 ecx: c1385620 edx: 00100100
esi: 0000d147 edi: 00001103 ebp: 0000d147 esp: c1093f3c
ds: 007b es: 007b ss: 0068
Process events/0 (pid: 3, threadinfo=c1092000 task=c1089030)
Stack: <0>c2a17580 c18d41a0 c39bbd16 00000038 c18d41e0 00000000 d147c640 c29e0b68
c29e0b90 00000212 c29e0b68 c39932b2 c29e0bb0 c10736a0 c0119ef0 c399326c
c10736a8 c10736a0 c10736b0 c0119f93 c011a06e 00000001 00000000 00000000
Call Trace:
[<c39bbd16>] handle_minor_send+0x1af/0x241 [capi]
[<c39932b2>] recv_handler+0x46/0x5f [kernelcapi]
[<c0119ef0>] run_workqueue+0x5e/0x8d
[<c399326c>] recv_handler+0x0/0x5f [kernelcapi]
[<c0119f93>] worker_thread+0x0/0x10b
[<c011a06e>] worker_thread+0xdb/0x10b
[<c010c998>] default_wake_function+0x0/0xc
[<c011c399>] kthread+0x90/0xbc
[<c011c309>] kthread+0x0/0xbc
[<c0100a65>] kernel_thread_helper+0x5/0xb
Code: 7e 02 89 ee 89 f0 5a f7 d0 c1 f8 1f 5b 21 f0 5e 5f 5d c3 56 53 8b 48 50 89 d6 89 c3 8b 11 eb 2f 66 39 71 08 75 25 8b 41 04 8b 11 <89> 10 89 42 04 c7 01 00 01 10 00 89 c8 c7 41 04 00 02 20 00 e8
The interresting part of it is the "virtual address 00200200", which is
LIST_POISON2. I thought about some race condition, but as this is an UP
system, it leads to questions on how it can happen. If we look at EFLAGS:
00010202, we see that interrupts are enabled at the time of the crash
(eflags & 0x200).
Finally, I don't understand all the capi code, but I think that
handle_minor_send() is racing somehow against capi_recv_message(), which
call both capiminor_del_ack(). So if an IRQ occurs in the middle of
capiminor_del_ack() and another instance of it is invoked, it leads to
linked list corruption.
I came up with the following patch. With this, I could not reproduce the
crash anymore. Clearly, this is not the correct fix for the issue. As this
seems to be some locking issue, there might be more locking issues in that
code. For example, doesn't the whole struct capiminor have to be locked
somehow?
Cc: Carsten Paeth <calle@calle.de>
Cc: Kai Germaschewski <kai.germaschewski@gmx.de>
Cc: Karsten Keil <kkeil@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 15:25:30 +08:00
|
|
|
if (unlikely(!n)) {
|
|
|
|
printk(KERN_ERR "capi: alloc datahandle failed\n");
|
|
|
|
return -1;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
n->datahandle = datahandle;
|
[PATCH] CAPI crash / race condition
I am getting more or less reproducible crashes from the CAPI subsystem
using the fcdsl driver:
Unable to handle kernel NULL pointer dereference at virtual address 00000010
printing eip:
c39bbca4
*pde = 00000000
Oops: 0000 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bbca4>] Tainted: P VLI
EFLAGS: 00010202 (2.6.16.11 #3)
EIP is at handle_minor_send+0x17a/0x241 [capi]
eax: c24abbc0 ebx: c0b4c980 ecx: 00000010 edx: 00000010
esi: c1679140 edi: c2783016 ebp: 0000c28d esp: c0327e24
ds: 007b es: 007b ss: 0068
Process swapper (pid: 0, threadinfo=c0326000 task=c02e1300)
Stack: <0>000005b4 c1679180 00000000 c28d0000 c1ce04e0 c2f69654 c221604e c1679140
c39bc19a 00000038 c20c0400 c075c560 c1f2f800 00000000 c01dc9b5 c1e96a40
c075c560 c2ed64c0 c1e96a40 c01dcd3b c2fb94e8 c075c560 c0327f00 c1e96a40
Call Trace:
[<c39bc19a>] capinc_tty_write+0xda/0xf3 [capi]
[<c01dc9b5>] ppp_sync_push+0x52/0xfe
[<c01dcd3b>] ppp_sync_send+0x1f5/0x204
[<c01d9bc1>] ppp_push+0x3e/0x9c
[<c01dacd4>] ppp_xmit_process+0x422/0x4cc
[<c01daf3f>] ppp_start_xmit+0x1c1/0x1f6
[<c0213ea5>] qdisc_restart+0xa7/0x135
[<c020b112>] dev_queue_xmit+0xba/0x19e
[<c0223f69>] ip_output+0x1eb/0x236
[<c0220907>] ip_forward+0x1c1/0x21a
[<c021fa6c>] ip_rcv+0x38e/0x3ea
[<c020b4c2>] netif_receive_skb+0x166/0x195
[<c020b55e>] process_backlog+0x6d/0xd2
[<c020a30f>] net_rx_action+0x6a/0xff
[<c0112909>] __do_softirq+0x35/0x7d
[<c0112973>] do_softirq+0x22/0x26
[<c0103a9d>] do_IRQ+0x1e/0x25
[<c010255a>] common_interrupt+0x1a/0x20
[<c01013c5>] default_idle+0x2b/0x53
[<c0101426>] cpu_idle+0x39/0x4e
[<c0328386>] start_kernel+0x20b/0x20d
Code: c0 e8 b3 b6 77 fc 85 c0 75 10 68 d8 c8 9b c3 e8 82 3d 75 fc 8b 43 60 5a eb 50 8d 56 50 c7 00 00 00 00 00 66 89 68 04 eb 02 89
ca <8b> 0a 85 c9 75 f8 89 02 89 da ff 46 54 8b 46 10 e8 30 79 fd ff
<0>Kernel panic - not syncing: Fatal exception in interrupt
That oops took me to the "ackqueue" implementation in capi.c. The crash
occured in capincci_add_ack() (auto-inlined by the compiler).
I read the code a bit and finally decided to replace the custom linked list
implementation (struct capiminor->ackqueue) by a struct list_head. That
did not solve the crash, but produced the following interresting oops:
Unable to handle kernel paging request at virtual address 00200200
printing eip:
c39bb1f5
*pde = 00000000
Oops: 0002 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bb1f5>] Tainted: P VLI
EFLAGS: 00010246 (2.6.16.11 #3)
EIP is at capiminor_del_ack+0x18/0x49 [capi]
eax: 00200200 ebx: c18d41a0 ecx: c1385620 edx: 00100100
esi: 0000d147 edi: 00001103 ebp: 0000d147 esp: c1093f3c
ds: 007b es: 007b ss: 0068
Process events/0 (pid: 3, threadinfo=c1092000 task=c1089030)
Stack: <0>c2a17580 c18d41a0 c39bbd16 00000038 c18d41e0 00000000 d147c640 c29e0b68
c29e0b90 00000212 c29e0b68 c39932b2 c29e0bb0 c10736a0 c0119ef0 c399326c
c10736a8 c10736a0 c10736b0 c0119f93 c011a06e 00000001 00000000 00000000
Call Trace:
[<c39bbd16>] handle_minor_send+0x1af/0x241 [capi]
[<c39932b2>] recv_handler+0x46/0x5f [kernelcapi]
[<c0119ef0>] run_workqueue+0x5e/0x8d
[<c399326c>] recv_handler+0x0/0x5f [kernelcapi]
[<c0119f93>] worker_thread+0x0/0x10b
[<c011a06e>] worker_thread+0xdb/0x10b
[<c010c998>] default_wake_function+0x0/0xc
[<c011c399>] kthread+0x90/0xbc
[<c011c309>] kthread+0x0/0xbc
[<c0100a65>] kernel_thread_helper+0x5/0xb
Code: 7e 02 89 ee 89 f0 5a f7 d0 c1 f8 1f 5b 21 f0 5e 5f 5d c3 56 53 8b 48 50 89 d6 89 c3 8b 11 eb 2f 66 39 71 08 75 25 8b 41 04 8b 11 <89> 10 89 42 04 c7 01 00 01 10 00 89 c8 c7 41 04 00 02 20 00 e8
The interresting part of it is the "virtual address 00200200", which is
LIST_POISON2. I thought about some race condition, but as this is an UP
system, it leads to questions on how it can happen. If we look at EFLAGS:
00010202, we see that interrupts are enabled at the time of the crash
(eflags & 0x200).
Finally, I don't understand all the capi code, but I think that
handle_minor_send() is racing somehow against capi_recv_message(), which
call both capiminor_del_ack(). So if an IRQ occurs in the middle of
capiminor_del_ack() and another instance of it is invoked, it leads to
linked list corruption.
I came up with the following patch. With this, I could not reproduce the
crash anymore. Clearly, this is not the correct fix for the issue. As this
seems to be some locking issue, there might be more locking issues in that
code. For example, doesn't the whole struct capiminor have to be locked
somehow?
Cc: Carsten Paeth <calle@calle.de>
Cc: Kai Germaschewski <kai.germaschewski@gmx.de>
Cc: Karsten Keil <kkeil@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 15:25:30 +08:00
|
|
|
INIT_LIST_HEAD(&n->list);
|
|
|
|
spin_lock_irqsave(&mp->ackqlock, flags);
|
|
|
|
list_add_tail(&n->list, &mp->ackqueue);
|
2005-04-17 06:20:36 +08:00
|
|
|
mp->nack++;
|
[PATCH] CAPI crash / race condition
I am getting more or less reproducible crashes from the CAPI subsystem
using the fcdsl driver:
Unable to handle kernel NULL pointer dereference at virtual address 00000010
printing eip:
c39bbca4
*pde = 00000000
Oops: 0000 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bbca4>] Tainted: P VLI
EFLAGS: 00010202 (2.6.16.11 #3)
EIP is at handle_minor_send+0x17a/0x241 [capi]
eax: c24abbc0 ebx: c0b4c980 ecx: 00000010 edx: 00000010
esi: c1679140 edi: c2783016 ebp: 0000c28d esp: c0327e24
ds: 007b es: 007b ss: 0068
Process swapper (pid: 0, threadinfo=c0326000 task=c02e1300)
Stack: <0>000005b4 c1679180 00000000 c28d0000 c1ce04e0 c2f69654 c221604e c1679140
c39bc19a 00000038 c20c0400 c075c560 c1f2f800 00000000 c01dc9b5 c1e96a40
c075c560 c2ed64c0 c1e96a40 c01dcd3b c2fb94e8 c075c560 c0327f00 c1e96a40
Call Trace:
[<c39bc19a>] capinc_tty_write+0xda/0xf3 [capi]
[<c01dc9b5>] ppp_sync_push+0x52/0xfe
[<c01dcd3b>] ppp_sync_send+0x1f5/0x204
[<c01d9bc1>] ppp_push+0x3e/0x9c
[<c01dacd4>] ppp_xmit_process+0x422/0x4cc
[<c01daf3f>] ppp_start_xmit+0x1c1/0x1f6
[<c0213ea5>] qdisc_restart+0xa7/0x135
[<c020b112>] dev_queue_xmit+0xba/0x19e
[<c0223f69>] ip_output+0x1eb/0x236
[<c0220907>] ip_forward+0x1c1/0x21a
[<c021fa6c>] ip_rcv+0x38e/0x3ea
[<c020b4c2>] netif_receive_skb+0x166/0x195
[<c020b55e>] process_backlog+0x6d/0xd2
[<c020a30f>] net_rx_action+0x6a/0xff
[<c0112909>] __do_softirq+0x35/0x7d
[<c0112973>] do_softirq+0x22/0x26
[<c0103a9d>] do_IRQ+0x1e/0x25
[<c010255a>] common_interrupt+0x1a/0x20
[<c01013c5>] default_idle+0x2b/0x53
[<c0101426>] cpu_idle+0x39/0x4e
[<c0328386>] start_kernel+0x20b/0x20d
Code: c0 e8 b3 b6 77 fc 85 c0 75 10 68 d8 c8 9b c3 e8 82 3d 75 fc 8b 43 60 5a eb 50 8d 56 50 c7 00 00 00 00 00 66 89 68 04 eb 02 89
ca <8b> 0a 85 c9 75 f8 89 02 89 da ff 46 54 8b 46 10 e8 30 79 fd ff
<0>Kernel panic - not syncing: Fatal exception in interrupt
That oops took me to the "ackqueue" implementation in capi.c. The crash
occured in capincci_add_ack() (auto-inlined by the compiler).
I read the code a bit and finally decided to replace the custom linked list
implementation (struct capiminor->ackqueue) by a struct list_head. That
did not solve the crash, but produced the following interresting oops:
Unable to handle kernel paging request at virtual address 00200200
printing eip:
c39bb1f5
*pde = 00000000
Oops: 0002 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bb1f5>] Tainted: P VLI
EFLAGS: 00010246 (2.6.16.11 #3)
EIP is at capiminor_del_ack+0x18/0x49 [capi]
eax: 00200200 ebx: c18d41a0 ecx: c1385620 edx: 00100100
esi: 0000d147 edi: 00001103 ebp: 0000d147 esp: c1093f3c
ds: 007b es: 007b ss: 0068
Process events/0 (pid: 3, threadinfo=c1092000 task=c1089030)
Stack: <0>c2a17580 c18d41a0 c39bbd16 00000038 c18d41e0 00000000 d147c640 c29e0b68
c29e0b90 00000212 c29e0b68 c39932b2 c29e0bb0 c10736a0 c0119ef0 c399326c
c10736a8 c10736a0 c10736b0 c0119f93 c011a06e 00000001 00000000 00000000
Call Trace:
[<c39bbd16>] handle_minor_send+0x1af/0x241 [capi]
[<c39932b2>] recv_handler+0x46/0x5f [kernelcapi]
[<c0119ef0>] run_workqueue+0x5e/0x8d
[<c399326c>] recv_handler+0x0/0x5f [kernelcapi]
[<c0119f93>] worker_thread+0x0/0x10b
[<c011a06e>] worker_thread+0xdb/0x10b
[<c010c998>] default_wake_function+0x0/0xc
[<c011c399>] kthread+0x90/0xbc
[<c011c309>] kthread+0x0/0xbc
[<c0100a65>] kernel_thread_helper+0x5/0xb
Code: 7e 02 89 ee 89 f0 5a f7 d0 c1 f8 1f 5b 21 f0 5e 5f 5d c3 56 53 8b 48 50 89 d6 89 c3 8b 11 eb 2f 66 39 71 08 75 25 8b 41 04 8b 11 <89> 10 89 42 04 c7 01 00 01 10 00 89 c8 c7 41 04 00 02 20 00 e8
The interresting part of it is the "virtual address 00200200", which is
LIST_POISON2. I thought about some race condition, but as this is an UP
system, it leads to questions on how it can happen. If we look at EFLAGS:
00010202, we see that interrupts are enabled at the time of the crash
(eflags & 0x200).
Finally, I don't understand all the capi code, but I think that
handle_minor_send() is racing somehow against capi_recv_message(), which
call both capiminor_del_ack(). So if an IRQ occurs in the middle of
capiminor_del_ack() and another instance of it is invoked, it leads to
linked list corruption.
I came up with the following patch. With this, I could not reproduce the
crash anymore. Clearly, this is not the correct fix for the issue. As this
seems to be some locking issue, there might be more locking issues in that
code. For example, doesn't the whole struct capiminor have to be locked
somehow?
Cc: Carsten Paeth <calle@calle.de>
Cc: Kai Germaschewski <kai.germaschewski@gmx.de>
Cc: Karsten Keil <kkeil@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 15:25:30 +08:00
|
|
|
spin_unlock_irqrestore(&mp->ackqlock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int capiminor_del_ack(struct capiminor *mp, u16 datahandle)
|
|
|
|
{
|
[PATCH] CAPI crash / race condition
I am getting more or less reproducible crashes from the CAPI subsystem
using the fcdsl driver:
Unable to handle kernel NULL pointer dereference at virtual address 00000010
printing eip:
c39bbca4
*pde = 00000000
Oops: 0000 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bbca4>] Tainted: P VLI
EFLAGS: 00010202 (2.6.16.11 #3)
EIP is at handle_minor_send+0x17a/0x241 [capi]
eax: c24abbc0 ebx: c0b4c980 ecx: 00000010 edx: 00000010
esi: c1679140 edi: c2783016 ebp: 0000c28d esp: c0327e24
ds: 007b es: 007b ss: 0068
Process swapper (pid: 0, threadinfo=c0326000 task=c02e1300)
Stack: <0>000005b4 c1679180 00000000 c28d0000 c1ce04e0 c2f69654 c221604e c1679140
c39bc19a 00000038 c20c0400 c075c560 c1f2f800 00000000 c01dc9b5 c1e96a40
c075c560 c2ed64c0 c1e96a40 c01dcd3b c2fb94e8 c075c560 c0327f00 c1e96a40
Call Trace:
[<c39bc19a>] capinc_tty_write+0xda/0xf3 [capi]
[<c01dc9b5>] ppp_sync_push+0x52/0xfe
[<c01dcd3b>] ppp_sync_send+0x1f5/0x204
[<c01d9bc1>] ppp_push+0x3e/0x9c
[<c01dacd4>] ppp_xmit_process+0x422/0x4cc
[<c01daf3f>] ppp_start_xmit+0x1c1/0x1f6
[<c0213ea5>] qdisc_restart+0xa7/0x135
[<c020b112>] dev_queue_xmit+0xba/0x19e
[<c0223f69>] ip_output+0x1eb/0x236
[<c0220907>] ip_forward+0x1c1/0x21a
[<c021fa6c>] ip_rcv+0x38e/0x3ea
[<c020b4c2>] netif_receive_skb+0x166/0x195
[<c020b55e>] process_backlog+0x6d/0xd2
[<c020a30f>] net_rx_action+0x6a/0xff
[<c0112909>] __do_softirq+0x35/0x7d
[<c0112973>] do_softirq+0x22/0x26
[<c0103a9d>] do_IRQ+0x1e/0x25
[<c010255a>] common_interrupt+0x1a/0x20
[<c01013c5>] default_idle+0x2b/0x53
[<c0101426>] cpu_idle+0x39/0x4e
[<c0328386>] start_kernel+0x20b/0x20d
Code: c0 e8 b3 b6 77 fc 85 c0 75 10 68 d8 c8 9b c3 e8 82 3d 75 fc 8b 43 60 5a eb 50 8d 56 50 c7 00 00 00 00 00 66 89 68 04 eb 02 89
ca <8b> 0a 85 c9 75 f8 89 02 89 da ff 46 54 8b 46 10 e8 30 79 fd ff
<0>Kernel panic - not syncing: Fatal exception in interrupt
That oops took me to the "ackqueue" implementation in capi.c. The crash
occured in capincci_add_ack() (auto-inlined by the compiler).
I read the code a bit and finally decided to replace the custom linked list
implementation (struct capiminor->ackqueue) by a struct list_head. That
did not solve the crash, but produced the following interresting oops:
Unable to handle kernel paging request at virtual address 00200200
printing eip:
c39bb1f5
*pde = 00000000
Oops: 0002 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bb1f5>] Tainted: P VLI
EFLAGS: 00010246 (2.6.16.11 #3)
EIP is at capiminor_del_ack+0x18/0x49 [capi]
eax: 00200200 ebx: c18d41a0 ecx: c1385620 edx: 00100100
esi: 0000d147 edi: 00001103 ebp: 0000d147 esp: c1093f3c
ds: 007b es: 007b ss: 0068
Process events/0 (pid: 3, threadinfo=c1092000 task=c1089030)
Stack: <0>c2a17580 c18d41a0 c39bbd16 00000038 c18d41e0 00000000 d147c640 c29e0b68
c29e0b90 00000212 c29e0b68 c39932b2 c29e0bb0 c10736a0 c0119ef0 c399326c
c10736a8 c10736a0 c10736b0 c0119f93 c011a06e 00000001 00000000 00000000
Call Trace:
[<c39bbd16>] handle_minor_send+0x1af/0x241 [capi]
[<c39932b2>] recv_handler+0x46/0x5f [kernelcapi]
[<c0119ef0>] run_workqueue+0x5e/0x8d
[<c399326c>] recv_handler+0x0/0x5f [kernelcapi]
[<c0119f93>] worker_thread+0x0/0x10b
[<c011a06e>] worker_thread+0xdb/0x10b
[<c010c998>] default_wake_function+0x0/0xc
[<c011c399>] kthread+0x90/0xbc
[<c011c309>] kthread+0x0/0xbc
[<c0100a65>] kernel_thread_helper+0x5/0xb
Code: 7e 02 89 ee 89 f0 5a f7 d0 c1 f8 1f 5b 21 f0 5e 5f 5d c3 56 53 8b 48 50 89 d6 89 c3 8b 11 eb 2f 66 39 71 08 75 25 8b 41 04 8b 11 <89> 10 89 42 04 c7 01 00 01 10 00 89 c8 c7 41 04 00 02 20 00 e8
The interresting part of it is the "virtual address 00200200", which is
LIST_POISON2. I thought about some race condition, but as this is an UP
system, it leads to questions on how it can happen. If we look at EFLAGS:
00010202, we see that interrupts are enabled at the time of the crash
(eflags & 0x200).
Finally, I don't understand all the capi code, but I think that
handle_minor_send() is racing somehow against capi_recv_message(), which
call both capiminor_del_ack(). So if an IRQ occurs in the middle of
capiminor_del_ack() and another instance of it is invoked, it leads to
linked list corruption.
I came up with the following patch. With this, I could not reproduce the
crash anymore. Clearly, this is not the correct fix for the issue. As this
seems to be some locking issue, there might be more locking issues in that
code. For example, doesn't the whole struct capiminor have to be locked
somehow?
Cc: Carsten Paeth <calle@calle.de>
Cc: Kai Germaschewski <kai.germaschewski@gmx.de>
Cc: Karsten Keil <kkeil@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 15:25:30 +08:00
|
|
|
struct datahandle_queue *p, *tmp;
|
|
|
|
unsigned long flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
[PATCH] CAPI crash / race condition
I am getting more or less reproducible crashes from the CAPI subsystem
using the fcdsl driver:
Unable to handle kernel NULL pointer dereference at virtual address 00000010
printing eip:
c39bbca4
*pde = 00000000
Oops: 0000 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bbca4>] Tainted: P VLI
EFLAGS: 00010202 (2.6.16.11 #3)
EIP is at handle_minor_send+0x17a/0x241 [capi]
eax: c24abbc0 ebx: c0b4c980 ecx: 00000010 edx: 00000010
esi: c1679140 edi: c2783016 ebp: 0000c28d esp: c0327e24
ds: 007b es: 007b ss: 0068
Process swapper (pid: 0, threadinfo=c0326000 task=c02e1300)
Stack: <0>000005b4 c1679180 00000000 c28d0000 c1ce04e0 c2f69654 c221604e c1679140
c39bc19a 00000038 c20c0400 c075c560 c1f2f800 00000000 c01dc9b5 c1e96a40
c075c560 c2ed64c0 c1e96a40 c01dcd3b c2fb94e8 c075c560 c0327f00 c1e96a40
Call Trace:
[<c39bc19a>] capinc_tty_write+0xda/0xf3 [capi]
[<c01dc9b5>] ppp_sync_push+0x52/0xfe
[<c01dcd3b>] ppp_sync_send+0x1f5/0x204
[<c01d9bc1>] ppp_push+0x3e/0x9c
[<c01dacd4>] ppp_xmit_process+0x422/0x4cc
[<c01daf3f>] ppp_start_xmit+0x1c1/0x1f6
[<c0213ea5>] qdisc_restart+0xa7/0x135
[<c020b112>] dev_queue_xmit+0xba/0x19e
[<c0223f69>] ip_output+0x1eb/0x236
[<c0220907>] ip_forward+0x1c1/0x21a
[<c021fa6c>] ip_rcv+0x38e/0x3ea
[<c020b4c2>] netif_receive_skb+0x166/0x195
[<c020b55e>] process_backlog+0x6d/0xd2
[<c020a30f>] net_rx_action+0x6a/0xff
[<c0112909>] __do_softirq+0x35/0x7d
[<c0112973>] do_softirq+0x22/0x26
[<c0103a9d>] do_IRQ+0x1e/0x25
[<c010255a>] common_interrupt+0x1a/0x20
[<c01013c5>] default_idle+0x2b/0x53
[<c0101426>] cpu_idle+0x39/0x4e
[<c0328386>] start_kernel+0x20b/0x20d
Code: c0 e8 b3 b6 77 fc 85 c0 75 10 68 d8 c8 9b c3 e8 82 3d 75 fc 8b 43 60 5a eb 50 8d 56 50 c7 00 00 00 00 00 66 89 68 04 eb 02 89
ca <8b> 0a 85 c9 75 f8 89 02 89 da ff 46 54 8b 46 10 e8 30 79 fd ff
<0>Kernel panic - not syncing: Fatal exception in interrupt
That oops took me to the "ackqueue" implementation in capi.c. The crash
occured in capincci_add_ack() (auto-inlined by the compiler).
I read the code a bit and finally decided to replace the custom linked list
implementation (struct capiminor->ackqueue) by a struct list_head. That
did not solve the crash, but produced the following interresting oops:
Unable to handle kernel paging request at virtual address 00200200
printing eip:
c39bb1f5
*pde = 00000000
Oops: 0002 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bb1f5>] Tainted: P VLI
EFLAGS: 00010246 (2.6.16.11 #3)
EIP is at capiminor_del_ack+0x18/0x49 [capi]
eax: 00200200 ebx: c18d41a0 ecx: c1385620 edx: 00100100
esi: 0000d147 edi: 00001103 ebp: 0000d147 esp: c1093f3c
ds: 007b es: 007b ss: 0068
Process events/0 (pid: 3, threadinfo=c1092000 task=c1089030)
Stack: <0>c2a17580 c18d41a0 c39bbd16 00000038 c18d41e0 00000000 d147c640 c29e0b68
c29e0b90 00000212 c29e0b68 c39932b2 c29e0bb0 c10736a0 c0119ef0 c399326c
c10736a8 c10736a0 c10736b0 c0119f93 c011a06e 00000001 00000000 00000000
Call Trace:
[<c39bbd16>] handle_minor_send+0x1af/0x241 [capi]
[<c39932b2>] recv_handler+0x46/0x5f [kernelcapi]
[<c0119ef0>] run_workqueue+0x5e/0x8d
[<c399326c>] recv_handler+0x0/0x5f [kernelcapi]
[<c0119f93>] worker_thread+0x0/0x10b
[<c011a06e>] worker_thread+0xdb/0x10b
[<c010c998>] default_wake_function+0x0/0xc
[<c011c399>] kthread+0x90/0xbc
[<c011c309>] kthread+0x0/0xbc
[<c0100a65>] kernel_thread_helper+0x5/0xb
Code: 7e 02 89 ee 89 f0 5a f7 d0 c1 f8 1f 5b 21 f0 5e 5f 5d c3 56 53 8b 48 50 89 d6 89 c3 8b 11 eb 2f 66 39 71 08 75 25 8b 41 04 8b 11 <89> 10 89 42 04 c7 01 00 01 10 00 89 c8 c7 41 04 00 02 20 00 e8
The interresting part of it is the "virtual address 00200200", which is
LIST_POISON2. I thought about some race condition, but as this is an UP
system, it leads to questions on how it can happen. If we look at EFLAGS:
00010202, we see that interrupts are enabled at the time of the crash
(eflags & 0x200).
Finally, I don't understand all the capi code, but I think that
handle_minor_send() is racing somehow against capi_recv_message(), which
call both capiminor_del_ack(). So if an IRQ occurs in the middle of
capiminor_del_ack() and another instance of it is invoked, it leads to
linked list corruption.
I came up with the following patch. With this, I could not reproduce the
crash anymore. Clearly, this is not the correct fix for the issue. As this
seems to be some locking issue, there might be more locking issues in that
code. For example, doesn't the whole struct capiminor have to be locked
somehow?
Cc: Carsten Paeth <calle@calle.de>
Cc: Kai Germaschewski <kai.germaschewski@gmx.de>
Cc: Karsten Keil <kkeil@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 15:25:30 +08:00
|
|
|
spin_lock_irqsave(&mp->ackqlock, flags);
|
|
|
|
list_for_each_entry_safe(p, tmp, &mp->ackqueue, list) {
|
|
|
|
if (p->datahandle == datahandle) {
|
|
|
|
list_del(&p->list);
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree(p);
|
|
|
|
mp->nack--;
|
[PATCH] CAPI crash / race condition
I am getting more or less reproducible crashes from the CAPI subsystem
using the fcdsl driver:
Unable to handle kernel NULL pointer dereference at virtual address 00000010
printing eip:
c39bbca4
*pde = 00000000
Oops: 0000 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bbca4>] Tainted: P VLI
EFLAGS: 00010202 (2.6.16.11 #3)
EIP is at handle_minor_send+0x17a/0x241 [capi]
eax: c24abbc0 ebx: c0b4c980 ecx: 00000010 edx: 00000010
esi: c1679140 edi: c2783016 ebp: 0000c28d esp: c0327e24
ds: 007b es: 007b ss: 0068
Process swapper (pid: 0, threadinfo=c0326000 task=c02e1300)
Stack: <0>000005b4 c1679180 00000000 c28d0000 c1ce04e0 c2f69654 c221604e c1679140
c39bc19a 00000038 c20c0400 c075c560 c1f2f800 00000000 c01dc9b5 c1e96a40
c075c560 c2ed64c0 c1e96a40 c01dcd3b c2fb94e8 c075c560 c0327f00 c1e96a40
Call Trace:
[<c39bc19a>] capinc_tty_write+0xda/0xf3 [capi]
[<c01dc9b5>] ppp_sync_push+0x52/0xfe
[<c01dcd3b>] ppp_sync_send+0x1f5/0x204
[<c01d9bc1>] ppp_push+0x3e/0x9c
[<c01dacd4>] ppp_xmit_process+0x422/0x4cc
[<c01daf3f>] ppp_start_xmit+0x1c1/0x1f6
[<c0213ea5>] qdisc_restart+0xa7/0x135
[<c020b112>] dev_queue_xmit+0xba/0x19e
[<c0223f69>] ip_output+0x1eb/0x236
[<c0220907>] ip_forward+0x1c1/0x21a
[<c021fa6c>] ip_rcv+0x38e/0x3ea
[<c020b4c2>] netif_receive_skb+0x166/0x195
[<c020b55e>] process_backlog+0x6d/0xd2
[<c020a30f>] net_rx_action+0x6a/0xff
[<c0112909>] __do_softirq+0x35/0x7d
[<c0112973>] do_softirq+0x22/0x26
[<c0103a9d>] do_IRQ+0x1e/0x25
[<c010255a>] common_interrupt+0x1a/0x20
[<c01013c5>] default_idle+0x2b/0x53
[<c0101426>] cpu_idle+0x39/0x4e
[<c0328386>] start_kernel+0x20b/0x20d
Code: c0 e8 b3 b6 77 fc 85 c0 75 10 68 d8 c8 9b c3 e8 82 3d 75 fc 8b 43 60 5a eb 50 8d 56 50 c7 00 00 00 00 00 66 89 68 04 eb 02 89
ca <8b> 0a 85 c9 75 f8 89 02 89 da ff 46 54 8b 46 10 e8 30 79 fd ff
<0>Kernel panic - not syncing: Fatal exception in interrupt
That oops took me to the "ackqueue" implementation in capi.c. The crash
occured in capincci_add_ack() (auto-inlined by the compiler).
I read the code a bit and finally decided to replace the custom linked list
implementation (struct capiminor->ackqueue) by a struct list_head. That
did not solve the crash, but produced the following interresting oops:
Unable to handle kernel paging request at virtual address 00200200
printing eip:
c39bb1f5
*pde = 00000000
Oops: 0002 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bb1f5>] Tainted: P VLI
EFLAGS: 00010246 (2.6.16.11 #3)
EIP is at capiminor_del_ack+0x18/0x49 [capi]
eax: 00200200 ebx: c18d41a0 ecx: c1385620 edx: 00100100
esi: 0000d147 edi: 00001103 ebp: 0000d147 esp: c1093f3c
ds: 007b es: 007b ss: 0068
Process events/0 (pid: 3, threadinfo=c1092000 task=c1089030)
Stack: <0>c2a17580 c18d41a0 c39bbd16 00000038 c18d41e0 00000000 d147c640 c29e0b68
c29e0b90 00000212 c29e0b68 c39932b2 c29e0bb0 c10736a0 c0119ef0 c399326c
c10736a8 c10736a0 c10736b0 c0119f93 c011a06e 00000001 00000000 00000000
Call Trace:
[<c39bbd16>] handle_minor_send+0x1af/0x241 [capi]
[<c39932b2>] recv_handler+0x46/0x5f [kernelcapi]
[<c0119ef0>] run_workqueue+0x5e/0x8d
[<c399326c>] recv_handler+0x0/0x5f [kernelcapi]
[<c0119f93>] worker_thread+0x0/0x10b
[<c011a06e>] worker_thread+0xdb/0x10b
[<c010c998>] default_wake_function+0x0/0xc
[<c011c399>] kthread+0x90/0xbc
[<c011c309>] kthread+0x0/0xbc
[<c0100a65>] kernel_thread_helper+0x5/0xb
Code: 7e 02 89 ee 89 f0 5a f7 d0 c1 f8 1f 5b 21 f0 5e 5f 5d c3 56 53 8b 48 50 89 d6 89 c3 8b 11 eb 2f 66 39 71 08 75 25 8b 41 04 8b 11 <89> 10 89 42 04 c7 01 00 01 10 00 89 c8 c7 41 04 00 02 20 00 e8
The interresting part of it is the "virtual address 00200200", which is
LIST_POISON2. I thought about some race condition, but as this is an UP
system, it leads to questions on how it can happen. If we look at EFLAGS:
00010202, we see that interrupts are enabled at the time of the crash
(eflags & 0x200).
Finally, I don't understand all the capi code, but I think that
handle_minor_send() is racing somehow against capi_recv_message(), which
call both capiminor_del_ack(). So if an IRQ occurs in the middle of
capiminor_del_ack() and another instance of it is invoked, it leads to
linked list corruption.
I came up with the following patch. With this, I could not reproduce the
crash anymore. Clearly, this is not the correct fix for the issue. As this
seems to be some locking issue, there might be more locking issues in that
code. For example, doesn't the whole struct capiminor have to be locked
somehow?
Cc: Carsten Paeth <calle@calle.de>
Cc: Kai Germaschewski <kai.germaschewski@gmx.de>
Cc: Karsten Keil <kkeil@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 15:25:30 +08:00
|
|
|
spin_unlock_irqrestore(&mp->ackqlock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
[PATCH] CAPI crash / race condition
I am getting more or less reproducible crashes from the CAPI subsystem
using the fcdsl driver:
Unable to handle kernel NULL pointer dereference at virtual address 00000010
printing eip:
c39bbca4
*pde = 00000000
Oops: 0000 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bbca4>] Tainted: P VLI
EFLAGS: 00010202 (2.6.16.11 #3)
EIP is at handle_minor_send+0x17a/0x241 [capi]
eax: c24abbc0 ebx: c0b4c980 ecx: 00000010 edx: 00000010
esi: c1679140 edi: c2783016 ebp: 0000c28d esp: c0327e24
ds: 007b es: 007b ss: 0068
Process swapper (pid: 0, threadinfo=c0326000 task=c02e1300)
Stack: <0>000005b4 c1679180 00000000 c28d0000 c1ce04e0 c2f69654 c221604e c1679140
c39bc19a 00000038 c20c0400 c075c560 c1f2f800 00000000 c01dc9b5 c1e96a40
c075c560 c2ed64c0 c1e96a40 c01dcd3b c2fb94e8 c075c560 c0327f00 c1e96a40
Call Trace:
[<c39bc19a>] capinc_tty_write+0xda/0xf3 [capi]
[<c01dc9b5>] ppp_sync_push+0x52/0xfe
[<c01dcd3b>] ppp_sync_send+0x1f5/0x204
[<c01d9bc1>] ppp_push+0x3e/0x9c
[<c01dacd4>] ppp_xmit_process+0x422/0x4cc
[<c01daf3f>] ppp_start_xmit+0x1c1/0x1f6
[<c0213ea5>] qdisc_restart+0xa7/0x135
[<c020b112>] dev_queue_xmit+0xba/0x19e
[<c0223f69>] ip_output+0x1eb/0x236
[<c0220907>] ip_forward+0x1c1/0x21a
[<c021fa6c>] ip_rcv+0x38e/0x3ea
[<c020b4c2>] netif_receive_skb+0x166/0x195
[<c020b55e>] process_backlog+0x6d/0xd2
[<c020a30f>] net_rx_action+0x6a/0xff
[<c0112909>] __do_softirq+0x35/0x7d
[<c0112973>] do_softirq+0x22/0x26
[<c0103a9d>] do_IRQ+0x1e/0x25
[<c010255a>] common_interrupt+0x1a/0x20
[<c01013c5>] default_idle+0x2b/0x53
[<c0101426>] cpu_idle+0x39/0x4e
[<c0328386>] start_kernel+0x20b/0x20d
Code: c0 e8 b3 b6 77 fc 85 c0 75 10 68 d8 c8 9b c3 e8 82 3d 75 fc 8b 43 60 5a eb 50 8d 56 50 c7 00 00 00 00 00 66 89 68 04 eb 02 89
ca <8b> 0a 85 c9 75 f8 89 02 89 da ff 46 54 8b 46 10 e8 30 79 fd ff
<0>Kernel panic - not syncing: Fatal exception in interrupt
That oops took me to the "ackqueue" implementation in capi.c. The crash
occured in capincci_add_ack() (auto-inlined by the compiler).
I read the code a bit and finally decided to replace the custom linked list
implementation (struct capiminor->ackqueue) by a struct list_head. That
did not solve the crash, but produced the following interresting oops:
Unable to handle kernel paging request at virtual address 00200200
printing eip:
c39bb1f5
*pde = 00000000
Oops: 0002 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bb1f5>] Tainted: P VLI
EFLAGS: 00010246 (2.6.16.11 #3)
EIP is at capiminor_del_ack+0x18/0x49 [capi]
eax: 00200200 ebx: c18d41a0 ecx: c1385620 edx: 00100100
esi: 0000d147 edi: 00001103 ebp: 0000d147 esp: c1093f3c
ds: 007b es: 007b ss: 0068
Process events/0 (pid: 3, threadinfo=c1092000 task=c1089030)
Stack: <0>c2a17580 c18d41a0 c39bbd16 00000038 c18d41e0 00000000 d147c640 c29e0b68
c29e0b90 00000212 c29e0b68 c39932b2 c29e0bb0 c10736a0 c0119ef0 c399326c
c10736a8 c10736a0 c10736b0 c0119f93 c011a06e 00000001 00000000 00000000
Call Trace:
[<c39bbd16>] handle_minor_send+0x1af/0x241 [capi]
[<c39932b2>] recv_handler+0x46/0x5f [kernelcapi]
[<c0119ef0>] run_workqueue+0x5e/0x8d
[<c399326c>] recv_handler+0x0/0x5f [kernelcapi]
[<c0119f93>] worker_thread+0x0/0x10b
[<c011a06e>] worker_thread+0xdb/0x10b
[<c010c998>] default_wake_function+0x0/0xc
[<c011c399>] kthread+0x90/0xbc
[<c011c309>] kthread+0x0/0xbc
[<c0100a65>] kernel_thread_helper+0x5/0xb
Code: 7e 02 89 ee 89 f0 5a f7 d0 c1 f8 1f 5b 21 f0 5e 5f 5d c3 56 53 8b 48 50 89 d6 89 c3 8b 11 eb 2f 66 39 71 08 75 25 8b 41 04 8b 11 <89> 10 89 42 04 c7 01 00 01 10 00 89 c8 c7 41 04 00 02 20 00 e8
The interresting part of it is the "virtual address 00200200", which is
LIST_POISON2. I thought about some race condition, but as this is an UP
system, it leads to questions on how it can happen. If we look at EFLAGS:
00010202, we see that interrupts are enabled at the time of the crash
(eflags & 0x200).
Finally, I don't understand all the capi code, but I think that
handle_minor_send() is racing somehow against capi_recv_message(), which
call both capiminor_del_ack(). So if an IRQ occurs in the middle of
capiminor_del_ack() and another instance of it is invoked, it leads to
linked list corruption.
I came up with the following patch. With this, I could not reproduce the
crash anymore. Clearly, this is not the correct fix for the issue. As this
seems to be some locking issue, there might be more locking issues in that
code. For example, doesn't the whole struct capiminor have to be locked
somehow?
Cc: Carsten Paeth <calle@calle.de>
Cc: Kai Germaschewski <kai.germaschewski@gmx.de>
Cc: Karsten Keil <kkeil@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 15:25:30 +08:00
|
|
|
spin_unlock_irqrestore(&mp->ackqlock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void capiminor_del_all_ack(struct capiminor *mp)
|
|
|
|
{
|
[PATCH] CAPI crash / race condition
I am getting more or less reproducible crashes from the CAPI subsystem
using the fcdsl driver:
Unable to handle kernel NULL pointer dereference at virtual address 00000010
printing eip:
c39bbca4
*pde = 00000000
Oops: 0000 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bbca4>] Tainted: P VLI
EFLAGS: 00010202 (2.6.16.11 #3)
EIP is at handle_minor_send+0x17a/0x241 [capi]
eax: c24abbc0 ebx: c0b4c980 ecx: 00000010 edx: 00000010
esi: c1679140 edi: c2783016 ebp: 0000c28d esp: c0327e24
ds: 007b es: 007b ss: 0068
Process swapper (pid: 0, threadinfo=c0326000 task=c02e1300)
Stack: <0>000005b4 c1679180 00000000 c28d0000 c1ce04e0 c2f69654 c221604e c1679140
c39bc19a 00000038 c20c0400 c075c560 c1f2f800 00000000 c01dc9b5 c1e96a40
c075c560 c2ed64c0 c1e96a40 c01dcd3b c2fb94e8 c075c560 c0327f00 c1e96a40
Call Trace:
[<c39bc19a>] capinc_tty_write+0xda/0xf3 [capi]
[<c01dc9b5>] ppp_sync_push+0x52/0xfe
[<c01dcd3b>] ppp_sync_send+0x1f5/0x204
[<c01d9bc1>] ppp_push+0x3e/0x9c
[<c01dacd4>] ppp_xmit_process+0x422/0x4cc
[<c01daf3f>] ppp_start_xmit+0x1c1/0x1f6
[<c0213ea5>] qdisc_restart+0xa7/0x135
[<c020b112>] dev_queue_xmit+0xba/0x19e
[<c0223f69>] ip_output+0x1eb/0x236
[<c0220907>] ip_forward+0x1c1/0x21a
[<c021fa6c>] ip_rcv+0x38e/0x3ea
[<c020b4c2>] netif_receive_skb+0x166/0x195
[<c020b55e>] process_backlog+0x6d/0xd2
[<c020a30f>] net_rx_action+0x6a/0xff
[<c0112909>] __do_softirq+0x35/0x7d
[<c0112973>] do_softirq+0x22/0x26
[<c0103a9d>] do_IRQ+0x1e/0x25
[<c010255a>] common_interrupt+0x1a/0x20
[<c01013c5>] default_idle+0x2b/0x53
[<c0101426>] cpu_idle+0x39/0x4e
[<c0328386>] start_kernel+0x20b/0x20d
Code: c0 e8 b3 b6 77 fc 85 c0 75 10 68 d8 c8 9b c3 e8 82 3d 75 fc 8b 43 60 5a eb 50 8d 56 50 c7 00 00 00 00 00 66 89 68 04 eb 02 89
ca <8b> 0a 85 c9 75 f8 89 02 89 da ff 46 54 8b 46 10 e8 30 79 fd ff
<0>Kernel panic - not syncing: Fatal exception in interrupt
That oops took me to the "ackqueue" implementation in capi.c. The crash
occured in capincci_add_ack() (auto-inlined by the compiler).
I read the code a bit and finally decided to replace the custom linked list
implementation (struct capiminor->ackqueue) by a struct list_head. That
did not solve the crash, but produced the following interresting oops:
Unable to handle kernel paging request at virtual address 00200200
printing eip:
c39bb1f5
*pde = 00000000
Oops: 0002 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bb1f5>] Tainted: P VLI
EFLAGS: 00010246 (2.6.16.11 #3)
EIP is at capiminor_del_ack+0x18/0x49 [capi]
eax: 00200200 ebx: c18d41a0 ecx: c1385620 edx: 00100100
esi: 0000d147 edi: 00001103 ebp: 0000d147 esp: c1093f3c
ds: 007b es: 007b ss: 0068
Process events/0 (pid: 3, threadinfo=c1092000 task=c1089030)
Stack: <0>c2a17580 c18d41a0 c39bbd16 00000038 c18d41e0 00000000 d147c640 c29e0b68
c29e0b90 00000212 c29e0b68 c39932b2 c29e0bb0 c10736a0 c0119ef0 c399326c
c10736a8 c10736a0 c10736b0 c0119f93 c011a06e 00000001 00000000 00000000
Call Trace:
[<c39bbd16>] handle_minor_send+0x1af/0x241 [capi]
[<c39932b2>] recv_handler+0x46/0x5f [kernelcapi]
[<c0119ef0>] run_workqueue+0x5e/0x8d
[<c399326c>] recv_handler+0x0/0x5f [kernelcapi]
[<c0119f93>] worker_thread+0x0/0x10b
[<c011a06e>] worker_thread+0xdb/0x10b
[<c010c998>] default_wake_function+0x0/0xc
[<c011c399>] kthread+0x90/0xbc
[<c011c309>] kthread+0x0/0xbc
[<c0100a65>] kernel_thread_helper+0x5/0xb
Code: 7e 02 89 ee 89 f0 5a f7 d0 c1 f8 1f 5b 21 f0 5e 5f 5d c3 56 53 8b 48 50 89 d6 89 c3 8b 11 eb 2f 66 39 71 08 75 25 8b 41 04 8b 11 <89> 10 89 42 04 c7 01 00 01 10 00 89 c8 c7 41 04 00 02 20 00 e8
The interresting part of it is the "virtual address 00200200", which is
LIST_POISON2. I thought about some race condition, but as this is an UP
system, it leads to questions on how it can happen. If we look at EFLAGS:
00010202, we see that interrupts are enabled at the time of the crash
(eflags & 0x200).
Finally, I don't understand all the capi code, but I think that
handle_minor_send() is racing somehow against capi_recv_message(), which
call both capiminor_del_ack(). So if an IRQ occurs in the middle of
capiminor_del_ack() and another instance of it is invoked, it leads to
linked list corruption.
I came up with the following patch. With this, I could not reproduce the
crash anymore. Clearly, this is not the correct fix for the issue. As this
seems to be some locking issue, there might be more locking issues in that
code. For example, doesn't the whole struct capiminor have to be locked
somehow?
Cc: Carsten Paeth <calle@calle.de>
Cc: Kai Germaschewski <kai.germaschewski@gmx.de>
Cc: Karsten Keil <kkeil@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 15:25:30 +08:00
|
|
|
struct datahandle_queue *p, *tmp;
|
|
|
|
unsigned long flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
[PATCH] CAPI crash / race condition
I am getting more or less reproducible crashes from the CAPI subsystem
using the fcdsl driver:
Unable to handle kernel NULL pointer dereference at virtual address 00000010
printing eip:
c39bbca4
*pde = 00000000
Oops: 0000 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bbca4>] Tainted: P VLI
EFLAGS: 00010202 (2.6.16.11 #3)
EIP is at handle_minor_send+0x17a/0x241 [capi]
eax: c24abbc0 ebx: c0b4c980 ecx: 00000010 edx: 00000010
esi: c1679140 edi: c2783016 ebp: 0000c28d esp: c0327e24
ds: 007b es: 007b ss: 0068
Process swapper (pid: 0, threadinfo=c0326000 task=c02e1300)
Stack: <0>000005b4 c1679180 00000000 c28d0000 c1ce04e0 c2f69654 c221604e c1679140
c39bc19a 00000038 c20c0400 c075c560 c1f2f800 00000000 c01dc9b5 c1e96a40
c075c560 c2ed64c0 c1e96a40 c01dcd3b c2fb94e8 c075c560 c0327f00 c1e96a40
Call Trace:
[<c39bc19a>] capinc_tty_write+0xda/0xf3 [capi]
[<c01dc9b5>] ppp_sync_push+0x52/0xfe
[<c01dcd3b>] ppp_sync_send+0x1f5/0x204
[<c01d9bc1>] ppp_push+0x3e/0x9c
[<c01dacd4>] ppp_xmit_process+0x422/0x4cc
[<c01daf3f>] ppp_start_xmit+0x1c1/0x1f6
[<c0213ea5>] qdisc_restart+0xa7/0x135
[<c020b112>] dev_queue_xmit+0xba/0x19e
[<c0223f69>] ip_output+0x1eb/0x236
[<c0220907>] ip_forward+0x1c1/0x21a
[<c021fa6c>] ip_rcv+0x38e/0x3ea
[<c020b4c2>] netif_receive_skb+0x166/0x195
[<c020b55e>] process_backlog+0x6d/0xd2
[<c020a30f>] net_rx_action+0x6a/0xff
[<c0112909>] __do_softirq+0x35/0x7d
[<c0112973>] do_softirq+0x22/0x26
[<c0103a9d>] do_IRQ+0x1e/0x25
[<c010255a>] common_interrupt+0x1a/0x20
[<c01013c5>] default_idle+0x2b/0x53
[<c0101426>] cpu_idle+0x39/0x4e
[<c0328386>] start_kernel+0x20b/0x20d
Code: c0 e8 b3 b6 77 fc 85 c0 75 10 68 d8 c8 9b c3 e8 82 3d 75 fc 8b 43 60 5a eb 50 8d 56 50 c7 00 00 00 00 00 66 89 68 04 eb 02 89
ca <8b> 0a 85 c9 75 f8 89 02 89 da ff 46 54 8b 46 10 e8 30 79 fd ff
<0>Kernel panic - not syncing: Fatal exception in interrupt
That oops took me to the "ackqueue" implementation in capi.c. The crash
occured in capincci_add_ack() (auto-inlined by the compiler).
I read the code a bit and finally decided to replace the custom linked list
implementation (struct capiminor->ackqueue) by a struct list_head. That
did not solve the crash, but produced the following interresting oops:
Unable to handle kernel paging request at virtual address 00200200
printing eip:
c39bb1f5
*pde = 00000000
Oops: 0002 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bb1f5>] Tainted: P VLI
EFLAGS: 00010246 (2.6.16.11 #3)
EIP is at capiminor_del_ack+0x18/0x49 [capi]
eax: 00200200 ebx: c18d41a0 ecx: c1385620 edx: 00100100
esi: 0000d147 edi: 00001103 ebp: 0000d147 esp: c1093f3c
ds: 007b es: 007b ss: 0068
Process events/0 (pid: 3, threadinfo=c1092000 task=c1089030)
Stack: <0>c2a17580 c18d41a0 c39bbd16 00000038 c18d41e0 00000000 d147c640 c29e0b68
c29e0b90 00000212 c29e0b68 c39932b2 c29e0bb0 c10736a0 c0119ef0 c399326c
c10736a8 c10736a0 c10736b0 c0119f93 c011a06e 00000001 00000000 00000000
Call Trace:
[<c39bbd16>] handle_minor_send+0x1af/0x241 [capi]
[<c39932b2>] recv_handler+0x46/0x5f [kernelcapi]
[<c0119ef0>] run_workqueue+0x5e/0x8d
[<c399326c>] recv_handler+0x0/0x5f [kernelcapi]
[<c0119f93>] worker_thread+0x0/0x10b
[<c011a06e>] worker_thread+0xdb/0x10b
[<c010c998>] default_wake_function+0x0/0xc
[<c011c399>] kthread+0x90/0xbc
[<c011c309>] kthread+0x0/0xbc
[<c0100a65>] kernel_thread_helper+0x5/0xb
Code: 7e 02 89 ee 89 f0 5a f7 d0 c1 f8 1f 5b 21 f0 5e 5f 5d c3 56 53 8b 48 50 89 d6 89 c3 8b 11 eb 2f 66 39 71 08 75 25 8b 41 04 8b 11 <89> 10 89 42 04 c7 01 00 01 10 00 89 c8 c7 41 04 00 02 20 00 e8
The interresting part of it is the "virtual address 00200200", which is
LIST_POISON2. I thought about some race condition, but as this is an UP
system, it leads to questions on how it can happen. If we look at EFLAGS:
00010202, we see that interrupts are enabled at the time of the crash
(eflags & 0x200).
Finally, I don't understand all the capi code, but I think that
handle_minor_send() is racing somehow against capi_recv_message(), which
call both capiminor_del_ack(). So if an IRQ occurs in the middle of
capiminor_del_ack() and another instance of it is invoked, it leads to
linked list corruption.
I came up with the following patch. With this, I could not reproduce the
crash anymore. Clearly, this is not the correct fix for the issue. As this
seems to be some locking issue, there might be more locking issues in that
code. For example, doesn't the whole struct capiminor have to be locked
somehow?
Cc: Carsten Paeth <calle@calle.de>
Cc: Kai Germaschewski <kai.germaschewski@gmx.de>
Cc: Karsten Keil <kkeil@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 15:25:30 +08:00
|
|
|
spin_lock_irqsave(&mp->ackqlock, flags);
|
|
|
|
list_for_each_entry_safe(p, tmp, &mp->ackqueue, list) {
|
|
|
|
list_del(&p->list);
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree(p);
|
|
|
|
mp->nack--;
|
|
|
|
}
|
[PATCH] CAPI crash / race condition
I am getting more or less reproducible crashes from the CAPI subsystem
using the fcdsl driver:
Unable to handle kernel NULL pointer dereference at virtual address 00000010
printing eip:
c39bbca4
*pde = 00000000
Oops: 0000 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bbca4>] Tainted: P VLI
EFLAGS: 00010202 (2.6.16.11 #3)
EIP is at handle_minor_send+0x17a/0x241 [capi]
eax: c24abbc0 ebx: c0b4c980 ecx: 00000010 edx: 00000010
esi: c1679140 edi: c2783016 ebp: 0000c28d esp: c0327e24
ds: 007b es: 007b ss: 0068
Process swapper (pid: 0, threadinfo=c0326000 task=c02e1300)
Stack: <0>000005b4 c1679180 00000000 c28d0000 c1ce04e0 c2f69654 c221604e c1679140
c39bc19a 00000038 c20c0400 c075c560 c1f2f800 00000000 c01dc9b5 c1e96a40
c075c560 c2ed64c0 c1e96a40 c01dcd3b c2fb94e8 c075c560 c0327f00 c1e96a40
Call Trace:
[<c39bc19a>] capinc_tty_write+0xda/0xf3 [capi]
[<c01dc9b5>] ppp_sync_push+0x52/0xfe
[<c01dcd3b>] ppp_sync_send+0x1f5/0x204
[<c01d9bc1>] ppp_push+0x3e/0x9c
[<c01dacd4>] ppp_xmit_process+0x422/0x4cc
[<c01daf3f>] ppp_start_xmit+0x1c1/0x1f6
[<c0213ea5>] qdisc_restart+0xa7/0x135
[<c020b112>] dev_queue_xmit+0xba/0x19e
[<c0223f69>] ip_output+0x1eb/0x236
[<c0220907>] ip_forward+0x1c1/0x21a
[<c021fa6c>] ip_rcv+0x38e/0x3ea
[<c020b4c2>] netif_receive_skb+0x166/0x195
[<c020b55e>] process_backlog+0x6d/0xd2
[<c020a30f>] net_rx_action+0x6a/0xff
[<c0112909>] __do_softirq+0x35/0x7d
[<c0112973>] do_softirq+0x22/0x26
[<c0103a9d>] do_IRQ+0x1e/0x25
[<c010255a>] common_interrupt+0x1a/0x20
[<c01013c5>] default_idle+0x2b/0x53
[<c0101426>] cpu_idle+0x39/0x4e
[<c0328386>] start_kernel+0x20b/0x20d
Code: c0 e8 b3 b6 77 fc 85 c0 75 10 68 d8 c8 9b c3 e8 82 3d 75 fc 8b 43 60 5a eb 50 8d 56 50 c7 00 00 00 00 00 66 89 68 04 eb 02 89
ca <8b> 0a 85 c9 75 f8 89 02 89 da ff 46 54 8b 46 10 e8 30 79 fd ff
<0>Kernel panic - not syncing: Fatal exception in interrupt
That oops took me to the "ackqueue" implementation in capi.c. The crash
occured in capincci_add_ack() (auto-inlined by the compiler).
I read the code a bit and finally decided to replace the custom linked list
implementation (struct capiminor->ackqueue) by a struct list_head. That
did not solve the crash, but produced the following interresting oops:
Unable to handle kernel paging request at virtual address 00200200
printing eip:
c39bb1f5
*pde = 00000000
Oops: 0002 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bb1f5>] Tainted: P VLI
EFLAGS: 00010246 (2.6.16.11 #3)
EIP is at capiminor_del_ack+0x18/0x49 [capi]
eax: 00200200 ebx: c18d41a0 ecx: c1385620 edx: 00100100
esi: 0000d147 edi: 00001103 ebp: 0000d147 esp: c1093f3c
ds: 007b es: 007b ss: 0068
Process events/0 (pid: 3, threadinfo=c1092000 task=c1089030)
Stack: <0>c2a17580 c18d41a0 c39bbd16 00000038 c18d41e0 00000000 d147c640 c29e0b68
c29e0b90 00000212 c29e0b68 c39932b2 c29e0bb0 c10736a0 c0119ef0 c399326c
c10736a8 c10736a0 c10736b0 c0119f93 c011a06e 00000001 00000000 00000000
Call Trace:
[<c39bbd16>] handle_minor_send+0x1af/0x241 [capi]
[<c39932b2>] recv_handler+0x46/0x5f [kernelcapi]
[<c0119ef0>] run_workqueue+0x5e/0x8d
[<c399326c>] recv_handler+0x0/0x5f [kernelcapi]
[<c0119f93>] worker_thread+0x0/0x10b
[<c011a06e>] worker_thread+0xdb/0x10b
[<c010c998>] default_wake_function+0x0/0xc
[<c011c399>] kthread+0x90/0xbc
[<c011c309>] kthread+0x0/0xbc
[<c0100a65>] kernel_thread_helper+0x5/0xb
Code: 7e 02 89 ee 89 f0 5a f7 d0 c1 f8 1f 5b 21 f0 5e 5f 5d c3 56 53 8b 48 50 89 d6 89 c3 8b 11 eb 2f 66 39 71 08 75 25 8b 41 04 8b 11 <89> 10 89 42 04 c7 01 00 01 10 00 89 c8 c7 41 04 00 02 20 00 e8
The interresting part of it is the "virtual address 00200200", which is
LIST_POISON2. I thought about some race condition, but as this is an UP
system, it leads to questions on how it can happen. If we look at EFLAGS:
00010202, we see that interrupts are enabled at the time of the crash
(eflags & 0x200).
Finally, I don't understand all the capi code, but I think that
handle_minor_send() is racing somehow against capi_recv_message(), which
call both capiminor_del_ack(). So if an IRQ occurs in the middle of
capiminor_del_ack() and another instance of it is invoked, it leads to
linked list corruption.
I came up with the following patch. With this, I could not reproduce the
crash anymore. Clearly, this is not the correct fix for the issue. As this
seems to be some locking issue, there might be more locking issues in that
code. For example, doesn't the whole struct capiminor have to be locked
somehow?
Cc: Carsten Paeth <calle@calle.de>
Cc: Kai Germaschewski <kai.germaschewski@gmx.de>
Cc: Karsten Keil <kkeil@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 15:25:30 +08:00
|
|
|
spin_unlock_irqrestore(&mp->ackqlock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* -------- struct capiminor ---------------------------------------- */
|
|
|
|
|
|
|
|
static struct capiminor *capiminor_alloc(struct capi20_appl *ap, u32 ncci)
|
|
|
|
{
|
|
|
|
struct capiminor *mp, *p;
|
|
|
|
unsigned int minor = 0;
|
|
|
|
unsigned long flags;
|
|
|
|
|
2006-12-08 18:39:35 +08:00
|
|
|
mp = kzalloc(sizeof(*mp), GFP_ATOMIC);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!mp) {
|
|
|
|
printk(KERN_ERR "capi: can't alloc capiminor\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
mp->ap = ap;
|
|
|
|
mp->ncci = ncci;
|
|
|
|
mp->msgid = 0;
|
|
|
|
atomic_set(&mp->ttyopencount,0);
|
[PATCH] CAPI crash / race condition
I am getting more or less reproducible crashes from the CAPI subsystem
using the fcdsl driver:
Unable to handle kernel NULL pointer dereference at virtual address 00000010
printing eip:
c39bbca4
*pde = 00000000
Oops: 0000 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bbca4>] Tainted: P VLI
EFLAGS: 00010202 (2.6.16.11 #3)
EIP is at handle_minor_send+0x17a/0x241 [capi]
eax: c24abbc0 ebx: c0b4c980 ecx: 00000010 edx: 00000010
esi: c1679140 edi: c2783016 ebp: 0000c28d esp: c0327e24
ds: 007b es: 007b ss: 0068
Process swapper (pid: 0, threadinfo=c0326000 task=c02e1300)
Stack: <0>000005b4 c1679180 00000000 c28d0000 c1ce04e0 c2f69654 c221604e c1679140
c39bc19a 00000038 c20c0400 c075c560 c1f2f800 00000000 c01dc9b5 c1e96a40
c075c560 c2ed64c0 c1e96a40 c01dcd3b c2fb94e8 c075c560 c0327f00 c1e96a40
Call Trace:
[<c39bc19a>] capinc_tty_write+0xda/0xf3 [capi]
[<c01dc9b5>] ppp_sync_push+0x52/0xfe
[<c01dcd3b>] ppp_sync_send+0x1f5/0x204
[<c01d9bc1>] ppp_push+0x3e/0x9c
[<c01dacd4>] ppp_xmit_process+0x422/0x4cc
[<c01daf3f>] ppp_start_xmit+0x1c1/0x1f6
[<c0213ea5>] qdisc_restart+0xa7/0x135
[<c020b112>] dev_queue_xmit+0xba/0x19e
[<c0223f69>] ip_output+0x1eb/0x236
[<c0220907>] ip_forward+0x1c1/0x21a
[<c021fa6c>] ip_rcv+0x38e/0x3ea
[<c020b4c2>] netif_receive_skb+0x166/0x195
[<c020b55e>] process_backlog+0x6d/0xd2
[<c020a30f>] net_rx_action+0x6a/0xff
[<c0112909>] __do_softirq+0x35/0x7d
[<c0112973>] do_softirq+0x22/0x26
[<c0103a9d>] do_IRQ+0x1e/0x25
[<c010255a>] common_interrupt+0x1a/0x20
[<c01013c5>] default_idle+0x2b/0x53
[<c0101426>] cpu_idle+0x39/0x4e
[<c0328386>] start_kernel+0x20b/0x20d
Code: c0 e8 b3 b6 77 fc 85 c0 75 10 68 d8 c8 9b c3 e8 82 3d 75 fc 8b 43 60 5a eb 50 8d 56 50 c7 00 00 00 00 00 66 89 68 04 eb 02 89
ca <8b> 0a 85 c9 75 f8 89 02 89 da ff 46 54 8b 46 10 e8 30 79 fd ff
<0>Kernel panic - not syncing: Fatal exception in interrupt
That oops took me to the "ackqueue" implementation in capi.c. The crash
occured in capincci_add_ack() (auto-inlined by the compiler).
I read the code a bit and finally decided to replace the custom linked list
implementation (struct capiminor->ackqueue) by a struct list_head. That
did not solve the crash, but produced the following interresting oops:
Unable to handle kernel paging request at virtual address 00200200
printing eip:
c39bb1f5
*pde = 00000000
Oops: 0002 [#1]
Modules linked in: netconsole capi capifs 3c59x mii fcdsl kernelcapi uhci_hcd usbcore ide_cd cdrom
CPU: 0
EIP: 0060:[<c39bb1f5>] Tainted: P VLI
EFLAGS: 00010246 (2.6.16.11 #3)
EIP is at capiminor_del_ack+0x18/0x49 [capi]
eax: 00200200 ebx: c18d41a0 ecx: c1385620 edx: 00100100
esi: 0000d147 edi: 00001103 ebp: 0000d147 esp: c1093f3c
ds: 007b es: 007b ss: 0068
Process events/0 (pid: 3, threadinfo=c1092000 task=c1089030)
Stack: <0>c2a17580 c18d41a0 c39bbd16 00000038 c18d41e0 00000000 d147c640 c29e0b68
c29e0b90 00000212 c29e0b68 c39932b2 c29e0bb0 c10736a0 c0119ef0 c399326c
c10736a8 c10736a0 c10736b0 c0119f93 c011a06e 00000001 00000000 00000000
Call Trace:
[<c39bbd16>] handle_minor_send+0x1af/0x241 [capi]
[<c39932b2>] recv_handler+0x46/0x5f [kernelcapi]
[<c0119ef0>] run_workqueue+0x5e/0x8d
[<c399326c>] recv_handler+0x0/0x5f [kernelcapi]
[<c0119f93>] worker_thread+0x0/0x10b
[<c011a06e>] worker_thread+0xdb/0x10b
[<c010c998>] default_wake_function+0x0/0xc
[<c011c399>] kthread+0x90/0xbc
[<c011c309>] kthread+0x0/0xbc
[<c0100a65>] kernel_thread_helper+0x5/0xb
Code: 7e 02 89 ee 89 f0 5a f7 d0 c1 f8 1f 5b 21 f0 5e 5f 5d c3 56 53 8b 48 50 89 d6 89 c3 8b 11 eb 2f 66 39 71 08 75 25 8b 41 04 8b 11 <89> 10 89 42 04 c7 01 00 01 10 00 89 c8 c7 41 04 00 02 20 00 e8
The interresting part of it is the "virtual address 00200200", which is
LIST_POISON2. I thought about some race condition, but as this is an UP
system, it leads to questions on how it can happen. If we look at EFLAGS:
00010202, we see that interrupts are enabled at the time of the crash
(eflags & 0x200).
Finally, I don't understand all the capi code, but I think that
handle_minor_send() is racing somehow against capi_recv_message(), which
call both capiminor_del_ack(). So if an IRQ occurs in the middle of
capiminor_del_ack() and another instance of it is invoked, it leads to
linked list corruption.
I came up with the following patch. With this, I could not reproduce the
crash anymore. Clearly, this is not the correct fix for the issue. As this
seems to be some locking issue, there might be more locking issues in that
code. For example, doesn't the whole struct capiminor have to be locked
somehow?
Cc: Carsten Paeth <calle@calle.de>
Cc: Kai Germaschewski <kai.germaschewski@gmx.de>
Cc: Karsten Keil <kkeil@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-06-26 15:25:30 +08:00
|
|
|
INIT_LIST_HEAD(&mp->ackqueue);
|
|
|
|
spin_lock_init(&mp->ackqlock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
skb_queue_head_init(&mp->inqueue);
|
|
|
|
skb_queue_head_init(&mp->outqueue);
|
|
|
|
|
|
|
|
/* Allocate the least unused minor number.
|
|
|
|
*/
|
|
|
|
write_lock_irqsave(&capiminor_list_lock, flags);
|
|
|
|
if (list_empty(&capiminor_list))
|
|
|
|
list_add(&mp->list, &capiminor_list);
|
|
|
|
else {
|
|
|
|
list_for_each_entry(p, &capiminor_list, list) {
|
|
|
|
if (p->minor > minor)
|
|
|
|
break;
|
|
|
|
minor++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (minor < capi_ttyminors) {
|
|
|
|
mp->minor = minor;
|
|
|
|
list_add(&mp->list, p->list.prev);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
write_unlock_irqrestore(&capiminor_list_lock, flags);
|
|
|
|
|
|
|
|
if (!(minor < capi_ttyminors)) {
|
|
|
|
printk(KERN_NOTICE "capi: out of minors\n");
|
|
|
|
kfree(mp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return mp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void capiminor_free(struct capiminor *mp)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
write_lock_irqsave(&capiminor_list_lock, flags);
|
|
|
|
list_del(&mp->list);
|
|
|
|
write_unlock_irqrestore(&capiminor_list_lock, flags);
|
|
|
|
|
|
|
|
if (mp->ttyskb) kfree_skb(mp->ttyskb);
|
|
|
|
mp->ttyskb = NULL;
|
|
|
|
skb_queue_purge(&mp->inqueue);
|
|
|
|
skb_queue_purge(&mp->outqueue);
|
|
|
|
capiminor_del_all_ack(mp);
|
|
|
|
kfree(mp);
|
|
|
|
}
|
|
|
|
|
2005-05-01 23:59:29 +08:00
|
|
|
static struct capiminor *capiminor_find(unsigned int minor)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct list_head *l;
|
|
|
|
struct capiminor *p = NULL;
|
|
|
|
|
|
|
|
read_lock(&capiminor_list_lock);
|
|
|
|
list_for_each(l, &capiminor_list) {
|
|
|
|
p = list_entry(l, struct capiminor, list);
|
|
|
|
if (p->minor == minor)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
read_unlock(&capiminor_list_lock);
|
|
|
|
if (l == &capiminor_list)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
|
|
|
|
|
|
|
|
/* -------- struct capincci ----------------------------------------- */
|
|
|
|
|
|
|
|
static struct capincci *capincci_alloc(struct capidev *cdev, u32 ncci)
|
|
|
|
{
|
|
|
|
struct capincci *np, **pp;
|
|
|
|
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
|
|
|
|
struct capiminor *mp = NULL;
|
|
|
|
#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
|
|
|
|
|
2006-12-08 18:39:35 +08:00
|
|
|
np = kzalloc(sizeof(*np), GFP_ATOMIC);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!np)
|
|
|
|
return NULL;
|
|
|
|
np->ncci = ncci;
|
|
|
|
np->cdev = cdev;
|
|
|
|
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
|
|
|
|
mp = NULL;
|
|
|
|
if (cdev->userflags & CAPIFLAG_HIGHJACKING)
|
|
|
|
mp = np->minorp = capiminor_alloc(&cdev->ap, ncci);
|
|
|
|
if (mp) {
|
|
|
|
mp->nccip = np;
|
|
|
|
#ifdef _DEBUG_REFCOUNT
|
|
|
|
printk(KERN_DEBUG "set mp->nccip\n");
|
|
|
|
#endif
|
|
|
|
#if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE)
|
|
|
|
capifs_new_ncci(mp->minor, MKDEV(capi_ttymajor, mp->minor));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
|
|
|
|
for (pp=&cdev->nccis; *pp; pp = &(*pp)->next)
|
|
|
|
;
|
|
|
|
*pp = np;
|
|
|
|
return np;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void capincci_free(struct capidev *cdev, u32 ncci)
|
|
|
|
{
|
|
|
|
struct capincci *np, **pp;
|
|
|
|
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
|
|
|
|
struct capiminor *mp;
|
|
|
|
#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
|
|
|
|
|
|
|
|
pp=&cdev->nccis;
|
|
|
|
while (*pp) {
|
|
|
|
np = *pp;
|
|
|
|
if (ncci == 0xffffffff || np->ncci == ncci) {
|
|
|
|
*pp = (*pp)->next;
|
|
|
|
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
|
2008-04-28 17:14:37 +08:00
|
|
|
if ((mp = np->minorp) != NULL) {
|
2005-04-17 06:20:36 +08:00
|
|
|
#if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE)
|
|
|
|
capifs_free_ncci(mp->minor);
|
|
|
|
#endif
|
|
|
|
if (mp->tty) {
|
|
|
|
mp->nccip = NULL;
|
|
|
|
#ifdef _DEBUG_REFCOUNT
|
|
|
|
printk(KERN_DEBUG "reset mp->nccip\n");
|
|
|
|
#endif
|
|
|
|
tty_hangup(mp->tty);
|
|
|
|
} else {
|
|
|
|
capiminor_free(mp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
|
|
|
|
kfree(np);
|
2008-04-28 17:14:37 +08:00
|
|
|
if (*pp == NULL) return;
|
2005-04-17 06:20:36 +08:00
|
|
|
} else {
|
|
|
|
pp = &(*pp)->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct capincci *capincci_find(struct capidev *cdev, u32 ncci)
|
|
|
|
{
|
|
|
|
struct capincci *p;
|
|
|
|
|
|
|
|
for (p=cdev->nccis; p ; p = p->next) {
|
|
|
|
if (p->ncci == ncci)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -------- struct capidev ------------------------------------------ */
|
|
|
|
|
|
|
|
static struct capidev *capidev_alloc(void)
|
|
|
|
{
|
|
|
|
struct capidev *cdev;
|
|
|
|
unsigned long flags;
|
|
|
|
|
2006-12-08 18:39:35 +08:00
|
|
|
cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!cdev)
|
|
|
|
return NULL;
|
|
|
|
|
2007-05-08 15:32:43 +08:00
|
|
|
mutex_init(&cdev->ncci_list_mtx);
|
2005-04-17 06:20:36 +08:00
|
|
|
skb_queue_head_init(&cdev->recvqueue);
|
|
|
|
init_waitqueue_head(&cdev->recvwait);
|
|
|
|
write_lock_irqsave(&capidev_list_lock, flags);
|
|
|
|
list_add_tail(&cdev->list, &capidev_list);
|
|
|
|
write_unlock_irqrestore(&capidev_list_lock, flags);
|
|
|
|
return cdev;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void capidev_free(struct capidev *cdev)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
if (cdev->ap.applid) {
|
|
|
|
capi20_release(&cdev->ap);
|
|
|
|
cdev->ap.applid = 0;
|
|
|
|
}
|
|
|
|
skb_queue_purge(&cdev->recvqueue);
|
|
|
|
|
2007-05-08 15:32:43 +08:00
|
|
|
mutex_lock(&cdev->ncci_list_mtx);
|
2005-04-17 06:20:36 +08:00
|
|
|
capincci_free(cdev, 0xffffffff);
|
2007-05-08 15:32:43 +08:00
|
|
|
mutex_unlock(&cdev->ncci_list_mtx);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
write_lock_irqsave(&capidev_list_lock, flags);
|
|
|
|
list_del(&cdev->list);
|
|
|
|
write_unlock_irqrestore(&capidev_list_lock, flags);
|
|
|
|
kfree(cdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
|
|
|
|
/* -------- handle data queue --------------------------------------- */
|
|
|
|
|
|
|
|
static struct sk_buff *
|
|
|
|
gen_data_b3_resp_for(struct capiminor *mp, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct sk_buff *nskb;
|
|
|
|
nskb = alloc_skb(CAPI_DATA_B3_RESP_LEN, GFP_ATOMIC);
|
|
|
|
if (nskb) {
|
|
|
|
u16 datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4+4+2);
|
|
|
|
unsigned char *s = skb_put(nskb, CAPI_DATA_B3_RESP_LEN);
|
|
|
|
capimsg_setu16(s, 0, CAPI_DATA_B3_RESP_LEN);
|
|
|
|
capimsg_setu16(s, 2, mp->ap->applid);
|
|
|
|
capimsg_setu8 (s, 4, CAPI_DATA_B3);
|
|
|
|
capimsg_setu8 (s, 5, CAPI_RESP);
|
|
|
|
capimsg_setu16(s, 6, mp->msgid++);
|
|
|
|
capimsg_setu32(s, 8, mp->ncci);
|
|
|
|
capimsg_setu16(s, 12, datahandle);
|
|
|
|
}
|
|
|
|
return nskb;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct sk_buff *nskb;
|
|
|
|
int datalen;
|
|
|
|
u16 errcode, datahandle;
|
|
|
|
struct tty_ldisc *ld;
|
|
|
|
|
|
|
|
datalen = skb->len - CAPIMSG_LEN(skb->data);
|
|
|
|
if (mp->tty == NULL)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG_DATAFLOW
|
|
|
|
printk(KERN_DEBUG "capi: currently no receiver\n");
|
|
|
|
#endif
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ld = tty_ldisc_ref(mp->tty);
|
|
|
|
if (ld == NULL)
|
|
|
|
return -1;
|
2008-07-17 04:53:12 +08:00
|
|
|
if (ld->ops->receive_buf == NULL) {
|
2005-04-17 06:20:36 +08:00
|
|
|
#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
|
|
|
|
printk(KERN_DEBUG "capi: ldisc has no receive_buf function\n");
|
|
|
|
#endif
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
if (mp->ttyinstop) {
|
|
|
|
#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
|
|
|
|
printk(KERN_DEBUG "capi: recv tty throttled\n");
|
|
|
|
#endif
|
|
|
|
goto bad;
|
|
|
|
}
|
[PATCH] TTY layer buffering revamp
The API and code have been through various bits of initial review by
serial driver people but they definitely need to live somewhere for a
while so the unconverted drivers can get knocked into shape, existing
drivers that have been updated can be better tuned and bugs whacked out.
This replaces the tty flip buffers with kmalloc objects in rings. In the
normal situation for an IRQ driven serial port at typical speeds the
behaviour is pretty much the same, two buffers end up allocated and the
kernel cycles between them as before.
When there are delays or at high speed we now behave far better as the
buffer pool can grow a bit rather than lose characters. This also means
that we can operate at higher speeds reliably.
For drivers that receive characters in blocks (DMA based, USB and
especially virtualisation) the layer allows a lot of driver specific
code that works around the tty layer with private secondary queues to be
removed. The IBM folks need this sort of layer, the smart serial port
people do, the virtualisers do (because a virtualised tty typically
operates at infinite speed rather than emulating 9600 baud).
Finally many drivers had invalid and unsafe attempts to avoid buffer
overflows by directly invoking tty methods extracted out of the innards
of work queue structs. These are no longer needed and all go away. That
fixes various random hangs with serial ports on overflow.
The other change in here is to optimise the receive_room path that is
used by some callers. It turns out that only one ldisc uses receive room
except asa constant and it updates it far far less than the value is
read. We thus make it a variable not a function call.
I expect the code to contain bugs due to the size alone but I'll be
watching and squashing them and feeding out new patches as it goes.
Because the buffers now dynamically expand you should only run out of
buffering when the kernel runs out of memory for real. That means a lot of
the horrible hacks high performance drivers used to do just aren't needed any
more.
Description:
tty_insert_flip_char is an old API and continues to work as before, as does
tty_flip_buffer_push() [this is why many drivers dont need modification]. It
does now also return the number of chars inserted
There are also
tty_buffer_request_room(tty, len)
which asks for a buffer block of the length requested and returns the space
found. This improves efficiency with hardware that knows how much to
transfer.
and tty_insert_flip_string_flags(tty, str, flags, len)
to insert a string of characters and flags
For a smart interface the usual code is
len = tty_request_buffer_room(tty, amount_hardware_says);
tty_insert_flip_string(tty, buffer_from_card, len);
More description!
At the moment tty buffers are attached directly to the tty. This is causing a
lot of the problems related to tty layer locking, also problems at high speed
and also with bursty data (such as occurs in virtualised environments)
I'm working on ripping out the flip buffers and replacing them with a pool of
dynamically allocated buffers. This allows both for old style "byte I/O"
devices and also helps virtualisation and smart devices where large blocks of
data suddenely materialise and need storing.
So far so good. Lots of drivers reference tty->flip.*. Several of them also
call directly and unsafely into function pointers it provides. This will all
break. Most drivers can use tty_insert_flip_char which can be kept as an API
but others need more.
At the moment I've added the following interfaces, if people think more will
be needed now is a good time to say
int tty_buffer_request_room(tty, size)
Try and ensure at least size bytes are available, returns actual room (may be
zero). At the moment it just uses the flipbuf space but that will change.
Repeated calls without characters being added are not cumulative. (ie if you
call it with 1, 1, 1, and then 4 you'll have four characters of space. The
other functions will also try and grow buffers in future but this will be a
more efficient way when you know block sizes.
int tty_insert_flip_char(tty, ch, flag)
As before insert a character if there is room. Now returns 1 for success, 0
for failure.
int tty_insert_flip_string(tty, str, len)
Insert a block of non error characters. Returns the number inserted.
int tty_prepare_flip_string(tty, strptr, len)
Adjust the buffer to allow len characters to be added. Returns a buffer
pointer in strptr and the length available. This allows for hardware that
needs to use functions like insl or mencpy_fromio.
Signed-off-by: Alan Cox <alan@redhat.com>
Cc: Paul Fulghum <paulkf@microgate.com>
Signed-off-by: Hirokazu Takata <takata@linux-m32r.org>
Signed-off-by: Serge Hallyn <serue@us.ibm.com>
Signed-off-by: Jeff Dike <jdike@addtoit.com>
Signed-off-by: John Hawkes <hawkes@sgi.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-10 12:54:13 +08:00
|
|
|
if (mp->tty->receive_room < datalen) {
|
2005-04-17 06:20:36 +08:00
|
|
|
#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
|
|
|
|
printk(KERN_DEBUG "capi: no room in tty\n");
|
|
|
|
#endif
|
|
|
|
goto bad;
|
|
|
|
}
|
2008-04-28 17:14:37 +08:00
|
|
|
if ((nskb = gen_data_b3_resp_for(mp, skb)) == NULL) {
|
2005-04-17 06:20:36 +08:00
|
|
|
printk(KERN_ERR "capi: gen_data_b3_resp failed\n");
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4);
|
|
|
|
errcode = capi20_put_message(mp->ap, nskb);
|
|
|
|
if (errcode != CAPI_NOERROR) {
|
|
|
|
printk(KERN_ERR "capi: send DATA_B3_RESP failed=%x\n",
|
|
|
|
errcode);
|
|
|
|
kfree_skb(nskb);
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
(void)skb_pull(skb, CAPIMSG_LEN(skb->data));
|
|
|
|
#ifdef _DEBUG_DATAFLOW
|
|
|
|
printk(KERN_DEBUG "capi: DATA_B3_RESP %u len=%d => ldisc\n",
|
|
|
|
datahandle, skb->len);
|
|
|
|
#endif
|
2008-07-17 04:53:12 +08:00
|
|
|
ld->ops->receive_buf(mp->tty, skb->data, NULL, skb->len);
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree_skb(skb);
|
|
|
|
tty_ldisc_deref(ld);
|
|
|
|
return 0;
|
|
|
|
bad:
|
|
|
|
tty_ldisc_deref(ld);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void handle_minor_recv(struct capiminor *mp)
|
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
2008-04-28 17:14:37 +08:00
|
|
|
while ((skb = skb_dequeue(&mp->inqueue)) != NULL) {
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned int len = skb->len;
|
|
|
|
mp->inbytes -= len;
|
|
|
|
if (handle_recv_skb(mp, skb) < 0) {
|
|
|
|
skb_queue_head(&mp->inqueue, skb);
|
|
|
|
mp->inbytes += len;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int handle_minor_send(struct capiminor *mp)
|
|
|
|
{
|
|
|
|
struct sk_buff *skb;
|
|
|
|
u16 len;
|
|
|
|
int count = 0;
|
|
|
|
u16 errcode;
|
|
|
|
u16 datahandle;
|
|
|
|
|
|
|
|
if (mp->tty && mp->ttyoutstop) {
|
|
|
|
#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
|
|
|
|
printk(KERN_DEBUG "capi: send: tty stopped\n");
|
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-04-28 17:14:37 +08:00
|
|
|
while ((skb = skb_dequeue(&mp->outqueue)) != NULL) {
|
2005-04-17 06:20:36 +08:00
|
|
|
datahandle = mp->datahandle;
|
|
|
|
len = (u16)skb->len;
|
|
|
|
skb_push(skb, CAPI_DATA_B3_REQ_LEN);
|
|
|
|
memset(skb->data, 0, CAPI_DATA_B3_REQ_LEN);
|
|
|
|
capimsg_setu16(skb->data, 0, CAPI_DATA_B3_REQ_LEN);
|
|
|
|
capimsg_setu16(skb->data, 2, mp->ap->applid);
|
|
|
|
capimsg_setu8 (skb->data, 4, CAPI_DATA_B3);
|
|
|
|
capimsg_setu8 (skb->data, 5, CAPI_REQ);
|
|
|
|
capimsg_setu16(skb->data, 6, mp->msgid++);
|
|
|
|
capimsg_setu32(skb->data, 8, mp->ncci); /* NCCI */
|
2007-07-17 19:04:22 +08:00
|
|
|
capimsg_setu32(skb->data, 12, (u32)(long)skb->data);/* Data32 */
|
2005-04-17 06:20:36 +08:00
|
|
|
capimsg_setu16(skb->data, 16, len); /* Data length */
|
|
|
|
capimsg_setu16(skb->data, 18, datahandle);
|
|
|
|
capimsg_setu16(skb->data, 20, 0); /* Flags */
|
|
|
|
|
|
|
|
if (capincci_add_ack(mp, datahandle) < 0) {
|
|
|
|
skb_pull(skb, CAPI_DATA_B3_REQ_LEN);
|
|
|
|
skb_queue_head(&mp->outqueue, skb);
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
errcode = capi20_put_message(mp->ap, skb);
|
|
|
|
if (errcode == CAPI_NOERROR) {
|
|
|
|
mp->datahandle++;
|
|
|
|
count++;
|
|
|
|
mp->outbytes -= len;
|
|
|
|
#ifdef _DEBUG_DATAFLOW
|
|
|
|
printk(KERN_DEBUG "capi: DATA_B3_REQ %u len=%u\n",
|
|
|
|
datahandle, len);
|
|
|
|
#endif
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
capiminor_del_ack(mp, datahandle);
|
|
|
|
|
|
|
|
if (errcode == CAPI_SENDQUEUEFULL) {
|
|
|
|
skb_pull(skb, CAPI_DATA_B3_REQ_LEN);
|
|
|
|
skb_queue_head(&mp->outqueue, skb);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ups, drop packet */
|
|
|
|
printk(KERN_ERR "capi: put_message = %x\n", errcode);
|
|
|
|
mp->outbytes -= len;
|
|
|
|
kfree_skb(skb);
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
|
|
|
|
/* -------- function called by lower level -------------------------- */
|
|
|
|
|
|
|
|
static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
|
|
|
|
{
|
|
|
|
struct capidev *cdev = ap->private;
|
|
|
|
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
|
|
|
|
struct capiminor *mp;
|
|
|
|
u16 datahandle;
|
|
|
|
#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
|
|
|
|
struct capincci *np;
|
|
|
|
u32 ncci;
|
2007-02-12 16:53:26 +08:00
|
|
|
unsigned long flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_CONF) {
|
|
|
|
u16 info = CAPIMSG_U16(skb->data, 12); // Info field
|
|
|
|
if (info == 0) {
|
2007-05-08 15:32:43 +08:00
|
|
|
mutex_lock(&cdev->ncci_list_mtx);
|
2005-04-17 06:20:36 +08:00
|
|
|
capincci_alloc(cdev, CAPIMSG_NCCI(skb->data));
|
2007-05-08 15:32:43 +08:00
|
|
|
mutex_unlock(&cdev->ncci_list_mtx);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_IND) {
|
2007-05-08 15:32:43 +08:00
|
|
|
mutex_lock(&cdev->ncci_list_mtx);
|
2005-04-17 06:20:36 +08:00
|
|
|
capincci_alloc(cdev, CAPIMSG_NCCI(skb->data));
|
2007-05-08 15:32:43 +08:00
|
|
|
mutex_unlock(&cdev->ncci_list_mtx);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2007-02-12 16:53:26 +08:00
|
|
|
spin_lock_irqsave(&workaround_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (CAPIMSG_COMMAND(skb->data) != CAPI_DATA_B3) {
|
|
|
|
skb_queue_tail(&cdev->recvqueue, skb);
|
|
|
|
wake_up_interruptible(&cdev->recvwait);
|
2007-02-12 16:53:26 +08:00
|
|
|
spin_unlock_irqrestore(&workaround_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
ncci = CAPIMSG_CONTROL(skb->data);
|
|
|
|
for (np = cdev->nccis; np && np->ncci != ncci; np = np->next)
|
|
|
|
;
|
|
|
|
if (!np) {
|
|
|
|
printk(KERN_ERR "BUG: capi_signal: ncci not found\n");
|
|
|
|
skb_queue_tail(&cdev->recvqueue, skb);
|
|
|
|
wake_up_interruptible(&cdev->recvwait);
|
2007-02-12 16:53:26 +08:00
|
|
|
spin_unlock_irqrestore(&workaround_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
#ifndef CONFIG_ISDN_CAPI_MIDDLEWARE
|
|
|
|
skb_queue_tail(&cdev->recvqueue, skb);
|
|
|
|
wake_up_interruptible(&cdev->recvwait);
|
|
|
|
#else /* CONFIG_ISDN_CAPI_MIDDLEWARE */
|
|
|
|
mp = np->minorp;
|
|
|
|
if (!mp) {
|
|
|
|
skb_queue_tail(&cdev->recvqueue, skb);
|
|
|
|
wake_up_interruptible(&cdev->recvwait);
|
2007-02-12 16:53:26 +08:00
|
|
|
spin_unlock_irqrestore(&workaround_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND) {
|
|
|
|
|
|
|
|
datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4+4+2);
|
|
|
|
#ifdef _DEBUG_DATAFLOW
|
|
|
|
printk(KERN_DEBUG "capi_signal: DATA_B3_IND %u len=%d\n",
|
|
|
|
datahandle, skb->len-CAPIMSG_LEN(skb->data));
|
|
|
|
#endif
|
|
|
|
skb_queue_tail(&mp->inqueue, skb);
|
|
|
|
mp->inbytes += skb->len;
|
|
|
|
handle_minor_recv(mp);
|
|
|
|
|
|
|
|
} else if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_CONF) {
|
|
|
|
|
|
|
|
datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4);
|
|
|
|
#ifdef _DEBUG_DATAFLOW
|
|
|
|
printk(KERN_DEBUG "capi_signal: DATA_B3_CONF %u 0x%x\n",
|
|
|
|
datahandle,
|
|
|
|
CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4+2));
|
|
|
|
#endif
|
|
|
|
kfree_skb(skb);
|
|
|
|
(void)capiminor_del_ack(mp, datahandle);
|
|
|
|
if (mp->tty)
|
|
|
|
tty_wakeup(mp->tty);
|
|
|
|
(void)handle_minor_send(mp);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/* ups, let capi application handle it :-) */
|
|
|
|
skb_queue_tail(&cdev->recvqueue, skb);
|
|
|
|
wake_up_interruptible(&cdev->recvwait);
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
|
2007-02-12 16:53:26 +08:00
|
|
|
spin_unlock_irqrestore(&workaround_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* -------- file_operations for capidev ----------------------------- */
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
capi_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct capidev *cdev = (struct capidev *)file->private_data;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
size_t copied;
|
|
|
|
|
|
|
|
if (!cdev->ap.applid)
|
|
|
|
return -ENODEV;
|
|
|
|
|
2008-04-28 17:14:37 +08:00
|
|
|
if ((skb = skb_dequeue(&cdev->recvqueue)) == NULL) {
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (file->f_flags & O_NONBLOCK)
|
|
|
|
return -EAGAIN;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
interruptible_sleep_on(&cdev->recvwait);
|
2008-04-28 17:14:37 +08:00
|
|
|
if ((skb = skb_dequeue(&cdev->recvqueue)) != NULL)
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
if (signal_pending(current))
|
|
|
|
break;
|
|
|
|
}
|
2008-04-28 17:14:37 +08:00
|
|
|
if (skb == NULL)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ERESTARTNOHAND;
|
|
|
|
}
|
|
|
|
if (skb->len > count) {
|
|
|
|
skb_queue_head(&cdev->recvqueue, skb);
|
|
|
|
return -EMSGSIZE;
|
|
|
|
}
|
|
|
|
if (copy_to_user(buf, skb->data, skb->len)) {
|
|
|
|
skb_queue_head(&cdev->recvqueue, skb);
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
copied = skb->len;
|
|
|
|
|
|
|
|
kfree_skb(skb);
|
|
|
|
|
|
|
|
return copied;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t
|
|
|
|
capi_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
|
|
|
|
{
|
|
|
|
struct capidev *cdev = (struct capidev *)file->private_data;
|
|
|
|
struct sk_buff *skb;
|
|
|
|
u16 mlen;
|
|
|
|
|
|
|
|
if (!cdev->ap.applid)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
skb = alloc_skb(count, GFP_USER);
|
|
|
|
if (!skb)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
if (copy_from_user(skb_put(skb, count), buf, count)) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
mlen = CAPIMSG_LEN(skb->data);
|
|
|
|
if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) {
|
|
|
|
if ((size_t)(mlen + CAPIMSG_DATALEN(skb->data)) != count) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (mlen != count) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CAPIMSG_SETAPPID(skb->data, cdev->ap.applid);
|
|
|
|
|
|
|
|
if (CAPIMSG_CMD(skb->data) == CAPI_DISCONNECT_B3_RESP) {
|
2007-05-08 15:32:43 +08:00
|
|
|
mutex_lock(&cdev->ncci_list_mtx);
|
2005-04-17 06:20:36 +08:00
|
|
|
capincci_free(cdev, CAPIMSG_NCCI(skb->data));
|
2007-05-08 15:32:43 +08:00
|
|
|
mutex_unlock(&cdev->ncci_list_mtx);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
cdev->errcode = capi20_put_message(&cdev->ap, skb);
|
|
|
|
|
|
|
|
if (cdev->errcode) {
|
|
|
|
kfree_skb(skb);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int
|
|
|
|
capi_poll(struct file *file, poll_table * wait)
|
|
|
|
{
|
|
|
|
struct capidev *cdev = (struct capidev *)file->private_data;
|
|
|
|
unsigned int mask = 0;
|
|
|
|
|
|
|
|
if (!cdev->ap.applid)
|
|
|
|
return POLLERR;
|
|
|
|
|
|
|
|
poll_wait(file, &(cdev->recvwait), wait);
|
|
|
|
mask = POLLOUT | POLLWRNORM;
|
|
|
|
if (!skb_queue_empty(&cdev->recvqueue))
|
|
|
|
mask |= POLLIN | POLLRDNORM;
|
|
|
|
return mask;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
capi_ioctl(struct inode *inode, struct file *file,
|
|
|
|
unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
|
|
|
struct capidev *cdev = file->private_data;
|
|
|
|
struct capi20_appl *ap = &cdev->ap;
|
|
|
|
capi_ioctl_struct data;
|
|
|
|
int retval = -EINVAL;
|
|
|
|
void __user *argp = (void __user *)arg;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case CAPI_REGISTER:
|
|
|
|
{
|
|
|
|
if (ap->applid)
|
|
|
|
return -EEXIST;
|
|
|
|
|
|
|
|
if (copy_from_user(&cdev->ap.rparam, argp,
|
|
|
|
sizeof(struct capi_register_params)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
cdev->ap.private = cdev;
|
|
|
|
cdev->ap.recv_message = capi_recv_message;
|
|
|
|
cdev->errcode = capi20_register(ap);
|
|
|
|
if (cdev->errcode) {
|
|
|
|
ap->applid = 0;
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (int)ap->applid;
|
|
|
|
|
|
|
|
case CAPI_GET_VERSION:
|
|
|
|
{
|
|
|
|
if (copy_from_user(&data.contr, argp,
|
|
|
|
sizeof(data.contr)))
|
|
|
|
return -EFAULT;
|
|
|
|
cdev->errcode = capi20_get_version(data.contr, &data.version);
|
|
|
|
if (cdev->errcode)
|
|
|
|
return -EIO;
|
|
|
|
if (copy_to_user(argp, &data.version,
|
|
|
|
sizeof(data.version)))
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case CAPI_GET_SERIAL:
|
|
|
|
{
|
|
|
|
if (copy_from_user(&data.contr, argp,
|
|
|
|
sizeof(data.contr)))
|
|
|
|
return -EFAULT;
|
|
|
|
cdev->errcode = capi20_get_serial (data.contr, data.serial);
|
|
|
|
if (cdev->errcode)
|
|
|
|
return -EIO;
|
|
|
|
if (copy_to_user(argp, data.serial,
|
|
|
|
sizeof(data.serial)))
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
case CAPI_GET_PROFILE:
|
|
|
|
{
|
|
|
|
if (copy_from_user(&data.contr, argp,
|
|
|
|
sizeof(data.contr)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
if (data.contr == 0) {
|
|
|
|
cdev->errcode = capi20_get_profile(data.contr, &data.profile);
|
|
|
|
if (cdev->errcode)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
retval = copy_to_user(argp,
|
|
|
|
&data.profile.ncontroller,
|
|
|
|
sizeof(data.profile.ncontroller));
|
|
|
|
|
|
|
|
} else {
|
|
|
|
cdev->errcode = capi20_get_profile(data.contr, &data.profile);
|
|
|
|
if (cdev->errcode)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
retval = copy_to_user(argp, &data.profile,
|
|
|
|
sizeof(data.profile));
|
|
|
|
}
|
|
|
|
if (retval)
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case CAPI_GET_MANUFACTURER:
|
|
|
|
{
|
|
|
|
if (copy_from_user(&data.contr, argp,
|
|
|
|
sizeof(data.contr)))
|
|
|
|
return -EFAULT;
|
|
|
|
cdev->errcode = capi20_get_manufacturer(data.contr, data.manufacturer);
|
|
|
|
if (cdev->errcode)
|
|
|
|
return -EIO;
|
|
|
|
|
|
|
|
if (copy_to_user(argp, data.manufacturer,
|
|
|
|
sizeof(data.manufacturer)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
case CAPI_GET_ERRCODE:
|
|
|
|
data.errcode = cdev->errcode;
|
|
|
|
cdev->errcode = CAPI_NOERROR;
|
|
|
|
if (arg) {
|
|
|
|
if (copy_to_user(argp, &data.errcode,
|
|
|
|
sizeof(data.errcode)))
|
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
return data.errcode;
|
|
|
|
|
|
|
|
case CAPI_INSTALLED:
|
|
|
|
if (capi20_isinstalled() == CAPI_NOERROR)
|
|
|
|
return 0;
|
|
|
|
return -ENXIO;
|
|
|
|
|
|
|
|
case CAPI_MANUFACTURER_CMD:
|
|
|
|
{
|
|
|
|
struct capi_manufacturer_cmd mcmd;
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
|
|
|
return -EPERM;
|
|
|
|
if (copy_from_user(&mcmd, argp, sizeof(mcmd)))
|
|
|
|
return -EFAULT;
|
|
|
|
return capi20_manufacturer(mcmd.cmd, mcmd.data);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case CAPI_SET_FLAGS:
|
|
|
|
case CAPI_CLR_FLAGS:
|
|
|
|
{
|
|
|
|
unsigned userflags;
|
|
|
|
if (copy_from_user(&userflags, argp,
|
|
|
|
sizeof(userflags)))
|
|
|
|
return -EFAULT;
|
|
|
|
if (cmd == CAPI_SET_FLAGS)
|
|
|
|
cdev->userflags |= userflags;
|
|
|
|
else
|
|
|
|
cdev->userflags &= ~userflags;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case CAPI_GET_FLAGS:
|
|
|
|
if (copy_to_user(argp, &cdev->userflags,
|
|
|
|
sizeof(cdev->userflags)))
|
|
|
|
return -EFAULT;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case CAPI_NCCI_OPENCOUNT:
|
|
|
|
{
|
|
|
|
struct capincci *nccip;
|
|
|
|
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
|
|
|
|
struct capiminor *mp;
|
|
|
|
#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
|
|
|
|
unsigned ncci;
|
|
|
|
int count = 0;
|
|
|
|
if (copy_from_user(&ncci, argp, sizeof(ncci)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
2007-05-08 15:32:43 +08:00
|
|
|
mutex_lock(&cdev->ncci_list_mtx);
|
2008-04-28 17:14:37 +08:00
|
|
|
if ((nccip = capincci_find(cdev, (u32) ncci)) == NULL) {
|
2007-05-08 15:32:43 +08:00
|
|
|
mutex_unlock(&cdev->ncci_list_mtx);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
|
2008-04-28 17:14:37 +08:00
|
|
|
if ((mp = nccip->minorp) != NULL) {
|
2005-04-17 06:20:36 +08:00
|
|
|
count += atomic_read(&mp->ttyopencount);
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
|
2007-05-08 15:32:43 +08:00
|
|
|
mutex_unlock(&cdev->ncci_list_mtx);
|
2005-04-17 06:20:36 +08:00
|
|
|
return count;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
|
|
|
|
case CAPI_NCCI_GETUNIT:
|
|
|
|
{
|
|
|
|
struct capincci *nccip;
|
|
|
|
struct capiminor *mp;
|
|
|
|
unsigned ncci;
|
|
|
|
int unit = 0;
|
|
|
|
if (copy_from_user(&ncci, argp,
|
|
|
|
sizeof(ncci)))
|
|
|
|
return -EFAULT;
|
2007-05-08 15:32:43 +08:00
|
|
|
mutex_lock(&cdev->ncci_list_mtx);
|
2005-04-17 06:20:36 +08:00
|
|
|
nccip = capincci_find(cdev, (u32) ncci);
|
2008-04-28 17:14:37 +08:00
|
|
|
if (!nccip || (mp = nccip->minorp) == NULL) {
|
2007-05-08 15:32:43 +08:00
|
|
|
mutex_unlock(&cdev->ncci_list_mtx);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ESRCH;
|
|
|
|
}
|
|
|
|
unit = mp->minor;
|
2007-05-08 15:32:43 +08:00
|
|
|
mutex_unlock(&cdev->ncci_list_mtx);
|
2005-04-17 06:20:36 +08:00
|
|
|
return unit;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
|
|
|
|
}
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
capi_open(struct inode *inode, struct file *file)
|
|
|
|
{
|
2008-05-17 04:15:33 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
lock_kernel();
|
2005-04-17 06:20:36 +08:00
|
|
|
if (file->private_data)
|
2008-05-17 04:15:33 +08:00
|
|
|
ret = -EEXIST;
|
|
|
|
else if ((file->private_data = capidev_alloc()) == NULL)
|
|
|
|
ret = -ENOMEM;
|
|
|
|
else
|
|
|
|
ret = nonseekable_open(inode, file);
|
|
|
|
unlock_kernel();
|
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
capi_release(struct inode *inode, struct file *file)
|
|
|
|
{
|
|
|
|
struct capidev *cdev = (struct capidev *)file->private_data;
|
|
|
|
|
|
|
|
capidev_free(cdev);
|
|
|
|
file->private_data = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-02-12 16:55:32 +08:00
|
|
|
static const struct file_operations capi_fops =
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.llseek = no_llseek,
|
|
|
|
.read = capi_read,
|
|
|
|
.write = capi_write,
|
|
|
|
.poll = capi_poll,
|
|
|
|
.ioctl = capi_ioctl,
|
|
|
|
.open = capi_open,
|
|
|
|
.release = capi_release,
|
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
|
|
|
|
/* -------- tty_operations for capincci ----------------------------- */
|
|
|
|
|
|
|
|
static int capinc_tty_open(struct tty_struct * tty, struct file * file)
|
|
|
|
{
|
|
|
|
struct capiminor *mp;
|
2007-02-12 16:53:26 +08:00
|
|
|
unsigned long flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-04-28 17:14:37 +08:00
|
|
|
if ((mp = capiminor_find(iminor(file->f_path.dentry->d_inode))) == NULL)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ENXIO;
|
2008-04-28 17:14:37 +08:00
|
|
|
if (mp->nccip == NULL)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ENXIO;
|
|
|
|
|
|
|
|
tty->driver_data = (void *)mp;
|
|
|
|
|
2007-02-12 16:53:26 +08:00
|
|
|
spin_lock_irqsave(&workaround_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (atomic_read(&mp->ttyopencount) == 0)
|
|
|
|
mp->tty = tty;
|
|
|
|
atomic_inc(&mp->ttyopencount);
|
|
|
|
#ifdef _DEBUG_REFCOUNT
|
|
|
|
printk(KERN_DEBUG "capinc_tty_open ocount=%d\n", atomic_read(&mp->ttyopencount));
|
|
|
|
#endif
|
|
|
|
handle_minor_recv(mp);
|
2007-02-12 16:53:26 +08:00
|
|
|
spin_unlock_irqrestore(&workaround_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void capinc_tty_close(struct tty_struct * tty, struct file * file)
|
|
|
|
{
|
|
|
|
struct capiminor *mp;
|
|
|
|
|
|
|
|
mp = (struct capiminor *)tty->driver_data;
|
|
|
|
if (mp) {
|
|
|
|
if (atomic_dec_and_test(&mp->ttyopencount)) {
|
|
|
|
#ifdef _DEBUG_REFCOUNT
|
|
|
|
printk(KERN_DEBUG "capinc_tty_close lastclose\n");
|
|
|
|
#endif
|
|
|
|
tty->driver_data = NULL;
|
|
|
|
mp->tty = NULL;
|
|
|
|
}
|
|
|
|
#ifdef _DEBUG_REFCOUNT
|
|
|
|
printk(KERN_DEBUG "capinc_tty_close ocount=%d\n", atomic_read(&mp->ttyopencount));
|
|
|
|
#endif
|
2008-04-28 17:14:37 +08:00
|
|
|
if (mp->nccip == NULL)
|
2005-04-17 06:20:36 +08:00
|
|
|
capiminor_free(mp);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef _DEBUG_REFCOUNT
|
|
|
|
printk(KERN_DEBUG "capinc_tty_close\n");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static int capinc_tty_write(struct tty_struct * tty,
|
|
|
|
const unsigned char *buf, int count)
|
|
|
|
{
|
|
|
|
struct capiminor *mp = (struct capiminor *)tty->driver_data;
|
|
|
|
struct sk_buff *skb;
|
2007-02-12 16:53:26 +08:00
|
|
|
unsigned long flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#ifdef _DEBUG_TTYFUNCS
|
|
|
|
printk(KERN_DEBUG "capinc_tty_write(count=%d)\n", count);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!mp || !mp->nccip) {
|
|
|
|
#ifdef _DEBUG_TTYFUNCS
|
|
|
|
printk(KERN_DEBUG "capinc_tty_write: mp or mp->ncci NULL\n");
|
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-02-12 16:53:26 +08:00
|
|
|
spin_lock_irqsave(&workaround_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
skb = mp->ttyskb;
|
|
|
|
if (skb) {
|
|
|
|
mp->ttyskb = NULL;
|
|
|
|
skb_queue_tail(&mp->outqueue, skb);
|
|
|
|
mp->outbytes += skb->len;
|
|
|
|
}
|
|
|
|
|
|
|
|
skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+count, GFP_ATOMIC);
|
|
|
|
if (!skb) {
|
|
|
|
printk(KERN_ERR "capinc_tty_write: alloc_skb failed\n");
|
2007-02-12 16:53:26 +08:00
|
|
|
spin_unlock_irqrestore(&workaround_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
skb_reserve(skb, CAPI_DATA_B3_REQ_LEN);
|
|
|
|
memcpy(skb_put(skb, count), buf, count);
|
|
|
|
|
|
|
|
skb_queue_tail(&mp->outqueue, skb);
|
|
|
|
mp->outbytes += skb->len;
|
|
|
|
(void)handle_minor_send(mp);
|
|
|
|
(void)handle_minor_recv(mp);
|
2007-02-12 16:53:26 +08:00
|
|
|
spin_unlock_irqrestore(&workaround_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2008-04-30 15:54:09 +08:00
|
|
|
static int capinc_tty_put_char(struct tty_struct *tty, unsigned char ch)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct capiminor *mp = (struct capiminor *)tty->driver_data;
|
|
|
|
struct sk_buff *skb;
|
2007-02-12 16:53:26 +08:00
|
|
|
unsigned long flags;
|
2008-04-30 15:54:09 +08:00
|
|
|
int ret = 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#ifdef _DEBUG_TTYFUNCS
|
|
|
|
printk(KERN_DEBUG "capinc_put_char(%u)\n", ch);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!mp || !mp->nccip) {
|
|
|
|
#ifdef _DEBUG_TTYFUNCS
|
|
|
|
printk(KERN_DEBUG "capinc_tty_put_char: mp or mp->ncci NULL\n");
|
|
|
|
#endif
|
2008-04-30 15:54:09 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-02-12 16:53:26 +08:00
|
|
|
spin_lock_irqsave(&workaround_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
skb = mp->ttyskb;
|
|
|
|
if (skb) {
|
|
|
|
if (skb_tailroom(skb) > 0) {
|
|
|
|
*(skb_put(skb, 1)) = ch;
|
2007-02-12 16:53:26 +08:00
|
|
|
spin_unlock_irqrestore(&workaround_lock, flags);
|
2008-04-30 15:54:09 +08:00
|
|
|
return 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
mp->ttyskb = NULL;
|
|
|
|
skb_queue_tail(&mp->outqueue, skb);
|
|
|
|
mp->outbytes += skb->len;
|
|
|
|
(void)handle_minor_send(mp);
|
|
|
|
}
|
|
|
|
skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+CAPI_MAX_BLKSIZE, GFP_ATOMIC);
|
|
|
|
if (skb) {
|
|
|
|
skb_reserve(skb, CAPI_DATA_B3_REQ_LEN);
|
|
|
|
*(skb_put(skb, 1)) = ch;
|
|
|
|
mp->ttyskb = skb;
|
|
|
|
} else {
|
|
|
|
printk(KERN_ERR "capinc_put_char: char %u lost\n", ch);
|
2008-04-30 15:54:09 +08:00
|
|
|
ret = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2007-02-12 16:53:26 +08:00
|
|
|
spin_unlock_irqrestore(&workaround_lock, flags);
|
2008-04-30 15:54:09 +08:00
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void capinc_tty_flush_chars(struct tty_struct *tty)
|
|
|
|
{
|
|
|
|
struct capiminor *mp = (struct capiminor *)tty->driver_data;
|
|
|
|
struct sk_buff *skb;
|
2007-02-12 16:53:26 +08:00
|
|
|
unsigned long flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#ifdef _DEBUG_TTYFUNCS
|
|
|
|
printk(KERN_DEBUG "capinc_tty_flush_chars\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!mp || !mp->nccip) {
|
|
|
|
#ifdef _DEBUG_TTYFUNCS
|
|
|
|
printk(KERN_DEBUG "capinc_tty_flush_chars: mp or mp->ncci NULL\n");
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-02-12 16:53:26 +08:00
|
|
|
spin_lock_irqsave(&workaround_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
skb = mp->ttyskb;
|
|
|
|
if (skb) {
|
|
|
|
mp->ttyskb = NULL;
|
|
|
|
skb_queue_tail(&mp->outqueue, skb);
|
|
|
|
mp->outbytes += skb->len;
|
|
|
|
(void)handle_minor_send(mp);
|
|
|
|
}
|
|
|
|
(void)handle_minor_recv(mp);
|
2007-02-12 16:53:26 +08:00
|
|
|
spin_unlock_irqrestore(&workaround_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int capinc_tty_write_room(struct tty_struct *tty)
|
|
|
|
{
|
|
|
|
struct capiminor *mp = (struct capiminor *)tty->driver_data;
|
|
|
|
int room;
|
|
|
|
if (!mp || !mp->nccip) {
|
|
|
|
#ifdef _DEBUG_TTYFUNCS
|
|
|
|
printk(KERN_DEBUG "capinc_tty_write_room: mp or mp->ncci NULL\n");
|
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
room = CAPINC_MAX_SENDQUEUE-skb_queue_len(&mp->outqueue);
|
|
|
|
room *= CAPI_MAX_BLKSIZE;
|
|
|
|
#ifdef _DEBUG_TTYFUNCS
|
|
|
|
printk(KERN_DEBUG "capinc_tty_write_room = %d\n", room);
|
|
|
|
#endif
|
|
|
|
return room;
|
|
|
|
}
|
|
|
|
|
2005-05-01 23:59:29 +08:00
|
|
|
static int capinc_tty_chars_in_buffer(struct tty_struct *tty)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct capiminor *mp = (struct capiminor *)tty->driver_data;
|
|
|
|
if (!mp || !mp->nccip) {
|
|
|
|
#ifdef _DEBUG_TTYFUNCS
|
|
|
|
printk(KERN_DEBUG "capinc_tty_chars_in_buffer: mp or mp->ncci NULL\n");
|
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#ifdef _DEBUG_TTYFUNCS
|
|
|
|
printk(KERN_DEBUG "capinc_tty_chars_in_buffer = %d nack=%d sq=%d rq=%d\n",
|
|
|
|
mp->outbytes, mp->nack,
|
|
|
|
skb_queue_len(&mp->outqueue),
|
|
|
|
skb_queue_len(&mp->inqueue));
|
|
|
|
#endif
|
|
|
|
return mp->outbytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int capinc_tty_ioctl(struct tty_struct *tty, struct file * file,
|
|
|
|
unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
|
|
|
int error = 0;
|
|
|
|
switch (cmd) {
|
|
|
|
default:
|
|
|
|
error = n_tty_ioctl (tty, file, cmd, arg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2006-12-08 18:38:45 +08:00
|
|
|
static void capinc_tty_set_termios(struct tty_struct *tty, struct ktermios * old)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
#ifdef _DEBUG_TTYFUNCS
|
|
|
|
printk(KERN_DEBUG "capinc_tty_set_termios\n");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void capinc_tty_throttle(struct tty_struct * tty)
|
|
|
|
{
|
|
|
|
struct capiminor *mp = (struct capiminor *)tty->driver_data;
|
|
|
|
#ifdef _DEBUG_TTYFUNCS
|
|
|
|
printk(KERN_DEBUG "capinc_tty_throttle\n");
|
|
|
|
#endif
|
|
|
|
if (mp)
|
|
|
|
mp->ttyinstop = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void capinc_tty_unthrottle(struct tty_struct * tty)
|
|
|
|
{
|
|
|
|
struct capiminor *mp = (struct capiminor *)tty->driver_data;
|
2007-02-12 16:53:26 +08:00
|
|
|
unsigned long flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
#ifdef _DEBUG_TTYFUNCS
|
|
|
|
printk(KERN_DEBUG "capinc_tty_unthrottle\n");
|
|
|
|
#endif
|
|
|
|
if (mp) {
|
2007-02-12 16:53:26 +08:00
|
|
|
spin_lock_irqsave(&workaround_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
mp->ttyinstop = 0;
|
|
|
|
handle_minor_recv(mp);
|
2007-02-12 16:53:26 +08:00
|
|
|
spin_unlock_irqrestore(&workaround_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void capinc_tty_stop(struct tty_struct *tty)
|
|
|
|
{
|
|
|
|
struct capiminor *mp = (struct capiminor *)tty->driver_data;
|
|
|
|
#ifdef _DEBUG_TTYFUNCS
|
|
|
|
printk(KERN_DEBUG "capinc_tty_stop\n");
|
|
|
|
#endif
|
|
|
|
if (mp) {
|
|
|
|
mp->ttyoutstop = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void capinc_tty_start(struct tty_struct *tty)
|
|
|
|
{
|
|
|
|
struct capiminor *mp = (struct capiminor *)tty->driver_data;
|
2007-02-12 16:53:26 +08:00
|
|
|
unsigned long flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
#ifdef _DEBUG_TTYFUNCS
|
|
|
|
printk(KERN_DEBUG "capinc_tty_start\n");
|
|
|
|
#endif
|
|
|
|
if (mp) {
|
2007-02-12 16:53:26 +08:00
|
|
|
spin_lock_irqsave(&workaround_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
mp->ttyoutstop = 0;
|
|
|
|
(void)handle_minor_send(mp);
|
2007-02-12 16:53:26 +08:00
|
|
|
spin_unlock_irqrestore(&workaround_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void capinc_tty_hangup(struct tty_struct *tty)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG_TTYFUNCS
|
|
|
|
printk(KERN_DEBUG "capinc_tty_hangup\n");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2008-07-22 18:18:03 +08:00
|
|
|
static int capinc_tty_break_ctl(struct tty_struct *tty, int state)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
#ifdef _DEBUG_TTYFUNCS
|
|
|
|
printk(KERN_DEBUG "capinc_tty_break_ctl(%d)\n", state);
|
|
|
|
#endif
|
2008-07-22 18:18:03 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void capinc_tty_flush_buffer(struct tty_struct *tty)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG_TTYFUNCS
|
|
|
|
printk(KERN_DEBUG "capinc_tty_flush_buffer\n");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void capinc_tty_set_ldisc(struct tty_struct *tty)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG_TTYFUNCS
|
|
|
|
printk(KERN_DEBUG "capinc_tty_set_ldisc\n");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void capinc_tty_send_xchar(struct tty_struct *tty, char ch)
|
|
|
|
{
|
|
|
|
#ifdef _DEBUG_TTYFUNCS
|
|
|
|
printk(KERN_DEBUG "capinc_tty_send_xchar(%d)\n", ch);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static int capinc_tty_read_proc(char *page, char **start, off_t off,
|
|
|
|
int count, int *eof, void *data)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct tty_driver *capinc_tty_driver;
|
|
|
|
|
2006-10-02 17:17:18 +08:00
|
|
|
static const struct tty_operations capinc_ops = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.open = capinc_tty_open,
|
|
|
|
.close = capinc_tty_close,
|
|
|
|
.write = capinc_tty_write,
|
|
|
|
.put_char = capinc_tty_put_char,
|
|
|
|
.flush_chars = capinc_tty_flush_chars,
|
|
|
|
.write_room = capinc_tty_write_room,
|
|
|
|
.chars_in_buffer = capinc_tty_chars_in_buffer,
|
|
|
|
.ioctl = capinc_tty_ioctl,
|
|
|
|
.set_termios = capinc_tty_set_termios,
|
|
|
|
.throttle = capinc_tty_throttle,
|
|
|
|
.unthrottle = capinc_tty_unthrottle,
|
|
|
|
.stop = capinc_tty_stop,
|
|
|
|
.start = capinc_tty_start,
|
|
|
|
.hangup = capinc_tty_hangup,
|
|
|
|
.break_ctl = capinc_tty_break_ctl,
|
|
|
|
.flush_buffer = capinc_tty_flush_buffer,
|
|
|
|
.set_ldisc = capinc_tty_set_ldisc,
|
|
|
|
.send_xchar = capinc_tty_send_xchar,
|
|
|
|
.read_proc = capinc_tty_read_proc,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int capinc_tty_init(void)
|
|
|
|
{
|
|
|
|
struct tty_driver *drv;
|
|
|
|
|
|
|
|
if (capi_ttyminors > CAPINC_MAX_PORTS)
|
|
|
|
capi_ttyminors = CAPINC_MAX_PORTS;
|
|
|
|
if (capi_ttyminors <= 0)
|
|
|
|
capi_ttyminors = CAPINC_NR_PORTS;
|
|
|
|
|
|
|
|
drv = alloc_tty_driver(capi_ttyminors);
|
|
|
|
if (!drv)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
drv->owner = THIS_MODULE;
|
|
|
|
drv->driver_name = "capi_nc";
|
|
|
|
drv->name = "capi";
|
|
|
|
drv->major = capi_ttymajor;
|
|
|
|
drv->minor_start = 0;
|
|
|
|
drv->type = TTY_DRIVER_TYPE_SERIAL;
|
|
|
|
drv->subtype = SERIAL_TYPE_NORMAL;
|
|
|
|
drv->init_termios = tty_std_termios;
|
|
|
|
drv->init_termios.c_iflag = ICRNL;
|
|
|
|
drv->init_termios.c_oflag = OPOST | ONLCR;
|
|
|
|
drv->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
|
|
|
|
drv->init_termios.c_lflag = 0;
|
|
|
|
drv->flags = TTY_DRIVER_REAL_RAW|TTY_DRIVER_RESET_TERMIOS;
|
|
|
|
tty_set_operations(drv, &capinc_ops);
|
|
|
|
if (tty_register_driver(drv)) {
|
|
|
|
put_tty_driver(drv);
|
|
|
|
printk(KERN_ERR "Couldn't register capi_nc driver\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
capinc_tty_driver = drv;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void capinc_tty_exit(void)
|
|
|
|
{
|
|
|
|
struct tty_driver *drv = capinc_tty_driver;
|
|
|
|
int retval;
|
|
|
|
if ((retval = tty_unregister_driver(drv)))
|
|
|
|
printk(KERN_ERR "capi: failed to unregister capi_nc driver (%d)\n", retval);
|
|
|
|
put_tty_driver(drv);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
|
|
|
|
|
|
|
|
/* -------- /proc functions ----------------------------------------- */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* /proc/capi/capi20:
|
|
|
|
* minor applid nrecvctlpkt nrecvdatapkt nsendctlpkt nsenddatapkt
|
|
|
|
*/
|
|
|
|
static int proc_capidev_read_proc(char *page, char **start, off_t off,
|
|
|
|
int count, int *eof, void *data)
|
|
|
|
{
|
|
|
|
struct capidev *cdev;
|
|
|
|
struct list_head *l;
|
|
|
|
int len = 0;
|
|
|
|
|
|
|
|
read_lock(&capidev_list_lock);
|
|
|
|
list_for_each(l, &capidev_list) {
|
|
|
|
cdev = list_entry(l, struct capidev, list);
|
|
|
|
len += sprintf(page+len, "0 %d %lu %lu %lu %lu\n",
|
|
|
|
cdev->ap.applid,
|
|
|
|
cdev->ap.nrecvctlpkt,
|
|
|
|
cdev->ap.nrecvdatapkt,
|
|
|
|
cdev->ap.nsentctlpkt,
|
|
|
|
cdev->ap.nsentdatapkt);
|
|
|
|
if (len <= off) {
|
|
|
|
off -= len;
|
|
|
|
len = 0;
|
|
|
|
} else {
|
|
|
|
if (len-off > count)
|
|
|
|
goto endloop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
endloop:
|
|
|
|
read_unlock(&capidev_list_lock);
|
|
|
|
if (len < count)
|
|
|
|
*eof = 1;
|
|
|
|
if (len > count) len = count;
|
|
|
|
if (len < 0) len = 0;
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* /proc/capi/capi20ncci:
|
|
|
|
* applid ncci
|
|
|
|
*/
|
|
|
|
static int proc_capincci_read_proc(char *page, char **start, off_t off,
|
|
|
|
int count, int *eof, void *data)
|
|
|
|
{
|
|
|
|
struct capidev *cdev;
|
|
|
|
struct capincci *np;
|
|
|
|
struct list_head *l;
|
|
|
|
int len = 0;
|
|
|
|
|
|
|
|
read_lock(&capidev_list_lock);
|
|
|
|
list_for_each(l, &capidev_list) {
|
|
|
|
cdev = list_entry(l, struct capidev, list);
|
|
|
|
for (np=cdev->nccis; np; np = np->next) {
|
|
|
|
len += sprintf(page+len, "%d 0x%x\n",
|
|
|
|
cdev->ap.applid,
|
|
|
|
np->ncci);
|
|
|
|
if (len <= off) {
|
|
|
|
off -= len;
|
|
|
|
len = 0;
|
|
|
|
} else {
|
|
|
|
if (len-off > count)
|
|
|
|
goto endloop;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
endloop:
|
|
|
|
read_unlock(&capidev_list_lock);
|
|
|
|
*start = page+off;
|
|
|
|
if (len < count)
|
|
|
|
*eof = 1;
|
|
|
|
if (len>count) len = count;
|
|
|
|
if (len<0) len = 0;
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct procfsentries {
|
|
|
|
char *name;
|
|
|
|
mode_t mode;
|
|
|
|
int (*read_proc)(char *page, char **start, off_t off,
|
|
|
|
int count, int *eof, void *data);
|
|
|
|
struct proc_dir_entry *procent;
|
|
|
|
} procfsentries[] = {
|
|
|
|
/* { "capi", S_IFDIR, 0 }, */
|
|
|
|
{ "capi/capi20", 0 , proc_capidev_read_proc },
|
|
|
|
{ "capi/capi20ncci", 0 , proc_capincci_read_proc },
|
|
|
|
};
|
|
|
|
|
|
|
|
static void __init proc_init(void)
|
|
|
|
{
|
2007-02-12 16:53:19 +08:00
|
|
|
int nelem = ARRAY_SIZE(procfsentries);
|
2005-04-17 06:20:36 +08:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i=0; i < nelem; i++) {
|
|
|
|
struct procfsentries *p = procfsentries + i;
|
|
|
|
p->procent = create_proc_entry(p->name, p->mode, NULL);
|
|
|
|
if (p->procent) p->procent->read_proc = p->read_proc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit proc_exit(void)
|
|
|
|
{
|
2007-02-12 16:53:19 +08:00
|
|
|
int nelem = ARRAY_SIZE(procfsentries);
|
2005-04-17 06:20:36 +08:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i=nelem-1; i >= 0; i--) {
|
|
|
|
struct procfsentries *p = procfsentries + i;
|
|
|
|
if (p->procent) {
|
|
|
|
remove_proc_entry(p->name, NULL);
|
|
|
|
p->procent = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -------- init function and module interface ---------------------- */
|
|
|
|
|
|
|
|
|
|
|
|
static char rev[32];
|
|
|
|
|
|
|
|
static int __init capi_init(void)
|
|
|
|
{
|
|
|
|
char *p;
|
|
|
|
char *compileinfo;
|
2006-03-28 17:56:19 +08:00
|
|
|
int major_ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-04-28 17:14:37 +08:00
|
|
|
if ((p = strchr(revision, ':')) != NULL && p[1]) {
|
2005-04-17 06:20:36 +08:00
|
|
|
strlcpy(rev, p + 2, sizeof(rev));
|
2008-04-28 17:14:37 +08:00
|
|
|
if ((p = strchr(rev, '$')) != NULL && p > rev)
|
2005-04-17 06:20:36 +08:00
|
|
|
*(p-1) = 0;
|
|
|
|
} else
|
|
|
|
strcpy(rev, "1.0");
|
|
|
|
|
2006-03-28 17:56:19 +08:00
|
|
|
major_ret = register_chrdev(capi_major, "capi20", &capi_fops);
|
|
|
|
if (major_ret < 0) {
|
2005-04-17 06:20:36 +08:00
|
|
|
printk(KERN_ERR "capi20: unable to get major %d\n", capi_major);
|
2006-03-28 17:56:19 +08:00
|
|
|
return major_ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2005-03-24 02:01:41 +08:00
|
|
|
capi_class = class_create(THIS_MODULE, "capi");
|
2005-04-17 06:20:36 +08:00
|
|
|
if (IS_ERR(capi_class)) {
|
|
|
|
unregister_chrdev(capi_major, "capi20");
|
|
|
|
return PTR_ERR(capi_class);
|
|
|
|
}
|
|
|
|
|
2008-05-22 03:52:33 +08:00
|
|
|
device_create_drvdata(capi_class, NULL, MKDEV(capi_major, 0), NULL,
|
|
|
|
"capi");
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
|
|
|
|
if (capinc_tty_init() < 0) {
|
2007-09-25 08:03:03 +08:00
|
|
|
device_destroy(capi_class, MKDEV(capi_major, 0));
|
2005-03-24 02:01:41 +08:00
|
|
|
class_destroy(capi_class);
|
2005-04-17 06:20:36 +08:00
|
|
|
unregister_chrdev(capi_major, "capi20");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
|
|
|
|
|
|
|
|
proc_init();
|
|
|
|
|
|
|
|
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
|
|
|
|
#if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE)
|
|
|
|
compileinfo = " (middleware+capifs)";
|
|
|
|
#else
|
|
|
|
compileinfo = " (no capifs)";
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
compileinfo = " (no middleware)";
|
|
|
|
#endif
|
|
|
|
printk(KERN_NOTICE "capi20: Rev %s: started up with major %d%s\n",
|
|
|
|
rev, capi_major, compileinfo);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit capi_exit(void)
|
|
|
|
{
|
|
|
|
proc_exit();
|
|
|
|
|
2007-09-25 08:03:03 +08:00
|
|
|
device_destroy(capi_class, MKDEV(capi_major, 0));
|
2005-03-24 02:01:41 +08:00
|
|
|
class_destroy(capi_class);
|
2005-04-17 06:20:36 +08:00
|
|
|
unregister_chrdev(capi_major, "capi20");
|
|
|
|
|
|
|
|
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
|
|
|
|
capinc_tty_exit();
|
|
|
|
#endif
|
|
|
|
printk(KERN_NOTICE "capi: Rev %s: unloaded\n", rev);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(capi_init);
|
|
|
|
module_exit(capi_exit);
|