[PATCH 2.4] : ir241_flow_sched_ttp-6.diff

From: Jean Tourrilhes (jt@bougret.hpl.hp.com)
Date: Mon Dec 02 2002 - 18:37:47 EST


ir241_flow_sched_ttp-6.diff :
---------------------------
                <Won't compile without ir241_flow_sched_lap_lmp-6.diff>
        o [CORRECT] Fix race condition when starting todo timer
        o [CORRECT] Fix race condition when stopping higher layer
                Higher layer would think it is stopped and us it is started
        o [CORRECT] Give credit even if packets in Tx queue
                If Tx queue was stopped, could starve peer and deadlock
        o [CORRECT] Protect Rx credit update with spinlock
        o [CORRECT] Calculate properly self->avail_credit
                Didn't take into account queued Rx fragments
                Incremented even if Rx frame not delivered to higher layer
                -> would never stop the peer (i.e. not flow control)
                -> could become infinite
        o [CORRECT] Send credit when higher layer reenable receive
                Peer wouldn't restart Tx to us if flow stopped
        o [FEATURE] Implement LAP queue not full notification
                Lower latency, ...
        o [FEATURE] Reduce Tx queue to 8 packets (from 10)
                But make sure we can always send a full LAP window (7)
        o [FEATURE] Fix and optimise TTP flow control
                Make sure peer can always send a full LAP window (7)
                Minimise explicit credit updates (give_credit)
        o [FEATURE] Remove need for todo timer in Tx/Rx paths
                Less potential races, lower latency, lower context switches
                Could not use tasklet because broken API, better anyway ;-)

diff -u -p -r linux/include/net/irda/irttp.d6.h linux/include/net/irda/irttp.h
--- linux/include/net/irda/irttp.d6.h Wed Apr 10 14:04:47 2002
+++ linux/include/net/irda/irttp.h Wed Apr 10 16:31:44 2002
@@ -42,11 +42,48 @@
 #define TTP_PARAMETERS 0x80
 #define TTP_MORE 0x80
 
-#define DEFAULT_INITIAL_CREDIT 14
+/* Transmission queue sizes */
+/* Worst case scenario, two window of data - Jean II */
+#define TTP_TX_MAX_QUEUE 14
+/* We need to keep at least 5 frames to make sure that we can refill
+ * appropriately the LAP layer. LAP keeps only two buffers, and we need
+ * to have 7 to make a full window - Jean II */
+#define TTP_TX_LOW_THRESHOLD 5
+/* Most clients are synchronous with respect to flow control, so we can
+ * keep a low number of Tx buffers in TTP - Jean II */
+#define TTP_TX_HIGH_THRESHOLD 7
+
+/* Receive queue sizes */
+/* Minimum of credit that the peer should hold.
+ * If the peer has less credits than 9 frames, we will explicitely send
+ * him some credits (through irttp_give_credit() and a specific frame).
+ * Note that when we give credits it's likely that it won't be sent in
+ * this LAP window, but in the next one. So, we make sure that the peer
+ * has something to send while waiting for credits (one LAP window == 7
+ * + 1 frames while he process the credits). - Jean II */
+#define TTP_RX_MIN_CREDIT 8
+/* This is the default maximum number of credits held by the peer, so the
+ * default maximum number of frames he can send us before needing flow
+ * control answer from us (this may be negociated differently at TSAP setup).
+ * We want to minimise the number of times we have to explicitely send some
+ * credit to the peer, hoping we can piggyback it on the return data. In
+ * particular, it doesn't make sense for us to send credit more than once
+ * per LAP window.
+ * Moreover, giving credits has some latency, so we need strictly more than
+ * a LAP window, otherwise we may already have credits in our Tx queue.
+ * But on the other hand, we don't want to keep too many Rx buffer here
+ * before starting to flow control the other end, so make it exactly one
+ * LAP window + 1 + MIN_CREDITS. - Jean II */
+#define TTP_RX_DEFAULT_CREDIT 16
+/* Maximum number of credits we can allow the peer to have, and therefore
+ * maximum Rx queue size.
+ * Note that we try to deliver packets to the higher layer every time we
+ * receive something, so in normal mode the Rx queue will never contains
+ * more than one or two packets. - Jean II */
+#define TTP_RX_MAX_CREDIT 21
 
-#define TTP_LOW_THRESHOLD 4
-#define TTP_HIGH_THRESHOLD 10
-#define TTP_MAX_QUEUE 14
+/* What clients should use when calling ttp_open_tsap() */
+#define DEFAULT_INITIAL_CREDIT TTP_RX_DEFAULT_CREDIT
 
 /* Some priorities for disconnect requests */
 #define P_NORMAL 0
@@ -90,7 +127,7 @@ struct tsap_cb {
 
         struct net_device_stats stats;
         struct timer_list todo_timer;
-
+
         __u32 max_seg_size; /* Max data that fit into an IrLAP frame */
         __u8 max_header_size;
 
@@ -131,6 +168,7 @@ int irttp_disconnect_request(struct tsap
 void irttp_flow_request(struct tsap_cb *self, LOCAL_FLOW flow);
 void irttp_status_indication(void *instance,
                              LINK_STATUS link, LOCK_STATUS lock);
+void irttp_flow_indication(void *instance, void *sap, LOCAL_FLOW flow);
 struct tsap_cb *irttp_dup(struct tsap_cb *self, void *instance);
 
 static __inline __u32 irttp_get_saddr(struct tsap_cb *self)
diff -u -p -r linux/net/irda/irttp.d6.c linux/net/irda/irttp.c
--- linux/net/irda/irttp.d6.c Wed Apr 10 16:31:06 2002
+++ linux/net/irda/irttp.c Wed Apr 10 16:31:44 2002
@@ -59,8 +59,8 @@ static void irttp_run_rx_queue(struct ts
 
 static void irttp_flush_queues(struct tsap_cb *self);
 static void irttp_fragment_skb(struct tsap_cb *self, struct sk_buff *skb);
-static void irttp_start_todo_timer(struct tsap_cb *self, int timeout);
 static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self);
+static void irttp_todo_expired(unsigned long data);
 static int irttp_param_max_sdu_size(void *instance, irda_param_t *param,
                                     int get);
 
@@ -72,6 +72,8 @@ static pi_minor_info_t pi_minor_call_tab
 static pi_major_info_t pi_major_call_table[] = {{ pi_minor_call_table, 2 }};
 static pi_param_info_t param_info = { pi_major_call_table, 1, 0x0f, 4 };
 
+/************************ GLOBAL PROCEDURES ************************/
+
 /*
  * Function irttp_init (void)
  *
@@ -126,6 +128,239 @@ void irttp_cleanup(void)
 }
 #endif
 
+/*************************** SUBROUTINES ***************************/
+
+/*
+ * Function irttp_start_todo_timer (self, timeout)
+ *
+ * Start todo timer.
+ *
+ * Made it more effient and unsensitive to race conditions - Jean II
+ */
+static inline void irttp_start_todo_timer(struct tsap_cb *self, int timeout)
+{
+ /* Set new value for timer */
+ mod_timer(&self->todo_timer, jiffies + timeout);
+}
+
+/*
+ * Function irttp_todo_expired (data)
+ *
+ * Todo timer has expired!
+ *
+ * One of the restriction of the timer is that it is run only on the timer
+ * interrupt which run every 10ms. This mean that even if you set the timer
+ * with a delay of 0, it may take up to 10ms before it's run.
+ * So, to minimise latency and keep cache fresh, we try to avoid using
+ * it as much as possible.
+ * Note : we can't use tasklets, because they can't be asynchronously
+ * killed (need user context), and we can't guarantee that here...
+ * Jean II
+ */
+static void irttp_todo_expired(unsigned long data)
+{
+ struct tsap_cb *self = (struct tsap_cb *) data;
+
+ /* Check that we still exist */
+ if (!self || self->magic != TTP_TSAP_MAGIC)
+ return;
+
+ IRDA_DEBUG(4, __FUNCTION__ "(instance=%p)\n", self);
+
+ /* Try to make some progress, especially on Tx side - Jean II */
+ irttp_run_rx_queue(self);
+ irttp_run_tx_queue(self);
+
+ /* Check if time for disconnect */
+ 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 */
+ 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, HZ/10);
+
+ /* No reason to try and close now */
+ return;
+ }
+ }
+
+ /* Check if it's closing time */
+ if (self->close_pend)
+ /* Finish cleanup */
+ irttp_close_tsap(self);
+}
+
+/*
+ * Function irttp_flush_queues (self)
+ *
+ * Flushes (removes all frames) in transitt-buffer (tx_list)
+ */
+void irttp_flush_queues(struct tsap_cb *self)
+{
+ struct sk_buff* skb;
+
+ IRDA_DEBUG(4, __FUNCTION__ "()\n");
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
+
+ /* Deallocate frames waiting to be sent */
+ while ((skb = skb_dequeue(&self->tx_queue)) != NULL)
+ dev_kfree_skb(skb);
+
+ /* Deallocate received frames */
+ while ((skb = skb_dequeue(&self->rx_queue)) != NULL)
+ dev_kfree_skb(skb);
+
+ /* Deallocate received fragments */
+ while ((skb = skb_dequeue(&self->rx_fragments)) != NULL)
+ dev_kfree_skb(skb);
+}
+
+/*
+ * Function irttp_reassemble (self)
+ *
+ * Makes a new (continuous) skb of all the fragments in the fragment
+ * queue
+ *
+ */
+static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self)
+{
+ struct sk_buff *skb, *frag;
+ int n = 0; /* Fragment index */
+
+ ASSERT(self != NULL, return NULL;);
+ ASSERT(self->magic == TTP_TSAP_MAGIC, return NULL;);
+
+ IRDA_DEBUG(2, __FUNCTION__ "(), self->rx_sdu_size=%d\n",
+ self->rx_sdu_size);
+
+ skb = dev_alloc_skb(TTP_HEADER + self->rx_sdu_size);
+ if (!skb)
+ return NULL;
+
+ /*
+ * Need to reserve space for TTP header in case this skb needs to
+ * be requeued in case delivery failes
+ */
+ skb_reserve(skb, TTP_HEADER);
+ skb_put(skb, self->rx_sdu_size);
+
+ /*
+ * Copy all fragments to a new buffer
+ */
+ while ((frag = skb_dequeue(&self->rx_fragments)) != NULL) {
+ memcpy(skb->data+n, frag->data, frag->len);
+ n += frag->len;
+
+ dev_kfree_skb(frag);
+ }
+ IRDA_DEBUG(2, __FUNCTION__ "(), frame len=%d\n", n);
+
+ IRDA_DEBUG(2, __FUNCTION__ "(), rx_sdu_size=%d\n", self->rx_sdu_size);
+ ASSERT(n <= self->rx_sdu_size, return NULL;);
+
+ /* Set the new length */
+ skb_trim(skb, n);
+
+ self->rx_sdu_size = 0;
+
+ return skb;
+}
+
+/*
+ * Function irttp_fragment_skb (skb)
+ *
+ * Fragments a frame and queues all the fragments for transmission
+ *
+ */
+static inline void irttp_fragment_skb(struct tsap_cb *self,
+ struct sk_buff *skb)
+{
+ struct sk_buff *frag;
+ __u8 *frame;
+
+ IRDA_DEBUG(2, __FUNCTION__ "()\n");
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
+ ASSERT(skb != NULL, return;);
+
+ /*
+ * Split frame into a number of segments
+ */
+ while (skb->len > self->max_seg_size) {
+ IRDA_DEBUG(2, __FUNCTION__ "(), fragmenting ...\n");
+
+ /* Make new segment */
+ frag = dev_alloc_skb(self->max_seg_size+self->max_header_size);
+ if (!frag)
+ return;
+
+ skb_reserve(frag, self->max_header_size);
+
+ /* Copy data from the original skb into this fragment. */
+ memcpy(skb_put(frag, self->max_seg_size), skb->data,
+ self->max_seg_size);
+
+ /* Insert TTP header, with the more bit set */
+ frame = skb_push(frag, TTP_HEADER);
+ frame[0] = TTP_MORE;
+
+ /* Hide the copied data from the original skb */
+ skb_pull(skb, self->max_seg_size);
+
+ /* Queue fragment */
+ skb_queue_tail(&self->tx_queue, frag);
+ }
+ /* Queue what is left of the original skb */
+ IRDA_DEBUG(2, __FUNCTION__ "(), queuing last segment\n");
+
+ frame = skb_push(skb, TTP_HEADER);
+ frame[0] = 0x00; /* Clear more bit */
+
+ /* Queue fragment */
+ skb_queue_tail(&self->tx_queue, skb);
+}
+
+/*
+ * Function irttp_param_max_sdu_size (self, param)
+ *
+ * Handle the MaxSduSize parameter in the connect frames, this function
+ * will be called both when this parameter needs to be inserted into, and
+ * extracted from the connect frames
+ */
+static int irttp_param_max_sdu_size(void *instance, irda_param_t *param,
+ int get)
+{
+ struct tsap_cb *self;
+
+ self = (struct tsap_cb *) instance;
+
+ ASSERT(self != NULL, return -1;);
+ ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
+
+ if (get)
+ param->pv.i = self->tx_max_sdu_size;
+ else
+ self->tx_max_sdu_size = param->pv.i;
+
+ IRDA_DEBUG(1, __FUNCTION__ "(), MaxSduSize=%d\n", param->pv.i);
+
+ return 0;
+}
+
+/*************************** CLIENT CALLS ***************************/
+/************************** LMP CALLBACKS **************************/
+/* Everything is happily mixed up. Waiting for next clean up - Jean II */
+
 /*
  * Function irttp_open_tsap (stsap, notify)
  *
@@ -157,7 +392,10 @@ struct tsap_cb *irttp_open_tsap(__u8 sts
         memset(self, 0, sizeof(struct tsap_cb));
         spin_lock_init(&self->lock);
 
+ /* Initialise todo timer */
         init_timer(&self->todo_timer);
+ self->todo_timer.data = (unsigned long) self;
+ self->todo_timer.function = &irttp_todo_expired;
 
         /* Initialize callbacks for IrLMP to use */
         irda_notify_init(&ttp_notify);
@@ -166,6 +404,7 @@ struct tsap_cb *irttp_open_tsap(__u8 sts
         ttp_notify.disconnect_indication = irttp_disconnect_indication;
         ttp_notify.data_indication = irttp_data_indication;
         ttp_notify.udata_indication = irttp_udata_indication;
+ ttp_notify.flow_indication = irttp_flow_indication;
         if(notify->status_indication != NULL)
                 ttp_notify.status_indication = irttp_status_indication;
         ttp_notify.instance = self;
@@ -199,8 +438,8 @@ struct tsap_cb *irttp_open_tsap(__u8 sts
 
         hashbin_insert(irttp->tsaps, (irda_queue_t *) self, (int) self, NULL);
 
- if (credit > TTP_MAX_QUEUE)
- self->initial_credit = TTP_MAX_QUEUE;
+ if (credit > TTP_RX_MAX_CREDIT)
+ self->initial_credit = TTP_RX_MAX_CREDIT;
         else
                 self->initial_credit = credit;
 
@@ -224,7 +463,7 @@ static void __irttp_close_tsap(struct ts
 
         del_timer(&self->todo_timer);
 
- /* This one won't be cleaned up if we are diconnect_pend + close_pend
+ /* This one won't be cleaned up if we are disconnect_pend + close_pend
          * and we receive a disconnect_indication */
         if (self->disconnect_skb)
                 dev_kfree_skb(self->disconnect_skb);
@@ -262,7 +501,7 @@ int irttp_close_tsap(struct tsap_cb *sel
                         irttp_disconnect_request(self, NULL, P_NORMAL);
                 }
                 self->close_pend = TRUE;
- irttp_start_todo_timer(self, 1*HZ);
+ irttp_start_todo_timer(self, HZ/10);
 
                 return 0; /* Will be back! */
         }
@@ -327,6 +566,9 @@ int irttp_data_request(struct tsap_cb *s
         ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
         ASSERT(skb != NULL, return -1;);
 
+ IRDA_DEBUG(2, __FUNCTION__ " : queue len = %d\n",
+ skb_queue_len(&self->tx_queue));
+
         /* Check that nothing bad happens */
         if ((skb->len == 0) || (!self->connected)) {
                 WARNING(__FUNCTION__ "(), No data, or not connected\n");
@@ -358,12 +600,14 @@ int irttp_data_request(struct tsap_cb *s
         /*
          * Check if transmit queue is full
          */
- if (skb_queue_len(&self->tx_queue) >= TTP_MAX_QUEUE) {
+ if (skb_queue_len(&self->tx_queue) >= TTP_TX_MAX_QUEUE) {
                 /*
                  * Give it a chance to empty itself
                  */
                 irttp_run_tx_queue(self);
-
+
+ /* Drop packet. This error code should trigger the caller
+ * to requeue the packet in the client code - Jean II */
                 return -ENOBUFS;
         }
        
@@ -387,20 +631,25 @@ int irttp_data_request(struct tsap_cb *s
 
         /* Check if we can accept more data from client */
         if ((!self->tx_sdu_busy) &&
- (skb_queue_len(&self->tx_queue) > TTP_HIGH_THRESHOLD)) {
-
- /* Tx queue filling up, so stop client */
- self->tx_sdu_busy = TRUE;
-
- if (self->notify.flow_indication) {
+ (skb_queue_len(&self->tx_queue) > TTP_TX_HIGH_THRESHOLD)) {
+ /* Tx queue filling up, so stop client. */
+ if (self->notify.flow_indication) {
                          self->notify.flow_indication(self->notify.instance,
                                                      self, FLOW_STOP);
                 }
- }
+ /* self->tx_sdu_busy is the state of the client.
+ * Update state after notifying client to avoid
+ * race condition with irttp_flow_indication().
+ * If the queue empty itself after our test but before
+ * we set the flag, we will fix ourselves below in
+ * irttp_run_tx_queue().
+ * Jean II */
+ self->tx_sdu_busy = TRUE;
+ }
         
         /* Try to make some progress */
         irttp_run_tx_queue(self);
-
+
         return 0;
 }
 
@@ -416,28 +665,20 @@ static void irttp_run_tx_queue(struct ts
         unsigned long flags;
         int n;
 
+ IRDA_DEBUG(2, __FUNCTION__ "() : send_credit = %d, queue_len = %d\n",
+ self->send_credit, skb_queue_len(&self->tx_queue));
+
         /* Get exclusive access to the tx queue, otherwise don't touch it */
         if (irda_lock(&self->tx_queue_lock) == FALSE)
                 return;
 
- /* Try to send out frames as long as we have credits */
+ /* Try to send out frames as long as we have credits
+ * and as long as LAP is not full. If LAP is full, it will
+ * poll us through irttp_flow_indication() - Jean II */
         while ((self->send_credit > 0) &&
+ (!irlmp_lap_tx_queue_full(self->lsap)) &&
                (skb = skb_dequeue(&self->tx_queue)))
         {
- /*
- * Make sure we don't flood IrLAP with frames just because
- * the remote device has given us a lot of credits
- */
- if (irlmp_get_lap_tx_queue_len(self->lsap) > LAP_MAX_QUEUE) {
- /* Re-queue packet */
- skb_queue_head(&self->tx_queue, skb);
-
- /* Try later. Would be better if IrLAP could notify us */
- irttp_start_todo_timer(self, MSECS_TO_JIFFIES(10));
-
- break;
- }
-
                 /*
                  * Since we can transmit and receive frames concurrently,
                  * the code below is a critical region and we must assure that
@@ -484,32 +725,35 @@ static void irttp_run_tx_queue(struct ts
                  * Jean II */
                 if (skb->sk != NULL) {
                         /* IrSOCK application, IrOBEX, ... */
- IRDA_DEBUG(4, __FUNCTION__ "() : Detaching SKB from socket.\n");
-
- /* 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");
                 }
+ /* IrCOMM over IrTTP, IrLAN, ... */
 
                 /* Pass the skb to IrLMP - done */
                 irlmp_data_request(self->lsap, skb);
                 self->stats.tx_packets++;
+ }
 
- /* Check if we can accept more frames from client */
- if ((self->tx_sdu_busy) &&
- (skb_queue_len(&self->tx_queue) < TTP_LOW_THRESHOLD))
- {
- self->tx_sdu_busy = FALSE;
-
- if (self->notify.flow_indication)
- self->notify.flow_indication(
- self->notify.instance, self,
- FLOW_START);
- }
+ /* Check if we can accept more frames from client.
+ * We don't want to wait until the todo timer to do that, and we
+ * can't use tasklets (grr...), so we are obliged to give control
+ * to client. That's ok, this test will be true not too often
+ * (max once per LAP window) and we are called from places
+ * where we can spend a bit of time doing stuff. - Jean II */
+ if ((self->tx_sdu_busy) &&
+ (skb_queue_len(&self->tx_queue) < TTP_TX_LOW_THRESHOLD) &&
+ (!self->close_pend))
+ {
+ if (self->notify.flow_indication)
+ self->notify.flow_indication(self->notify.instance,
+ self, FLOW_START);
+
+ /* self->tx_sdu_busy is the state of the client.
+ * We don't really have a race here, but it's always safer
+ * to update our state after the client - Jean II */
+ self->tx_sdu_busy = FALSE;
         }
-
+
         /* Reset lock */
         self->tx_queue_lock = 0;
 }
@@ -520,7 +764,7 @@ static void irttp_run_tx_queue(struct ts
  * Send a dataless flowdata TTP-PDU and give available credit to peer
  * TSAP
  */
-void irttp_give_credit(struct tsap_cb *self)
+static inline void irttp_give_credit(struct tsap_cb *self)
 {
         struct sk_buff *tx_skb = NULL;
         unsigned long flags;
@@ -531,7 +775,7 @@ void irttp_give_credit(struct tsap_cb *s
 
         IRDA_DEBUG(4, __FUNCTION__ "() send=%d,avail=%d,remote=%d\n",
                    self->send_credit, self->avail_credit, self->remote_credit);
-
+
         /* Give credit to peer */
         tx_skb = dev_alloc_skb(64);
         if (!tx_skb)
@@ -606,6 +850,7 @@ static int irttp_data_indication(void *i
                                  struct sk_buff *skb)
 {
         struct tsap_cb *self;
+ unsigned long flags;
         int n;
 
         self = (struct tsap_cb *) instance;
@@ -614,47 +859,66 @@ static int irttp_data_indication(void *i
 
         self->stats.rx_packets++;
 
+ /* Deal with inbound credit
+ * Since we can transmit and receive frames concurrently,
+ * the code below is a critical region and we must assure that
+ * nobody messes with the credits while we update them.
+ */
+ spin_lock_irqsave(&self->lock, flags);
+ self->send_credit += n;
+ if (skb->len > 1)
+ self->remote_credit--;
+ spin_unlock_irqrestore(&self->lock, flags);
+
         /*
          * Data or dataless packet? Dataless frames contains only the
          * TTP_HEADER.
          */
         if (skb->len > 1) {
- /* Deal with inbound credit */
- self->send_credit += n;
- self->remote_credit--;
-
                 /*
                  * We don't remove the TTP header, since we must preserve the
                  * more bit, so the defragment routing knows what to do
                  */
                 skb_queue_tail(&self->rx_queue, skb);
         } else {
- self->send_credit += n; /* Dataless flowdata TTP-PDU */
+ /* Dataless flowdata TTP-PDU */
                 dev_kfree_skb(skb);
         }
 
+
+ /* Push data to the higher layer.
+ * We do it synchronously because running the todo timer for each
+ * receive packet would be too much overhead and latency.
+ * By passing control to the higher layer, we run the risk that
+ * it may take time or grab a lock. Most often, the higher layer
+ * will only put packet in a queue.
+ * Anyway, packets are only dripping through the IrDA, so we can
+ * have time before the next packet.
+ * Further, we are run from NET_BH, so the worse that can happen is
+ * us missing the optimal time to send back the PF bit in LAP.
+ * Jean II */
         irttp_run_rx_queue(self);
 
- /*
- * Give avay some credits to peer?
- */
- if ((skb_queue_empty(&self->tx_queue)) &&
- (self->remote_credit < TTP_LOW_THRESHOLD) &&
- (self->avail_credit > 0))
- {
- /* Schedule to start immediately after this thread */
- irttp_start_todo_timer(self, 0);
- /*irttp_give_credit(self);*/
- }
-
+ /* We now give credits to peer in irttp_run_rx_queue().
+ * We need to send credit *NOW*, otherwise we are going
+ * to miss the next Tx window. The todo timer may take
+ * a while before it's run... - Jean II */
+
         /*
          * If the peer device has given us some credits and we didn't have
- * anyone from before, the we need to shedule the tx queue?
+ * anyone from before, then we need to shedule the tx queue.
+ * We need to do that because our Tx have stopped (so we may not
+ * get any LAP flow indication) and the user may be stopped as
+ * well. - Jean II
          */
         if (self->send_credit == n) {
- /*irttp_run_tx_queue(self);*/
- irttp_start_todo_timer(self, 0);
+ /* Restart pushing stuff to LAP */
+ irttp_run_tx_queue(self);
+ /* Note : we don't want to schedule the todo timer
+ * because it has horrible latency. No tasklets
+ * because the tasklet API is broken. - Jean II */
         }
+
         return 0;
 }
 
@@ -687,6 +951,50 @@ void irttp_status_indication(void *insta
 }
 
 /*
+ * Function irttp_flow_indication (self, reason)
+ *
+ * Flow_indication : IrLAP tells us to send more data.
+ *
+ */
+void irttp_flow_indication(void *instance, void *sap, LOCAL_FLOW flow)
+{
+ struct tsap_cb *self;
+
+ self = (struct tsap_cb *) instance;
+
+ ASSERT(self != NULL, return;);
+ ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
+
+ IRDA_DEBUG(4, __FUNCTION__ "(instance=%p)\n", self);
+
+ /* We are "polled" directly from LAP, and the LAP want to fill
+ * its Tx window. We want to do our best to send it data, so that
+ * we maximise the window. On the other hand, we want to limit the
+ * amount of work here so that LAP doesn't hang forever waiting
+ * for packets. - Jean II */
+
+ /* Try to send some packets. Currently, LAP calls us every time
+ * there is one free slot, so we will send only one packet.
+ * This allow the scheduler to do its round robin - Jean II */
+ irttp_run_tx_queue(self);
+
+ /* Note regarding the interraction with higher layer.
+ * irttp_run_tx_queue() may call the client when its queue
+ * start to empty, via notify.flow_indication(). Initially.
+ * I wanted this to happen in a tasklet, to avoid client
+ * grabbing the CPU, but we can't use tasklets safely. And timer
+ * is definitely too slow.
+ * This will happen only once per LAP window, and usually at
+ * the third packet (unless window is smaller). LAP is still
+ * doing mtt and sending first packet so it's sort of OK
+ * to do that. Jean II */
+
+ /* If we need to send disconnect. try to do it now */
+ if(self->disconnect_pend)
+ irttp_start_todo_timer(self, 0);
+}
+
+/*
  * Function irttp_flow_request (self, command)
  *
  * This funtion could be used by the upper layers to tell IrTTP to stop
@@ -709,7 +1017,10 @@ void irttp_flow_request(struct tsap_cb *
                 IRDA_DEBUG(1, __FUNCTION__ "(), flow start\n");
                 self->rx_sdu_busy = FALSE;
                 
+ /* Client say he can accept more data, try to free our
+ * queues ASAP - Jean II */
                 irttp_run_rx_queue(self);
+
                 break;
         default:
                 IRDA_DEBUG(1, __FUNCTION__ "(), Unknown flow command!\n");
@@ -1147,10 +1458,15 @@ int irttp_disconnect_request(struct tsap
 
                         irttp_run_tx_queue(self);
 
- irttp_start_todo_timer(self, MSECS_TO_JIFFIES(1000));
+ irttp_start_todo_timer(self, HZ/10);
                         return -1;
                 }
         }
+ /* Note : we don't need to check if self->rx_queue is full and the
+ * state of self->rx_sdu_busy because the disconnect response will
+ * be sent at the LMP level (so even if the peer has its Tx queue
+ * full of data). - Jean II */
+
         IRDA_DEBUG(1, __FUNCTION__ "(), Disconnecting ...\n");
         self->connected = FALSE;
 
@@ -1281,8 +1597,7 @@ void irttp_run_rx_queue(struct tsap_cb *
          * Reassemble all frames in receive queue and deliver them
          */
         while (!self->rx_sdu_busy && (skb = skb_dequeue(&self->rx_queue))) {
- self->avail_credit++;
-
+ /* This bit will tell us if it's the last fragment or not */
                 more = skb->data[0] & 0x80;
 
                 /* Remove TTP header */
@@ -1293,7 +1608,7 @@ void irttp_run_rx_queue(struct tsap_cb *
 
                 /*
                  * If SAR is disabled, or user has requested no reassembly
- * of received fragements then we just deliver them
+ * of received fragments then we just deliver them
                  * immediately. This can be requested by clients that
                  * implements byte streams without any message boundaries
                  */
@@ -1353,236 +1668,46 @@ void irttp_run_rx_queue(struct tsap_cb *
                 }
                 self->rx_sdu_size = 0;
         }
- /* Reset lock */
- self->rx_queue_lock = 0;
-}
-
-/*
- * Function irttp_flush_queues (self)
- *
- * Flushes (removes all frames) in transitt-buffer (tx_list)
- */
-void irttp_flush_queues(struct tsap_cb *self)
-{
- struct sk_buff* skb;
-
- IRDA_DEBUG(4, __FUNCTION__ "()\n");
-
- ASSERT(self != NULL, return;);
- ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
-
- /* Deallocate frames waiting to be sent */
- while ((skb = skb_dequeue(&self->tx_queue)) != NULL)
- dev_kfree_skb(skb);
-
- /* Deallocate received frames */
- while ((skb = skb_dequeue(&self->rx_queue)) != NULL)
- dev_kfree_skb(skb);
-
- /* Deallocate received fragments */
- while ((skb = skb_dequeue(&self->rx_fragments)) != NULL)
- dev_kfree_skb(skb);
-}
-
-/*
- * Function irttp_reasseble (self)
- *
- * Makes a new (continuous) skb of all the fragments in the fragment
- * queue
- *
- */
-static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self)
-{
- struct sk_buff *skb, *frag;
- int n = 0; /* Fragment index */
-
- ASSERT(self != NULL, return NULL;);
- ASSERT(self->magic == TTP_TSAP_MAGIC, return NULL;);
-
- IRDA_DEBUG(2, __FUNCTION__ "(), self->rx_sdu_size=%d\n",
- self->rx_sdu_size);
-
- skb = dev_alloc_skb(TTP_HEADER + self->rx_sdu_size);
- if (!skb)
- return NULL;
-
- /*
- * Need to reserve space for TTP header in case this skb needs to
- * be requeued in case delivery failes
- */
- skb_reserve(skb, TTP_HEADER);
- skb_put(skb, self->rx_sdu_size);
 
         /*
- * Copy all fragments to a new buffer
+ * It's not trivial to keep track of how many credits are available
+ * by incrementing at each packet, because delivery may fail
+ * (irttp_do_data_indication() may requeue the frame) and because
+ * we need to take care of fragmentation.
+ * We want the other side to send up to initial_credit packets.
+ * We have some frames in our queues, and we have already allowed it
+ * to send remote_credit.
+ * No need to spinlock, write is atomic and self correcting...
+ * Jean II
          */
- while ((frag = skb_dequeue(&self->rx_fragments)) != NULL) {
- memcpy(skb->data+n, frag->data, frag->len);
- n += frag->len;
-
- dev_kfree_skb(frag);
- }
- IRDA_DEBUG(2, __FUNCTION__ "(), frame len=%d\n", n);
-
- IRDA_DEBUG(2, __FUNCTION__ "(), rx_sdu_size=%d\n", self->rx_sdu_size);
- ASSERT(n <= self->rx_sdu_size, return NULL;);
-
- /* Set the new length */
- skb_trim(skb, n);
-
- self->rx_sdu_size = 0;
-
- return skb;
-}
-
-/*
- * Function irttp_fragment_skb (skb)
- *
- * Fragments a frame and queues all the fragments for transmission
- *
- */
-static void irttp_fragment_skb(struct tsap_cb *self, struct sk_buff *skb)
-{
- struct sk_buff *frag;
- __u8 *frame;
-
- IRDA_DEBUG(2, __FUNCTION__ "()\n");
-
- ASSERT(self != NULL, return;);
- ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
- ASSERT(skb != NULL, return;);
-
- /*
- * Split frame into a number of segments
- */
- while (skb->len > self->max_seg_size) {
- IRDA_DEBUG(2, __FUNCTION__ "(), fragmenting ...\n");
-
- /* Make new segment */
- frag = dev_alloc_skb(self->max_seg_size+self->max_header_size);
- if (!frag)
- return;
-
- skb_reserve(frag, self->max_header_size);
-
- /* Copy data from the original skb into this fragment. */
- memcpy(skb_put(frag, self->max_seg_size), skb->data,
- self->max_seg_size);
-
- /* Insert TTP header, with the more bit set */
- frame = skb_push(frag, TTP_HEADER);
- frame[0] = TTP_MORE;
-
- /* Hide the copied data from the original skb */
- skb_pull(skb, self->max_seg_size);
-
- /* Queue fragment */
- skb_queue_tail(&self->tx_queue, frag);
- }
- /* Queue what is left of the original skb */
- IRDA_DEBUG(2, __FUNCTION__ "(), queuing last segment\n");
-
- frame = skb_push(skb, TTP_HEADER);
- frame[0] = 0x00; /* Clear more bit */
-
- /* Queue fragment */
- skb_queue_tail(&self->tx_queue, skb);
-}
-
-/*
- * Function irttp_param_max_sdu_size (self, param)
- *
- * Handle the MaxSduSize parameter in the connect frames, this function
- * will be called both when this parameter needs to be inserted into, and
- * extracted from the connect frames
- */
-static int irttp_param_max_sdu_size(void *instance, irda_param_t *param,
- int get)
-{
- struct tsap_cb *self;
-
- self = (struct tsap_cb *) instance;
-
- ASSERT(self != NULL, return -1;);
- ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
-
- if (get)
- param->pv.i = self->tx_max_sdu_size;
- else
- self->tx_max_sdu_size = param->pv.i;
-
- IRDA_DEBUG(1, __FUNCTION__ "(), MaxSduSize=%d\n", param->pv.i);
-
- return 0;
-}
-
-/*
- * Function irttp_todo_expired (data)
- *
- * Todo timer has expired!
- *
- */
-static void irttp_todo_expired(unsigned long data)
-{
- struct tsap_cb *self = (struct tsap_cb *) data;
-
- /* Check that we still exist */
- if (!self || self->magic != TTP_TSAP_MAGIC)
- return;
-
- irttp_run_rx_queue(self);
- irttp_run_tx_queue(self);
-
- /* Give avay some credits to peer? */
- if ((self->remote_credit < TTP_LOW_THRESHOLD) &&
- (self->avail_credit > 0) && (skb_queue_empty(&self->tx_queue)))
- {
+ self->avail_credit = (self->initial_credit -
+ (self->remote_credit +
+ skb_queue_len(&self->rx_queue) +
+ skb_queue_len(&self->rx_fragments)));
+
+ /* Do we have too much credits to send to peer ? */
+ if ((self->remote_credit <= TTP_RX_MIN_CREDIT) &&
+ (self->avail_credit > 0)) {
+ /* Send explicit credit frame */
                 irttp_give_credit(self);
+ /* Note : do *NOT* check if tx_queue is non-empty, that
+ * will produce deadlocks. I repeat : send a credit frame
+ * even if we have something to send in our Tx queue.
+ * If we have credits, it means that our Tx queue is blocked.
+ *
+ * Let's suppose the peer can't keep up with our Tx. He will
+ * flow control us by not sending us any credits, and we
+ * will stop Tx and start accumulating credits here.
+ * Up to the point where the peer will stop its Tx queue,
+ * for lack of credits.
+ * Let's assume the peer application is single threaded.
+ * It will block on Tx and never consume any Rx buffer.
+ * Deadlock. Guaranteed. - Jean II
+ */
         }
 
- /* Check if time for disconnect */
- 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 */
- 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);
-
- /* No reason to try and close now */
- return;
- }
- }
-
- /* Check if it's closing time */
- if (self->close_pend)
- irttp_close_tsap(self);
-}
-
-/*
- * Function irttp_start_todo_timer (self, timeout)
- *
- * Start todo timer.
- *
- */
-static void irttp_start_todo_timer(struct tsap_cb *self, int timeout)
-{
- ASSERT(self != NULL, return;);
- ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
-
- del_timer(&self->todo_timer);
-
- self->todo_timer.data = (unsigned long) self;
- self->todo_timer.function = &irttp_todo_expired;
- self->todo_timer.expires = jiffies + timeout;
-
- add_timer(&self->todo_timer);
+ /* Reset lock */
+ self->rx_queue_lock = 0;
 }
 
 #ifdef CONFIG_PROC_FS
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Sat Dec 07 2002 - 22:00:15 EST