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