Commit ee28db84 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] i4l: kernelcapi receive workqueue and locking rework

From: Armin Schindler <armin@melware.de>

With this patch the ISDN kernel CAPI code uses a per application workqueue
with proper locking to prevent message re-ordering due to the fact a
workqueue may run on another CPU at the same time.  Also some locks for
internal data is added.

Removed global recv_queue work, use per application workqueue.  Added
proper locking mechanisms for application, controller and application
workqueue function.  Increased max.  number of possible applications and
controllers.
parent efffe9c8
/* $Id: kcapi.c,v 1.1.2.7 2004/03/16 08:01:47 armin Exp $ /* $Id: kcapi.c,v 1.1.2.8 2004/03/26 19:57:20 armin Exp $
* *
* Kernel CAPI 2.0 Module * Kernel CAPI 2.0 Module
* *
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
#include <linux/b1lli.h> #include <linux/b1lli.h>
#endif #endif
static char *revision = "$Revision: 1.1.2.7 $"; static char *revision = "$Revision: 1.1.2.8 $";
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
...@@ -63,13 +63,13 @@ static char capi_manufakturer[64] = "AVM Berlin"; ...@@ -63,13 +63,13 @@ static char capi_manufakturer[64] = "AVM Berlin";
LIST_HEAD(capi_drivers); LIST_HEAD(capi_drivers);
rwlock_t capi_drivers_list_lock = RW_LOCK_UNLOCKED; rwlock_t capi_drivers_list_lock = RW_LOCK_UNLOCKED;
static rwlock_t application_lock = RW_LOCK_UNLOCKED;
static DECLARE_MUTEX(controller_sem);
struct capi20_appl *capi_applications[CAPI_MAXAPPL]; struct capi20_appl *capi_applications[CAPI_MAXAPPL];
struct capi_ctr *capi_cards[CAPI_MAXCONTR]; struct capi_ctr *capi_cards[CAPI_MAXCONTR];
static int ncards; static int ncards;
static struct sk_buff_head recv_queue;
static struct work_struct tq_recv_notify;
/* -------- controller ref counting -------------------------------------- */ /* -------- controller ref counting -------------------------------------- */
...@@ -174,7 +174,7 @@ static void notify_up(u32 contr) ...@@ -174,7 +174,7 @@ static void notify_up(u32 contr)
for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { for (applid = 1; applid <= CAPI_MAXAPPL; applid++) {
ap = get_capi_appl_by_nr(applid); ap = get_capi_appl_by_nr(applid);
if (ap && ap->callback) if (ap && ap->callback && !ap->release_in_progress)
ap->callback(KCI_CONTRUP, contr, &card->profile); ap->callback(KCI_CONTRUP, contr, &card->profile);
} }
} }
...@@ -192,7 +192,7 @@ static void notify_down(u32 contr) ...@@ -192,7 +192,7 @@ static void notify_down(u32 contr)
for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { for (applid = 1; applid <= CAPI_MAXAPPL; applid++) {
ap = get_capi_appl_by_nr(applid); ap = get_capi_appl_by_nr(applid);
if (ap && ap->callback) if (ap && ap->callback && !ap->release_in_progress)
ap->callback(KCI_CONTRDOWN, contr, 0); ap->callback(KCI_CONTRDOWN, contr, 0);
} }
} }
...@@ -237,38 +237,39 @@ static int notify_push(unsigned int cmd, u32 controller, u16 applid, u32 ncci) ...@@ -237,38 +237,39 @@ static int notify_push(unsigned int cmd, u32 controller, u16 applid, u32 ncci)
/* -------- Receiver ------------------------------------------ */ /* -------- Receiver ------------------------------------------ */
static void recv_handler(void *dummy) static void recv_handler(void *_ap)
{ {
struct sk_buff *skb; struct sk_buff *skb;
struct capi20_appl *ap; struct capi20_appl *ap = (struct capi20_appl *) _ap;
while ((skb = skb_dequeue(&recv_queue)) != 0) { if ((!ap) || (ap->release_in_progress))
ap = get_capi_appl_by_nr(CAPIMSG_APPID(skb->data)); return;
if (!ap) {
printk(KERN_ERR "kcapi: recv_handler: applid %d ? (%s)\n",
CAPIMSG_APPID(skb->data), capi_message2str(skb->data));
kfree_skb(skb);
continue;
}
down(&ap->recv_sem);
while ((skb = skb_dequeue(&ap->recv_queue))) {
if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_IND) if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_IND)
ap->nrecvdatapkt++; ap->nrecvdatapkt++;
else else
ap->nrecvctlpkt++; ap->nrecvctlpkt++;
ap->recv_message(ap, skb); ap->recv_message(ap, skb);
} }
up(&ap->recv_sem);
} }
void capi_ctr_handle_message(struct capi_ctr * card, u16 appl, struct sk_buff *skb) void capi_ctr_handle_message(struct capi_ctr * card, u16 appl, struct sk_buff *skb)
{ {
struct capi20_appl *ap;
int showctl = 0; int showctl = 0;
u8 cmd, subcmd; u8 cmd, subcmd;
unsigned long flags;
if (card->cardstate != CARD_RUNNING) { if (card->cardstate != CARD_RUNNING) {
printk(KERN_INFO "kcapi: controller %d not active, got: %s", printk(KERN_INFO "kcapi: controller %d not active, got: %s",
card->cnr, capi_message2str(skb->data)); card->cnr, capi_message2str(skb->data));
goto error; goto error;
} }
cmd = CAPIMSG_COMMAND(skb->data); cmd = CAPIMSG_COMMAND(skb->data);
subcmd = CAPIMSG_SUBCOMMAND(skb->data); subcmd = CAPIMSG_SUBCOMMAND(skb->data);
if (cmd == CAPI_DATA_B3 && subcmd == CAPI_IND) { if (cmd == CAPI_DATA_B3 && subcmd == CAPI_IND) {
...@@ -293,8 +294,19 @@ void capi_ctr_handle_message(struct capi_ctr * card, u16 appl, struct sk_buff *s ...@@ -293,8 +294,19 @@ void capi_ctr_handle_message(struct capi_ctr * card, u16 appl, struct sk_buff *s
} }
} }
skb_queue_tail(&recv_queue, skb);
schedule_work(&tq_recv_notify); read_lock_irqsave(&application_lock, flags);
ap = get_capi_appl_by_nr(CAPIMSG_APPID(skb->data));
if ((!ap) || (ap->release_in_progress)) {
read_unlock_irqrestore(&application_lock, flags);
printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s)\n",
CAPIMSG_APPID(skb->data), capi_message2str(skb->data));
goto error;
}
skb_queue_tail(&ap->recv_queue, skb);
schedule_work(&ap->recv_work);
read_unlock_irqrestore(&application_lock, flags);
return; return;
error: error:
...@@ -310,11 +322,13 @@ void capi_ctr_ready(struct capi_ctr * card) ...@@ -310,11 +322,13 @@ void capi_ctr_ready(struct capi_ctr * card)
card->cardstate = CARD_RUNNING; card->cardstate = CARD_RUNNING;
down(&controller_sem);
for (appl = 1; appl <= CAPI_MAXAPPL; appl++) { for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
ap = get_capi_appl_by_nr(appl); ap = get_capi_appl_by_nr(appl);
if (!ap) continue; if (!ap || ap->release_in_progress) continue;
register_appl(card, appl, &ap->rparam); register_appl(card, appl, &ap->rparam);
} }
up(&controller_sem);
printk(KERN_NOTICE "kcapi: card %d \"%s\" ready.\n", printk(KERN_NOTICE "kcapi: card %d \"%s\" ready.\n",
card->cnr, card->name); card->cnr, card->name);
...@@ -342,7 +356,7 @@ void capi_ctr_reseted(struct capi_ctr * card) ...@@ -342,7 +356,7 @@ void capi_ctr_reseted(struct capi_ctr * card)
for (appl = 1; appl <= CAPI_MAXAPPL; appl++) { for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
struct capi20_appl *ap = get_capi_appl_by_nr(appl); struct capi20_appl *ap = get_capi_appl_by_nr(appl);
if (!ap) if (!ap || ap->release_in_progress)
continue; continue;
capi_ctr_put(card); capi_ctr_put(card);
...@@ -382,16 +396,21 @@ attach_capi_ctr(struct capi_ctr *card) ...@@ -382,16 +396,21 @@ attach_capi_ctr(struct capi_ctr *card)
{ {
int i; int i;
down(&controller_sem);
for (i = 0; i < CAPI_MAXCONTR; i++) { for (i = 0; i < CAPI_MAXCONTR; i++) {
if (capi_cards[i] == NULL) if (capi_cards[i] == NULL)
break; break;
} }
if (i == CAPI_MAXCONTR) { if (i == CAPI_MAXCONTR) {
up(&controller_sem);
printk(KERN_ERR "kcapi: out of controller slots\n"); printk(KERN_ERR "kcapi: out of controller slots\n");
return -EBUSY; return -EBUSY;
} }
capi_cards[i] = card; capi_cards[i] = card;
up(&controller_sem);
card->nrecvctlpkt = 0; card->nrecvctlpkt = 0;
card->nrecvdatapkt = 0; card->nrecvdatapkt = 0;
card->nsentctlpkt = 0; card->nsentctlpkt = 0;
...@@ -480,18 +499,23 @@ u16 capi20_register(struct capi20_appl *ap) ...@@ -480,18 +499,23 @@ u16 capi20_register(struct capi20_appl *ap)
{ {
int i; int i;
u16 applid; u16 applid;
unsigned long flags;
DBG(""); DBG("");
if (ap->rparam.datablklen < 128) if (ap->rparam.datablklen < 128)
return CAPI_LOGBLKSIZETOSMALL; return CAPI_LOGBLKSIZETOSMALL;
write_lock_irqsave(&application_lock, flags);
for (applid = 1; applid <= CAPI_MAXAPPL; applid++) { for (applid = 1; applid <= CAPI_MAXAPPL; applid++) {
if (capi_applications[applid - 1] == NULL) if (capi_applications[applid - 1] == NULL)
break; break;
} }
if (applid > CAPI_MAXAPPL) if (applid > CAPI_MAXAPPL) {
write_unlock_irqrestore(&application_lock, flags);
return CAPI_TOOMANYAPPLS; return CAPI_TOOMANYAPPLS;
}
ap->applid = applid; ap->applid = applid;
capi_applications[applid - 1] = ap; capi_applications[applid - 1] = ap;
...@@ -501,12 +525,21 @@ u16 capi20_register(struct capi20_appl *ap) ...@@ -501,12 +525,21 @@ u16 capi20_register(struct capi20_appl *ap)
ap->nsentctlpkt = 0; ap->nsentctlpkt = 0;
ap->nsentdatapkt = 0; ap->nsentdatapkt = 0;
ap->callback = 0; ap->callback = 0;
init_MUTEX(&ap->recv_sem);
skb_queue_head_init(&ap->recv_queue);
INIT_WORK(&ap->recv_work, recv_handler, (void *)ap);
ap->release_in_progress = 0;
write_unlock_irqrestore(&application_lock, flags);
down(&controller_sem);
for (i = 0; i < CAPI_MAXCONTR; i++) { for (i = 0; i < CAPI_MAXCONTR; i++) {
if (!capi_cards[i] || capi_cards[i]->cardstate != CARD_RUNNING) if (!capi_cards[i] || capi_cards[i]->cardstate != CARD_RUNNING)
continue; continue;
register_appl(capi_cards[i], applid, &ap->rparam); register_appl(capi_cards[i], applid, &ap->rparam);
} }
up(&controller_sem);
if (showcapimsgs & 1) { if (showcapimsgs & 1) {
printk(KERN_DEBUG "kcapi: appl %d up\n", applid); printk(KERN_DEBUG "kcapi: appl %d up\n", applid);
} }
...@@ -519,15 +552,26 @@ EXPORT_SYMBOL(capi20_register); ...@@ -519,15 +552,26 @@ EXPORT_SYMBOL(capi20_register);
u16 capi20_release(struct capi20_appl *ap) u16 capi20_release(struct capi20_appl *ap)
{ {
int i; int i;
unsigned long flags;
DBG("applid %#x", ap->applid); DBG("applid %#x", ap->applid);
write_lock_irqsave(&application_lock, flags);
ap->release_in_progress = 1;
capi_applications[ap->applid - 1] = NULL;
write_unlock_irqrestore(&application_lock, flags);
down(&controller_sem);
for (i = 0; i < CAPI_MAXCONTR; i++) { for (i = 0; i < CAPI_MAXCONTR; i++) {
if (!capi_cards[i] || capi_cards[i]->cardstate != CARD_RUNNING) if (!capi_cards[i] || capi_cards[i]->cardstate != CARD_RUNNING)
continue; continue;
release_appl(capi_cards[i], ap->applid); release_appl(capi_cards[i], ap->applid);
} }
capi_applications[ap->applid - 1] = NULL; up(&controller_sem);
flush_scheduled_work();
skb_queue_purge(&ap->recv_queue);
if (showcapimsgs & 1) { if (showcapimsgs & 1) {
printk(KERN_DEBUG "kcapi: appl %d down\n", ap->applid); printk(KERN_DEBUG "kcapi: appl %d down\n", ap->applid);
} }
...@@ -547,7 +591,7 @@ u16 capi20_put_message(struct capi20_appl *ap, struct sk_buff *skb) ...@@ -547,7 +591,7 @@ u16 capi20_put_message(struct capi20_appl *ap, struct sk_buff *skb)
if (ncards == 0) if (ncards == 0)
return CAPI_REGNOTINSTALLED; return CAPI_REGNOTINSTALLED;
if (ap->applid == 0) if ((ap->applid == 0) || ap->release_in_progress)
return CAPI_ILLAPPNR; return CAPI_ILLAPPNR;
if (skb->len < 12 if (skb->len < 12
|| !capi_cmd_valid(CAPIMSG_COMMAND(skb->data)) || !capi_cmd_valid(CAPIMSG_COMMAND(skb->data))
...@@ -925,10 +969,6 @@ static int __init kcapi_init(void) ...@@ -925,10 +969,6 @@ static int __init kcapi_init(void)
char *p; char *p;
char rev[32]; char rev[32];
skb_queue_head_init(&recv_queue);
INIT_WORK(&tq_recv_notify, recv_handler, NULL);
kcapi_proc_init(); kcapi_proc_init();
if ((p = strchr(revision, ':')) != 0 && p[1]) { if ((p = strchr(revision, ':')) != 0 && p[1]) {
......
...@@ -10,10 +10,8 @@ ...@@ -10,10 +10,8 @@
#ifndef __KERNELCAPI_H__ #ifndef __KERNELCAPI_H__
#define __KERNELCAPI_H__ #define __KERNELCAPI_H__
#include <linux/list.h> #define CAPI_MAXAPPL 240 /* maximum number of applications */
#define CAPI_MAXCONTR 32 /* maximum number of controller */
#define CAPI_MAXAPPL 128 /* maximum number of applications */
#define CAPI_MAXCONTR 16 /* maximum number of controller */
#define CAPI_MAXDATAWINDOW 8 #define CAPI_MAXDATAWINDOW 8
...@@ -47,6 +45,7 @@ typedef struct kcapi_carddef { ...@@ -47,6 +45,7 @@ typedef struct kcapi_carddef {
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/list.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#define KCI_CONTRUP 0 /* arg: struct capi_profile */ #define KCI_CONTRUP 0 /* arg: struct capi_profile */
...@@ -63,6 +62,10 @@ struct capi20_appl { ...@@ -63,6 +62,10 @@ struct capi20_appl {
unsigned long nrecvdatapkt; unsigned long nrecvdatapkt;
unsigned long nsentctlpkt; unsigned long nsentctlpkt;
unsigned long nsentdatapkt; unsigned long nsentdatapkt;
struct semaphore recv_sem;
struct sk_buff_head recv_queue;
struct work_struct recv_work;
int release_in_progress;
/* ugly hack to allow for notification of added/removed /* ugly hack to allow for notification of added/removed
* controllers. The Right Way (tm) is known. XXX * controllers. The Right Way (tm) is known. XXX
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment