mmc: s3c6410: enable ADMA feature in 6410 sdhci controller
[safe/jmp/linux-2.6] / net / sctp / sm_sideeffect.c
index 9732c79..f5e5e27 100644 (file)
@@ -51,6 +51,7 @@
 #include <linux/types.h>
 #include <linux/socket.h>
 #include <linux/ip.h>
+#include <linux/gfp.h>
 #include <net/sock.h>
 #include <net/sctp/sctp.h>
 #include <net/sctp/sm.h>
@@ -217,8 +218,7 @@ static int sctp_gen_sack(struct sctp_association *asoc, int force,
                sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART,
                                SCTP_TO(SCTP_EVENT_TIMEOUT_SACK));
        } else {
-               if (asoc->a_rwnd > asoc->rwnd)
-                       asoc->a_rwnd = asoc->rwnd;
+               asoc->a_rwnd = asoc->rwnd;
                sack = sctp_make_sack(asoc);
                if (!sack)
                        goto nomem;
@@ -397,6 +397,41 @@ out_unlock:
        sctp_transport_put(transport);
 }
 
+/* Handle the timeout of the ICMP protocol unreachable timer.  Trigger
+ * the correct state machine transition that will close the association.
+ */
+void sctp_generate_proto_unreach_event(unsigned long data)
+{
+       struct sctp_transport *transport = (struct sctp_transport *) data;
+       struct sctp_association *asoc = transport->asoc;
+       
+       sctp_bh_lock_sock(asoc->base.sk);
+       if (sock_owned_by_user(asoc->base.sk)) {
+               SCTP_DEBUG_PRINTK("%s:Sock is busy.\n", __func__);
+
+               /* Try again later.  */
+               if (!mod_timer(&transport->proto_unreach_timer,
+                               jiffies + (HZ/20)))
+                       sctp_association_hold(asoc);
+               goto out_unlock;
+       }
+
+       /* Is this structure just waiting around for us to actually
+        * get destroyed?
+        */
+       if (asoc->base.dead)
+               goto out_unlock;
+
+       sctp_do_sm(SCTP_EVENT_T_OTHER,
+                  SCTP_ST_OTHER(SCTP_EVENT_ICMP_PROTO_UNREACH),
+                  asoc->state, asoc->ep, asoc, transport, GFP_ATOMIC);
+
+out_unlock:
+       sctp_bh_unlock_sock(asoc->base.sk);
+       sctp_association_put(asoc);
+}
+
+
 /* Inject a SACK Timeout event into the state machine.  */
 static void sctp_generate_sack_event(unsigned long data)
 {
@@ -434,19 +469,32 @@ 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.
         */
-       /* When probing UNCONFIRMED addresses, the association overall
-        * error count is NOT incremented
+       /* We are here due to a timer expiration.  If the timer was
+        * not a HEARTBEAT, then normal error tracking is done.
+        * If the timer was a heartbeat, we only increment error counts
+        * when we already have an outstanding HEARTBEAT that has not
+        * been acknowledged.
+        * Additionaly, some tranport states inhibit error increments.
         */
-       if (transport->state != SCTP_UNCONFIRMED)
+       if (!is_hb) {
                asoc->overall_error_count++;
+               if (transport->state != SCTP_INACTIVE)
+                       transport->error_count++;
+        } else if (transport->hb_sent) {
+               if (transport->state != SCTP_UNCONFIRMED)
+                       asoc->overall_error_count++;
+               if (transport->state != SCTP_INACTIVE)
+                       transport->error_count++;
+       }
 
        if (transport->state != SCTP_INACTIVE &&
-           (transport->error_count++ >= transport->pathmaxrxt)) {
+           (transport->error_count > transport->pathmaxrxt)) {
                SCTP_DEBUG_PRINTK_IPADDR("transport_strike:association %p",
                                         " transport IP: port:%d failed.\n",
                                         asoc,
@@ -461,9 +509,14 @@ 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 unacknowledged 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->rto = min((transport->rto * 2), transport->asoc->rto_max);
+       }
 }
 
 /* Worker routine to handle INIT command failure.  */
@@ -621,6 +674,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,18 +704,6 @@ 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,
@@ -686,10 +732,15 @@ static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds,
 {
        struct sctp_transport *t;
 
-       t = sctp_assoc_choose_shutdown_transport(asoc);
+       if (chunk->transport)
+               t = chunk->transport;
+       else {
+               t = sctp_assoc_choose_alter_transport(asoc,
+                                             asoc->shutdown_last_sent_to);
+               chunk->transport = t;
+       }
        asoc->shutdown_last_sent_to = t;
        asoc->timeouts[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN] = t->rto;
-       chunk->transport = t;
 }
 
 /* Helper function to change the state of an association. */
@@ -706,7 +757,7 @@ static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds,
 
        if (sctp_style(sk, TCP)) {
                /* Change the sk->sk_state of a TCP-style socket that has
-                * sucessfully completed a connect() call.
+                * successfully completed a connect() call.
                 */
                if (sctp_state(asoc, ESTABLISHED) && sctp_sstate(sk, CLOSED))
                        sk->sk_state = SCTP_SS_ESTABLISHED;
@@ -777,7 +828,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;
 }
@@ -787,36 +838,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;
        }
 }
 
@@ -829,8 +892,6 @@ static void sctp_cmd_process_fwdtsn(struct sctp_ulpq *ulpq,
        sctp_walk_fwdtsn(skip, chunk) {
                sctp_ulpq_skip(ulpq, ntohs(skip->stream), ntohs(skip->ssn));
        }
-
-       return;
 }
 
 /* Helper function to remove the association non-primary peer
@@ -849,8 +910,6 @@ static void sctp_cmd_del_non_primary(struct sctp_association *asoc)
                        sctp_assoc_del_peer(asoc, &t->ipaddr);
                }
        }
-
-       return;
 }
 
 /* Helper function to set sk_err on a 1-1 style socket. */
@@ -889,6 +948,79 @@ 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]);
+       }
+
+}
+
+/* Send the whole message, chunk by chunk, to the outqueue.
+ * This way the whole message is queued up and bundling if
+ * encouraged for small fragments.
+ */
+static int sctp_cmd_send_msg(struct sctp_association *asoc,
+                               struct sctp_datamsg *msg)
+{
+       struct sctp_chunk *chunk;
+       int error = 0;
+
+       list_for_each_entry(chunk, &msg->chunks, frag_list) {
+               error = sctp_outq_tail(&asoc->outqueue, chunk);
+               if (error)
+                       break;
+       }
+
+       return error;
+}
+
+
+/* Sent the next ASCONF packet currently stored in the association.
+ * This happens after the ASCONF_ACK was succeffully processed.
+ */
+static void sctp_cmd_send_asconf(struct sctp_association *asoc)
+{
+       /* Send the next asconf chunk from the addip chunk
+        * queue.
+        */
+       if (!list_empty(&asoc->addip_chunk_list)) {
+               struct list_head *entry = asoc->addip_chunk_list.next;
+               struct sctp_chunk *asconf = list_entry(entry,
+                                               struct sctp_chunk, list);
+               list_del_init(entry);
+
+               /* Hold the chunk until an ASCONF_ACK is received. */
+               sctp_chunk_hold(asconf);
+               if (sctp_primitive_ASCONF(asoc, asconf))
+                       sctp_chunk_free(asconf);
+               else
+                       asoc->addip_last_asconf = asconf;
+       }
+}
+
+
 /* 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.
@@ -1123,7 +1255,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:
@@ -1196,6 +1329,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
@@ -1332,10 +1470,13 @@ 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++;
+                       /* Set the new transport as primary */
+                       sctp_assoc_set_primary(asoc, t);
                        break;
 
                case SCTP_CMD_INIT_RESTART:
@@ -1345,26 +1486,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));
@@ -1377,20 +1501,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.
@@ -1422,6 +1535,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:
@@ -1435,12 +1552,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:
@@ -1468,7 +1592,8 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
                case SCTP_CMD_PROCESS_CTSN:
                        /* Dummy up a SACK for processing. */
                        sackh.cum_tsn_ack = cmd->obj.be32;
-                       sackh.a_rwnd = 0;
+                       sackh.a_rwnd = asoc->peer.rwnd +
+                                       asoc->outqueue.outstanding_bytes;
                        sackh.num_gap_ack_blocks = 0;
                        sackh.num_dup_tsns = 0;
                        sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_SACK,
@@ -1543,7 +1668,16 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,
                case SCTP_CMD_UPDATE_INITTAG:
                        asoc->peer.i.init_tag = cmd->obj.u32;
                        break;
-
+               case SCTP_CMD_SEND_MSG:
+                       if (!asoc->outqueue.cork) {
+                               sctp_outq_cork(&asoc->outqueue);
+                               local_cork = 1;
+                       }
+                       error = sctp_cmd_send_msg(asoc, cmd->obj.msg);
+                       break;
+               case SCTP_CMD_SEND_NEXT_ASCONF:
+                       sctp_cmd_send_asconf(asoc);
+                       break;
                default:
                        printk(KERN_WARNING "Impossible command: %u, %p\n",
                               cmd->verb, cmd->obj.ptr);
@@ -1561,9 +1695,9 @@ out:
         */
        if (asoc && SCTP_EVENT_T_CHUNK == event_type && chunk) {
                if (chunk->end_of_packet || chunk->singleton)
-                       sctp_outq_uncork(&asoc->outqueue);
+                       error = sctp_outq_uncork(&asoc->outqueue);
        } else if (local_cork)
-                       sctp_outq_uncork(&asoc->outqueue);
+               error = sctp_outq_uncork(&asoc->outqueue);
        return error;
 nomem:
        error = -ENOMEM;