2003-03-24 15:25:28

by Stelian Pop

[permalink] [raw]
Subject: pcmcia_bus_type changes cause oops...

Hi,

The recent changes in 2.5.65-BK broke my pcmcia network driver.
It smells like a synchronisation and locking issue between the
pcmcia / ds / pcnet_cs modules loading and initialisation.

This is reproducible 99%, in 1% of the cases timing gets slightly
modified and I get a succesful loading, see below.

I've enabled debugging in lib/kobject.c and here is what I get:

---------8<---------------------------------------8<-----------
Linux Kernel Card Services 3.1.22
options: [pci] [cardbus] [pm]
subsystem pcmcia_socket: registering
kobject pcmcia_socket: registering. parent: <NULL>, set: class
kobject devices: registering. parent: pcmcia_socket, set: <NULL>
kobject drivers: registering. parent: pcmcia_socket, set: <NULL>
subsystem pcmcia: registering
kobject pcmcia: registering. parent: <NULL>, set: bus
kobject devices: registering. parent: pcmcia, set: <NULL>
kobject drivers: registering. parent: pcmcia, set: <NULL>
ds: no socket drivers loaded!
pcnet_cs: Unknown symbol pcmcia_unregister_driver
pcnet_cs: Unknown symbol pcmcia_register_driver
kobject cardbus: registering. parent: <NULL>, set: drivers
Yenta IRQ list 0cb8, PCI irq9
Socket status: 30000006
subsystem pcmcia: registering
kobject pcmcia: registering. parent: <NULL>, set: bus
Unable to handle kernel paging request at virtual address c7980928
printing eip:
c01b8748
*pde = 01171067
*pte = 00000000
Oops: 0002 [#1]
CPU: 0
EIP: 0060:[<c01b8748>] Not tainted
EFLAGS: 00010282
EIP is at kobject_add+0x108/0x120
eax: c03123a8 ebx: c03123a0 ecx: c7980928 edx: c797b928
esi: c03123dc edi: c797b914 ebp: c6397f50 esp: c6397f30
ds: 007b es: 007b ss: 0068
Process modprobe (pid: 682, threadinfo=c6396000 task=c6ab06c0)
Stack: c029b6f4 00000042 c02ac63a c03123b0 00000000 c797b904 c797b904 c797b924
c6397f64 c01b8aff c797b914 c797b914 c797b900 c6397f84 c0212909 c797b904
0000001d 0000000d c797b9e0 c02debd8 c02debd8 c6397f90 c7951012 c797b900
Call Trace:
[<c797b904>] pcmcia_bus_type+0x4/0xe0 [ds]
[<c797b904>] pcmcia_bus_type+0x4/0xe0 [ds]
[<c797b924>] pcmcia_bus_type+0x24/0xe0 [ds]
[<c01b8aff>] subsystem_register+0x2f/0x50
[<c797b914>] pcmcia_bus_type+0x14/0xe0 [ds]
[<c797b914>] pcmcia_bus_type+0x14/0xe0 [ds]
[<c797b900>] pcmcia_bus_type+0x0/0xe0 [ds]
[<c0212909>] bus_register+0x39/0xb0
[<c797b904>] pcmcia_bus_type+0x4/0xe0 [ds]
[<c797b9e0>] +0x0/0x100 [ds]
[<c7951012>] +0x12/0x20 [ds]
[<c797b900>] pcmcia_bus_type+0x0/0xe0 [ds]
[<c7951248>] init_pcmcia_module+0x8/0xe [ds]
[<c0135c5a>] sys_init_module+0x15a/0x240
[<c010b25b>] syscall_call+0x7/0xb

Code: 89 11 89 4a 04 8b 47 20 83 c0 10 89 04 24 e8 e5 00 00 00 89
---------8<---------------------------------------8<-----------

Under some circumstances modifying slightly the timings (here it
was a forced fsck on /), all loads corectly, as shown below:

---------8<---------------------------------------8<-----------
Linux Kernel Card Services 3.1.22
options: [pci] [cardbus] [pm]
subsystem pcmcia_socket: registering
kobject pcmcia_socket: registering. parent: <NULL>, set: class
kobject devices: registering. parent: pcmcia_socket, set: <NULL>
kobject drivers: registering. parent: pcmcia_socket, set: <NULL>
subsystem pcmcia: registering
kobject pcmcia: registering. parent: <NULL>, set: bus
kobject devices: registering. parent: pcmcia, set: <NULL>
kobject drivers: registering. parent: pcmcia, set: <NULL>
ds: no socket drivers loaded!
pcnet_cs: Unknown symbol pcmcia_unregister_driver
pcnet_cs: Unknown symbol pcmcia_register_driver
kobject cardbus: registering. parent: <NULL>, set: drivers
Yenta IRQ list 0cb8, PCI irq9
Socket status: 30000419
subsystem pcmcia: registering
kobject pcmcia: registering. parent: <NULL>, set: bus
kobject devices: registering. parent: pcmcia, set: <NULL>
kobject drivers: registering. parent: pcmcia, set: <NULL>
cs: IO port probe 0x0c00-0x0cff: clean.
cs: IO port probe 0x0100-0x04ff: excluding 0x170-0x177 0x370-0x377 0x3c0-0x3df 0x4d0-0x4d7
cs: IO port probe 0x0a00-0x0aff: clean.
cs: memory probe 0xa0000000-0xa0ffffff: clean.
kobject pcnet_cs: registering. parent: <NULL>, set: drivers
kobject eth0: registering. parent: <NULL>, set: net
eth0: NE2000 Compatible: io 0x300, irq 3, hw_addr 00:50:01:00:38:DE
Module pcnet_cs cannot be unloaded due to unsafe usage in include/linux/module.h:432
---------8<---------------------------------------8<-----------

Need more details ?

Thanks,

Stelian.
--
Stelian Pop <[email protected]>
Alcove - http://www.alcove.com


2003-03-24 16:14:57

by Dominik Brodowski

[permalink] [raw]
Subject: Re: pcmcia_bus_type changes cause oops...

Hi Stelian,

Thanks for your bug report. Let me analyze it a bit:

> ds: no socket drivers loaded!

ds.o is loaded for the first time, but fails as the yenta-socket driver was
not loaded before

> pcnet_cs: Unknown symbol pcmcia_unregister_driver
> pcnet_cs: Unknown symbol pcmcia_register_driver

This is strange. There is an EXPORT_SYMBOL(pcmcia_register_driver) ...
maybe gets away after a "make clean"... not worriesome, though; as the
loading continues.

> kobject pcmcia: registering. parent: <NULL>, set: bus
ds.o is loaded for the second time; and pcmcia_bus_type is to be added
again. Due to a bug in ds.c it didn't get unregistered in the failed loading
of ds.o - the attached patch should fix it. Could you please try it?

It is strange, though, that you nonetheless managed to get it to work with
"inadvertedly modified" timings...

Thanks,

Dominik

--- linux/drivers/pcmcia/ds.c.original 2003-03-24 17:23:29.000000000 +0100
+++ linux/drivers/pcmcia/ds.c 2003-03-24 17:14:10.000000000 +0100
@@ -920,16 +920,16 @@
pcmcia_get_card_services_info(&serv);
if (serv.Revision != CS_RELEASE_CODE) {
printk(KERN_NOTICE "ds: Card Services release does not match!\n");
- return -1;
+ goto error;
}
if (serv.Count == 0) {
printk(KERN_NOTICE "ds: no socket drivers loaded!\n");
- return -1;
+ goto error;
}

sockets = serv.Count;
socket_table = kmalloc(sockets*sizeof(socket_info_t), GFP_KERNEL);
- if (!socket_table) return -1;
+ if (!socket_table) goto error;
for (i = 0, s = socket_table; i < sockets; i++, s++) {
s->state = 0;
s->user = NULL;
@@ -984,6 +984,9 @@
init_status = 0;
#endif
return 0;
+ error:
+ bus_unregister(&pcmcia_bus_type);
+ return 1;
}


2003-03-24 16:26:18

by Stelian Pop

[permalink] [raw]
Subject: Re: pcmcia_bus_type changes cause oops...

On Mon, Mar 24, 2003 at 05:25:19PM +0100, Dominik Brodowski wrote:

> Hi Stelian,
>
> Thanks for your bug report. Let me analyze it a bit:
>
> > ds: no socket drivers loaded!
>
> ds.o is loaded for the first time, but fails as the yenta-socket driver was
> not loaded before
>
> > pcnet_cs: Unknown symbol pcmcia_unregister_driver
> > pcnet_cs: Unknown symbol pcmcia_register_driver
>
> This is strange. There is an EXPORT_SYMBOL(pcmcia_register_driver) ...
> maybe gets away after a "make clean"... not worriesome, though; as the
> loading continues.

I did a make mrproper before compiling it. I could do it again but
it would take some time. But I agree it is strance.

> > kobject pcmcia: registering. parent: <NULL>, set: bus
> ds.o is loaded for the second time; and pcmcia_bus_type is to be added
> again. Due to a bug in ds.c it didn't get unregistered in the failed loading
> of ds.o - the attached patch should fix it. Could you please try it?

It does something for sure, but not enough to get it work:

------------------8<-------------------------8<----------------
Linux Kernel Card Services 3.1.22
options: [pci] [cardbus] [pm]
subsystem pcmcia_socket: registering
kobject pcmcia_socket: registering. parent: <NULL>, set: class
kobject devices: registering. parent: pcmcia_socket, set: <NULL>
kobject drivers: registering. parent: pcmcia_socket, set: <NULL>
subsystem pcmcia: registering
kobject pcmcia: registering. parent: <NULL>, set: bus
kobject devices: registering. parent: pcmcia, set: <NULL>
kobject drivers: registering. parent: pcmcia, set: <NULL>
ds: no socket drivers loaded!
kobject drivers: unregistering
kobject drivers: cleaning up
kobject devices: unregistering
kobject devices: cleaning up
subsystem pcmcia: unregistering
kobject pcmcia: unregistering
kobject pcmcia: cleaning up
Unable to handle kernel NULL pointer dereference at virtual address 00000068
printing eip:
c01b8855
*pde = 00000000
Oops: 0000 [#1]
CPU: 0
EIP: 0060:[<c01b8855>] Not tainted
EFLAGS: 00010202
EIP is at kobject_get+0x15/0x50
eax: c5ca0000 ebx: 00000058 ecx: 00000000 edx: c79a5c94
esi: c79a47b8 edi: c79a5cc0 ebp: c5ca1f00 esp: c5ca1efc
ds: 007b es: 007b ss: 0068
Process modprobe (pid: 603, threadinfo=c5ca0000 task=c6a8c6c0)
Stack: c79a5cb0 c5ca1f10 c01b857e 00000058 c79a5cb0 c5ca1f2c c01b8778 c79a5cb0
c79a5d00 c5ca1f2c c021286f c79a5cb0 c5ca1f4c c0212697 c79a5cb0 00000000
fffffffc c79a5d00 c02debd8 c02debd8 c5ca1f68 c0212ba1 c79a5c94 00000000
Call Trace:
[<c79a5cb0>] pcnet_driver+0x30/0x80 [pcnet_cs]
[<c01b857e>] kobject_init+0x2e/0x50
[<c79a5cb0>] pcnet_driver+0x30/0x80 [pcnet_cs]
[<c01b8778>] kobject_register+0x18/0x70
[<c79a5cb0>] pcnet_driver+0x30/0x80 [pcnet_cs]
[<c79a5d00>] +0x0/0x100 [pcnet_cs]
[<c021286f>] get_bus+0x1f/0x40
[<c79a5cb0>] pcnet_driver+0x30/0x80 [pcnet_cs]
[<c0212697>] bus_add_driver+0x57/0xf0
[<c79a5cb0>] pcnet_driver+0x30/0x80 [pcnet_cs]
[<c79a5d00>] +0x0/0x100 [pcnet_cs]
[<c0212ba1>] driver_register+0x31/0x40
[<c79a5c94>] pcnet_driver+0x14/0x80 [pcnet_cs]
[<c79a5d00>] +0x0/0x100 [pcnet_cs]
[<c7951043>] +0x43/0x47 [pcnet_cs]
[<c79a5c94>] pcnet_driver+0x14/0x80 [pcnet_cs]
[<c79941c0>] +0x1e0/0x3a0 [pcmcia_core]
[<c0135c5a>] sys_init_module+0x15a/0x240
[<c010b25b>] syscall_call+0x7/0xb

Code: 8b 43 10 85 c0 7e 24 ff 43 10 b8 00 e0 ff ff 21 e0 ff 48 14
<6>note: modprobe[603] exited with preempt_count 1
kobject cardbus: registering. parent: <NULL>, set: drivers
Yenta IRQ list 0cb8, PCI irq9
Socket status: 30000419
------------------8<-------------------------8<----------------
>
> It is strange, though, that you nonetheless managed to get it to work with
> "inadvertedly modified" timings...

The bug is probably somewhere else, I guess...

Thanks,

Stelian.

--
Stelian Pop <[email protected]>
Alcove - http://www.alcove.com

2003-03-24 17:15:57

by Stelian Pop

[permalink] [raw]
Subject: Re: pcmcia_bus_type changes cause oops...

On Mon, Mar 24, 2003 at 06:17:03PM +0100, Dominik Brodowski wrote:

> OK, understanding more and more of the problem:
>
> Firstly, though: is all compiled as modules?

Yes indeed.

> If not, it's really strange, as
> I use the same driver locally, but built into the kernel. This is what I'd
> recommend for cs.o, ds.o and pci-socket.o/yenta.o anyways.
[...]

> So, I'd suggest you do one of these two things:
> a) modify the process done by modprobe to load the modules in the following
> order:
> - cs.ko

cs.ko doesn't exist, it is part of pcmcia_core here.

> - pci-socket.ko/yenta.ko
> - ds.ko
> - pcnet_cs.ko

Actually this is using the RedHat standard way: /etc/init.d/pcmcia
loads, in order pcmcia_core, yenta.

pcnet_cs get loaded by hotplug, triggered by one of the above modules.

So it seems that what you want is that I disable hotplugging and
loading pcnet_cs by hand later... Hmmm.

> _or_ b) compile cs.ko / pci-socket.ko/yenta.ko into the kernel
>
> until I get a proper fix for this problem.

Sure, I'll try that.

> The requirement of having to load
> the socket drivers before ds.ko will be removed soon... that patch is
> already in my queue somewhere (and will remove the issue you're seeing
> almost surely), but depends on some other stuff. But maybe I can split the
> decisive things out sooner, let's see...

Stelian.
--
Stelian Pop <[email protected]>
Alcove - http://www.alcove.com

2003-03-24 17:08:16

by Dominik Brodowski

[permalink] [raw]
Subject: Re: pcmcia_bus_type changes cause oops...

OK, understanding more and more of the problem:

Firstly, though: is all compiled as modules? If not, it's really strange, as
I use the same driver locally, but built into the kernel. This is what I'd
recommend for cs.o, ds.o and pci-socket.o/yenta.o anyways.

On Mon, Mar 24, 2003 at 05:37:44PM +0100, Stelian Pop wrote:
> On Mon, Mar 24, 2003 at 05:25:19PM +0100, Dominik Brodowski wrote:
>
> > Hi Stelian,
> >
> > Thanks for your bug report. Let me analyze it a bit:
> >
> > > ds: no socket drivers loaded!
> >
> > ds.o is loaded for the first time, but fails as the yenta-socket driver was
> > not loaded before
> >
> > > pcnet_cs: Unknown symbol pcmcia_unregister_driver
> > > pcnet_cs: Unknown symbol pcmcia_register_driver
> >
> > This is strange. There is an EXPORT_SYMBOL(pcmcia_register_driver) ...
> > maybe gets away after a "make clean"... not worriesome, though; as the
> > loading continues.
>
> I did a make mrproper before compiling it. I could do it again but
> it would take some time. But I agree it is strance.

[CC'ed Russell on this 'cause its partly a module loading problem.]

After further reflection, it isn't strange at all: Module initialization of
ds.ko failed, so pcmcia_register_driver shouldn't be available to other
modules. Somehow, though, pcnet_cs DOES get access to it. Strrrange...

> Linux Kernel Card Services 3.1.22
> options: [pci] [cardbus] [pm]
> subsystem pcmcia_socket: registering
> kobject pcmcia_socket: registering. parent: <NULL>, set: class
> kobject devices: registering. parent: pcmcia_socket, set: <NULL>
> kobject drivers: registering. parent: pcmcia_socket, set: <NULL>
> subsystem pcmcia: registering
> kobject pcmcia: registering. parent: <NULL>, set: bus
> kobject devices: registering. parent: pcmcia, set: <NULL>
> kobject drivers: registering. parent: pcmcia, set: <NULL>
> ds: no socket drivers loaded!
> kobject drivers: unregistering
> kobject drivers: cleaning up
> kobject devices: unregistering
> kobject devices: cleaning up
> subsystem pcmcia: unregistering
> kobject pcmcia: unregistering
> kobject pcmcia: cleaning up

At this stage ds.ko should not be available to anyone. trying to load
pcnet_cs.ko should fail with unresolved symbols. Of course, as ds.ko doesn't
really exist any more, we get into trouble....

> Unable to handle kernel NULL pointer dereference at virtual address 00000068
> printing eip:
> [<c79a5cb0>] pcnet_driver+0x30/0x80 [pcnet_cs]
> [<c01b857e>] kobject_init+0x2e/0x50
> [<c79a5cb0>] pcnet_driver+0x30/0x80 [pcnet_cs]
> [<c01b8778>] kobject_register+0x18/0x70
> [<c79a5cb0>] pcnet_driver+0x30/0x80 [pcnet_cs]
> [<c79a5d00>] +0x0/0x100 [pcnet_cs]
> [<c021286f>] get_bus+0x1f/0x40
> [<c79a5cb0>] pcnet_driver+0x30/0x80 [pcnet_cs]
> [<c0212697>] bus_add_driver+0x57/0xf0
> [<c79a5cb0>] pcnet_driver+0x30/0x80 [pcnet_cs]
> [<c79a5d00>] +0x0/0x100 [pcnet_cs]
> [<c0212ba1>] driver_register+0x31/0x40
> [<c79a5c94>] pcnet_driver+0x14/0x80 [pcnet_cs]
> [<c79a5d00>] +0x0/0x100 [pcnet_cs]
> [<c7951043>] +0x43/0x47 [pcnet_cs]
> [<c79a5c94>] pcnet_driver+0x14/0x80 [pcnet_cs]
> [<c79941c0>] +0x1e0/0x3a0 [pcmcia_core]
> [<c0135c5a>] sys_init_module+0x15a/0x240
> [<c010b25b>] syscall_call+0x7/0xb
>
> Code: 8b 43 10 85 c0 7e 24 ff 43 10 b8 00 e0 ff ff 21 e0 ff 48 14
> <6>note: modprobe[603] exited with preempt_count 1
> kobject cardbus: registering. parent: <NULL>, set: drivers
> Yenta IRQ list 0cb8, PCI irq9
> Socket status: 30000419
> ------------------8<-------------------------8<----------------

So, I'd suggest you do one of these two things:
a) modify the process done by modprobe to load the modules in the following
order:
- cs.ko
- pci-socket.ko/yenta.ko
- ds.ko
- pcnet_cs.ko

_or_ b) compile cs.ko / pci-socket.ko/yenta.ko into the kernel

until I get a proper fix for this problem. The requirement of having to load
the socket drivers before ds.ko will be removed soon... that patch is
already in my queue somewhere (and will remove the issue you're seeing
almost surely), but depends on some other stuff. But maybe I can split the
decisive things out sooner, let's see...

Thanks,

Dominik

2003-03-24 20:04:37

by Dominik Brodowski

[permalink] [raw]
Subject: [CFT/RFC] pcmcia bus driver as an interface to pcmcia_socket class_type devices [Was: Re: pcmcia_bus_type changes cause oops...]

[Stelian: could you try this patch instead of the much smaller one
sent before, please? Thanks!]

Previously, "Driver Services" could only be called when the socket
drivers were initialized earlier. This caused an awful lot of
problems, especially when modprobe tried to load ds.ko and a pcmcia
card driver at once.

As all socket devices are registered with the driver model core as
being of "class_type pcmcia_socket_class", we can take use of that and
register them with "Driver Services" upon detection or upon
module loading of ds.c.

Currently, "Card Services" and "Driver Services" reference the sockets
by an assigned, incremental number between zero and seven, IIRC. While
this patch tries not to break this existing setup, this referencing
will go away in future. Instead a "struct pcmcia_socket" (cs.c) and
"struct pcmcia_bus_socket" (ds.c) - with the latter including a
pointer to the former - will be the recommended ways of addressing the
sockets. But until this is done, some ugly workarounds[*] are needed.

Also, the "I-need-two-initcalls-in-a-module"-tweak can go away.

ds.c | 363 +++++++++++++++++++++++++++++++++++++++++--------------------------
1 files changed, 223 insertions(+), 140 deletions(-)
(parts of this purely cosmetical -- I couldn't resist....)

oh, and the patch is for 2.5.65-bk-current, of course.

diff -ruN linux-original/drivers/pcmcia/ds.c linux/drivers/pcmcia/ds.c
--- linux-original/drivers/pcmcia/ds.c 2003-03-23 22:44:16.000000000 +0100
+++ linux/drivers/pcmcia/ds.c 2003-03-24 20:45:42.000000000 +0100
@@ -48,6 +48,7 @@
#include <linux/proc_fs.h>
#include <linux/poll.h>
#include <linux/pci.h>
+#include <linux/list.h>

#include <pcmcia/version.h>
#include <pcmcia/cs_types.h>
@@ -55,6 +56,7 @@
#include <pcmcia/bulkmem.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>
+#include <pcmcia/ss.h>

/*====================================================================*/

@@ -105,6 +107,9 @@
wait_queue_head_t queue, request;
struct timer_list removal;
socket_bind_t *bind;
+ struct device *socket_dev;
+ struct list_head socket_list;
+ unsigned int socket_no; /* deprecated */
} socket_info_t;

#define SOCKET_PRESENT 0x01
@@ -116,8 +121,11 @@
/* Device driver ID passed to Card Services */
static dev_info_t dev_info = "Driver Services";

-static int sockets = 0, major_dev = -1;
-static socket_info_t *socket_table = NULL;
+static int major_dev = -1;
+
+/* list of all sockets registered with the pcmcia bus driver */
+static DECLARE_RWSEM(bus_socket_list_rwsem);
+static LIST_HEAD(bus_socket_list);

extern struct proc_dir_entry *proc_pccard;

@@ -135,6 +143,7 @@
/*======================================================================*/

static struct pcmcia_driver * get_pcmcia_driver (dev_info_t *dev_info);
+static socket_info_t * get_socket_info_by_nr(unsigned int nr);

/**
* pcmcia_register_driver - register a PCMCIA driver with the bus core
@@ -160,17 +169,19 @@
void pcmcia_unregister_driver(struct pcmcia_driver *driver)
{
socket_bind_t *b;
- int i;
+ socket_info_t *bus_sock;

if (driver->use_count > 0) {
/* Blank out any left-over device instances */
driver->attach = NULL; driver->detach = NULL;
- for (i = 0; i < sockets; i++)
- for (b = socket_table[i].bind; b; b = b->next)
+ down_read(&bus_socket_list_rwsem);
+ list_for_each_entry(bus_sock, &bus_socket_list, socket_list) {
+ for (b = bus_sock->bind; b; b = b->next)
if (b->driver == driver)
b->instance = NULL;
- }
-
+ }
+ up_read(&bus_socket_list_rwsem);
+ }
driver_unregister(&driver->drv);
}
EXPORT_SYMBOL(pcmcia_unregister_driver);
@@ -182,7 +193,7 @@
{
struct pcmcia_driver *driver;
socket_bind_t *b;
- int i;
+ socket_info_t *bus_sock;

DEBUG(0, "ds: register_pccard_driver('%s')\n", (char *)dev_info);
driver = get_pcmcia_driver(dev_info);
@@ -199,15 +210,17 @@
if (driver->use_count == 0) return 0;

/* Instantiate any already-bound devices */
- for (i = 0; i < sockets; i++)
- for (b = socket_table[i].bind; b; b = b->next) {
+ down_read(&bus_socket_list_rwsem);
+ list_for_each_entry(bus_sock, &bus_socket_list, socket_list) {
+ for (b = bus_sock->bind; b; b = b->next) {
if (b->driver != driver) continue;
b->instance = driver->attach();
if (b->instance == NULL)
printk(KERN_NOTICE "ds: unable to create instance "
"of '%s'!\n", driver->drv.name);
}
-
+ }
+ up_read(&bus_socket_list_rwsem);
return 0;
} /* register_pccard_driver */

@@ -309,7 +322,7 @@

static void handle_removal(u_long sn)
{
- socket_info_t *s = &socket_table[sn];
+ socket_info_t *s = get_socket_info_by_nr(sn);
handle_event(s, CS_EVENT_CARD_REMOVAL);
s->state &= ~SOCKET_REMOVAL_PENDING;
}
@@ -329,7 +342,7 @@
DEBUG(1, "ds: ds_event(0x%06x, %d, 0x%p)\n",
event, priority, args->client_handle);
s = args->client_data;
- i = s - socket_table;
+ i = s->socket_no;

switch (event) {

@@ -400,9 +413,12 @@
struct pcmcia_driver *driver;
socket_bind_t *b;
bind_req_t bind_req;
- socket_info_t *s = &socket_table[i];
+ socket_info_t *s = get_socket_info_by_nr(i);
int ret;

+ if (!s)
+ return -EINVAL;
+
DEBUG(2, "bind_request(%d, '%s')\n", i,
(char *)bind_info->dev_info);
driver = get_pcmcia_driver(&bind_info->dev_info);
@@ -464,7 +480,7 @@

static int get_device_info(int i, bind_info_t *bind_info, int first)
{
- socket_info_t *s = &socket_table[i];
+ socket_info_t *s = get_socket_info_by_nr(i);
socket_bind_t *b;
dev_node_t *node;

@@ -534,7 +550,7 @@

static int unbind_request(int i, bind_info_t *bind_info)
{
- socket_info_t *s = &socket_table[i];
+ socket_info_t *s = get_socket_info_by_nr(i);
socket_bind_t **b, *c;

DEBUG(2, "unbind_request(%d, '%s')\n", i,
@@ -572,9 +588,11 @@
user_info_t *user;

DEBUG(0, "ds_open(socket %d)\n", i);
- if ((i >= sockets) || (sockets == 0))
- return -ENODEV;
- s = &socket_table[i];
+
+ s = get_socket_info_by_nr(i);
+ if (!s)
+ return -ENODEV;
+
if ((file->f_flags & O_ACCMODE) != O_RDONLY) {
if (s->state & SOCKET_BUSY)
return -EBUSY;
@@ -604,9 +622,11 @@
user_info_t *user, **link;

DEBUG(0, "ds_release(socket %d)\n", i);
- if ((i >= sockets) || (sockets == 0))
- return 0;
- s = &socket_table[i];
+
+ s = get_socket_info_by_nr(i);
+ if (!s)
+ return 0;
+
user = file->private_data;
if (CHECK_USER(user))
goto out;
@@ -637,11 +657,13 @@

DEBUG(2, "ds_read(socket %d)\n", i);

- if ((i >= sockets) || (sockets == 0))
- return -ENODEV;
if (count < 4)
return -EINVAL;
- s = &socket_table[i];
+
+ s = get_socket_info_by_nr(i);
+ if (!s)
+ return -ENODEV;
+
user = file->private_data;
if (CHECK_USER(user))
return -EIO;
@@ -666,13 +688,15 @@

DEBUG(2, "ds_write(socket %d)\n", i);

- if ((i >= sockets) || (sockets == 0))
- return -ENODEV;
if (count != 4)
return -EINVAL;
if ((file->f_flags & O_ACCMODE) == O_RDONLY)
return -EBADF;
- s = &socket_table[i];
+
+ s = get_socket_info_by_nr(i);
+ if (!s)
+ return -ENODEV;
+
user = file->private_data;
if (CHECK_USER(user))
return -EIO;
@@ -699,9 +723,10 @@

DEBUG(2, "ds_poll(socket %d)\n", i);

- if ((i >= sockets) || (sockets == 0))
- return POLLERR;
- s = &socket_table[i];
+ s = get_socket_info_by_nr(i);
+ if (!s)
+ return POLLERR;
+
user = file->private_data;
if (CHECK_USER(user))
return POLLERR;
@@ -724,9 +749,9 @@

DEBUG(2, "ds_ioctl(socket %d, %#x, %#lx)\n", i, cmd, arg);

- if ((i >= sockets) || (sockets == 0))
- return -ENODEV;
- s = &socket_table[i];
+ s = get_socket_info_by_nr(i);
+ if (!s)
+ return -ENODEV;

size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
if (size > sizeof(ds_ioctl_arg_t)) return -EINVAL;
@@ -889,141 +914,199 @@

/*====================================================================*/

-struct bus_type pcmcia_bus_type = {
- .name = "pcmcia",
-};
-EXPORT_SYMBOL(pcmcia_bus_type);
-
-static int __init init_pcmcia_bus(void)
+static int __devinit pcmcia_bus_add_socket(struct device *dev, unsigned int socket_nr)
{
- bus_register(&pcmcia_bus_type);
- return 0;
-}
+ client_reg_t client_reg;
+ bind_req_t bind;
+ socket_info_t *s, *tmp_s;
+ int ret;
+ int i;

-static int __init init_pcmcia_ds(void)
-{
- client_reg_t client_reg;
- servinfo_t serv;
- bind_req_t bind;
- socket_info_t *s;
- int i, ret;
-
- DEBUG(0, "%s\n", version);
-
- /*
- * Ugly. But we want to wait for the socket threads to have started up.
- * We really should let the drivers themselves drive some of this..
- */
- current->state = TASK_INTERRUPTIBLE;
- schedule_timeout(HZ/4);
+ s = kmalloc(sizeof(socket_info_t), GFP_KERNEL);
+ if(!s)
+ return -ENOMEM;
+ memset(s, 0, sizeof(socket_info_t));
+
+ /*
+ * Ugly. But we want to wait for the socket threads to have started up.
+ * We really should let the drivers themselves drive some of this..
+ */
+ current->state = TASK_INTERRUPTIBLE;
+ schedule_timeout(HZ/4);

- pcmcia_get_card_services_info(&serv);
- if (serv.Revision != CS_RELEASE_CODE) {
- printk(KERN_NOTICE "ds: Card Services release does not match!\n");
- return -1;
- }
- if (serv.Count == 0) {
- printk(KERN_NOTICE "ds: no socket drivers loaded!\n");
- return -1;
- }
-
- sockets = serv.Count;
- socket_table = kmalloc(sockets*sizeof(socket_info_t), GFP_KERNEL);
- if (!socket_table) return -1;
- for (i = 0, s = socket_table; i < sockets; i++, s++) {
- s->state = 0;
- s->user = NULL;
- s->req_pending = 0;
init_waitqueue_head(&s->queue);
init_waitqueue_head(&s->request);
- s->handle = NULL;
- init_timer(&s->removal);
- s->removal.data = i;
+
+ /* find the lowest, unused socket no. Please note that this is a
+ * temporary workaround until "struct pcmcia_socket" is introduced
+ * into cs.c which will include this number, and which will be
+ * accessible to ds.c directly */
+ i = 0;
+ next_try:
+ list_for_each_entry(tmp_s, &bus_socket_list, socket_list) {
+ if (tmp_s->socket_no == i) {
+ i++;
+ goto next_try;
+ }
+ }
+ s->socket_no = i;
+
+ /* initialize data */
+ s->socket_dev = dev;
+ s->removal.data = s->socket_no;
s->removal.function = &handle_removal;
- s->bind = NULL;
- }
+ init_timer(&s->removal);

- /* Set up hotline to Card Services */
- client_reg.dev_info = bind.dev_info = &dev_info;
- client_reg.Attributes = INFO_MASTER_CLIENT;
- client_reg.EventMask =
- CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
- CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
- CS_EVENT_EJECTION_REQUEST | CS_EVENT_INSERTION_REQUEST |
- CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
- client_reg.event_handler = &ds_event;
- client_reg.Version = 0x0210;
- for (i = 0; i < sockets; i++) {
- bind.Socket = i;
+ /* Set up hotline to Card Services */
+ client_reg.dev_info = bind.dev_info = &dev_info;
+
+ bind.Socket = s->socket_no;
bind.Function = BIND_FN_ALL;
ret = pcmcia_bind_device(&bind);
if (ret != CS_SUCCESS) {
- cs_error(NULL, BindDevice, ret);
- break;
+ cs_error(NULL, BindDevice, ret);
+ kfree(s);
+ return -EINVAL;
}
- client_reg.event_callback_args.client_data = &socket_table[i];
- ret = pcmcia_register_client(&socket_table[i].handle,
- &client_reg);
+
+ client_reg.Attributes = INFO_MASTER_CLIENT;
+ client_reg.EventMask =
+ CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+ CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+ CS_EVENT_EJECTION_REQUEST | CS_EVENT_INSERTION_REQUEST |
+ CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+ client_reg.event_handler = &ds_event;
+ client_reg.Version = 0x0210;
+ client_reg.event_callback_args.client_data = s;
+ ret = pcmcia_register_client(&s->handle, &client_reg);
if (ret != CS_SUCCESS) {
- cs_error(NULL, RegisterClient, ret);
- break;
+ cs_error(NULL, RegisterClient, ret);
+ kfree(s);
+ return -EINVAL;
}
- }
-
- /* Set up character device for user mode clients */
- i = register_chrdev(0, "pcmcia", &ds_fops);
- if (i == -EBUSY)
- printk(KERN_NOTICE "unable to find a free device # for "
- "Driver Services\n");
- else
- major_dev = i;

-#ifdef CONFIG_PROC_FS
- if (proc_pccard)
- create_proc_read_entry("drivers",0,proc_pccard,proc_read_drivers,NULL);
- init_status = 0;
-#endif
- return 0;
+ list_add(&s->socket_list, &bus_socket_list);
+
+ return 0;
}


-static void __exit exit_pcmcia_ds(void)
+static int __devinit pcmcia_bus_add_socket_dev(struct device *dev)
{
- int i;
+ struct pcmcia_socket_class_data *cls_d = dev->class_data;
+ unsigned int i;
+ unsigned int ret = 0;
+
+ if (!cls_d)
+ return -ENODEV;
+
+ down_write(&bus_socket_list_rwsem);
+ for (i = 0; i < cls_d->nsock; i++)
+ ret += pcmcia_bus_add_socket(dev, i);
+ up_write(&bus_socket_list_rwsem);
+
+ return ret;
+}
+
+static int __devexit pcmcia_bus_remove_socket_dev(struct device *dev)
+{
+ struct pcmcia_socket_class_data *cls_d = dev->class_data;
+ struct list_head *list_loop;
+ struct list_head *tmp_storage;
+
+ if (!cls_d)
+ return -ENODEV;
+
+ down_write(&bus_socket_list_rwsem);
+ list_for_each_safe(list_loop, tmp_storage, &bus_socket_list) {
+ socket_info_t *bus_sock = container_of(list_loop, socket_info_t, socket_list);
+ if (bus_sock->socket_dev == dev) {
+ pcmcia_deregister_client(bus_sock->handle);
+ list_del(&bus_sock->socket_list);
+ kfree(bus_sock);
+ }
+ }
+ up_write(&bus_socket_list_rwsem);
+ return 0;
+}
+
+
+/* the pcmcia_bus_interface is used to handle pcmcia socket devices */
+static struct device_interface pcmcia_bus_interface = {
+ .name = "pcmcia-bus",
+ .devclass = &pcmcia_socket_class,
+ .add_device = &pcmcia_bus_add_socket_dev,
+ .remove_device = __devexit_p(&pcmcia_bus_remove_socket_dev),
+ .kset = { .subsys = &pcmcia_socket_class.subsys, },
+ .devnum = 0,
+};
+
+
+struct bus_type pcmcia_bus_type = {
+ .name = "pcmcia",
+};
+EXPORT_SYMBOL(pcmcia_bus_type);
+
+
+static int __init init_pcmcia_bus(void)
+{
+ int i;
+
+ bus_register(&pcmcia_bus_type);
+ interface_register(&pcmcia_bus_interface);
+
+ /* Set up character device for user mode clients */
+ i = register_chrdev(0, "pcmcia", &ds_fops);
+ if (i == -EBUSY)
+ printk(KERN_NOTICE "unable to find a free device # for "
+ "Driver Services\n");
+ else
+ major_dev = i;
+
#ifdef CONFIG_PROC_FS
- if (proc_pccard)
- remove_proc_entry("drivers", proc_pccard);
+ if (proc_pccard)
+ create_proc_read_entry("drivers",0,proc_pccard,proc_read_drivers,NULL);
#endif
- if (major_dev != -1)
- unregister_chrdev(major_dev, "pcmcia");
- for (i = 0; i < sockets; i++)
- pcmcia_deregister_client(socket_table[i].handle);
- sockets = 0;
- kfree(socket_table);
- bus_unregister(&pcmcia_bus_type);
+
+ return 0;
}
+fs_initcall(init_pcmcia_bus); /* one level after subsys_initcall so that
+ * pcmcia_socket_class is already registered */

-#ifdef MODULE

-/* init_pcmcia_bus must be done early, init_pcmcia_ds late. If we load this
- * as a module, we can only specify one initcall, though...
- */
-static int __init init_pcmcia_module(void) {
- init_pcmcia_bus();
- return init_pcmcia_ds();
-}
-module_init(init_pcmcia_module);
-
-#else /* !MODULE */
-subsys_initcall(init_pcmcia_bus);
-late_initcall(init_pcmcia_ds);
+static void __exit exit_pcmcia_bus(void)
+{
+ interface_unregister(&pcmcia_bus_interface);
+
+#ifdef CONFIG_PROC_FS
+ if (proc_pccard)
+ remove_proc_entry("drivers", proc_pccard);
#endif
+ if (major_dev != -1)
+ unregister_chrdev(major_dev, "pcmcia");
+
+ bus_unregister(&pcmcia_bus_type);
+}
+module_exit(exit_pcmcia_bus);

-module_exit(exit_pcmcia_ds);


/* helpers for backwards-compatible functions */

+
+static socket_info_t * get_socket_info_by_nr(unsigned int nr)
+{
+ socket_info_t * s;
+ down_read(&bus_socket_list_rwsem);
+ list_for_each_entry(s, &bus_socket_list, socket_list)
+ if (s->socket_no == nr) {
+ up_read(&bus_socket_list_rwsem);
+ return s;
+ }
+ up_read(&bus_socket_list_rwsem);
+ return NULL;
+}
+
/* backwards-compatible accessing of driver --- by name! */

struct cmp_data {

2003-03-24 22:48:27

by Stelian Pop

[permalink] [raw]
Subject: Re: [CFT/RFC] pcmcia bus driver as an interface to pcmcia_socket class_type devices [Was: Re: pcmcia_bus_type changes cause oops...]

On Mon, Mar 24, 2003 at 09:14:28PM +0100, Dominik Brodowski wrote:

> [Stelian: could you try this patch instead of the much smaller one
> sent before, please? Thanks!]

Patch works and reenables modular pcmcia to work. Thanks Dominik !

Except that those recent changes in the pcmcia broke the Red Hat (8.0)
boot scripts. Whether the problem lies in the scripts or the new
module dependencies is questionable.

The problem comes from the fact that loading ds.o would fail if
a socket driver is not already loaded. In the new kernel it just
wload sucessfuly.

As a side effect of that the interface is recognised as a wireless
one, and the pcmcia starting script finally doesn't load yenta_socket
because , as pcmcia_core is already loaded, it believes it is already
started. But as I said, the script logic is questionable.

Stelian.
--
Stelian Pop <[email protected]>
Alcove - http://www.alcove.com