sctp: Bring SCTP_DELAYED_ACK socket option into API compliance

Brings delayed_ack socket option set/get into line with the latest ietf
socket extensions API draft, while maintaining backwards compatibility.

Signed-off-by: Wei Yongjun <yjwei@cn.fujitsu.com>
Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Wei Yongjun 2008-05-09 15:13:26 -07:00 committed by David S. Miller
parent 5c5e12898a
commit d364d9276b
5 changed files with 187 additions and 93 deletions

View File

@ -300,6 +300,7 @@ struct sctp_sock {
/* The default SACK delay timeout for new associations. */ /* The default SACK delay timeout for new associations. */
__u32 sackdelay; __u32 sackdelay;
__u32 sackfreq;
/* Flags controlling Heartbeat, SACK delay, and Path MTU Discovery. */ /* Flags controlling Heartbeat, SACK delay, and Path MTU Discovery. */
__u32 param_flags; __u32 param_flags;
@ -938,6 +939,7 @@ struct sctp_transport {
/* SACK delay timeout */ /* SACK delay timeout */
unsigned long sackdelay; unsigned long sackdelay;
__u32 sackfreq;
/* When was the last time (in jiffies) that we heard from this /* When was the last time (in jiffies) that we heard from this
* transport? We use this to pick new active and retran paths. * transport? We use this to pick new active and retran paths.
@ -1542,6 +1544,7 @@ struct sctp_association {
* : SACK's are not delayed (see Section 6). * : SACK's are not delayed (see Section 6).
*/ */
__u8 sack_needed; /* Do we need to sack the peer? */ __u8 sack_needed; /* Do we need to sack the peer? */
__u32 sack_cnt;
/* These are capabilities which our peer advertised. */ /* These are capabilities which our peer advertised. */
__u8 ecn_capable; /* Can peer do ECN? */ __u8 ecn_capable; /* Can peer do ECN? */
@ -1651,6 +1654,7 @@ struct sctp_association {
/* SACK delay timeout */ /* SACK delay timeout */
unsigned long sackdelay; unsigned long sackdelay;
__u32 sackfreq;
unsigned long timeouts[SCTP_NUM_TIMEOUT_TYPES]; unsigned long timeouts[SCTP_NUM_TIMEOUT_TYPES];

View File

@ -93,8 +93,9 @@ enum sctp_optname {
#define SCTP_STATUS SCTP_STATUS #define SCTP_STATUS SCTP_STATUS
SCTP_GET_PEER_ADDR_INFO, SCTP_GET_PEER_ADDR_INFO,
#define SCTP_GET_PEER_ADDR_INFO SCTP_GET_PEER_ADDR_INFO #define SCTP_GET_PEER_ADDR_INFO SCTP_GET_PEER_ADDR_INFO
SCTP_DELAYED_ACK_TIME, SCTP_DELAYED_ACK,
#define SCTP_DELAYED_ACK_TIME SCTP_DELAYED_ACK_TIME #define SCTP_DELAYED_ACK_TIME SCTP_DELAYED_ACK
#define SCTP_DELAYED_ACK SCTP_DELAYED_ACK
SCTP_CONTEXT, /* Receive Context */ SCTP_CONTEXT, /* Receive Context */
#define SCTP_CONTEXT SCTP_CONTEXT #define SCTP_CONTEXT SCTP_CONTEXT
SCTP_FRAGMENT_INTERLEAVE, SCTP_FRAGMENT_INTERLEAVE,
@ -618,13 +619,26 @@ struct sctp_authkeyid {
}; };
/* 7.1.23. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME) /*
* 7.1.23. Get or set delayed ack timer (SCTP_DELAYED_SACK)
* *
* This options will get or set the delayed ack timer. The time is set * This option will effect the way delayed acks are performed. This
* in milliseconds. If the assoc_id is 0, then this sets or gets the * option allows you to get or set the delayed ack time, in
* endpoints default delayed ack timer value. If the assoc_id field is * milliseconds. It also allows changing the delayed ack frequency.
* non-zero, then the set or get effects the specified association. * Changing the frequency to 1 disables the delayed sack algorithm. If
* the assoc_id is 0, then this sets or gets the endpoints default
* values. If the assoc_id field is non-zero, then the set or get
* effects the specified association for the one to many model (the
* assoc_id field is ignored by the one to one model). Note that if
* sack_delay or sack_freq are 0 when setting this option, then the
* current values will remain unchanged.
*/ */
struct sctp_sack_info {
sctp_assoc_t sack_assoc_id;
uint32_t sack_delay;
uint32_t sack_freq;
};
struct sctp_assoc_value { struct sctp_assoc_value {
sctp_assoc_t assoc_id; sctp_assoc_t assoc_id;
uint32_t assoc_value; uint32_t assoc_value;

View File

@ -136,6 +136,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
/* Set association default SACK delay */ /* Set association default SACK delay */
asoc->sackdelay = msecs_to_jiffies(sp->sackdelay); asoc->sackdelay = msecs_to_jiffies(sp->sackdelay);
asoc->sackfreq = sp->sackfreq;
/* Set the association default flags controlling /* Set the association default flags controlling
* Heartbeat, SACK delay, and Path MTU Discovery. * Heartbeat, SACK delay, and Path MTU Discovery.
@ -261,6 +262,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
* already received one packet.] * already received one packet.]
*/ */
asoc->peer.sack_needed = 1; asoc->peer.sack_needed = 1;
asoc->peer.sack_cnt = 0;
/* Assume that the peer will tell us if he recognizes ASCONF /* Assume that the peer will tell us if he recognizes ASCONF
* as part of INIT exchange. * as part of INIT exchange.
@ -615,6 +617,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
* association configured value. * association configured value.
*/ */
peer->sackdelay = asoc->sackdelay; peer->sackdelay = asoc->sackdelay;
peer->sackfreq = asoc->sackfreq;
/* Enable/disable heartbeat, SACK delay, and path MTU discovery /* Enable/disable heartbeat, SACK delay, and path MTU discovery
* based on association setting. * based on association setting.

View File

@ -190,20 +190,28 @@ static int sctp_gen_sack(struct sctp_association *asoc, int force,
* unacknowledged DATA chunk. ... * unacknowledged DATA chunk. ...
*/ */
if (!asoc->peer.sack_needed) { if (!asoc->peer.sack_needed) {
/* We will need a SACK for the next packet. */ asoc->peer.sack_cnt++;
asoc->peer.sack_needed = 1;
/* Set the SACK delay timeout based on the /* Set the SACK delay timeout based on the
* SACK delay for the last transport * SACK delay for the last transport
* data was received from, or the default * data was received from, or the default
* for the association. * for the association.
*/ */
if (trans) if (trans) {
/* We will need a SACK for the next packet. */
if (asoc->peer.sack_cnt >= trans->sackfreq - 1)
asoc->peer.sack_needed = 1;
asoc->timeouts[SCTP_EVENT_TIMEOUT_SACK] = asoc->timeouts[SCTP_EVENT_TIMEOUT_SACK] =
trans->sackdelay; trans->sackdelay;
else } else {
/* We will need a SACK for the next packet. */
if (asoc->peer.sack_cnt >= asoc->sackfreq - 1)
asoc->peer.sack_needed = 1;
asoc->timeouts[SCTP_EVENT_TIMEOUT_SACK] = asoc->timeouts[SCTP_EVENT_TIMEOUT_SACK] =
asoc->sackdelay; asoc->sackdelay;
}
/* Restart the SACK timer. */ /* Restart the SACK timer. */
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART, sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART,
@ -216,6 +224,7 @@ static int sctp_gen_sack(struct sctp_association *asoc, int force,
goto nomem; goto nomem;
asoc->peer.sack_needed = 0; asoc->peer.sack_needed = 0;
asoc->peer.sack_cnt = 0;
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(sack)); sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(sack));

View File

@ -2305,74 +2305,98 @@ static int sctp_setsockopt_peer_addr_params(struct sock *sk,
return 0; return 0;
} }
/* 7.1.23. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME) /*
* 7.1.23. Get or set delayed ack timer (SCTP_DELAYED_SACK)
* *
* This options will get or set the delayed ack timer. The time is set * This option will effect the way delayed acks are performed. This
* in milliseconds. If the assoc_id is 0, then this sets or gets the * option allows you to get or set the delayed ack time, in
* endpoints default delayed ack timer value. If the assoc_id field is * milliseconds. It also allows changing the delayed ack frequency.
* non-zero, then the set or get effects the specified association. * Changing the frequency to 1 disables the delayed sack algorithm. If
* the assoc_id is 0, then this sets or gets the endpoints default
* values. If the assoc_id field is non-zero, then the set or get
* effects the specified association for the one to many model (the
* assoc_id field is ignored by the one to one model). Note that if
* sack_delay or sack_freq are 0 when setting this option, then the
* current values will remain unchanged.
* *
* struct sctp_assoc_value { * struct sctp_sack_info {
* sctp_assoc_t assoc_id; * sctp_assoc_t sack_assoc_id;
* uint32_t assoc_value; * uint32_t sack_delay;
* uint32_t sack_freq;
* }; * };
* *
* assoc_id - This parameter, indicates which association the * sack_assoc_id - This parameter, indicates which association the user
* user is preforming an action upon. Note that if * is performing an action upon. Note that if this field's value is
* this field's value is zero then the endpoints * zero then the endpoints default value is changed (effecting future
* default value is changed (effecting future
* associations only). * associations only).
* *
* assoc_value - This parameter contains the number of milliseconds * sack_delay - This parameter contains the number of milliseconds that
* that the user is requesting the delayed ACK timer * the user is requesting the delayed ACK timer be set to. Note that
* be set to. Note that this value is defined in * this value is defined in the standard to be between 200 and 500
* the standard to be between 200 and 500 milliseconds. * milliseconds.
* *
* Note: a value of zero will leave the value alone, * sack_freq - This parameter contains the number of packets that must
* but disable SACK delay. A non-zero value will also * be received before a sack is sent without waiting for the delay
* enable SACK delay. * timer to expire. The default value for this is 2, setting this
* value to 1 will disable the delayed sack algorithm.
*/ */
static int sctp_setsockopt_delayed_ack_time(struct sock *sk, static int sctp_setsockopt_delayed_ack(struct sock *sk,
char __user *optval, int optlen) char __user *optval, int optlen)
{ {
struct sctp_assoc_value params; struct sctp_sack_info params;
struct sctp_transport *trans = NULL; struct sctp_transport *trans = NULL;
struct sctp_association *asoc = NULL; struct sctp_association *asoc = NULL;
struct sctp_sock *sp = sctp_sk(sk); struct sctp_sock *sp = sctp_sk(sk);
if (optlen != sizeof(struct sctp_assoc_value)) if (optlen == sizeof(struct sctp_sack_info)) {
return - EINVAL;
if (copy_from_user(&params, optval, optlen)) if (copy_from_user(&params, optval, optlen))
return -EFAULT; return -EFAULT;
/* Validate value parameter. */ if (params.sack_delay == 0 && params.sack_freq == 0)
if (params.assoc_value > 500) return 0;
} else if (optlen == sizeof(struct sctp_assoc_value)) {
printk(KERN_WARNING "SCTP: Use of struct sctp_sack_info "
"in delayed_ack socket option deprecated\n");
printk(KERN_WARNING "SCTP: struct sctp_sack_info instead\n");
if (copy_from_user(&params, optval, optlen))
return -EFAULT;
if (params.sack_delay == 0)
params.sack_freq = 1;
else
params.sack_freq = 0;
} else
return - EINVAL; return - EINVAL;
/* Get association, if assoc_id != 0 and the socket is a one /* Validate value parameter. */
if (params.sack_delay > 500)
return -EINVAL;
/* Get association, if sack_assoc_id != 0 and the socket is a one
* to many style socket, and an association was not found, then * to many style socket, and an association was not found, then
* the id was invalid. * the id was invalid.
*/ */
asoc = sctp_id2assoc(sk, params.assoc_id); asoc = sctp_id2assoc(sk, params.sack_assoc_id);
if (!asoc && params.assoc_id && sctp_style(sk, UDP)) if (!asoc && params.sack_assoc_id && sctp_style(sk, UDP))
return -EINVAL; return -EINVAL;
if (params.assoc_value) { if (params.sack_delay) {
if (asoc) { if (asoc) {
asoc->sackdelay = asoc->sackdelay =
msecs_to_jiffies(params.assoc_value); msecs_to_jiffies(params.sack_delay);
asoc->param_flags = asoc->param_flags =
(asoc->param_flags & ~SPP_SACKDELAY) | (asoc->param_flags & ~SPP_SACKDELAY) |
SPP_SACKDELAY_ENABLE; SPP_SACKDELAY_ENABLE;
} else { } else {
sp->sackdelay = params.assoc_value; sp->sackdelay = params.sack_delay;
sp->param_flags = sp->param_flags =
(sp->param_flags & ~SPP_SACKDELAY) | (sp->param_flags & ~SPP_SACKDELAY) |
SPP_SACKDELAY_ENABLE; SPP_SACKDELAY_ENABLE;
} }
} else { }
if (params.sack_freq == 1) {
if (asoc) { if (asoc) {
asoc->param_flags = asoc->param_flags =
(asoc->param_flags & ~SPP_SACKDELAY) | (asoc->param_flags & ~SPP_SACKDELAY) |
@ -2382,22 +2406,40 @@ static int sctp_setsockopt_delayed_ack_time(struct sock *sk,
(sp->param_flags & ~SPP_SACKDELAY) | (sp->param_flags & ~SPP_SACKDELAY) |
SPP_SACKDELAY_DISABLE; SPP_SACKDELAY_DISABLE;
} }
} else if (params.sack_freq > 1) {
if (asoc) {
asoc->sackfreq = params.sack_freq;
asoc->param_flags =
(asoc->param_flags & ~SPP_SACKDELAY) |
SPP_SACKDELAY_ENABLE;
} else {
sp->sackfreq = params.sack_freq;
sp->param_flags =
(sp->param_flags & ~SPP_SACKDELAY) |
SPP_SACKDELAY_ENABLE;
}
} }
/* If change is for association, also apply to each transport. */ /* If change is for association, also apply to each transport. */
if (asoc) { if (asoc) {
list_for_each_entry(trans, &asoc->peer.transport_addr_list, list_for_each_entry(trans, &asoc->peer.transport_addr_list,
transports) { transports) {
if (params.assoc_value) { if (params.sack_delay) {
trans->sackdelay = trans->sackdelay =
msecs_to_jiffies(params.assoc_value); msecs_to_jiffies(params.sack_delay);
trans->param_flags = trans->param_flags =
(trans->param_flags & ~SPP_SACKDELAY) | (trans->param_flags & ~SPP_SACKDELAY) |
SPP_SACKDELAY_ENABLE; SPP_SACKDELAY_ENABLE;
} else { }
if (params.sack_delay == 1) {
trans->param_flags = trans->param_flags =
(trans->param_flags & ~SPP_SACKDELAY) | (trans->param_flags & ~SPP_SACKDELAY) |
SPP_SACKDELAY_DISABLE; SPP_SACKDELAY_DISABLE;
} else if (params.sack_freq > 1) {
trans->sackfreq = params.sack_freq;
trans->param_flags =
(trans->param_flags & ~SPP_SACKDELAY) |
SPP_SACKDELAY_ENABLE;
} }
} }
} }
@ -3186,8 +3228,8 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
retval = sctp_setsockopt_peer_addr_params(sk, optval, optlen); retval = sctp_setsockopt_peer_addr_params(sk, optval, optlen);
break; break;
case SCTP_DELAYED_ACK_TIME: case SCTP_DELAYED_ACK:
retval = sctp_setsockopt_delayed_ack_time(sk, optval, optlen); retval = sctp_setsockopt_delayed_ack(sk, optval, optlen);
break; break;
case SCTP_PARTIAL_DELIVERY_POINT: case SCTP_PARTIAL_DELIVERY_POINT:
retval = sctp_setsockopt_partial_delivery_point(sk, optval, optlen); retval = sctp_setsockopt_partial_delivery_point(sk, optval, optlen);
@ -3446,6 +3488,7 @@ SCTP_STATIC int sctp_init_sock(struct sock *sk)
sp->pathmaxrxt = sctp_max_retrans_path; sp->pathmaxrxt = sctp_max_retrans_path;
sp->pathmtu = 0; // allow default discovery sp->pathmtu = 0; // allow default discovery
sp->sackdelay = sctp_sack_timeout; sp->sackdelay = sctp_sack_timeout;
sp->sackfreq = 3;
sp->param_flags = SPP_HB_ENABLE | sp->param_flags = SPP_HB_ENABLE |
SPP_PMTUD_ENABLE | SPP_PMTUD_ENABLE |
SPP_SACKDELAY_ENABLE; SPP_SACKDELAY_ENABLE;
@ -3999,70 +4042,91 @@ static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len,
return 0; return 0;
} }
/* 7.1.23. Delayed Ack Timer (SCTP_DELAYED_ACK_TIME) /*
* 7.1.23. Get or set delayed ack timer (SCTP_DELAYED_SACK)
* *
* This options will get or set the delayed ack timer. The time is set * This option will effect the way delayed acks are performed. This
* in milliseconds. If the assoc_id is 0, then this sets or gets the * option allows you to get or set the delayed ack time, in
* endpoints default delayed ack timer value. If the assoc_id field is * milliseconds. It also allows changing the delayed ack frequency.
* non-zero, then the set or get effects the specified association. * Changing the frequency to 1 disables the delayed sack algorithm. If
* the assoc_id is 0, then this sets or gets the endpoints default
* values. If the assoc_id field is non-zero, then the set or get
* effects the specified association for the one to many model (the
* assoc_id field is ignored by the one to one model). Note that if
* sack_delay or sack_freq are 0 when setting this option, then the
* current values will remain unchanged.
* *
* struct sctp_assoc_value { * struct sctp_sack_info {
* sctp_assoc_t assoc_id; * sctp_assoc_t sack_assoc_id;
* uint32_t assoc_value; * uint32_t sack_delay;
* uint32_t sack_freq;
* }; * };
* *
* assoc_id - This parameter, indicates which association the * sack_assoc_id - This parameter, indicates which association the user
* user is preforming an action upon. Note that if * is performing an action upon. Note that if this field's value is
* this field's value is zero then the endpoints * zero then the endpoints default value is changed (effecting future
* default value is changed (effecting future
* associations only). * associations only).
* *
* assoc_value - This parameter contains the number of milliseconds * sack_delay - This parameter contains the number of milliseconds that
* that the user is requesting the delayed ACK timer * the user is requesting the delayed ACK timer be set to. Note that
* be set to. Note that this value is defined in * this value is defined in the standard to be between 200 and 500
* the standard to be between 200 and 500 milliseconds. * milliseconds.
* *
* Note: a value of zero will leave the value alone, * sack_freq - This parameter contains the number of packets that must
* but disable SACK delay. A non-zero value will also * be received before a sack is sent without waiting for the delay
* enable SACK delay. * timer to expire. The default value for this is 2, setting this
* value to 1 will disable the delayed sack algorithm.
*/ */
static int sctp_getsockopt_delayed_ack_time(struct sock *sk, int len, static int sctp_getsockopt_delayed_ack(struct sock *sk, int len,
char __user *optval, char __user *optval,
int __user *optlen) int __user *optlen)
{ {
struct sctp_assoc_value params; struct sctp_sack_info params;
struct sctp_association *asoc = NULL; struct sctp_association *asoc = NULL;
struct sctp_sock *sp = sctp_sk(sk); struct sctp_sock *sp = sctp_sk(sk);
if (len < sizeof(struct sctp_assoc_value)) if (len >= sizeof(struct sctp_sack_info)) {
return - EINVAL; len = sizeof(struct sctp_sack_info);
len = sizeof(struct sctp_assoc_value);
if (copy_from_user(&params, optval, len)) if (copy_from_user(&params, optval, len))
return -EFAULT; return -EFAULT;
} else if (len == sizeof(struct sctp_assoc_value)) {
printk(KERN_WARNING "SCTP: Use of struct sctp_sack_info "
"in delayed_ack socket option deprecated\n");
printk(KERN_WARNING "SCTP: struct sctp_sack_info instead\n");
if (copy_from_user(&params, optval, len))
return -EFAULT;
} else
return - EINVAL;
/* Get association, if assoc_id != 0 and the socket is a one /* Get association, if sack_assoc_id != 0 and the socket is a one
* to many style socket, and an association was not found, then * to many style socket, and an association was not found, then
* the id was invalid. * the id was invalid.
*/ */
asoc = sctp_id2assoc(sk, params.assoc_id); asoc = sctp_id2assoc(sk, params.sack_assoc_id);
if (!asoc && params.assoc_id && sctp_style(sk, UDP)) if (!asoc && params.sack_assoc_id && sctp_style(sk, UDP))
return -EINVAL; return -EINVAL;
if (asoc) { if (asoc) {
/* Fetch association values. */ /* Fetch association values. */
if (asoc->param_flags & SPP_SACKDELAY_ENABLE) if (asoc->param_flags & SPP_SACKDELAY_ENABLE) {
params.assoc_value = jiffies_to_msecs( params.sack_delay = jiffies_to_msecs(
asoc->sackdelay); asoc->sackdelay);
else params.sack_freq = asoc->sackfreq;
params.assoc_value = 0;
} else {
params.sack_delay = 0;
params.sack_freq = 1;
}
} else { } else {
/* Fetch socket values. */ /* Fetch socket values. */
if (sp->param_flags & SPP_SACKDELAY_ENABLE) if (sp->param_flags & SPP_SACKDELAY_ENABLE) {
params.assoc_value = sp->sackdelay; params.sack_delay = sp->sackdelay;
else params.sack_freq = sp->sackfreq;
params.assoc_value = 0; } else {
params.sack_delay = 0;
params.sack_freq = 1;
}
} }
if (copy_to_user(optval, &params, len)) if (copy_to_user(optval, &params, len))
@ -5218,8 +5282,8 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
retval = sctp_getsockopt_peer_addr_params(sk, len, optval, retval = sctp_getsockopt_peer_addr_params(sk, len, optval,
optlen); optlen);
break; break;
case SCTP_DELAYED_ACK_TIME: case SCTP_DELAYED_ACK:
retval = sctp_getsockopt_delayed_ack_time(sk, len, optval, retval = sctp_getsockopt_delayed_ack(sk, len, optval,
optlen); optlen);
break; break;
case SCTP_INITMSG: case SCTP_INITMSG: