[PATCHv2 3/5] sctp: extend socket API for sched configuration

From: Yaogong Wang
Date: Sat Sep 11 2010 - 21:13:11 EST


Augment SCTP socket API with a new socket option SCTP_SCHED
to choose and configure multistream scheduling algorithm.

Signed-off-by: Yaogong Wang <ywang15@xxxxxxxx>
---
include/net/sctp/structs.h | 4 ++
include/net/sctp/user.h | 19 ++++++++++
net/sctp/outqueue.c | 4 ++
net/sctp/sm_sideeffect.c | 45 +++++++++++++++++++++++--
net/sctp/socket.c | 80 ++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 149 insertions(+), 3 deletions(-)

diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index 52af764..1a76417 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -333,6 +333,8 @@ struct sctp_sock {

/* Multistream scheduling */
const struct sctp_sched_ops *sched_ops;
+ __u16 sched_config_len;
+ void *sched_config;

struct sctp_initmsg initmsg;
struct sctp_rtoinfo rtoinfo;
@@ -1173,6 +1175,8 @@ struct sctp_outq {

/* Multistream scheduling */
const struct sctp_sched_ops *sched_ops;
+ __u16 sched_config_len;
+ void *sched_config;

unsigned out_qlen; /* Total length of queued data chunks. */

diff --git a/include/net/sctp/user.h b/include/net/sctp/user.h
index 1e084f9..bb853d0 100644
--- a/include/net/sctp/user.h
+++ b/include/net/sctp/user.h
@@ -90,6 +90,7 @@ typedef __s32 sctp_assoc_t;
#define SCTP_PEER_AUTH_CHUNKS 26 /* Read only */
#define SCTP_LOCAL_AUTH_CHUNKS 27 /* Read only */
#define SCTP_GET_ASSOC_NUMBER 28 /* Read only */
+#define SCTP_SCHED 29 /* Configure multistream scheduling */

/* Internal Socket Options. Some of the sctp library functions are
* implemented using these socket options.
@@ -708,6 +709,24 @@ typedef struct {
int sd;
} sctp_peeloff_arg_t;

+/*
+ * SCTP Multistream Scheduling Structure (SCTP_SCHED)
+ *
+ * This cmsghdr structure provides information for the multistream
+ * scheduling algorithm used by a socket. The SCTP_SCHED socket option
+ * uses this same data structure.
+ *
+ * cmsg_level cmsg_type cmsg_data[]
+ * ------------ ------------ ----------------------
+ * IPPROTO_SCTP SCTP_SCHED struct sctp_sched
+ *
+ */
+
#define SCTP_SCHED_NAME_MAX 16
+struct sctp_sched {
+ char ssched_name[SCTP_SCHED_NAME_MAX];
+ __u16 ssched_config_len;
+ __u16 ssched_config[0];
+};

#endif /* __net_sctp_user_h__ */
diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
index 37ffa21..1d3bd2e 100644
--- a/net/sctp/outqueue.c
+++ b/net/sctp/outqueue.c
@@ -178,6 +178,8 @@ int sctp_outq_init(struct sctp_association *asoc,
struct sctp_outq *q,

/* initialize to the default FCFS at this stage */
q->sched_ops = sctp_default_sched_ops;
+ q->sched_config_len = 0;
+ q->sched_config = NULL;
err = q->sched_ops->init(q, gfp);
if (err)
return err;
@@ -269,6 +271,8 @@ void sctp_outq_free(struct sctp_outq *q)
sctp_outq_teardown(q);

q->sched_ops->release(q);
+ kfree(q->sched_config);
+ module_put(q->sched_ops->owner);

/* If we were kmalloc()'d, free the memory. */
if (q->malloced)
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index f5e5e27..e240557 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -744,17 +744,54 @@ static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds,
}

/* Helper function to change the state of an association. */
-static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds,
+static int sctp_cmd_new_state(sctp_cmd_seq_t *cmds,
struct sctp_association *asoc,
- sctp_state_t state)
+ sctp_state_t state,
+ gfp_t gfp)
{
struct sock *sk = asoc->base.sk;
+ struct sctp_sock *sp = sctp_sk(sk);

asoc->state = state;

SCTP_DEBUG_PRINTK("sctp_cmd_new_state: asoc %p[%s]\n",
asoc, sctp_state_tbl[state]);

+ /* Initialize mulitstream scheduling if user has customized it
+ * Otherwise do nothing
+ * Client: when entering COOKIE_ECHOED state
+ * Server: when entering ESTABLISHED state
+ */
+ if (sp->sched_ops != asoc->outqueue.sched_ops &&
+ (sctp_state(asoc, COOKIE_ECHOED) || sctp_state(asoc, ESTABLISHED))) {
+ if (!try_module_get(sp->sched_ops->owner))
+ return -EBUSY;
+ asoc->outqueue.sched_ops = sp->sched_ops;
+ asoc->outqueue.sched_config_len = sp->sched_config_len;
+ if (sp->sched_config_len) {
+ asoc->outqueue.sched_config = kmemdup(sp->sched_config,
+ sp->sched_config_len, gfp);
+ if (!asoc->outqueue.sched_config)
+ return -ENOMEM;
+ }
+ /* Initialize new data chunk queue(s)
+ * Move data chunks from old queue to new queue(s)
+ */
+ struct list_head *old = asoc->outqueue.out_chunk_list;
+ struct sctp_chunk *ch = NULL;
+ if (0 != asoc->outqueue.sched_ops->init(&asoc->outqueue, gfp))
+ return -ENOMEM;
+ while (!list_empty(old)) {
+ struct list_head *entry = old->next;
+ ch = list_entry(entry, struct sctp_chunk, list);
+ list_del_init(entry);
+ asoc->outqueue.out_qlen -= ch->skb->len;
+ asoc->outqueue.sched_ops->
+ enqueue_tail_data(&asoc->outqueue, ch);
+ }
+ kfree(old);
+ }
+
if (sctp_style(sk, TCP)) {
/* Change the sk->sk_state of a TCP-style socket that has
* successfully completed a connect() call.
@@ -796,6 +833,7 @@ static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds,
if (!sctp_style(sk, UDP))
sk->sk_state_change(sk);
}
+ return 0;
}

/* Helper function to delete an association. */
@@ -1250,7 +1288,8 @@ static int sctp_cmd_interpreter(sctp_event_t event_type,

case SCTP_CMD_NEW_STATE:
/* Enter a new state. */
- sctp_cmd_new_state(commands, asoc, cmd->obj.state);
+ error = sctp_cmd_new_state(commands, asoc,
+ cmd->obj.state, gfp);
break;

case SCTP_CMD_REPORT_TSN:
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 7d461be..481f231 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -2582,6 +2582,44 @@ static int sctp_setsockopt_initmsg(struct sock
*sk, char __user *optval, unsigne
return 0;
}

+/* Set the multistream scheduling algorithm */
+static int sctp_setsockopt_sched(struct sock *sk, char __user *optval,
+ unsigned int optlen)
+{
+ struct sctp_sched *ssched = NULL;
+ struct sctp_sock *sp = sctp_sk(sk);
+ int ret = 0;
+
+ if (optlen < sizeof(struct sctp_sched))
+ return -EINVAL;
+
+ ssched = memdup_user(optval, optlen);
+ if (IS_ERR(ssched)) {
+ ret = PTR_ERR(ssched);
+ goto out;
+ }
+
+ if (optlen != sizeof(struct sctp_sched) + ssched->ssched_config_len) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = sctp_set_sched(sk, ssched->ssched_name);
+ if (ret)
+ goto out;
+ sp->sched_config_len = ssched->ssched_config_len;
+ kfree(sp->sched_config);
+ if (sp->sched_config_len) {
+ sp->sched_config = kmemdup(ssched->ssched_config,
+ sp->sched_config_len, GFP_KERNEL);
+ if (!sp->sched_config)
+ ret = -ENOMEM;
+ }
+out:
+ kfree(ssched);
+ return ret;
+}
+
/*
* 7.1.14 Set default send parameters (SCTP_DEFAULT_SEND_PARAM)
*
@@ -3422,6 +3460,9 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk,
int level, int optname,
case SCTP_INITMSG:
retval = sctp_setsockopt_initmsg(sk, optval, optlen);
break;
+ case SCTP_SCHED:
+ retval = sctp_setsockopt_sched(sk, optval, optlen);
+ break;
case SCTP_DEFAULT_SEND_PARAM:
retval = sctp_setsockopt_default_send_param(sk, optval,
optlen);
@@ -3646,6 +3687,8 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)

/* Initialize default multistream scheduling algorithm */
sp->sched_ops = sctp_default_sched_ops;
+ sp->sched_config_len = 0;
+ sp->sched_config = NULL;

/* Initialize default RTO related parameters. These parameters can
* be modified for with the SCTP_RTOINFO socket option.
@@ -3738,6 +3781,9 @@ SCTP_STATIC void sctp_destroy_sock(struct sock *sk)

SCTP_DEBUG_PRINTK("sctp_destroy_sock(sk: %p)\n", sk);

+ sctp_cleanup_sched(sk);
+ kfree(sctp_sk(sk)->sched_config);
+
/* Release our hold on the endpoint. */
ep = sctp_sk(sk)->ep;
sctp_endpoint_free(ep);
@@ -4354,7 +4400,38 @@ static int sctp_getsockopt_initmsg(struct sock
*sk, int len, char __user *optval
return 0;
}

+/* Get the multistream scheduling algorithm */
+static int sctp_getsockopt_sched(struct sock *sk, int len, char __user *optval,
+ int __user *optlen)
+{
+ struct sctp_sched *ssched;
+ int sz = sizeof(struct sctp_sched) + sctp_sk(sk)->sched_config_len;
+ int ret = 0;
+
+ if (len < sz)
+ return -EINVAL;
+
+ ssched = kmalloc(sz, GFP_KERNEL);
+ if (!ssched)
+ return -ENOMEM;
+ memcpy(ssched->ssched_name, sctp_sk(sk)->sched_ops->name,
+ SCTP_SCHED_NAME_MAX);
+ ssched->ssched_config_len = sctp_sk(sk)->sched_config_len;
+ memcpy(ssched->ssched_config, sctp_sk(sk)->sched_config,
+ ssched->ssched_config_len);
+
+ if (copy_to_user(optval, ssched, sz)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ if (put_user(sz, optlen))
+ ret = -EFAULT;

+out:
+ kfree(ssched);
+ return ret;
+}
+
static int sctp_getsockopt_peer_addrs(struct sock *sk, int len,
char __user *optval, int __user *optlen)
{
@@ -5299,6 +5376,9 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk,
int level, int optname,
case SCTP_INITMSG:
retval = sctp_getsockopt_initmsg(sk, len, optval, optlen);
break;
+ case SCTP_SCHED:
+ retval = sctp_getsockopt_sched(sk, len, optval, optlen);
+ break;
case SCTP_GET_PEER_ADDRS:
retval = sctp_getsockopt_peer_addrs(sk, len, optval,
optlen);
--
1.7.0.4
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/