#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>
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;
/* 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,
* 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
+ * The first unacknowledged HB triggers it. We do this with a flag
* that indicates that we have an outstanding HB.
*/
if (!is_hb || transport->hb_sent) {
- transport->last_rto = transport->rto;
transport->rto = min((transport->rto * 2), transport->asoc->rto_max);
}
}
{
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;
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;
{
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;
}
}
+/* 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;
+}
+
+
+
/* 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.
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:
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,
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;
default:
printk(KERN_WARNING "Impossible command: %u, %p\n",
cmd->verb, cmd->obj.ptr);
*/
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;