patch-2.4.15 linux/net/irda/irttp.c

Next file: linux/net/irda/parameters.c
Previous file: linux/net/irda/irsyms.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.14/linux/net/irda/irttp.c linux/net/irda/irttp.c
@@ -11,6 +11,7 @@
  * 
  *     Copyright (c) 1998-2000 Dag Brattli <dagb@cs.uit.no>, 
  *     All Rights Reserved.
+ *     Copyright (c) 2000-2001 Jean Tourrilhes <jt@hpl.hp.com>
  *     
  *     This program is free software; you can redistribute it and/or 
  *     modify it under the terms of the GNU General Public License as 
@@ -223,6 +224,11 @@
 
 	del_timer(&self->todo_timer);
 
+	/* This one won't be cleaned up if we are diconnect_pend + close_pend
+	 * and we receive a disconnect_indication */
+	if (self->disconnect_skb)
+		dev_kfree_skb(self->disconnect_skb);
+
 	self->connected = FALSE;
 	self->magic = ~TTP_TSAP_MAGIC;
 
@@ -235,6 +241,9 @@
  *    Remove TSAP from list of all TSAPs and then deallocate all resources
  *    associated with this TSAP
  *
+ * Note : because we *free* the tsap structure, it is the responsability
+ * of the caller to make sure we are called only once and to deal with
+ * possible race conditions. - Jean II
  */
 int irttp_close_tsap(struct tsap_cb *self)
 {
@@ -248,8 +257,8 @@
 	/* Make sure tsap has been disconnected */
 	if (self->connected) {
 		/* Check if disconnect is not pending */
-		if (!self->disconnect_pend) {
-			IRDA_DEBUG(0, __FUNCTION__ "(), TSAP still connected!\n");
+		if (!test_bit(0, &self->disconnect_pend)) {
+			WARNING(__FUNCTION__ "(), TSAP still connected!\n");
 			irttp_disconnect_request(self, NULL, P_NORMAL);
 		}
 		self->close_pend = TRUE;
@@ -407,6 +416,7 @@
 	unsigned long flags;
 	int n;
 
+	/* Get exclusive access to the tx queue, otherwise don't touch it */
 	if (irda_lock(&self->tx_queue_lock) == FALSE)
 		return;
 
@@ -473,27 +483,17 @@
 		 * close the socket, we are dead !
 		 * Jean II */
 		if (skb->sk != NULL) {
-			struct sk_buff *tx_skb;
-
 			/* IrSOCK application, IrOBEX, ... */
 			IRDA_DEBUG(4, __FUNCTION__ "() : Detaching SKB from socket.\n");
-			/* Note : still looking for a more efficient way
-			 * to do that - Jean II */
 
-			/* Get another skb on the same buffer, but without
-			 * a reference to the socket (skb->sk = NULL) */
-			tx_skb = skb_clone(skb, GFP_ATOMIC);
-			if (tx_skb != NULL) {
-				/* Release the skb associated with the
-				 * socket, and use the new skb insted */
-				kfree_skb(skb);
-				skb = tx_skb;
-			}
+			/* That's the right way to do it - Jean II */
+			skb_orphan(skb);
 		} else {
 			/* IrCOMM over IrTTP, IrLAN, ... */
 			IRDA_DEBUG(4, __FUNCTION__ "() : Got SKB not attached to a socket.\n");
 		}
 
+		/* Pass the skb to IrLMP - done */
 		irlmp_data_request(self->lsap, skb);
 		self->stats.tx_packets++;
 
@@ -1105,18 +1105,23 @@
 	/* Already disconnected? */
 	if (!self->connected) {
 		IRDA_DEBUG(4, __FUNCTION__ "(), already disconnected!\n");
+		if (userdata)
+			dev_kfree_skb(userdata);
 		return -1;
 	}
 
-	/* Disconnect already pending? */
-	if (self->disconnect_pend) {
-		IRDA_DEBUG(1, __FUNCTION__ "(), disconnect already pending\n");
-		if (userdata) {
+	/* Disconnect already pending ?
+	 * We need to use an atomic operation to prevent reentry. This
+	 * function may be called from various context, like user, timer
+	 * for following a disconnect_indication() (i.e. net_bh).
+	 * Jean II */
+	if(test_and_set_bit(0, &self->disconnect_pend)) {
+		IRDA_DEBUG(0, __FUNCTION__ "(), disconnect already pending\n");
+		if (userdata)
 			dev_kfree_skb(userdata);
-		}
 
 		/* Try to make some progress */
-		irttp_run_rx_queue(self);
+		irttp_run_tx_queue(self);
 		return -1;
 	}
 
@@ -1125,25 +1130,20 @@
 	 */
 	if (skb_queue_len(&self->tx_queue) > 0) {
 		if (priority == P_HIGH) {
-			IRDA_DEBUG(1, __FUNCTION__  "High priority!!()\n" );
-			
 			/* 
 			 *  No need to send the queued data, if we are 
 			 *  disconnecting right now since the data will
 			 *  not have any usable connection to be sent on
 			 */
+			IRDA_DEBUG(1, __FUNCTION__  "High priority!!()\n" );
 			irttp_flush_queues(self);
 		} else if (priority == P_NORMAL) {
 			/* 
-			 *  Must delay disconnect til after all data segments
-			 *  have been sent an the tx_queue is empty
+			 *  Must delay disconnect until after all data segments
+			 *  have been sent and the tx_queue is empty
 			 */
-			if (userdata)
-				self->disconnect_skb = userdata;
-			else
-				self->disconnect_skb = NULL;
-
-			self->disconnect_pend = TRUE;
+			/* We'll reuse this one later for the disconnect */
+			self->disconnect_skb = userdata;  /* May be NULL */
 
 			irttp_run_tx_queue(self);
 
@@ -1152,9 +1152,8 @@
 		}
 	}
 	IRDA_DEBUG(1, __FUNCTION__ "(), Disconnecting ...\n");
-
 	self->connected = FALSE;
-	
+
 	if (!userdata) {
 		skb = dev_alloc_skb(64);
 		if (!skb)
@@ -1169,6 +1168,9 @@
 	}
 	ret = irlmp_disconnect_request(self->lsap, userdata);
 
+	/* The disconnect is no longer pending */
+	clear_bit(0, &self->disconnect_pend);	/* FALSE */
+
 	return ret;
 }
 
@@ -1190,19 +1192,27 @@
 	ASSERT(self != NULL, return;);
 	ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
 	
+	/* Prevent higher layer to send more data */
 	self->connected = FALSE;
 	
 	/* Check if client has already tried to close the TSAP */
 	if (self->close_pend) {
+		/* In this case, the higher layer is probably gone. Don't
+		 * bother it and clean up the remains - Jean II */
+		if (skb)
+			dev_kfree_skb(skb);
 		irttp_close_tsap(self);
 		return;
 	}
 
+	/* If we are here, we assume that is the higher layer is still
+	 * waiting for the disconnect notification and able to process it,
+	 * even if he tried to disconnect. Otherwise, it would have already
+	 * attempted to close the tsap and self->close_pend would be TRUE.
+	 * Jean II */
+
 	/* No need to notify the client if has already tried to disconnect */
-	if (self->disconnect_pend)
-		return;
-	
-	if (self->notify.disconnect_indication)
+	if(self->notify.disconnect_indication)
 		self->notify.disconnect_indication(self->notify.instance, self,
 						   reason, skb);
 	else
@@ -1222,7 +1232,7 @@
 	int err;
 
 	/* Check if client has already tried to close the TSAP */
-	if (self->close_pend || self->disconnect_pend) {
+	if (self->close_pend) {
 		dev_kfree_skb(skb);
 		return;
 	}
@@ -1263,6 +1273,7 @@
 	IRDA_DEBUG(2, __FUNCTION__ "() send=%d,avail=%d,remote=%d\n", 
 		   self->send_credit, self->avail_credit, self->remote_credit);
 
+	/* Get exclusive access to the rx queue, otherwise don't touch it */
 	if (irda_lock(&self->rx_queue_lock) == FALSE)
 		return;
 	
@@ -1500,7 +1511,7 @@
 	else
 		self->tx_max_sdu_size = param->pv.i;
 
-	IRDA_DEBUG(0, __FUNCTION__ "(), MaxSduSize=%d\n", param->pv.i);
+	IRDA_DEBUG(1, __FUNCTION__ "(), MaxSduSize=%d\n", param->pv.i);
 	
 	return 0;
 }
@@ -1530,18 +1541,16 @@
 	}
 
 	/* Check if time for disconnect */
-	if (self->disconnect_pend) {
+	if (test_bit(0, &self->disconnect_pend)) {
 		/* Check if it's possible to disconnect yet */
 		if (skb_queue_empty(&self->tx_queue)) {
-			
 			/* Make sure disconnect is not pending anymore */
-			self->disconnect_pend = FALSE;
-			if (self->disconnect_skb) {
-				irttp_disconnect_request(
-					self, self->disconnect_skb, P_NORMAL);
-				self->disconnect_skb = NULL;
-			} else
-				irttp_disconnect_request(self, NULL, P_NORMAL);
+			clear_bit(0, &self->disconnect_pend);	/* FALSE */
+
+			/* Note : self->disconnect_skb may be NULL */
+			irttp_disconnect_request(self, self->disconnect_skb,
+						 P_NORMAL);
+			self->disconnect_skb = NULL;
 		} else {
 			/* Try again later */
 			irttp_start_todo_timer(self, 1*HZ);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)