Hi,
This patch fixes a some race conditions in IrLMP, and various
problems in IrLAP. The main IrLAP change is the addition of a new
events to make sure the stack doesn't deadlock if you connect while
the media is busy (this simple fix took me two days of work).
I didn't had too much time to test, but it seems solid. Please
send me feedback so that I get that in the kernel ASAP...
Have fun...
Jean
diff -u -p linux/include/net/irda/irlmp.d2.h linux/include/net/irda/irlmp.h
--- linux/include/net/irda/irlmp.d2.h Fri Nov 2 14:11:19 2001
+++ linux/include/net/irda/irlmp.h Fri Nov 2 18:37:29 2001
@@ -131,7 +131,6 @@ struct lap_cb {
struct irlap_cb *irlap; /* Instance of IrLAP layer */
hashbin_t *lsaps; /* LSAP associated with this link */
- int refcount;
__u8 caddr; /* Connection address */
__u32 saddr; /* Source device address */
diff -u -p linux/include/net/irda/irlmp_event.d2.h linux/include/net/irda/irlmp_event.h
--- linux/include/net/irda/irlmp_event.d2.h Fri Nov 2 18:47:47 2001
+++ linux/include/net/irda/irlmp_event.h Fri Nov 2 15:46:29 2001
@@ -103,10 +103,6 @@ void irlmp_watchdog_timer_expired(void *
void irlmp_discovery_timer_expired(void *data);
void irlmp_idle_timer_expired(void *data);
-void irlmp_next_station_state(IRLMP_STATE state);
-void irlmp_next_lsap_state(struct lsap_cb *self, LSAP_STATE state);
-void irlmp_next_lap_state(struct lap_cb *self, IRLMP_STATE state);
-
void irlmp_do_lap_event(struct lap_cb *self, IRLMP_EVENT event,
struct sk_buff *skb);
int irlmp_do_lsap_event(struct lsap_cb *self, IRLMP_EVENT event,
diff -u -p linux/include/net/irda/irlap_event.d2.h linux/include/net/irda/irlap_event.h
--- linux/include/net/irda/irlap_event.d2.h Fri Nov 2 18:47:12 2001
+++ linux/include/net/irda/irlap_event.h Fri Nov 2 18:11:20 2001
@@ -103,6 +103,7 @@ typedef enum {
DISCOVERY_TIMER_EXPIRED,
WD_TIMER_EXPIRED,
BACKOFF_TIMER_EXPIRED,
+ MEDIA_BUSY_TIMER_EXPIRED,
} IRLAP_EVENT;
/*
diff -u -p linux/net/irda/irlmp.d2.c linux/net/irda/irlmp.c
--- linux/net/irda/irlmp.d2.c Wed Oct 31 15:44:59 2001
+++ linux/net/irda/irlmp.c Fri Nov 2 16:53:17 2001
@@ -75,7 +75,7 @@ int irlmp_proc_read(char *buf, char **st
*/
int __init irlmp_init(void)
{
- IRDA_DEBUG(0, __FUNCTION__ "()\n");
+ IRDA_DEBUG(1, __FUNCTION__ "()\n");
/* Initialize the irlmp structure. */
irlmp = kmalloc( sizeof(struct irlmp_cb), GFP_KERNEL);
if (irlmp == NULL)
@@ -170,14 +170,14 @@ struct lsap_cb *irlmp_open_lsap(__u8 sls
#endif /* CONFIG_IRDA_ULTRA */
} else
self->dlsap_sel = LSAP_ANY;
- self->connected = FALSE;
+ /* self->connected = FALSE; -> already NULL via memset() */
init_timer(&self->watchdog_timer);
ASSERT(notify->instance != NULL, return NULL;);
self->notify = *notify;
- irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
+ self->lsap_state = LSAP_DISCONNECTED;
/* Insert into queue of unconnected LSAPs */
hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) self, (int) self,
@@ -237,6 +237,7 @@ void irlmp_close_lsap(struct lsap_cb *se
ASSERT(lap->magic == LMP_LAP_MAGIC, return;);
lsap = hashbin_remove(lap->lsaps, (int) self, NULL);
}
+ self->lap = NULL;
/* Check if we found the LSAP! If not then try the unconnected lsaps */
if (!lsap) {
lsap = hashbin_remove(irlmp->unconnected_lsaps, (int) self,
@@ -281,7 +282,7 @@ void irlmp_register_link(struct irlap_cb
lap->daddr = DEV_ADDR_ANY;
lap->lsaps = hashbin_new(HB_GLOBAL);
- irlmp_next_lap_state(lap, LAP_STANDBY);
+ lap->lap_state = LAP_STANDBY;
init_timer(&lap->idle_timer);
@@ -346,7 +347,7 @@ int irlmp_connect_request(struct lsap_cb
"(), slsap_sel=%02x, dlsap_sel=%02x, saddr=%08x, daddr=%08x\n",
self->slsap_sel, dlsap_sel, saddr, daddr);
- if (self->connected)
+ if (test_bit(0, &self->connected))
return -EISCONN;
/* Client must supply destination device address */
@@ -435,7 +436,7 @@ int irlmp_connect_request(struct lsap_cb
hashbin_insert(self->lap->lsaps, (irda_queue_t *) self, (int) self, NULL);
- self->connected = TRUE;
+ set_bit(0, &self->connected); /* TRUE */
/*
* User supplied qos specifications?
@@ -481,6 +482,8 @@ void irlmp_connect_indication(struct lsa
self->notify.connect_indication(self->notify.instance, self,
&self->qos, max_seg_size,
max_header_size, skb);
+ else
+ dev_kfree_skb(skb);
}
/*
@@ -495,7 +498,7 @@ int irlmp_connect_response(struct lsap_c
ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
ASSERT(userdata != NULL, return -1;);
- self->connected = TRUE;
+ set_bit(0, &self->connected); /* TRUE */
IRDA_DEBUG(2, __FUNCTION__ "(), slsap_sel=%02x, dlsap_sel=%02x\n",
self->slsap_sel, self->dlsap_sel);
@@ -543,7 +546,8 @@ void irlmp_connect_confirm(struct lsap_c
self->notify.connect_confirm(self->notify.instance, self,
&self->qos, max_seg_size,
max_header_size, skb);
- }
+ } else
+ dev_kfree_skb(skb);
}
/*
@@ -598,16 +602,18 @@ int irlmp_disconnect_request(struct lsap
ASSERT(self != NULL, return -1;);
ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
+ ASSERT(userdata != NULL, return -1;);
- /* Already disconnected? */
- if (!self->connected) {
- WARNING(__FUNCTION__ "(), already disconnected!\n");
+ /* Already disconnected ?
+ * There is a race condition between irlmp_disconnect_indication()
+ * and us that might mess up the hashbins below. This fixes it.
+ * Jean II */
+ if (! test_and_clear_bit(0, &self->connected)) {
+ IRDA_DEBUG(0, __FUNCTION__ "(), already disconnected!\n");
+ dev_kfree_skb(userdata);
return -1;
}
- ASSERT(userdata != NULL, return -1;);
- ASSERT(self->connected == TRUE, return -1;);
-
skb_push(userdata, LMP_CONTROL_HEADER);
/*
@@ -634,7 +640,6 @@ int irlmp_disconnect_request(struct lsap
NULL);
/* Reset some values */
- self->connected = FALSE;
self->dlsap_sel = LSAP_ANY;
self->lap = NULL;
@@ -651,16 +656,23 @@ void irlmp_disconnect_indication(struct
{
struct lsap_cb *lsap;
- IRDA_DEBUG(1, __FUNCTION__ "(), reason=%s\n", lmp_reasons[reason]);
+ IRDA_DEBUG(1, __FUNCTION__ "(), reason=%s\n", lmp_reasons[reason]);
ASSERT(self != NULL, return;);
ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
- ASSERT(self->connected == TRUE, return;);
IRDA_DEBUG(3, __FUNCTION__ "(), slsap_sel=%02x, dlsap_sel=%02x\n",
self->slsap_sel, self->dlsap_sel);
- self->connected = FALSE;
- self->dlsap_sel = LSAP_ANY;
+ /* Already disconnected ?
+ * There is a race condition between irlmp_disconnect_request()
+ * and us that might mess up the hashbins below. This fixes it.
+ * Jean II */
+ if (! test_and_clear_bit(0, &self->connected)) {
+ IRDA_DEBUG(0, __FUNCTION__ "(), already disconnected!\n");
+ if (userdata)
+ dev_kfree_skb(userdata);
+ return;
+ }
#ifdef CONFIG_IRDA_CACHE_LAST_LSAP
irlmp->cache.valid = FALSE;
@@ -679,6 +691,7 @@ void irlmp_disconnect_indication(struct
hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) lsap, (int) lsap,
NULL);
+ self->dlsap_sel = LSAP_ANY;
self->lap = NULL;
/*
@@ -689,7 +702,8 @@ void irlmp_disconnect_indication(struct
self, reason, userdata);
else {
IRDA_DEBUG(0, __FUNCTION__ "(), no handler\n");
- dev_kfree_skb(userdata);
+ if (userdata)
+ dev_kfree_skb(userdata);
}
}
@@ -1401,7 +1415,7 @@ __u32 irlmp_register_client(__u16 hint_m
irlmp_client_t *client;
__u32 handle;
- IRDA_DEBUG(0, __FUNCTION__ "()\n");
+ IRDA_DEBUG(1, __FUNCTION__ "()\n");
ASSERT(irlmp != NULL, return 0;);
/* Get a unique handle for this client */
@@ -1673,14 +1687,15 @@ int irlmp_proc_read(char *buf, char **st
len += sprintf(buf+len, "saddr: %#08x, daddr: %#08x, ",
lap->saddr, lap->daddr);
- len += sprintf(buf+len, "refcount: %d", lap->refcount);
+ len += sprintf(buf+len, "num lsaps: %d",
+ HASHBIN_GET_SIZE(lap->lsaps));
len += sprintf(buf+len, "\n");
- len += sprintf(buf+len, "\nConnected LSAPs:\n");
+ len += sprintf(buf+len, "\n Connected LSAPs:\n");
self = (struct lsap_cb *) hashbin_get_first(lap->lsaps);
while (self != NULL) {
ASSERT(self->magic == LMP_LSAP_MAGIC, return 0;);
- len += sprintf(buf+len, "lsap state: %s, ",
+ len += sprintf(buf+len, " lsap state: %s, ",
irlsap_state[ self->lsap_state]);
len += sprintf(buf+len,
"slsap_sel: %#02x, dlsap_sel: %#02x, ",
diff -u -p linux/net/irda/irlmp_event.d2.c linux/net/irda/irlmp_event.c
--- linux/net/irda/irlmp_event.d2.c Fri Nov 2 13:55:19 2001
+++ linux/net/irda/irlmp_event.c Fri Nov 2 19:06:59 2001
@@ -114,6 +114,25 @@ static int (*lsap_state[])( struct lsap_
irlmp_state_setup_pend
};
+static inline void irlmp_next_lap_state(struct lap_cb *self,
+ IRLMP_STATE state)
+{
+ /*
+ IRDA_DEBUG(4, __FUNCTION__ "(), LMP LAP = %s\n", irlmp_state[state]);
+ */
+ self->lap_state = state;
+}
+
+static inline void irlmp_next_lsap_state(struct lsap_cb *self,
+ LSAP_STATE state)
+{
+ /*
+ ASSERT(self != NULL, return;);
+ IRDA_DEBUG(4, __FUNCTION__ "(), LMP LSAP = %s\n", irlsap_state[state]);
+ */
+ self->lsap_state = state;
+}
+
/* Do connection control events */
int irlmp_do_lsap_event(struct lsap_cb *self, IRLMP_EVENT event,
struct sk_buff *skb)
@@ -223,7 +242,6 @@ static void irlmp_state_standby(struct l
IRDA_DEBUG(4, __FUNCTION__ "() LS_CONNECT_REQUEST\n");
irlmp_next_lap_state(self, LAP_U_CONNECT);
- self->refcount++;
/* FIXME: need to set users requested QoS */
irlap_connect_request(self->irlap, self->daddr, NULL, 0);
@@ -278,12 +296,12 @@ static void irlmp_state_u_connect(struct
* the lsaps may already have gone. This avoid getting stuck
* forever in LAP_ACTIVE state - Jean II */
if (HASHBIN_GET_SIZE(self->lsaps) == 0) {
+ IRDA_DEBUG(0, __FUNCTION__ "() NO LSAPs !\n");
irlmp_start_idle_timer(self, LM_IDLE_TIMEOUT);
}
break;
case LM_LAP_CONNECT_REQUEST:
/* Already trying to connect */
- self->refcount++;
break;
case LM_LAP_CONNECT_CONFIRM:
/* For all lsap_ce E Associated do LS_Connect_confirm */
@@ -298,12 +316,13 @@ static void irlmp_state_u_connect(struct
* the lsaps may already have gone. This avoid getting stuck
* forever in LAP_ACTIVE state - Jean II */
if (HASHBIN_GET_SIZE(self->lsaps) == 0) {
+ IRDA_DEBUG(0, __FUNCTION__ "() NO LSAPs !\n");
irlmp_start_idle_timer(self, LM_IDLE_TIMEOUT);
}
break;
case LM_LAP_DISCONNECT_INDICATION:
+ IRDA_DEBUG(1, __FUNCTION__ "(), LM_LAP_DISCONNECT_REQUEST\n");
irlmp_next_lap_state(self, LAP_STANDBY);
- self->refcount = 0;
/* Send disconnect event to all LSAPs using this link */
lsap = (struct lsap_cb *) hashbin_get_first( self->lsaps);
@@ -320,11 +339,13 @@ static void irlmp_state_u_connect(struct
}
break;
case LM_LAP_DISCONNECT_REQUEST:
- IRDA_DEBUG(4, __FUNCTION__ "(), LM_LAP_DISCONNECT_REQUEST\n");
+ IRDA_DEBUG(1, __FUNCTION__ "(), LM_LAP_DISCONNECT_REQUEST\n");
- self->refcount--;
- if (self->refcount == 0)
- irlmp_next_lap_state(self, LAP_STANDBY);
+ /* One of the LSAP did timeout or was closed, if it was
+ * the last one, try to get out of here - Jean II */
+ if (HASHBIN_GET_SIZE(self->lsaps) <= 1) {
+ irlap_disconnect_request(self->irlap);
+ }
break;
default:
IRDA_DEBUG(0, __FUNCTION__ "(), Unknown event %s\n",
@@ -352,7 +373,6 @@ static void irlmp_state_active(struct la
switch (event) {
case LM_LAP_CONNECT_REQUEST:
IRDA_DEBUG(4, __FUNCTION__ "(), LS_CONNECT_REQUEST\n");
- self->refcount++;
/*
* LAP connection allready active, just bounce back! Since we
@@ -379,8 +399,6 @@ static void irlmp_state_active(struct la
/* Keep state */
break;
case LM_LAP_DISCONNECT_REQUEST:
- self->refcount--;
-
/*
* Need to find out if we should close IrLAP or not. If there
* is only one LSAP connection left on this link, that LSAP
@@ -419,7 +437,6 @@ static void irlmp_state_active(struct la
break;
case LM_LAP_DISCONNECT_INDICATION:
irlmp_next_lap_state(self, LAP_STANDBY);
- self->refcount = 0;
/* In some case, at this point our side has already closed
* all lsaps, and we are waiting for the idle_timer to
@@ -517,6 +534,8 @@ static int irlmp_state_disconnected(stru
* If we receive this event while our LAP is closing down,
* the LM_LAP_CONNECT_REQUEST get lost and we get stuck in
* CONNECT_PEND state forever.
+ * The other cause of getting stuck down there is if the
+ * higher layer never reply to the CONNECT_INDICATION.
* Anyway, it make sense to make sure that we always have
* a backup plan. 1 second is plenty (should be immediate).
* Jean II */
@@ -577,9 +596,8 @@ static int irlmp_state_connect(struct ls
* Jean II */
IRDA_DEBUG(0, __FUNCTION__ "() WATCHDOG_TIMEOUT!\n");
- /* Here, we should probably disconnect proper */
+ /* Disconnect, get out... - Jean II */
self->dlsap_sel = LSAP_ANY;
- self->conn_skb = NULL;
irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
break;
default:
@@ -612,15 +630,6 @@ static int irlmp_state_connect_pend(stru
case LM_CONNECT_REQUEST:
/* Keep state */
break;
- case LM_CONNECT_INDICATION:
- /* Will happen in some rare cases when the socket get stuck,
- * the other side retries the connect request.
- * We just unstuck the socket - Jean II */
- IRDA_DEBUG(0, __FUNCTION__ "(), LM_CONNECT_INDICATION, "
- "LSAP stuck in CONNECT_PEND state...\n");
- /* Keep state */
- irlmp_do_lap_event(self->lap, LM_LAP_CONNECT_REQUEST, NULL);
- break;
case LM_CONNECT_RESPONSE:
IRDA_DEBUG(0, __FUNCTION__ "(), LM_CONNECT_RESPONSE, "
"no indication issued yet\n");
@@ -648,6 +657,8 @@ static int irlmp_state_connect_pend(stru
/* Go back to disconnected mode, keep the socket waiting */
self->dlsap_sel = LSAP_ANY;
+ if(self->conn_skb)
+ dev_kfree_skb(self->conn_skb);
self->conn_skb = NULL;
irlmp_next_lsap_state(self, LSAP_DISCONNECTED);
break;
@@ -856,7 +867,7 @@ static int irlmp_state_setup_pend(struct
irlmp_next_lsap_state(self, LSAP_SETUP);
break;
case LM_WATCHDOG_TIMEOUT:
- IRDA_DEBUG(0, __FUNCTION__ "() WATCHDOG_TIMEOUT!\n");
+ IRDA_DEBUG(0, __FUNCTION__ "() : WATCHDOG_TIMEOUT !\n");
ASSERT(self->lap != NULL, return -1;);
irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL);
@@ -881,18 +892,4 @@ static int irlmp_state_setup_pend(struct
break;
}
return ret;
-}
-
-void irlmp_next_lap_state(struct lap_cb *self, IRLMP_STATE state)
-{
- IRDA_DEBUG(4, __FUNCTION__ "(), LMP LAP = %s\n", irlmp_state[state]);
- self->lap_state = state;
-}
-
-void irlmp_next_lsap_state(struct lsap_cb *self, LSAP_STATE state)
-{
- ASSERT(self != NULL, return;);
-
- IRDA_DEBUG(4, __FUNCTION__ "(), LMP LSAP = %s\n", irlsap_state[state]);
- self->lsap_state = state;
}
diff -u -p linux/net/irda/timer.d2.c linux/net/irda/timer.c
--- linux/net/irda/timer.d2.c Fri Nov 2 18:04:22 2001
+++ linux/net/irda/timer.c Fri Nov 2 19:03:02 2001
@@ -106,17 +106,13 @@ void irlap_stop_mbusy_timer(struct irlap
if(timer_pending(&self->media_busy_timer))
del_timer(&self->media_busy_timer);
-#ifdef CONFIG_IRDA_ULTRA
- /* Send any pending Ultra frames if any */
- if (!skb_queue_empty(&self->txq_ultra))
- /* Note : we don't send the frame, just post an event.
- * Frames will be sent only if we are in NDM mode (see
- * irlap_event.c).
- * Also, moved this code from irlap_media_busy_expired()
- * to here to catch properly all cases...
- * Jean II */
- irlap_do_event(self, SEND_UI_FRAME, NULL, NULL);
-#endif /* CONFIG_IRDA_ULTRA */
+ /* If we are in NDM, there is a bunch of events in LAP that
+ * that be pending due to the media_busy condition, such as
+ * CONNECT_REQUEST and SEND_UI_FRAME. If we don't generate
+ * an event, they will wait forever...
+ * Jean II */
+ if (self->state == LAP_NDM)
+ irlap_do_event(self, MEDIA_BUSY_TIMER_EXPIRED, NULL, NULL);
}
void irlmp_start_watchdog_timer(struct lsap_cb *self, int timeout)
@@ -237,5 +233,6 @@ void irlap_media_busy_expired(void* data
ASSERT(self != NULL, return;);
irda_device_set_media_busy(self->netdev, FALSE);
- /* Note : will deal with Ultra frames */
+ /* Note : the LAP event will be send in irlap_stop_mbusy_timer(),
+ * to catch other cases and do it after clearing the mbusy flag */
}
diff -u -p linux/net/irda/irlap_event.d2.c linux/net/irda/irlap_event.c
--- linux/net/irda/irlap_event.d2.c Fri Nov 2 16:28:13 2001
+++ linux/net/irda/irlap_event.c Fri Nov 2 19:15:59 2001
@@ -114,6 +114,7 @@ static const char *irlap_event[] = {
"DISCOVERY_TIMER_EXPIRED",
"WD_TIMER_EXPIRED",
"BACKOFF_TIMER_EXPIRED",
+ "MEDIA_BUSY_TIMER_EXPIRED",
};
const char *irlap_state[] = {
@@ -264,6 +265,15 @@ void irlap_do_event(struct irlap_cb *sel
}
break;
case LAP_NDM:
+ /* This one *should* not pend in this state, except if a
+ * socket try to connect and immediately disconnected.
+ * clear - Jean II */
+ if (self->disconnect_pending) {
+ IRDA_DEBUG(0, __FUNCTION__ "(), Clear pending disconnect!\n");
+ self->disconnect_pending = FALSE;
+ self->connect_pending = FALSE;
+ irlap_disconnect_indication(self, LAP_DISC_INDICATION);
+ }
/* Check if we should try to connect */
if ((self->connect_pending) && !self->media_busy) {
self->connect_pending = FALSE;
@@ -440,6 +450,20 @@ static int irlap_state_ndm(struct irlap_
irlap_discovery_indication(self, info->discovery);
}
break;
+ case MEDIA_BUSY_TIMER_EXPIRED:
+ /* A bunch of events may be postponed because the media is busy
+ * This event indicate the the media is free, therefore
+ * we can process those events... - Jean II */
+#ifdef CONFIG_IRDA_ULTRA
+ /* Send any pending Ultra frames if any */
+ if (!skb_queue_empty(&self->txq_ultra))
+ /* We don't send the frame, just post an event.
+ * Also, previously this code was in timer.c...
+ * Jean II */
+ ret = (*state[self->state])(self, SEND_UI_FRAME,
+ NULL, NULL);
+#endif /* CONFIG_IRDA_ULTRA */
+ break;
#ifdef CONFIG_IRDA_ULTRA
case SEND_UI_FRAME:
/* Only allowed to repeat an operation twice */
@@ -735,8 +759,10 @@ static int irlap_state_conn(struct irlap
break;
case DISCONNECT_REQUEST:
+ IRDA_DEBUG(0, __FUNCTION__ "(), Disconnect request!\n");
irlap_send_dm_frame(self);
- irlap_next_state( self, LAP_CONN);
+ irlap_next_state( self, LAP_NDM);
+ irlap_disconnect_indication(self, LAP_DISC_INDICATION);
break;
default:
IRDA_DEBUG(1, __FUNCTION__ "(), Unknown event %d, %s\n", event,
@@ -1417,13 +1443,12 @@ static int irlap_state_nrm_p(struct irla
irlap_start_final_timer(self, self->final_timeout);
break;
case RECV_RD_RSP:
- IRDA_DEBUG(0, __FUNCTION__ "(), RECV_RD_RSP\n");
+ IRDA_DEBUG(1, __FUNCTION__ "(), RECV_RD_RSP\n");
- irlap_next_state(self, LAP_PCLOSE);
- irlap_send_disc_frame(self);
irlap_flush_all_queues(self);
- irlap_start_final_timer(self, self->final_timeout);
- self->retry_count = 0;
+ irlap_next_state(self, LAP_XMIT_P);
+ /* Call back the LAP state machine to do a proper disconnect */
+ irlap_disconnect_request(self);
break;
default:
IRDA_DEBUG(1, __FUNCTION__ "(), Unknown event %s\n",