Commit 3347deae authored by Sridhar Samudrala's avatar Sridhar Samudrala

[SCTP] Blocking connect() support.

parent 664bf802
...@@ -86,6 +86,8 @@ static int sctp_cmd_process_sack(sctp_cmd_seq_t *, sctp_association_t *, ...@@ -86,6 +86,8 @@ static int sctp_cmd_process_sack(sctp_cmd_seq_t *, sctp_association_t *,
sctp_sackhdr_t *); sctp_sackhdr_t *);
static void sctp_cmd_setup_t2(sctp_cmd_seq_t *, sctp_association_t *, static void sctp_cmd_setup_t2(sctp_cmd_seq_t *, sctp_association_t *,
sctp_chunk_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 /* 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 * 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, ...@@ -305,8 +307,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
case SCTP_CMD_NEW_STATE: case SCTP_CMD_NEW_STATE:
/* Enter a new state. */ /* Enter a new state. */
asoc->state = command->obj.state; sctp_cmd_new_state(commands, asoc, command->obj.state);
asoc->state_timestamp = jiffies;
break; break;
case SCTP_CMD_REPORT_TSN: case SCTP_CMD_REPORT_TSN:
...@@ -1233,3 +1234,18 @@ static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds, sctp_association_t *asoc, ...@@ -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; asoc->timeouts[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN] = t->rto;
chunk->transport = t; 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);
}
...@@ -86,6 +86,7 @@ static void sctp_wfree(struct sk_buff *skb); ...@@ -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, static int sctp_wait_for_sndbuf(sctp_association_t *asoc, long *timeo_p,
int msg_len); int msg_len);
static int sctp_wait_for_packet(struct sock * sk, int *err, long *timeo_p); 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 *, static inline void sctp_sk_addr_set(struct sock *,
const union sctp_addr *newaddr, const union sctp_addr *newaddr,
union sctp_addr *saveaddr); union sctp_addr *saveaddr);
...@@ -1432,6 +1433,7 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr, ...@@ -1432,6 +1433,7 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
sctp_transport_t *transport; sctp_transport_t *transport;
union sctp_addr to; union sctp_addr to;
sctp_scope_t scope; sctp_scope_t scope;
long timeo;
int err = 0; int err = 0;
sctp_lock_sock(sk); sctp_lock_sock(sk);
...@@ -1495,14 +1497,13 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr, ...@@ -1495,14 +1497,13 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
transport = sctp_assoc_add_peer(asoc, &to, GFP_KERNEL); transport = sctp_assoc_add_peer(asoc, &to, GFP_KERNEL);
err = sctp_primitive_ASSOCIATE(asoc, NULL); err = sctp_primitive_ASSOCIATE(asoc, NULL);
if (err < 0) if (err < 0) {
sctp_association_free(asoc); sctp_association_free(asoc);
goto out_unlock;
}
/* FIXME: Currently we support only non-blocking connect(). timeo = sock_sndtimeo(sk, sk->socket->file->f_flags & O_NONBLOCK);
* To support blocking connect(), we need to wait for the association err = sctp_wait_for_connect(asoc, &timeo);
* to be ESTABLISHED before returning.
*/
err = -EINPROGRESS;
out_unlock: out_unlock:
sctp_release_sock(sk); sctp_release_sock(sk);
...@@ -2903,6 +2904,70 @@ static int sctp_writeable(struct sock *sk) ...@@ -2903,6 +2904,70 @@ static int sctp_writeable(struct sock *sk)
return amt; 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. */ /* This proto struct describes the ULP interface for SCTP. */
struct proto sctp_prot = { struct proto sctp_prot = {
.name = "SCTP", .name = "SCTP",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment