summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSridhar Samudrala <sridhar@dyn9-47-18-140.beaverton.ibm.com>2002-11-10 18:00:34 -0800
committerSridhar Samudrala <sridhar@dyn9-47-18-140.beaverton.ibm.com>2002-11-10 18:00:34 -0800
commitab2cb9268369e8ab5a02e0700a38ad712fd3e51e (patch)
tree62d775cb1a77cb1883a2f37951e698004a24b4e1
parenteb34f986a8693c1d005a374e14291a9ffcbbea39 (diff)
parent3347deae58e3a969520aeb249598dee618bf45fc (diff)
Merge dyn9-47-18-140.beaverton.ibm.com:/home/sridhar/BK/sctp-2.5
into dyn9-47-18-140.beaverton.ibm.com:/home/sridhar/BK/lksctp-2.5.46
-rw-r--r--net/sctp/sm_sideeffect.c20
-rw-r--r--net/sctp/socket.c77
2 files changed, 89 insertions, 8 deletions
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index 1b8eb76f689e..69ca9dccc364 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -86,6 +86,8 @@ static int sctp_cmd_process_sack(sctp_cmd_seq_t *, sctp_association_t *,
sctp_sackhdr_t *);
static void sctp_cmd_setup_t2(sctp_cmd_seq_t *, sctp_association_t *,
sctp_chunk_t *);
+static void sctp_cmd_new_state(sctp_cmd_seq_t *, sctp_association_t *,
+ sctp_state_t);
/* 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
@@ -305,8 +307,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
case SCTP_CMD_NEW_STATE:
/* Enter a new state. */
- asoc->state = command->obj.state;
- asoc->state_timestamp = jiffies;
+ sctp_cmd_new_state(commands, asoc, command->obj.state);
break;
case SCTP_CMD_REPORT_TSN:
@@ -1233,3 +1234,18 @@ static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds, sctp_association_t *asoc,
asoc->timeouts[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN] = t->rto;
chunk->transport = t;
}
+
+/* Helper function to change the state of an association. */
+static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds, sctp_association_t *asoc,
+ sctp_state_t state)
+{
+ asoc->state = state;
+ asoc->state_timestamp = jiffies;
+
+ /* Wake up any process waiting for the association to
+ * get established.
+ */
+ if ((SCTP_STATE_ESTABLISHED == asoc->state) &&
+ (waitqueue_active(&asoc->wait)))
+ wake_up_interruptible(&asoc->wait);
+}
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 52387d93fa32..9e8bcc765b5e 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -86,6 +86,7 @@ static void sctp_wfree(struct sk_buff *skb);
static int sctp_wait_for_sndbuf(sctp_association_t *asoc, long *timeo_p,
int msg_len);
static int sctp_wait_for_packet(struct sock * sk, int *err, long *timeo_p);
+static int sctp_wait_for_connect(sctp_association_t *asoc, long *timeo_p);
static inline void sctp_sk_addr_set(struct sock *,
const union sctp_addr *newaddr,
union sctp_addr *saveaddr);
@@ -1432,6 +1433,7 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
sctp_transport_t *transport;
union sctp_addr to;
sctp_scope_t scope;
+ long timeo;
int err = 0;
sctp_lock_sock(sk);
@@ -1495,14 +1497,13 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
transport = sctp_assoc_add_peer(asoc, &to, GFP_KERNEL);
err = sctp_primitive_ASSOCIATE(asoc, NULL);
- if (err < 0)
+ if (err < 0) {
sctp_association_free(asoc);
+ goto out_unlock;
+ }
- /* FIXME: Currently we support only non-blocking connect().
- * To support blocking connect(), we need to wait for the association
- * to be ESTABLISHED before returning.
- */
- err = -EINPROGRESS;
+ timeo = sock_sndtimeo(sk, sk->socket->file->f_flags & O_NONBLOCK);
+ err = sctp_wait_for_connect(asoc, &timeo);
out_unlock:
sctp_release_sock(sk);
@@ -2898,6 +2899,70 @@ static int sctp_writeable(struct sock *sk)
return amt;
}
+/* Wait for an association to go into ESTABLISHED state. If timeout is 0,
+ * returns immediately with EINPROGRESS.
+ */
+static int sctp_wait_for_connect(sctp_association_t *asoc, long *timeo_p)
+{
+ struct sock *sk = asoc->base.sk;
+ int err = 0;
+ long current_timeo = *timeo_p;
+ DECLARE_WAITQUEUE(wait, current);
+
+ SCTP_DEBUG_PRINTK("%s: asoc=%p, timeo=%ld\n", __FUNCTION__, asoc,
+ (long)(*timeo_p));
+
+ add_wait_queue_exclusive(&asoc->wait, &wait);
+
+ /* Increment the association's refcnt. */
+ sctp_association_hold(asoc);
+
+ for (;;) {
+ __set_current_state(TASK_INTERRUPTIBLE);
+ if (!*timeo_p)
+ goto do_nonblock;
+ if (sk->err || asoc->state >= SCTP_STATE_SHUTDOWN_PENDING ||
+ asoc->base.dead)
+ goto do_error;
+ if (signal_pending(current))
+ goto do_interrupted;
+
+ if (asoc->state == SCTP_STATE_ESTABLISHED)
+ break;
+
+ /* Let another process have a go. Since we are going
+ * to sleep anyway.
+ */
+ sctp_release_sock(sk);
+ current_timeo = schedule_timeout(current_timeo);
+ sctp_lock_sock(sk);
+
+ *timeo_p = current_timeo;
+ }
+
+out:
+ remove_wait_queue(&asoc->wait, &wait);
+
+ /* Release the association's refcnt. */
+ sctp_association_put(asoc);
+
+ __set_current_state(TASK_RUNNING);
+
+ return err;
+
+do_error:
+ err = -ECONNABORTED;
+ goto out;
+
+do_interrupted:
+ err = sock_intr_errno(*timeo_p);
+ goto out;
+
+do_nonblock:
+ err = -EINPROGRESS;
+ goto out;
+}
+
/* This proto struct describes the ULP interface for SCTP. */
struct proto sctp_prot = {
.name = "SCTP",