net: mark read-only arrays as const
[safe/jmp/linux-2.6] / net / sctp / sm_sideeffect.c
index b083312..86426aa 100644 (file)
@@ -434,7 +434,8 @@ sctp_timer_event_t *sctp_timer_events[SCTP_NUM_TIMEOUT_TYPES] = {
  *
  */
 static void sctp_do_8_2_transport_strike(struct sctp_association *asoc,
-                                        struct sctp_transport *transport)
+                                        struct sctp_transport *transport,
+                                        int is_hb)
 {
        /* The check for association's overall error counter exceeding the
         * threshold is done in the state function.
@@ -461,9 +462,15 @@ static void sctp_do_8_2_transport_strike(struct sctp_association *asoc,
         * expires, set RTO <- RTO * 2 ("back off the timer").  The
         * maximum value discussed in rule C7 above (RTO.max) may be
         * used to provide an upper bound to this doubling operation.
+        *
+        * Special Case:  the first HB doesn't trigger exponential backoff.
+        * The first unacknowleged HB triggers it.  We do this with a flag
+        * that indicates that we have an outstanding HB.
         */
-       transport->last_rto = transport->rto;
-       transport->rto = min((transport->rto * 2), transport->asoc->rto_max);
+       if (!is_hb || transport->hb_sent) {
+               transport->last_rto = transport->rto;
+               transport->rto = min((transport->rto * 2), transport->asoc->rto_max);
+       }
 }
 
 /* Worker routine to handle INIT command failure.  */
@@ -621,6 +628,11 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds,
        t->error_count = 0;
        t->asoc->overall_error_count = 0;
 
+       /* Clear the hb_sent flag to signal that we had a good
+        * acknowledgement.
+        */
+       t->hb_sent = 0;
+
        /* Mark the destination transport address as active if it is not so
         * marked.
         */
@@ -646,25 +658,13 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds,
                sctp_transport_hold(t);
 }
 
-/* Helper function to do a transport reset at the expiry of the hearbeat
- * timer.
- */
-static void sctp_cmd_transport_reset(sctp_cmd_seq_t *cmds,
-                                    struct sctp_association *asoc,
-                                    struct sctp_transport *t)
-{
-       sctp_transport_lower_cwnd(t, SCTP_LOWER_CWND_INACTIVE);
-
-       /* Mark one strike against a transport.  */
-       sctp_do_8_2_transport_strike(asoc, t);
-}
 
 /* Helper function to process the process SACK command.  */
 static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds,
                                 struct sctp_association *asoc,
                                 struct sctp_sackhdr *sackh)
 {
-       int err;
+       int err = 0;
 
        if (sctp_outq_sack(&asoc->outqueue, sackh)) {
                /* There are no more TSNs awaiting SACK.  */
@@ -672,11 +672,6 @@ static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds,
                                 SCTP_ST_OTHER(SCTP_EVENT_NO_PENDING_TSN),
                                 asoc->state, asoc->ep, asoc, NULL,
                                 GFP_ATOMIC);
-       } else {
-               /* Windows may have opened, so we need
-                * to check if we have DATA to transmit
-                */
-               err = sctp_outq_flush(&asoc->outqueue, 0);
        }
 
        return err;
@@ -691,7 +686,8 @@ static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds,
 {
        struct sctp_transport *t;
 
-       t = sctp_assoc_choose_shutdown_transport(asoc);
+       t = sctp_assoc_choose_alter_transport(asoc,
+                                             asoc->shutdown_last_sent_to);
        asoc->shutdown_last_sent_to = t;
        asoc->timeouts[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN] = t->rto;
        chunk->transport = t;
@@ -782,7 +778,7 @@ static void sctp_cmd_setup_t4(sctp_cmd_seq_t *cmds,
 {
        struct sctp_transport *t;
 
-       t = asoc->peer.active_path;
+       t = sctp_assoc_choose_alter_transport(asoc, chunk->transport);
        asoc->timeouts[SCTP_EVENT_TIMEOUT_T4_RTO] = t->rto;
        chunk->transport = t;
 }
@@ -792,36 +788,48 @@ static void sctp_cmd_process_operr(sctp_cmd_seq_t *cmds,
                                   struct sctp_association *asoc,
                                   struct sctp_chunk *chunk)
 {
-       struct sctp_operr_chunk *operr_chunk;
        struct sctp_errhdr *err_hdr;
+       struct sctp_ulpevent *ev;
 
-       operr_chunk = (struct sctp_operr_chunk *)chunk->chunk_hdr;
-       err_hdr = &operr_chunk->err_hdr;
+       while (chunk->chunk_end > chunk->skb->data) {
+               err_hdr = (struct sctp_errhdr *)(chunk->skb->data);
 
-       switch (err_hdr->cause) {
-       case SCTP_ERROR_UNKNOWN_CHUNK:
-       {
-               struct sctp_chunkhdr *unk_chunk_hdr;
+               ev = sctp_ulpevent_make_remote_error(asoc, chunk, 0,
+                                                    GFP_ATOMIC);
+               if (!ev)
+                       return;
 
-               unk_chunk_hdr = (struct sctp_chunkhdr *)err_hdr->variable;
-               switch (unk_chunk_hdr->type) {
-               /* ADDIP 4.1 A9) If the peer responds to an ASCONF with an
-                * ERROR chunk reporting that it did not recognized the ASCONF
-                * chunk type, the sender of the ASCONF MUST NOT send any
-                * further ASCONF chunks and MUST stop its T-4 timer.
-                */
-               case SCTP_CID_ASCONF:
-                       asoc->peer.asconf_capable = 0;
-                       sctp_add_cmd_sf(cmds, SCTP_CMD_TIMER_STOP,
+               sctp_ulpq_tail_event(&asoc->ulpq, ev);
+
+               switch (err_hdr->cause) {
+               case SCTP_ERROR_UNKNOWN_CHUNK:
+               {
+                       sctp_chunkhdr_t *unk_chunk_hdr;
+
+                       unk_chunk_hdr = (sctp_chunkhdr_t *)err_hdr->variable;
+                       switch (unk_chunk_hdr->type) {
+                       /* ADDIP 4.1 A9) If the peer responds to an ASCONF with
+                        * an ERROR chunk reporting that it did not recognized
+                        * the ASCONF chunk type, the sender of the ASCONF MUST
+                        * NOT send any further ASCONF chunks and MUST stop its
+                        * T-4 timer.
+                        */
+                       case SCTP_CID_ASCONF:
+                               if (asoc->peer.asconf_capable == 0)
+                                       break;
+
+                               asoc->peer.asconf_capable = 0;
+                               sctp_add_cmd_sf(cmds, SCTP_CMD_TIMER_STOP,
                                        SCTP_TO(SCTP_EVENT_TIMEOUT_T4_RTO));
+                               break;
+                       default:
+                               break;
+                       }
                        break;
+               }
                default:
                        break;
                }
-               break;
-       }
-       default:
-               break;
        }
 }
 
@@ -894,6 +902,35 @@ static void sctp_cmd_adaptation_ind(sctp_cmd_seq_t *commands,
                sctp_ulpq_tail_event(&asoc->ulpq, ev);
 }
 
+
+static void sctp_cmd_t1_timer_update(struct sctp_association *asoc,
+                                   sctp_event_timeout_t timer,
+                                   char *name)
+{
+       struct sctp_transport *t;
+
+       t = asoc->init_last_sent_to;
+       asoc->init_err_counter++;
+
+       if (t->init_sent_count > (asoc->init_cycle + 1)) {
+               asoc->timeouts[timer] *= 2;
+               if (asoc->timeouts[timer] > asoc->max_init_timeo) {
+                       asoc->timeouts[timer] = asoc->max_init_timeo;
+               }
+               asoc->init_cycle++;
+               SCTP_DEBUG_PRINTK(
+                       "T1 %s Timeout adjustment"
+                       " init_err_counter: %d"
+                       " cycle: %d"
+                       " timeout: %ld\n",
+                       name,
+                       asoc->init_err_counter,
+                       asoc->init_cycle,
+                       asoc->timeouts[timer]);
+       }
+
+}
+
 /* These three macros allow us to pull the debugging code out of the
  * main flow of sctp_do_sm() to keep attention focused on the real
  * functionality there.
@@ -1128,7 +1165,8 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
 
                case SCTP_CMD_REPORT_TSN:
                        /* Record the arrival of a TSN.  */
-                       sctp_tsnmap_mark(&asoc->peer.tsn_map, cmd->obj.u32);
+                       error = sctp_tsnmap_mark(&asoc->peer.tsn_map,
+                                                cmd->obj.u32);
                        break;
 
                case SCTP_CMD_REPORT_FWDTSN:
@@ -1201,6 +1239,11 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
                                sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
                                                SCTP_CHUNK(cmd->obj.ptr));
 
+                       if (new_obj->transport) {
+                               new_obj->transport->init_sent_count++;
+                               asoc->init_last_sent_to = new_obj->transport;
+                       }
+
                        /* FIXME - Eventually come up with a cleaner way to
                         * enabling COOKIE-ECHO + DATA bundling during
                         * multihoming stale cookie scenarios, the following
@@ -1337,7 +1380,8 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
 
                case SCTP_CMD_INIT_CHOOSE_TRANSPORT:
                        chunk = cmd->obj.ptr;
-                       t = sctp_assoc_choose_init_transport(asoc);
+                       t = sctp_assoc_choose_alter_transport(asoc,
+                                               asoc->init_last_sent_to);
                        asoc->init_last_sent_to = t;
                        chunk->transport = t;
                        t->init_sent_count++;
@@ -1350,26 +1394,9 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
                         * all transports have been tried at the current
                         * timeout.
                         */
-                       t = asoc->init_last_sent_to;
-                       asoc->init_err_counter++;
-
-                       if (t->init_sent_count > (asoc->init_cycle + 1)) {
-                               asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_INIT] *= 2;
-                               if (asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_INIT] >
-                                   asoc->max_init_timeo) {
-                                       asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_INIT] =
-                                               asoc->max_init_timeo;
-                               }
-                               asoc->init_cycle++;
-                               SCTP_DEBUG_PRINTK(
-                                       "T1 INIT Timeout adjustment"
-                                       " init_err_counter: %d"
-                                       " cycle: %d"
-                                       " timeout: %ld\n",
-                                       asoc->init_err_counter,
-                                       asoc->init_cycle,
-                                       asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_INIT]);
-                       }
+                       sctp_cmd_t1_timer_update(asoc,
+                                               SCTP_EVENT_TIMEOUT_T1_INIT,
+                                               "INIT");
 
                        sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART,
                                        SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT));
@@ -1382,20 +1409,9 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
                         * all transports have been tried at the current
                         * timeout.
                         */
-                       asoc->init_err_counter++;
-
-                       asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_COOKIE] *= 2;
-                       if (asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_COOKIE] >
-                           asoc->max_init_timeo) {
-                               asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_COOKIE] =
-                                       asoc->max_init_timeo;
-                       }
-                       SCTP_DEBUG_PRINTK(
-                               "T1 COOKIE Timeout adjustment"
-                               " init_err_counter: %d"
-                               " timeout: %ld\n",
-                               asoc->init_err_counter,
-                               asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_COOKIE]);
+                       sctp_cmd_t1_timer_update(asoc,
+                                               SCTP_EVENT_TIMEOUT_T1_COOKIE,
+                                               "COOKIE");
 
                        /* If we've sent any data bundled with
                         * COOKIE-ECHO we need to resend.
@@ -1427,6 +1443,10 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
                case SCTP_CMD_INIT_COUNTER_RESET:
                        asoc->init_err_counter = 0;
                        asoc->init_cycle = 0;
+                       list_for_each_entry(t, &asoc->peer.transport_addr_list,
+                                           transports) {
+                               t->init_sent_count = 0;
+                       }
                        break;
 
                case SCTP_CMD_REPORT_DUP:
@@ -1440,12 +1460,19 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
 
                case SCTP_CMD_STRIKE:
                        /* Mark one strike against a transport.  */
-                       sctp_do_8_2_transport_strike(asoc, cmd->obj.transport);
+                       sctp_do_8_2_transport_strike(asoc, cmd->obj.transport,
+                                                   0);
+                       break;
+
+               case SCTP_CMD_TRANSPORT_IDLE:
+                       t = cmd->obj.transport;
+                       sctp_transport_lower_cwnd(t, SCTP_LOWER_CWND_INACTIVE);
                        break;
 
-               case SCTP_CMD_TRANSPORT_RESET:
+               case SCTP_CMD_TRANSPORT_HB_SENT:
                        t = cmd->obj.transport;
-                       sctp_cmd_transport_reset(commands, asoc, t);
+                       sctp_do_8_2_transport_strike(asoc, t, 1);
+                       t->hb_sent = 1;
                        break;
 
                case SCTP_CMD_TRANSPORT_ON:
@@ -1481,8 +1508,15 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
                        break;
 
                case SCTP_CMD_DISCARD_PACKET:
-                       /* We need to discard the whole packet.  */
+                       /* We need to discard the whole packet.
+                        * Uncork the queue since there might be
+                        * responses pending
+                        */
                        chunk->pdiscard = 1;
+                       if (asoc) {
+                               sctp_outq_uncork(&asoc->outqueue);
+                               local_cork = 0;
+                       }
                        break;
 
                case SCTP_CMD_RTO_PENDING:
@@ -1553,8 +1587,15 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
        }
 
 out:
-       if (local_cork)
-               sctp_outq_uncork(&asoc->outqueue);
+       /* If this is in response to a received chunk, wait until
+        * we are done with the packet to open the queue so that we don't
+        * send multiple packets in response to a single request.
+        */
+       if (asoc && SCTP_EVENT_T_CHUNK == event_type && chunk) {
+               if (chunk->end_of_packet || chunk->singleton)
+                       sctp_outq_uncork(&asoc->outqueue);
+       } else if (local_cork)
+                       sctp_outq_uncork(&asoc->outqueue);
        return error;
 nomem:
        error = -ENOMEM;