mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 11:11:45 -04:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6: (1699 commits) bnx2/bnx2x: Unsupported Ethtool operations should return -EINVAL. vlan: Calling vlan_hwaccel_do_receive() is always valid. tproxy: use the interface primary IP address as a default value for --on-ip tproxy: added IPv6 support to the socket match cxgb3: function namespace cleanup tproxy: added IPv6 support to the TPROXY target tproxy: added IPv6 socket lookup function to nf_tproxy_core be2net: Changes to use only priority codes allowed by f/w tproxy: allow non-local binds of IPv6 sockets if IP_TRANSPARENT is enabled tproxy: added tproxy sockopt interface in the IPV6 layer tproxy: added udp6_lib_lookup function tproxy: added const specifiers to udp lookup functions tproxy: split off ipv6 defragmentation to a separate module l2tp: small cleanup nf_nat: restrict ICMP translation for embedded header can: mcp251x: fix generation of error frames can: mcp251x: fix endless loop in interrupt handler if CANINTF_MERRF is set can-raw: add msg_flags to distinguish local traffic 9p: client code cleanup rds: make local functions/variables static ... Fix up conflicts in net/core/dev.c, drivers/net/pcmcia/smc91c92_cs.c and drivers/net/wireless/ath/ath9k/debug.c as per David
This commit is contained in:
commit
5f05647dd8
1341 changed files with 157282 additions and 84033 deletions
|
|
@ -62,22 +62,18 @@ struct ccid_operations {
|
|||
void (*ccid_hc_tx_exit)(struct sock *sk);
|
||||
void (*ccid_hc_rx_packet_recv)(struct sock *sk,
|
||||
struct sk_buff *skb);
|
||||
int (*ccid_hc_rx_parse_options)(struct sock *sk,
|
||||
unsigned char option,
|
||||
unsigned char len, u16 idx,
|
||||
unsigned char* value);
|
||||
int (*ccid_hc_rx_parse_options)(struct sock *sk, u8 pkt,
|
||||
u8 opt, u8 *val, u8 len);
|
||||
int (*ccid_hc_rx_insert_options)(struct sock *sk,
|
||||
struct sk_buff *skb);
|
||||
void (*ccid_hc_tx_packet_recv)(struct sock *sk,
|
||||
struct sk_buff *skb);
|
||||
int (*ccid_hc_tx_parse_options)(struct sock *sk,
|
||||
unsigned char option,
|
||||
unsigned char len, u16 idx,
|
||||
unsigned char* value);
|
||||
int (*ccid_hc_tx_parse_options)(struct sock *sk, u8 pkt,
|
||||
u8 opt, u8 *val, u8 len);
|
||||
int (*ccid_hc_tx_send_packet)(struct sock *sk,
|
||||
struct sk_buff *skb);
|
||||
void (*ccid_hc_tx_packet_sent)(struct sock *sk,
|
||||
int more, unsigned int len);
|
||||
unsigned int len);
|
||||
void (*ccid_hc_rx_get_info)(struct sock *sk,
|
||||
struct tcp_info *info);
|
||||
void (*ccid_hc_tx_get_info)(struct sock *sk,
|
||||
|
|
@ -148,10 +144,10 @@ static inline int ccid_hc_tx_send_packet(struct ccid *ccid, struct sock *sk,
|
|||
}
|
||||
|
||||
static inline void ccid_hc_tx_packet_sent(struct ccid *ccid, struct sock *sk,
|
||||
int more, unsigned int len)
|
||||
unsigned int len)
|
||||
{
|
||||
if (ccid->ccid_ops->ccid_hc_tx_packet_sent != NULL)
|
||||
ccid->ccid_ops->ccid_hc_tx_packet_sent(sk, more, len);
|
||||
ccid->ccid_ops->ccid_hc_tx_packet_sent(sk, len);
|
||||
}
|
||||
|
||||
static inline void ccid_hc_rx_packet_recv(struct ccid *ccid, struct sock *sk,
|
||||
|
|
@ -168,27 +164,31 @@ static inline void ccid_hc_tx_packet_recv(struct ccid *ccid, struct sock *sk,
|
|||
ccid->ccid_ops->ccid_hc_tx_packet_recv(sk, skb);
|
||||
}
|
||||
|
||||
/**
|
||||
* ccid_hc_tx_parse_options - Parse CCID-specific options sent by the receiver
|
||||
* @pkt: type of packet that @opt appears on (RFC 4340, 5.1)
|
||||
* @opt: the CCID-specific option type (RFC 4340, 5.8 and 10.3)
|
||||
* @val: value of @opt
|
||||
* @len: length of @val in bytes
|
||||
*/
|
||||
static inline int ccid_hc_tx_parse_options(struct ccid *ccid, struct sock *sk,
|
||||
unsigned char option,
|
||||
unsigned char len, u16 idx,
|
||||
unsigned char* value)
|
||||
u8 pkt, u8 opt, u8 *val, u8 len)
|
||||
{
|
||||
int rc = 0;
|
||||
if (ccid->ccid_ops->ccid_hc_tx_parse_options != NULL)
|
||||
rc = ccid->ccid_ops->ccid_hc_tx_parse_options(sk, option, len, idx,
|
||||
value);
|
||||
return rc;
|
||||
if (ccid->ccid_ops->ccid_hc_tx_parse_options == NULL)
|
||||
return 0;
|
||||
return ccid->ccid_ops->ccid_hc_tx_parse_options(sk, pkt, opt, val, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* ccid_hc_rx_parse_options - Parse CCID-specific options sent by the sender
|
||||
* Arguments are analogous to ccid_hc_tx_parse_options()
|
||||
*/
|
||||
static inline int ccid_hc_rx_parse_options(struct ccid *ccid, struct sock *sk,
|
||||
unsigned char option,
|
||||
unsigned char len, u16 idx,
|
||||
unsigned char* value)
|
||||
u8 pkt, u8 opt, u8 *val, u8 len)
|
||||
{
|
||||
int rc = 0;
|
||||
if (ccid->ccid_ops->ccid_hc_rx_parse_options != NULL)
|
||||
rc = ccid->ccid_ops->ccid_hc_rx_parse_options(sk, option, len, idx, value);
|
||||
return rc;
|
||||
if (ccid->ccid_ops->ccid_hc_rx_parse_options == NULL)
|
||||
return 0;
|
||||
return ccid->ccid_ops->ccid_hc_rx_parse_options(sk, pkt, opt, val, len);
|
||||
}
|
||||
|
||||
static inline int ccid_hc_rx_insert_options(struct ccid *ccid, struct sock *sk,
|
||||
|
|
|
|||
|
|
@ -47,37 +47,6 @@ config IP_DCCP_CCID3_DEBUG
|
|||
|
||||
If in doubt, say N.
|
||||
|
||||
config IP_DCCP_CCID3_RTO
|
||||
int "Use higher bound for nofeedback timer"
|
||||
default 100
|
||||
depends on IP_DCCP_CCID3 && EXPERIMENTAL
|
||||
---help---
|
||||
Use higher lower bound for nofeedback timer expiration.
|
||||
|
||||
The TFRC nofeedback timer normally expires after the maximum of 4
|
||||
RTTs and twice the current send interval (RFC 3448, 4.3). On LANs
|
||||
with a small RTT this can mean a high processing load and reduced
|
||||
performance, since then the nofeedback timer is triggered very
|
||||
frequently.
|
||||
|
||||
This option enables to set a higher lower bound for the nofeedback
|
||||
value. Values in units of milliseconds can be set here.
|
||||
|
||||
A value of 0 disables this feature by enforcing the value specified
|
||||
in RFC 3448. The following values have been suggested as bounds for
|
||||
experimental use:
|
||||
* 16-20ms to match the typical multimedia inter-frame interval
|
||||
* 100ms as a reasonable compromise [default]
|
||||
* 1000ms corresponds to the lower TCP RTO bound (RFC 2988, 2.4)
|
||||
|
||||
The default of 100ms is a compromise between a large value for
|
||||
efficient DCCP implementations, and a small value to avoid disrupting
|
||||
the network in times of congestion.
|
||||
|
||||
The purpose of the nofeedback timer is to slow DCCP down when there
|
||||
is serious network congestion: experimenting with larger values should
|
||||
therefore not be performed on WANs.
|
||||
|
||||
config IP_DCCP_TFRC_LIB
|
||||
def_bool y if IP_DCCP_CCID3
|
||||
|
||||
|
|
|
|||
|
|
@ -25,59 +25,14 @@
|
|||
*/
|
||||
#include <linux/slab.h>
|
||||
#include "../feat.h"
|
||||
#include "../ccid.h"
|
||||
#include "../dccp.h"
|
||||
#include "ccid2.h"
|
||||
|
||||
|
||||
#ifdef CONFIG_IP_DCCP_CCID2_DEBUG
|
||||
static int ccid2_debug;
|
||||
#define ccid2_pr_debug(format, a...) DCCP_PR_DEBUG(ccid2_debug, format, ##a)
|
||||
|
||||
static void ccid2_hc_tx_check_sanity(const struct ccid2_hc_tx_sock *hc)
|
||||
{
|
||||
int len = 0;
|
||||
int pipe = 0;
|
||||
struct ccid2_seq *seqp = hc->tx_seqh;
|
||||
|
||||
/* there is data in the chain */
|
||||
if (seqp != hc->tx_seqt) {
|
||||
seqp = seqp->ccid2s_prev;
|
||||
len++;
|
||||
if (!seqp->ccid2s_acked)
|
||||
pipe++;
|
||||
|
||||
while (seqp != hc->tx_seqt) {
|
||||
struct ccid2_seq *prev = seqp->ccid2s_prev;
|
||||
|
||||
len++;
|
||||
if (!prev->ccid2s_acked)
|
||||
pipe++;
|
||||
|
||||
/* packets are sent sequentially */
|
||||
BUG_ON(dccp_delta_seqno(seqp->ccid2s_seq,
|
||||
prev->ccid2s_seq ) >= 0);
|
||||
BUG_ON(time_before(seqp->ccid2s_sent,
|
||||
prev->ccid2s_sent));
|
||||
|
||||
seqp = prev;
|
||||
}
|
||||
}
|
||||
|
||||
BUG_ON(pipe != hc->tx_pipe);
|
||||
ccid2_pr_debug("len of chain=%d\n", len);
|
||||
|
||||
do {
|
||||
seqp = seqp->ccid2s_prev;
|
||||
len++;
|
||||
} while (seqp != hc->tx_seqh);
|
||||
|
||||
ccid2_pr_debug("total len=%d\n", len);
|
||||
BUG_ON(len != hc->tx_seqbufc * CCID2_SEQBUF_LEN);
|
||||
}
|
||||
#else
|
||||
#define ccid2_pr_debug(format, a...)
|
||||
#define ccid2_hc_tx_check_sanity(hc)
|
||||
#endif
|
||||
|
||||
static int ccid2_hc_tx_alloc_seq(struct ccid2_hc_tx_sock *hc)
|
||||
|
|
@ -156,19 +111,10 @@ static void ccid2_change_l_ack_ratio(struct sock *sk, u32 val)
|
|||
dp->dccps_l_ack_ratio = val;
|
||||
}
|
||||
|
||||
static void ccid2_change_srtt(struct ccid2_hc_tx_sock *hc, long val)
|
||||
{
|
||||
ccid2_pr_debug("change SRTT to %ld\n", val);
|
||||
hc->tx_srtt = val;
|
||||
}
|
||||
|
||||
static void ccid2_start_rto_timer(struct sock *sk);
|
||||
|
||||
static void ccid2_hc_tx_rto_expire(unsigned long data)
|
||||
{
|
||||
struct sock *sk = (struct sock *)data;
|
||||
struct ccid2_hc_tx_sock *hc = ccid2_hc_tx_sk(sk);
|
||||
long s;
|
||||
|
||||
bh_lock_sock(sk);
|
||||
if (sock_owned_by_user(sk)) {
|
||||
|
|
@ -178,23 +124,19 @@ static void ccid2_hc_tx_rto_expire(unsigned long data)
|
|||
|
||||
ccid2_pr_debug("RTO_EXPIRE\n");
|
||||
|
||||
ccid2_hc_tx_check_sanity(hc);
|
||||
|
||||
/* back-off timer */
|
||||
hc->tx_rto <<= 1;
|
||||
if (hc->tx_rto > DCCP_RTO_MAX)
|
||||
hc->tx_rto = DCCP_RTO_MAX;
|
||||
|
||||
s = hc->tx_rto / HZ;
|
||||
if (s > 60)
|
||||
hc->tx_rto = 60 * HZ;
|
||||
|
||||
ccid2_start_rto_timer(sk);
|
||||
sk_reset_timer(sk, &hc->tx_rtotimer, jiffies + hc->tx_rto);
|
||||
|
||||
/* adjust pipe, cwnd etc */
|
||||
hc->tx_ssthresh = hc->tx_cwnd / 2;
|
||||
if (hc->tx_ssthresh < 2)
|
||||
hc->tx_ssthresh = 2;
|
||||
hc->tx_cwnd = 1;
|
||||
hc->tx_pipe = 0;
|
||||
hc->tx_cwnd = 1;
|
||||
hc->tx_pipe = 0;
|
||||
|
||||
/* clear state about stuff we sent */
|
||||
hc->tx_seqt = hc->tx_seqh;
|
||||
|
|
@ -204,23 +146,12 @@ static void ccid2_hc_tx_rto_expire(unsigned long data)
|
|||
hc->tx_rpseq = 0;
|
||||
hc->tx_rpdupack = -1;
|
||||
ccid2_change_l_ack_ratio(sk, 1);
|
||||
ccid2_hc_tx_check_sanity(hc);
|
||||
out:
|
||||
bh_unlock_sock(sk);
|
||||
sock_put(sk);
|
||||
}
|
||||
|
||||
static void ccid2_start_rto_timer(struct sock *sk)
|
||||
{
|
||||
struct ccid2_hc_tx_sock *hc = ccid2_hc_tx_sk(sk);
|
||||
|
||||
ccid2_pr_debug("setting RTO timeout=%ld\n", hc->tx_rto);
|
||||
|
||||
BUG_ON(timer_pending(&hc->tx_rtotimer));
|
||||
sk_reset_timer(sk, &hc->tx_rtotimer, jiffies + hc->tx_rto);
|
||||
}
|
||||
|
||||
static void ccid2_hc_tx_packet_sent(struct sock *sk, int more, unsigned int len)
|
||||
static void ccid2_hc_tx_packet_sent(struct sock *sk, unsigned int len)
|
||||
{
|
||||
struct dccp_sock *dp = dccp_sk(sk);
|
||||
struct ccid2_hc_tx_sock *hc = ccid2_hc_tx_sk(sk);
|
||||
|
|
@ -230,7 +161,7 @@ static void ccid2_hc_tx_packet_sent(struct sock *sk, int more, unsigned int len)
|
|||
|
||||
hc->tx_seqh->ccid2s_seq = dp->dccps_gss;
|
||||
hc->tx_seqh->ccid2s_acked = 0;
|
||||
hc->tx_seqh->ccid2s_sent = jiffies;
|
||||
hc->tx_seqh->ccid2s_sent = ccid2_time_stamp;
|
||||
|
||||
next = hc->tx_seqh->ccid2s_next;
|
||||
/* check if we need to alloc more space */
|
||||
|
|
@ -296,23 +227,20 @@ static void ccid2_hc_tx_packet_sent(struct sock *sk, int more, unsigned int len)
|
|||
}
|
||||
#endif
|
||||
|
||||
/* setup RTO timer */
|
||||
if (!timer_pending(&hc->tx_rtotimer))
|
||||
ccid2_start_rto_timer(sk);
|
||||
sk_reset_timer(sk, &hc->tx_rtotimer, jiffies + hc->tx_rto);
|
||||
|
||||
#ifdef CONFIG_IP_DCCP_CCID2_DEBUG
|
||||
do {
|
||||
struct ccid2_seq *seqp = hc->tx_seqt;
|
||||
|
||||
while (seqp != hc->tx_seqh) {
|
||||
ccid2_pr_debug("out seq=%llu acked=%d time=%lu\n",
|
||||
ccid2_pr_debug("out seq=%llu acked=%d time=%u\n",
|
||||
(unsigned long long)seqp->ccid2s_seq,
|
||||
seqp->ccid2s_acked, seqp->ccid2s_sent);
|
||||
seqp = seqp->ccid2s_next;
|
||||
}
|
||||
} while (0);
|
||||
ccid2_pr_debug("=========\n");
|
||||
ccid2_hc_tx_check_sanity(hc);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -378,17 +306,87 @@ out_invalid_option:
|
|||
return -1;
|
||||
}
|
||||
|
||||
static void ccid2_hc_tx_kill_rto_timer(struct sock *sk)
|
||||
/**
|
||||
* ccid2_rtt_estimator - Sample RTT and compute RTO using RFC2988 algorithm
|
||||
* This code is almost identical with TCP's tcp_rtt_estimator(), since
|
||||
* - it has a higher sampling frequency (recommended by RFC 1323),
|
||||
* - the RTO does not collapse into RTT due to RTTVAR going towards zero,
|
||||
* - it is simple (cf. more complex proposals such as Eifel timer or research
|
||||
* which suggests that the gain should be set according to window size),
|
||||
* - in tests it was found to work well with CCID2 [gerrit].
|
||||
*/
|
||||
static void ccid2_rtt_estimator(struct sock *sk, const long mrtt)
|
||||
{
|
||||
struct ccid2_hc_tx_sock *hc = ccid2_hc_tx_sk(sk);
|
||||
long m = mrtt ? : 1;
|
||||
|
||||
sk_stop_timer(sk, &hc->tx_rtotimer);
|
||||
ccid2_pr_debug("deleted RTO timer\n");
|
||||
if (hc->tx_srtt == 0) {
|
||||
/* First measurement m */
|
||||
hc->tx_srtt = m << 3;
|
||||
hc->tx_mdev = m << 1;
|
||||
|
||||
hc->tx_mdev_max = max(hc->tx_mdev, tcp_rto_min(sk));
|
||||
hc->tx_rttvar = hc->tx_mdev_max;
|
||||
|
||||
hc->tx_rtt_seq = dccp_sk(sk)->dccps_gss;
|
||||
} else {
|
||||
/* Update scaled SRTT as SRTT += 1/8 * (m - SRTT) */
|
||||
m -= (hc->tx_srtt >> 3);
|
||||
hc->tx_srtt += m;
|
||||
|
||||
/* Similarly, update scaled mdev with regard to |m| */
|
||||
if (m < 0) {
|
||||
m = -m;
|
||||
m -= (hc->tx_mdev >> 2);
|
||||
/*
|
||||
* This neutralises RTO increase when RTT < SRTT - mdev
|
||||
* (see P. Sarolahti, A. Kuznetsov,"Congestion Control
|
||||
* in Linux TCP", USENIX 2002, pp. 49-62).
|
||||
*/
|
||||
if (m > 0)
|
||||
m >>= 3;
|
||||
} else {
|
||||
m -= (hc->tx_mdev >> 2);
|
||||
}
|
||||
hc->tx_mdev += m;
|
||||
|
||||
if (hc->tx_mdev > hc->tx_mdev_max) {
|
||||
hc->tx_mdev_max = hc->tx_mdev;
|
||||
if (hc->tx_mdev_max > hc->tx_rttvar)
|
||||
hc->tx_rttvar = hc->tx_mdev_max;
|
||||
}
|
||||
|
||||
/*
|
||||
* Decay RTTVAR at most once per flight, exploiting that
|
||||
* 1) pipe <= cwnd <= Sequence_Window = W (RFC 4340, 7.5.2)
|
||||
* 2) AWL = GSS-W+1 <= GAR <= GSS (RFC 4340, 7.5.1)
|
||||
* GAR is a useful bound for FlightSize = pipe.
|
||||
* AWL is probably too low here, as it over-estimates pipe.
|
||||
*/
|
||||
if (after48(dccp_sk(sk)->dccps_gar, hc->tx_rtt_seq)) {
|
||||
if (hc->tx_mdev_max < hc->tx_rttvar)
|
||||
hc->tx_rttvar -= (hc->tx_rttvar -
|
||||
hc->tx_mdev_max) >> 2;
|
||||
hc->tx_rtt_seq = dccp_sk(sk)->dccps_gss;
|
||||
hc->tx_mdev_max = tcp_rto_min(sk);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set RTO from SRTT and RTTVAR
|
||||
* As in TCP, 4 * RTTVAR >= TCP_RTO_MIN, giving a minimum RTO of 200 ms.
|
||||
* This agrees with RFC 4341, 5:
|
||||
* "Because DCCP does not retransmit data, DCCP does not require
|
||||
* TCP's recommended minimum timeout of one second".
|
||||
*/
|
||||
hc->tx_rto = (hc->tx_srtt >> 3) + hc->tx_rttvar;
|
||||
|
||||
if (hc->tx_rto > DCCP_RTO_MAX)
|
||||
hc->tx_rto = DCCP_RTO_MAX;
|
||||
}
|
||||
|
||||
static inline void ccid2_new_ack(struct sock *sk,
|
||||
struct ccid2_seq *seqp,
|
||||
unsigned int *maxincr)
|
||||
static void ccid2_new_ack(struct sock *sk, struct ccid2_seq *seqp,
|
||||
unsigned int *maxincr)
|
||||
{
|
||||
struct ccid2_hc_tx_sock *hc = ccid2_hc_tx_sk(sk);
|
||||
|
||||
|
|
@ -402,93 +400,27 @@ static inline void ccid2_new_ack(struct sock *sk,
|
|||
hc->tx_cwnd += 1;
|
||||
hc->tx_packets_acked = 0;
|
||||
}
|
||||
|
||||
/* update RTO */
|
||||
if (hc->tx_srtt == -1 ||
|
||||
time_after(jiffies, hc->tx_lastrtt + hc->tx_srtt)) {
|
||||
unsigned long r = (long)jiffies - (long)seqp->ccid2s_sent;
|
||||
int s;
|
||||
|
||||
/* first measurement */
|
||||
if (hc->tx_srtt == -1) {
|
||||
ccid2_pr_debug("R: %lu Time=%lu seq=%llu\n",
|
||||
r, jiffies,
|
||||
(unsigned long long)seqp->ccid2s_seq);
|
||||
ccid2_change_srtt(hc, r);
|
||||
hc->tx_rttvar = r >> 1;
|
||||
} else {
|
||||
/* RTTVAR */
|
||||
long tmp = hc->tx_srtt - r;
|
||||
long srtt;
|
||||
|
||||
if (tmp < 0)
|
||||
tmp *= -1;
|
||||
|
||||
tmp >>= 2;
|
||||
hc->tx_rttvar *= 3;
|
||||
hc->tx_rttvar >>= 2;
|
||||
hc->tx_rttvar += tmp;
|
||||
|
||||
/* SRTT */
|
||||
srtt = hc->tx_srtt;
|
||||
srtt *= 7;
|
||||
srtt >>= 3;
|
||||
tmp = r >> 3;
|
||||
srtt += tmp;
|
||||
ccid2_change_srtt(hc, srtt);
|
||||
}
|
||||
s = hc->tx_rttvar << 2;
|
||||
/* clock granularity is 1 when based on jiffies */
|
||||
if (!s)
|
||||
s = 1;
|
||||
hc->tx_rto = hc->tx_srtt + s;
|
||||
|
||||
/* must be at least a second */
|
||||
s = hc->tx_rto / HZ;
|
||||
/* DCCP doesn't require this [but I like it cuz my code sux] */
|
||||
#if 1
|
||||
if (s < 1)
|
||||
hc->tx_rto = HZ;
|
||||
#endif
|
||||
/* max 60 seconds */
|
||||
if (s > 60)
|
||||
hc->tx_rto = HZ * 60;
|
||||
|
||||
hc->tx_lastrtt = jiffies;
|
||||
|
||||
ccid2_pr_debug("srtt: %ld rttvar: %ld rto: %ld (HZ=%d) R=%lu\n",
|
||||
hc->tx_srtt, hc->tx_rttvar,
|
||||
hc->tx_rto, HZ, r);
|
||||
}
|
||||
|
||||
/* we got a new ack, so re-start RTO timer */
|
||||
ccid2_hc_tx_kill_rto_timer(sk);
|
||||
ccid2_start_rto_timer(sk);
|
||||
}
|
||||
|
||||
static void ccid2_hc_tx_dec_pipe(struct sock *sk)
|
||||
{
|
||||
struct ccid2_hc_tx_sock *hc = ccid2_hc_tx_sk(sk);
|
||||
|
||||
if (hc->tx_pipe == 0)
|
||||
DCCP_BUG("pipe == 0");
|
||||
else
|
||||
hc->tx_pipe--;
|
||||
|
||||
if (hc->tx_pipe == 0)
|
||||
ccid2_hc_tx_kill_rto_timer(sk);
|
||||
/*
|
||||
* FIXME: RTT is sampled several times per acknowledgment (for each
|
||||
* entry in the Ack Vector), instead of once per Ack (as in TCP SACK).
|
||||
* This causes the RTT to be over-estimated, since the older entries
|
||||
* in the Ack Vector have earlier sending times.
|
||||
* The cleanest solution is to not use the ccid2s_sent field at all
|
||||
* and instead use DCCP timestamps: requires changes in other places.
|
||||
*/
|
||||
ccid2_rtt_estimator(sk, ccid2_time_stamp - seqp->ccid2s_sent);
|
||||
}
|
||||
|
||||
static void ccid2_congestion_event(struct sock *sk, struct ccid2_seq *seqp)
|
||||
{
|
||||
struct ccid2_hc_tx_sock *hc = ccid2_hc_tx_sk(sk);
|
||||
|
||||
if (time_before(seqp->ccid2s_sent, hc->tx_last_cong)) {
|
||||
if ((s32)(seqp->ccid2s_sent - hc->tx_last_cong) < 0) {
|
||||
ccid2_pr_debug("Multiple losses in an RTT---treating as one\n");
|
||||
return;
|
||||
}
|
||||
|
||||
hc->tx_last_cong = jiffies;
|
||||
hc->tx_last_cong = ccid2_time_stamp;
|
||||
|
||||
hc->tx_cwnd = hc->tx_cwnd / 2 ? : 1U;
|
||||
hc->tx_ssthresh = max(hc->tx_cwnd, 2U);
|
||||
|
|
@ -510,7 +442,6 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
|
|||
int done = 0;
|
||||
unsigned int maxincr = 0;
|
||||
|
||||
ccid2_hc_tx_check_sanity(hc);
|
||||
/* check reverse path congestion */
|
||||
seqno = DCCP_SKB_CB(skb)->dccpd_seq;
|
||||
|
||||
|
|
@ -620,7 +551,7 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
|
|||
seqp->ccid2s_acked = 1;
|
||||
ccid2_pr_debug("Got ack for %llu\n",
|
||||
(unsigned long long)seqp->ccid2s_seq);
|
||||
ccid2_hc_tx_dec_pipe(sk);
|
||||
hc->tx_pipe--;
|
||||
}
|
||||
if (seqp == hc->tx_seqt) {
|
||||
done = 1;
|
||||
|
|
@ -677,7 +608,7 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
|
|||
* one ack vector.
|
||||
*/
|
||||
ccid2_congestion_event(sk, seqp);
|
||||
ccid2_hc_tx_dec_pipe(sk);
|
||||
hc->tx_pipe--;
|
||||
}
|
||||
if (seqp == hc->tx_seqt)
|
||||
break;
|
||||
|
|
@ -695,7 +626,11 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
|
|||
hc->tx_seqt = hc->tx_seqt->ccid2s_next;
|
||||
}
|
||||
|
||||
ccid2_hc_tx_check_sanity(hc);
|
||||
/* restart RTO timer if not all outstanding data has been acked */
|
||||
if (hc->tx_pipe == 0)
|
||||
sk_stop_timer(sk, &hc->tx_rtotimer);
|
||||
else
|
||||
sk_reset_timer(sk, &hc->tx_rtotimer, jiffies + hc->tx_rto);
|
||||
}
|
||||
|
||||
static int ccid2_hc_tx_init(struct ccid *ccid, struct sock *sk)
|
||||
|
|
@ -707,12 +642,8 @@ static int ccid2_hc_tx_init(struct ccid *ccid, struct sock *sk)
|
|||
/* RFC 4341, 5: initialise ssthresh to arbitrarily high (max) value */
|
||||
hc->tx_ssthresh = ~0U;
|
||||
|
||||
/*
|
||||
* RFC 4341, 5: "The cwnd parameter is initialized to at most four
|
||||
* packets for new connections, following the rules from [RFC3390]".
|
||||
* We need to convert the bytes of RFC3390 into the packets of RFC 4341.
|
||||
*/
|
||||
hc->tx_cwnd = clamp(4380U / dp->dccps_mss_cache, 2U, 4U);
|
||||
/* Use larger initial windows (RFC 4341, section 5). */
|
||||
hc->tx_cwnd = rfc3390_bytes_to_packets(dp->dccps_mss_cache);
|
||||
|
||||
/* Make sure that Ack Ratio is enabled and within bounds. */
|
||||
max_ratio = DIV_ROUND_UP(hc->tx_cwnd, 2);
|
||||
|
|
@ -723,15 +654,11 @@ static int ccid2_hc_tx_init(struct ccid *ccid, struct sock *sk)
|
|||
if (ccid2_hc_tx_alloc_seq(hc))
|
||||
return -ENOMEM;
|
||||
|
||||
hc->tx_rto = 3 * HZ;
|
||||
ccid2_change_srtt(hc, -1);
|
||||
hc->tx_rttvar = -1;
|
||||
hc->tx_rto = DCCP_TIMEOUT_INIT;
|
||||
hc->tx_rpdupack = -1;
|
||||
hc->tx_last_cong = jiffies;
|
||||
hc->tx_last_cong = ccid2_time_stamp;
|
||||
setup_timer(&hc->tx_rtotimer, ccid2_hc_tx_rto_expire,
|
||||
(unsigned long)sk);
|
||||
|
||||
ccid2_hc_tx_check_sanity(hc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -740,7 +667,7 @@ static void ccid2_hc_tx_exit(struct sock *sk)
|
|||
struct ccid2_hc_tx_sock *hc = ccid2_hc_tx_sk(sk);
|
||||
int i;
|
||||
|
||||
ccid2_hc_tx_kill_rto_timer(sk);
|
||||
sk_stop_timer(sk, &hc->tx_rtotimer);
|
||||
|
||||
for (i = 0; i < hc->tx_seqbufc; i++)
|
||||
kfree(hc->tx_seqbuf[i]);
|
||||
|
|
|
|||
|
|
@ -18,18 +18,23 @@
|
|||
#ifndef _DCCP_CCID2_H_
|
||||
#define _DCCP_CCID2_H_
|
||||
|
||||
#include <linux/dccp.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/types.h>
|
||||
#include "../ccid.h"
|
||||
#include "../dccp.h"
|
||||
|
||||
/*
|
||||
* CCID-2 timestamping faces the same issues as TCP timestamping.
|
||||
* Hence we reuse/share as much of the code as possible.
|
||||
*/
|
||||
#define ccid2_time_stamp tcp_time_stamp
|
||||
|
||||
/* NUMDUPACK parameter from RFC 4341, p. 6 */
|
||||
#define NUMDUPACK 3
|
||||
|
||||
struct sock;
|
||||
|
||||
struct ccid2_seq {
|
||||
u64 ccid2s_seq;
|
||||
unsigned long ccid2s_sent;
|
||||
u32 ccid2s_sent;
|
||||
int ccid2s_acked;
|
||||
struct ccid2_seq *ccid2s_prev;
|
||||
struct ccid2_seq *ccid2s_next;
|
||||
|
|
@ -42,7 +47,12 @@ struct ccid2_seq {
|
|||
* struct ccid2_hc_tx_sock - CCID2 TX half connection
|
||||
* @tx_{cwnd,ssthresh,pipe}: as per RFC 4341, section 5
|
||||
* @tx_packets_acked: Ack counter for deriving cwnd growth (RFC 3465)
|
||||
* @tx_lastrtt: time RTT was last measured
|
||||
* @tx_srtt: smoothed RTT estimate, scaled by 2^3
|
||||
* @tx_mdev: smoothed RTT variation, scaled by 2^2
|
||||
* @tx_mdev_max: maximum of @mdev during one flight
|
||||
* @tx_rttvar: moving average/maximum of @mdev_max
|
||||
* @tx_rto: RTO value deriving from SRTT and RTTVAR (RFC 2988)
|
||||
* @tx_rtt_seq: to decay RTTVAR at most once per flight
|
||||
* @tx_rpseq: last consecutive seqno
|
||||
* @tx_rpdupack: dupacks since rpseq
|
||||
*/
|
||||
|
|
@ -55,14 +65,19 @@ struct ccid2_hc_tx_sock {
|
|||
int tx_seqbufc;
|
||||
struct ccid2_seq *tx_seqh;
|
||||
struct ccid2_seq *tx_seqt;
|
||||
long tx_rto;
|
||||
long tx_srtt;
|
||||
long tx_rttvar;
|
||||
unsigned long tx_lastrtt;
|
||||
|
||||
/* RTT measurement: variables/principles are the same as in TCP */
|
||||
u32 tx_srtt,
|
||||
tx_mdev,
|
||||
tx_mdev_max,
|
||||
tx_rttvar,
|
||||
tx_rto;
|
||||
u64 tx_rtt_seq:48;
|
||||
struct timer_list tx_rtotimer;
|
||||
|
||||
u64 tx_rpseq;
|
||||
int tx_rpdupack;
|
||||
unsigned long tx_last_cong;
|
||||
u32 tx_last_cong;
|
||||
u64 tx_high_ack;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,6 @@ static const char *ccid3_tx_state_name(enum ccid3_hc_tx_states state)
|
|||
[TFRC_SSTATE_NO_SENT] = "NO_SENT",
|
||||
[TFRC_SSTATE_NO_FBACK] = "NO_FBACK",
|
||||
[TFRC_SSTATE_FBACK] = "FBACK",
|
||||
[TFRC_SSTATE_TERM] = "TERM",
|
||||
};
|
||||
|
||||
return ccid3_state_names[state];
|
||||
|
|
@ -91,19 +90,16 @@ static inline u64 rfc3390_initial_rate(struct sock *sk)
|
|||
return scaled_div(w_init << 6, hc->tx_rtt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Recalculate t_ipi and delta (should be called whenever X changes)
|
||||
/**
|
||||
* ccid3_update_send_interval - Calculate new t_ipi = s / X_inst
|
||||
* This respects the granularity of X_inst (64 * bytes/second).
|
||||
*/
|
||||
static void ccid3_update_send_interval(struct ccid3_hc_tx_sock *hc)
|
||||
{
|
||||
/* Calculate new t_ipi = s / X_inst (X_inst is in 64 * bytes/second) */
|
||||
hc->tx_t_ipi = scaled_div32(((u64)hc->tx_s) << 6, hc->tx_x);
|
||||
|
||||
/* Calculate new delta by delta = min(t_ipi / 2, t_gran / 2) */
|
||||
hc->tx_delta = min_t(u32, hc->tx_t_ipi / 2, TFRC_OPSYS_HALF_TIME_GRAN);
|
||||
|
||||
ccid3_pr_debug("t_ipi=%u, delta=%u, s=%u, X=%u\n", hc->tx_t_ipi,
|
||||
hc->tx_delta, hc->tx_s, (unsigned)(hc->tx_x >> 6));
|
||||
ccid3_pr_debug("t_ipi=%u, s=%u, X=%u\n", hc->tx_t_ipi,
|
||||
hc->tx_s, (unsigned)(hc->tx_x >> 6));
|
||||
}
|
||||
|
||||
static u32 ccid3_hc_tx_idle_rtt(struct ccid3_hc_tx_sock *hc, ktime_t now)
|
||||
|
|
@ -211,16 +207,19 @@ static void ccid3_hc_tx_no_feedback_timer(unsigned long data)
|
|||
ccid3_pr_debug("%s(%p, state=%s) - entry\n", dccp_role(sk), sk,
|
||||
ccid3_tx_state_name(hc->tx_state));
|
||||
|
||||
/* Ignore and do not restart after leaving the established state */
|
||||
if ((1 << sk->sk_state) & ~(DCCPF_OPEN | DCCPF_PARTOPEN))
|
||||
goto out;
|
||||
|
||||
/* Reset feedback state to "no feedback received" */
|
||||
if (hc->tx_state == TFRC_SSTATE_FBACK)
|
||||
ccid3_hc_tx_set_state(sk, TFRC_SSTATE_NO_FBACK);
|
||||
else if (hc->tx_state != TFRC_SSTATE_NO_FBACK)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Determine new allowed sending rate X as per draft rfc3448bis-00, 4.4
|
||||
* RTO is 0 if and only if no feedback has been received yet.
|
||||
*/
|
||||
if (hc->tx_t_rto == 0 || /* no feedback received yet */
|
||||
hc->tx_p == 0) {
|
||||
if (hc->tx_t_rto == 0 || hc->tx_p == 0) {
|
||||
|
||||
/* halve send rate directly */
|
||||
hc->tx_x = max(hc->tx_x / 2,
|
||||
|
|
@ -256,7 +255,7 @@ static void ccid3_hc_tx_no_feedback_timer(unsigned long data)
|
|||
* Set new timeout for the nofeedback timer.
|
||||
* See comments in packet_recv() regarding the value of t_RTO.
|
||||
*/
|
||||
if (unlikely(hc->tx_t_rto == 0)) /* no feedback yet */
|
||||
if (unlikely(hc->tx_t_rto == 0)) /* no feedback received yet */
|
||||
t_nfb = TFRC_INITIAL_TIMEOUT;
|
||||
else
|
||||
t_nfb = max(hc->tx_t_rto, 2 * hc->tx_t_ipi);
|
||||
|
|
@ -290,8 +289,7 @@ static int ccid3_hc_tx_send_packet(struct sock *sk, struct sk_buff *skb)
|
|||
if (unlikely(skb->len == 0))
|
||||
return -EBADMSG;
|
||||
|
||||
switch (hc->tx_state) {
|
||||
case TFRC_SSTATE_NO_SENT:
|
||||
if (hc->tx_state == TFRC_SSTATE_NO_SENT) {
|
||||
sk_reset_timer(sk, &hc->tx_no_feedback_timer, (jiffies +
|
||||
usecs_to_jiffies(TFRC_INITIAL_TIMEOUT)));
|
||||
hc->tx_last_win_count = 0;
|
||||
|
|
@ -326,27 +324,22 @@ static int ccid3_hc_tx_send_packet(struct sock *sk, struct sk_buff *skb)
|
|||
ccid3_update_send_interval(hc);
|
||||
|
||||
ccid3_hc_tx_set_state(sk, TFRC_SSTATE_NO_FBACK);
|
||||
break;
|
||||
case TFRC_SSTATE_NO_FBACK:
|
||||
case TFRC_SSTATE_FBACK:
|
||||
|
||||
} else {
|
||||
delay = ktime_us_delta(hc->tx_t_nom, now);
|
||||
ccid3_pr_debug("delay=%ld\n", (long)delay);
|
||||
/*
|
||||
* Scheduling of packet transmissions [RFC 3448, 4.6]
|
||||
* Scheduling of packet transmissions (RFC 5348, 8.3)
|
||||
*
|
||||
* if (t_now > t_nom - delta)
|
||||
* // send the packet now
|
||||
* else
|
||||
* // send the packet in (t_nom - t_now) milliseconds.
|
||||
*/
|
||||
if (delay - (s64)hc->tx_delta >= 1000)
|
||||
return (u32)delay / 1000L;
|
||||
if (delay >= TFRC_T_DELTA)
|
||||
return (u32)delay / USEC_PER_MSEC;
|
||||
|
||||
ccid3_hc_tx_update_win_count(hc, now);
|
||||
break;
|
||||
case TFRC_SSTATE_TERM:
|
||||
DCCP_BUG("%s(%p) - Illegal state TERM", dccp_role(sk), sk);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* prepare to send now (add options etc.) */
|
||||
|
|
@ -358,8 +351,7 @@ static int ccid3_hc_tx_send_packet(struct sock *sk, struct sk_buff *skb)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void ccid3_hc_tx_packet_sent(struct sock *sk, int more,
|
||||
unsigned int len)
|
||||
static void ccid3_hc_tx_packet_sent(struct sock *sk, unsigned int len)
|
||||
{
|
||||
struct ccid3_hc_tx_sock *hc = ccid3_hc_tx_sk(sk);
|
||||
|
||||
|
|
@ -372,48 +364,34 @@ static void ccid3_hc_tx_packet_sent(struct sock *sk, int more,
|
|||
static void ccid3_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
struct ccid3_hc_tx_sock *hc = ccid3_hc_tx_sk(sk);
|
||||
struct ccid3_options_received *opt_recv;
|
||||
struct tfrc_tx_hist_entry *acked;
|
||||
ktime_t now;
|
||||
unsigned long t_nfb;
|
||||
u32 pinv, r_sample;
|
||||
u32 r_sample;
|
||||
|
||||
/* we are only interested in ACKs */
|
||||
if (!(DCCP_SKB_CB(skb)->dccpd_type == DCCP_PKT_ACK ||
|
||||
DCCP_SKB_CB(skb)->dccpd_type == DCCP_PKT_DATAACK))
|
||||
return;
|
||||
/* ... and only in the established state */
|
||||
if (hc->tx_state != TFRC_SSTATE_FBACK &&
|
||||
hc->tx_state != TFRC_SSTATE_NO_FBACK)
|
||||
return;
|
||||
|
||||
opt_recv = &hc->tx_options_received;
|
||||
now = ktime_get_real();
|
||||
|
||||
/* Estimate RTT from history if ACK number is valid */
|
||||
r_sample = tfrc_tx_hist_rtt(hc->tx_hist,
|
||||
DCCP_SKB_CB(skb)->dccpd_ack_seq, now);
|
||||
if (r_sample == 0) {
|
||||
DCCP_WARN("%s(%p): %s with bogus ACK-%llu\n", dccp_role(sk), sk,
|
||||
dccp_packet_name(DCCP_SKB_CB(skb)->dccpd_type),
|
||||
(unsigned long long)DCCP_SKB_CB(skb)->dccpd_ack_seq);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update receive rate in units of 64 * bytes/second */
|
||||
hc->tx_x_recv = opt_recv->ccid3or_receive_rate;
|
||||
hc->tx_x_recv <<= 6;
|
||||
|
||||
/* Update loss event rate (which is scaled by 1e6) */
|
||||
pinv = opt_recv->ccid3or_loss_event_rate;
|
||||
if (pinv == ~0U || pinv == 0) /* see RFC 4342, 8.5 */
|
||||
hc->tx_p = 0;
|
||||
else /* can not exceed 100% */
|
||||
hc->tx_p = scaled_div(1, pinv);
|
||||
/*
|
||||
* Validate new RTT sample and update moving average
|
||||
* Locate the acknowledged packet in the TX history.
|
||||
*
|
||||
* Returning "entry not found" here can for instance happen when
|
||||
* - the host has not sent out anything (e.g. a passive server),
|
||||
* - the Ack is outdated (packet with higher Ack number was received),
|
||||
* - it is a bogus Ack (for a packet not sent on this connection).
|
||||
*/
|
||||
r_sample = dccp_sample_rtt(sk, r_sample);
|
||||
acked = tfrc_tx_hist_find_entry(hc->tx_hist, dccp_hdr_ack_seq(skb));
|
||||
if (acked == NULL)
|
||||
return;
|
||||
/* For the sake of RTT sampling, ignore/remove all older entries */
|
||||
tfrc_tx_hist_purge(&acked->next);
|
||||
|
||||
/* Update the moving average for the RTT estimate (RFC 3448, 4.3) */
|
||||
now = ktime_get_real();
|
||||
r_sample = dccp_sample_rtt(sk, ktime_us_delta(now, acked->stamp));
|
||||
hc->tx_rtt = tfrc_ewma(hc->tx_rtt, r_sample, 9);
|
||||
|
||||
/*
|
||||
* Update allowed sending rate X as per draft rfc3448bis-00, 4.2/3
|
||||
*/
|
||||
|
|
@ -461,13 +439,12 @@ done_computing_x:
|
|||
sk->sk_write_space(sk);
|
||||
|
||||
/*
|
||||
* Update timeout interval for the nofeedback timer.
|
||||
* We use a configuration option to increase the lower bound.
|
||||
* This can help avoid triggering the nofeedback timer too
|
||||
* often ('spinning') on LANs with small RTTs.
|
||||
* Update timeout interval for the nofeedback timer. In order to control
|
||||
* rate halving on networks with very low RTTs (<= 1 ms), use per-route
|
||||
* tunable RTAX_RTO_MIN value as the lower bound.
|
||||
*/
|
||||
hc->tx_t_rto = max_t(u32, 4 * hc->tx_rtt, (CONFIG_IP_DCCP_CCID3_RTO *
|
||||
(USEC_PER_SEC / 1000)));
|
||||
hc->tx_t_rto = max_t(u32, 4 * hc->tx_rtt,
|
||||
USEC_PER_SEC/HZ * tcp_rto_min(sk));
|
||||
/*
|
||||
* Schedule no feedback timer to expire in
|
||||
* max(t_RTO, 2 * s/X) = max(t_RTO, 2 * t_ipi)
|
||||
|
|
@ -482,66 +459,41 @@ done_computing_x:
|
|||
jiffies + usecs_to_jiffies(t_nfb));
|
||||
}
|
||||
|
||||
static int ccid3_hc_tx_parse_options(struct sock *sk, unsigned char option,
|
||||
unsigned char len, u16 idx,
|
||||
unsigned char *value)
|
||||
static int ccid3_hc_tx_parse_options(struct sock *sk, u8 packet_type,
|
||||
u8 option, u8 *optval, u8 optlen)
|
||||
{
|
||||
int rc = 0;
|
||||
const struct dccp_sock *dp = dccp_sk(sk);
|
||||
struct ccid3_hc_tx_sock *hc = ccid3_hc_tx_sk(sk);
|
||||
struct ccid3_options_received *opt_recv;
|
||||
__be32 opt_val;
|
||||
|
||||
opt_recv = &hc->tx_options_received;
|
||||
|
||||
if (opt_recv->ccid3or_seqno != dp->dccps_gsr) {
|
||||
opt_recv->ccid3or_seqno = dp->dccps_gsr;
|
||||
opt_recv->ccid3or_loss_event_rate = ~0;
|
||||
opt_recv->ccid3or_loss_intervals_idx = 0;
|
||||
opt_recv->ccid3or_loss_intervals_len = 0;
|
||||
opt_recv->ccid3or_receive_rate = 0;
|
||||
}
|
||||
|
||||
switch (option) {
|
||||
case TFRC_OPT_LOSS_EVENT_RATE:
|
||||
if (unlikely(len != 4)) {
|
||||
DCCP_WARN("%s(%p), invalid len %d "
|
||||
"for TFRC_OPT_LOSS_EVENT_RATE\n",
|
||||
dccp_role(sk), sk, len);
|
||||
rc = -EINVAL;
|
||||
} else {
|
||||
opt_val = get_unaligned((__be32 *)value);
|
||||
opt_recv->ccid3or_loss_event_rate = ntohl(opt_val);
|
||||
ccid3_pr_debug("%s(%p), LOSS_EVENT_RATE=%u\n",
|
||||
dccp_role(sk), sk,
|
||||
opt_recv->ccid3or_loss_event_rate);
|
||||
}
|
||||
break;
|
||||
case TFRC_OPT_LOSS_INTERVALS:
|
||||
opt_recv->ccid3or_loss_intervals_idx = idx;
|
||||
opt_recv->ccid3or_loss_intervals_len = len;
|
||||
ccid3_pr_debug("%s(%p), LOSS_INTERVALS=(%u, %u)\n",
|
||||
dccp_role(sk), sk,
|
||||
opt_recv->ccid3or_loss_intervals_idx,
|
||||
opt_recv->ccid3or_loss_intervals_len);
|
||||
break;
|
||||
case TFRC_OPT_RECEIVE_RATE:
|
||||
if (unlikely(len != 4)) {
|
||||
DCCP_WARN("%s(%p), invalid len %d "
|
||||
"for TFRC_OPT_RECEIVE_RATE\n",
|
||||
dccp_role(sk), sk, len);
|
||||
rc = -EINVAL;
|
||||
} else {
|
||||
opt_val = get_unaligned((__be32 *)value);
|
||||
opt_recv->ccid3or_receive_rate = ntohl(opt_val);
|
||||
ccid3_pr_debug("%s(%p), RECEIVE_RATE=%u\n",
|
||||
dccp_role(sk), sk,
|
||||
opt_recv->ccid3or_receive_rate);
|
||||
case TFRC_OPT_LOSS_EVENT_RATE:
|
||||
/* Must be ignored on Data packets, cf. RFC 4342 8.3 and 8.5 */
|
||||
if (packet_type == DCCP_PKT_DATA)
|
||||
break;
|
||||
if (unlikely(optlen != 4)) {
|
||||
DCCP_WARN("%s(%p), invalid len %d for %u\n",
|
||||
dccp_role(sk), sk, optlen, option);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
opt_val = ntohl(get_unaligned((__be32 *)optval));
|
||||
|
||||
return rc;
|
||||
if (option == TFRC_OPT_RECEIVE_RATE) {
|
||||
/* Receive Rate is kept in units of 64 bytes/second */
|
||||
hc->tx_x_recv = opt_val;
|
||||
hc->tx_x_recv <<= 6;
|
||||
|
||||
ccid3_pr_debug("%s(%p), RECEIVE_RATE=%u\n",
|
||||
dccp_role(sk), sk, opt_val);
|
||||
} else {
|
||||
/* Update the fixpoint Loss Event Rate fraction */
|
||||
hc->tx_p = tfrc_invert_loss_event_rate(opt_val);
|
||||
|
||||
ccid3_pr_debug("%s(%p), LOSS_EVENT_RATE=%u\n",
|
||||
dccp_role(sk), sk, opt_val);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ccid3_hc_tx_init(struct ccid *ccid, struct sock *sk)
|
||||
|
|
@ -559,42 +511,36 @@ static void ccid3_hc_tx_exit(struct sock *sk)
|
|||
{
|
||||
struct ccid3_hc_tx_sock *hc = ccid3_hc_tx_sk(sk);
|
||||
|
||||
ccid3_hc_tx_set_state(sk, TFRC_SSTATE_TERM);
|
||||
sk_stop_timer(sk, &hc->tx_no_feedback_timer);
|
||||
|
||||
tfrc_tx_hist_purge(&hc->tx_hist);
|
||||
}
|
||||
|
||||
static void ccid3_hc_tx_get_info(struct sock *sk, struct tcp_info *info)
|
||||
{
|
||||
struct ccid3_hc_tx_sock *hc;
|
||||
|
||||
/* Listen socks doesn't have a private CCID block */
|
||||
if (sk->sk_state == DCCP_LISTEN)
|
||||
return;
|
||||
|
||||
hc = ccid3_hc_tx_sk(sk);
|
||||
info->tcpi_rto = hc->tx_t_rto;
|
||||
info->tcpi_rtt = hc->tx_rtt;
|
||||
info->tcpi_rto = ccid3_hc_tx_sk(sk)->tx_t_rto;
|
||||
info->tcpi_rtt = ccid3_hc_tx_sk(sk)->tx_rtt;
|
||||
}
|
||||
|
||||
static int ccid3_hc_tx_getsockopt(struct sock *sk, const int optname, int len,
|
||||
u32 __user *optval, int __user *optlen)
|
||||
{
|
||||
const struct ccid3_hc_tx_sock *hc;
|
||||
const struct ccid3_hc_tx_sock *hc = ccid3_hc_tx_sk(sk);
|
||||
struct tfrc_tx_info tfrc;
|
||||
const void *val;
|
||||
|
||||
/* Listen socks doesn't have a private CCID block */
|
||||
if (sk->sk_state == DCCP_LISTEN)
|
||||
return -EINVAL;
|
||||
|
||||
hc = ccid3_hc_tx_sk(sk);
|
||||
switch (optname) {
|
||||
case DCCP_SOCKOPT_CCID_TX_INFO:
|
||||
if (len < sizeof(hc->tx_tfrc))
|
||||
if (len < sizeof(tfrc))
|
||||
return -EINVAL;
|
||||
len = sizeof(hc->tx_tfrc);
|
||||
val = &hc->tx_tfrc;
|
||||
tfrc.tfrctx_x = hc->tx_x;
|
||||
tfrc.tfrctx_x_recv = hc->tx_x_recv;
|
||||
tfrc.tfrctx_x_calc = hc->tx_x_calc;
|
||||
tfrc.tfrctx_rtt = hc->tx_rtt;
|
||||
tfrc.tfrctx_p = hc->tx_p;
|
||||
tfrc.tfrctx_rto = hc->tx_t_rto;
|
||||
tfrc.tfrctx_ipi = hc->tx_t_ipi;
|
||||
len = sizeof(tfrc);
|
||||
val = &tfrc;
|
||||
break;
|
||||
default:
|
||||
return -ENOPROTOOPT;
|
||||
|
|
@ -624,7 +570,6 @@ static const char *ccid3_rx_state_name(enum ccid3_hc_rx_states state)
|
|||
static const char *const ccid3_rx_state_names[] = {
|
||||
[TFRC_RSTATE_NO_DATA] = "NO_DATA",
|
||||
[TFRC_RSTATE_DATA] = "DATA",
|
||||
[TFRC_RSTATE_TERM] = "TERM",
|
||||
};
|
||||
|
||||
return ccid3_rx_state_names[state];
|
||||
|
|
@ -650,14 +595,9 @@ static void ccid3_hc_rx_send_feedback(struct sock *sk,
|
|||
{
|
||||
struct ccid3_hc_rx_sock *hc = ccid3_hc_rx_sk(sk);
|
||||
struct dccp_sock *dp = dccp_sk(sk);
|
||||
ktime_t now;
|
||||
ktime_t now = ktime_get_real();
|
||||
s64 delta = 0;
|
||||
|
||||
if (unlikely(hc->rx_state == TFRC_RSTATE_TERM))
|
||||
return;
|
||||
|
||||
now = ktime_get_real();
|
||||
|
||||
switch (fbtype) {
|
||||
case CCID3_FBACK_INITIAL:
|
||||
hc->rx_x_recv = 0;
|
||||
|
|
@ -701,14 +641,12 @@ static void ccid3_hc_rx_send_feedback(struct sock *sk,
|
|||
|
||||
static int ccid3_hc_rx_insert_options(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
const struct ccid3_hc_rx_sock *hc;
|
||||
const struct ccid3_hc_rx_sock *hc = ccid3_hc_rx_sk(sk);
|
||||
__be32 x_recv, pinv;
|
||||
|
||||
if (!(sk->sk_state == DCCP_OPEN || sk->sk_state == DCCP_PARTOPEN))
|
||||
return 0;
|
||||
|
||||
hc = ccid3_hc_rx_sk(sk);
|
||||
|
||||
if (dccp_packet_without_ack(skb))
|
||||
return 0;
|
||||
|
||||
|
|
@ -749,10 +687,11 @@ static u32 ccid3_first_li(struct sock *sk)
|
|||
x_recv = scaled_div32(hc->rx_bytes_recv, delta);
|
||||
if (x_recv == 0) { /* would also trigger divide-by-zero */
|
||||
DCCP_WARN("X_recv==0\n");
|
||||
if ((x_recv = hc->rx_x_recv) == 0) {
|
||||
if (hc->rx_x_recv == 0) {
|
||||
DCCP_BUG("stored value of X_recv is zero");
|
||||
return ~0U;
|
||||
}
|
||||
x_recv = hc->rx_x_recv;
|
||||
}
|
||||
|
||||
fval = scaled_div(hc->rx_s, hc->rx_rtt);
|
||||
|
|
@ -862,46 +801,31 @@ static void ccid3_hc_rx_exit(struct sock *sk)
|
|||
{
|
||||
struct ccid3_hc_rx_sock *hc = ccid3_hc_rx_sk(sk);
|
||||
|
||||
ccid3_hc_rx_set_state(sk, TFRC_RSTATE_TERM);
|
||||
|
||||
tfrc_rx_hist_purge(&hc->rx_hist);
|
||||
tfrc_lh_cleanup(&hc->rx_li_hist);
|
||||
}
|
||||
|
||||
static void ccid3_hc_rx_get_info(struct sock *sk, struct tcp_info *info)
|
||||
{
|
||||
const struct ccid3_hc_rx_sock *hc;
|
||||
|
||||
/* Listen socks doesn't have a private CCID block */
|
||||
if (sk->sk_state == DCCP_LISTEN)
|
||||
return;
|
||||
|
||||
hc = ccid3_hc_rx_sk(sk);
|
||||
info->tcpi_ca_state = hc->rx_state;
|
||||
info->tcpi_ca_state = ccid3_hc_rx_sk(sk)->rx_state;
|
||||
info->tcpi_options |= TCPI_OPT_TIMESTAMPS;
|
||||
info->tcpi_rcv_rtt = hc->rx_rtt;
|
||||
info->tcpi_rcv_rtt = ccid3_hc_rx_sk(sk)->rx_rtt;
|
||||
}
|
||||
|
||||
static int ccid3_hc_rx_getsockopt(struct sock *sk, const int optname, int len,
|
||||
u32 __user *optval, int __user *optlen)
|
||||
{
|
||||
const struct ccid3_hc_rx_sock *hc;
|
||||
const struct ccid3_hc_rx_sock *hc = ccid3_hc_rx_sk(sk);
|
||||
struct tfrc_rx_info rx_info;
|
||||
const void *val;
|
||||
|
||||
/* Listen socks doesn't have a private CCID block */
|
||||
if (sk->sk_state == DCCP_LISTEN)
|
||||
return -EINVAL;
|
||||
|
||||
hc = ccid3_hc_rx_sk(sk);
|
||||
switch (optname) {
|
||||
case DCCP_SOCKOPT_CCID_RX_INFO:
|
||||
if (len < sizeof(rx_info))
|
||||
return -EINVAL;
|
||||
rx_info.tfrcrx_x_recv = hc->rx_x_recv;
|
||||
rx_info.tfrcrx_rtt = hc->rx_rtt;
|
||||
rx_info.tfrcrx_p = hc->rx_pinv == 0 ? ~0U :
|
||||
scaled_div(1, hc->rx_pinv);
|
||||
rx_info.tfrcrx_p = tfrc_invert_loss_event_rate(hc->rx_pinv);
|
||||
len = sizeof(rx_info);
|
||||
val = &rx_info;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -42,35 +42,36 @@
|
|||
#include "lib/tfrc.h"
|
||||
#include "../ccid.h"
|
||||
|
||||
/* Two seconds as per RFC 3448 4.2 */
|
||||
/* Two seconds as per RFC 5348, 4.2 */
|
||||
#define TFRC_INITIAL_TIMEOUT (2 * USEC_PER_SEC)
|
||||
|
||||
/* In usecs - half the scheduling granularity as per RFC3448 4.6 */
|
||||
#define TFRC_OPSYS_HALF_TIME_GRAN (USEC_PER_SEC / (2 * HZ))
|
||||
|
||||
/* Parameter t_mbi from [RFC 3448, 4.3]: backoff interval in seconds */
|
||||
#define TFRC_T_MBI 64
|
||||
|
||||
/*
|
||||
* The t_delta parameter (RFC 5348, 8.3): delays of less than %USEC_PER_MSEC are
|
||||
* rounded down to 0, since sk_reset_timer() here uses millisecond granularity.
|
||||
* Hence we can use a constant t_delta = %USEC_PER_MSEC when HZ >= 500. A coarse
|
||||
* resolution of HZ < 500 means that the error is below one timer tick (t_gran)
|
||||
* when using the constant t_delta = t_gran / 2 = %USEC_PER_SEC / (2 * HZ).
|
||||
*/
|
||||
#if (HZ >= 500)
|
||||
# define TFRC_T_DELTA USEC_PER_MSEC
|
||||
#else
|
||||
# define TFRC_T_DELTA (USEC_PER_SEC / (2 * HZ))
|
||||
#endif
|
||||
|
||||
enum ccid3_options {
|
||||
TFRC_OPT_LOSS_EVENT_RATE = 192,
|
||||
TFRC_OPT_LOSS_INTERVALS = 193,
|
||||
TFRC_OPT_RECEIVE_RATE = 194,
|
||||
};
|
||||
|
||||
struct ccid3_options_received {
|
||||
u64 ccid3or_seqno:48,
|
||||
ccid3or_loss_intervals_idx:16;
|
||||
u16 ccid3or_loss_intervals_len;
|
||||
u32 ccid3or_loss_event_rate;
|
||||
u32 ccid3or_receive_rate;
|
||||
};
|
||||
|
||||
/* TFRC sender states */
|
||||
enum ccid3_hc_tx_states {
|
||||
TFRC_SSTATE_NO_SENT = 1,
|
||||
TFRC_SSTATE_NO_FBACK,
|
||||
TFRC_SSTATE_FBACK,
|
||||
TFRC_SSTATE_TERM,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -90,19 +91,16 @@ enum ccid3_hc_tx_states {
|
|||
* @tx_no_feedback_timer: Handle to no feedback timer
|
||||
* @tx_t_ld: Time last doubled during slow start
|
||||
* @tx_t_nom: Nominal send time of next packet
|
||||
* @tx_delta: Send timer delta (RFC 3448, 4.6) in usecs
|
||||
* @tx_hist: Packet history
|
||||
* @tx_options_received: Parsed set of retrieved options
|
||||
*/
|
||||
struct ccid3_hc_tx_sock {
|
||||
struct tfrc_tx_info tx_tfrc;
|
||||
#define tx_x tx_tfrc.tfrctx_x
|
||||
#define tx_x_recv tx_tfrc.tfrctx_x_recv
|
||||
#define tx_x_calc tx_tfrc.tfrctx_x_calc
|
||||
#define tx_rtt tx_tfrc.tfrctx_rtt
|
||||
#define tx_p tx_tfrc.tfrctx_p
|
||||
#define tx_t_rto tx_tfrc.tfrctx_rto
|
||||
#define tx_t_ipi tx_tfrc.tfrctx_ipi
|
||||
u64 tx_x;
|
||||
u64 tx_x_recv;
|
||||
u32 tx_x_calc;
|
||||
u32 tx_rtt;
|
||||
u32 tx_p;
|
||||
u32 tx_t_rto;
|
||||
u32 tx_t_ipi;
|
||||
u16 tx_s;
|
||||
enum ccid3_hc_tx_states tx_state:8;
|
||||
u8 tx_last_win_count;
|
||||
|
|
@ -110,9 +108,7 @@ struct ccid3_hc_tx_sock {
|
|||
struct timer_list tx_no_feedback_timer;
|
||||
ktime_t tx_t_ld;
|
||||
ktime_t tx_t_nom;
|
||||
u32 tx_delta;
|
||||
struct tfrc_tx_hist_entry *tx_hist;
|
||||
struct ccid3_options_received tx_options_received;
|
||||
};
|
||||
|
||||
static inline struct ccid3_hc_tx_sock *ccid3_hc_tx_sk(const struct sock *sk)
|
||||
|
|
@ -126,21 +122,16 @@ static inline struct ccid3_hc_tx_sock *ccid3_hc_tx_sk(const struct sock *sk)
|
|||
enum ccid3_hc_rx_states {
|
||||
TFRC_RSTATE_NO_DATA = 1,
|
||||
TFRC_RSTATE_DATA,
|
||||
TFRC_RSTATE_TERM = 127,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ccid3_hc_rx_sock - CCID3 receiver half-connection socket
|
||||
* @rx_x_recv: Receiver estimate of send rate (RFC 3448 4.3)
|
||||
* @rx_rtt: Receiver estimate of rtt (non-standard)
|
||||
* @rx_p: Current loss event rate (RFC 3448 5.4)
|
||||
* @rx_last_counter: Tracks window counter (RFC 4342, 8.1)
|
||||
* @rx_state: Receiver state, one of %ccid3_hc_rx_states
|
||||
* @rx_bytes_recv: Total sum of DCCP payload bytes
|
||||
* @rx_x_recv: Receiver estimate of send rate (RFC 3448, sec. 4.3)
|
||||
* @rx_rtt: Receiver estimate of RTT
|
||||
* @rx_tstamp_last_feedback: Time at which last feedback was sent
|
||||
* @rx_tstamp_last_ack: Time at which last feedback was sent
|
||||
* @rx_hist: Packet history (loss detection + RTT sampling)
|
||||
* @rx_li_hist: Loss Interval database
|
||||
* @rx_s: Received packet size in bytes
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ u8 tfrc_lh_update_i_mean(struct tfrc_loss_hist *lh, struct sk_buff *skb)
|
|||
cur->li_length = len;
|
||||
tfrc_lh_calc_i_mean(lh);
|
||||
|
||||
return (lh->i_mean < old_i_mean);
|
||||
return lh->i_mean < old_i_mean;
|
||||
}
|
||||
|
||||
/* Determine if `new_loss' does begin a new loss interval [RFC 4342, 10.2] */
|
||||
|
|
|
|||
|
|
@ -38,18 +38,6 @@
|
|||
#include "packet_history.h"
|
||||
#include "../../dccp.h"
|
||||
|
||||
/**
|
||||
* tfrc_tx_hist_entry - Simple singly-linked TX history list
|
||||
* @next: next oldest entry (LIFO order)
|
||||
* @seqno: sequence number of this entry
|
||||
* @stamp: send time of packet with sequence number @seqno
|
||||
*/
|
||||
struct tfrc_tx_hist_entry {
|
||||
struct tfrc_tx_hist_entry *next;
|
||||
u64 seqno;
|
||||
ktime_t stamp;
|
||||
};
|
||||
|
||||
/*
|
||||
* Transmitter History Routines
|
||||
*/
|
||||
|
|
@ -71,15 +59,6 @@ void tfrc_tx_packet_history_exit(void)
|
|||
}
|
||||
}
|
||||
|
||||
static struct tfrc_tx_hist_entry *
|
||||
tfrc_tx_hist_find_entry(struct tfrc_tx_hist_entry *head, u64 seqno)
|
||||
{
|
||||
while (head != NULL && head->seqno != seqno)
|
||||
head = head->next;
|
||||
|
||||
return head;
|
||||
}
|
||||
|
||||
int tfrc_tx_hist_add(struct tfrc_tx_hist_entry **headp, u64 seqno)
|
||||
{
|
||||
struct tfrc_tx_hist_entry *entry = kmem_cache_alloc(tfrc_tx_hist_slab, gfp_any());
|
||||
|
|
@ -107,24 +86,6 @@ void tfrc_tx_hist_purge(struct tfrc_tx_hist_entry **headp)
|
|||
*headp = NULL;
|
||||
}
|
||||
|
||||
u32 tfrc_tx_hist_rtt(struct tfrc_tx_hist_entry *head, const u64 seqno,
|
||||
const ktime_t now)
|
||||
{
|
||||
u32 rtt = 0;
|
||||
struct tfrc_tx_hist_entry *packet = tfrc_tx_hist_find_entry(head, seqno);
|
||||
|
||||
if (packet != NULL) {
|
||||
rtt = ktime_us_delta(now, packet->stamp);
|
||||
/*
|
||||
* Garbage-collect older (irrelevant) entries:
|
||||
*/
|
||||
tfrc_tx_hist_purge(&packet->next);
|
||||
}
|
||||
|
||||
return rtt;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Receiver History Routines
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -40,12 +40,28 @@
|
|||
#include <linux/slab.h>
|
||||
#include "tfrc.h"
|
||||
|
||||
struct tfrc_tx_hist_entry;
|
||||
/**
|
||||
* tfrc_tx_hist_entry - Simple singly-linked TX history list
|
||||
* @next: next oldest entry (LIFO order)
|
||||
* @seqno: sequence number of this entry
|
||||
* @stamp: send time of packet with sequence number @seqno
|
||||
*/
|
||||
struct tfrc_tx_hist_entry {
|
||||
struct tfrc_tx_hist_entry *next;
|
||||
u64 seqno;
|
||||
ktime_t stamp;
|
||||
};
|
||||
|
||||
static inline struct tfrc_tx_hist_entry *
|
||||
tfrc_tx_hist_find_entry(struct tfrc_tx_hist_entry *head, u64 seqno)
|
||||
{
|
||||
while (head != NULL && head->seqno != seqno)
|
||||
head = head->next;
|
||||
return head;
|
||||
}
|
||||
|
||||
extern int tfrc_tx_hist_add(struct tfrc_tx_hist_entry **headp, u64 seqno);
|
||||
extern void tfrc_tx_hist_purge(struct tfrc_tx_hist_entry **headp);
|
||||
extern u32 tfrc_tx_hist_rtt(struct tfrc_tx_hist_entry *head,
|
||||
const u64 seqno, const ktime_t now);
|
||||
|
||||
/* Subtraction a-b modulo-16, respects circular wrap-around */
|
||||
#define SUB16(a, b) (((a) + 16 - (b)) & 0xF)
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ static inline u32 tfrc_ewma(const u32 avg, const u32 newval, const u8 weight)
|
|||
|
||||
extern u32 tfrc_calc_x(u16 s, u32 R, u32 p);
|
||||
extern u32 tfrc_calc_x_reverse_lookup(u32 fvalue);
|
||||
extern u32 tfrc_invert_loss_event_rate(u32 loss_event_rate);
|
||||
|
||||
extern int tfrc_tx_packet_history_init(void);
|
||||
extern void tfrc_tx_packet_history_exit(void);
|
||||
|
|
|
|||
|
|
@ -687,3 +687,17 @@ u32 tfrc_calc_x_reverse_lookup(u32 fvalue)
|
|||
index = tfrc_binsearch(fvalue, 0);
|
||||
return (index + 1) * 1000000 / TFRC_CALC_X_ARRSIZE;
|
||||
}
|
||||
|
||||
/**
|
||||
* tfrc_invert_loss_event_rate - Compute p so that 10^6 corresponds to 100%
|
||||
* When @loss_event_rate is large, there is a chance that p is truncated to 0.
|
||||
* To avoid re-entering slow-start in that case, we set p = TFRC_SMALLEST_P > 0.
|
||||
*/
|
||||
u32 tfrc_invert_loss_event_rate(u32 loss_event_rate)
|
||||
{
|
||||
if (loss_event_rate == UINT_MAX) /* see RFC 4342, 8.5 */
|
||||
return 0;
|
||||
if (unlikely(loss_event_rate == 0)) /* map 1/0 into 100% */
|
||||
return 1000000;
|
||||
return max_t(u32, scaled_div(1, loss_event_rate), TFRC_SMALLEST_P);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,18 +153,27 @@ static inline u64 max48(const u64 seq1, const u64 seq2)
|
|||
}
|
||||
|
||||
/**
|
||||
* dccp_loss_free - Evaluates condition for data loss from RFC 4340, 7.7.1
|
||||
* @s1: start sequence number
|
||||
* @s2: end sequence number
|
||||
* dccp_loss_count - Approximate the number of lost data packets in a burst loss
|
||||
* @s1: last known sequence number before the loss ('hole')
|
||||
* @s2: first sequence number seen after the 'hole'
|
||||
* @ndp: NDP count on packet with sequence number @s2
|
||||
* Returns true if the sequence range s1...s2 has no data loss.
|
||||
*/
|
||||
static inline bool dccp_loss_free(const u64 s1, const u64 s2, const u64 ndp)
|
||||
static inline u64 dccp_loss_count(const u64 s1, const u64 s2, const u64 ndp)
|
||||
{
|
||||
s64 delta = dccp_delta_seqno(s1, s2);
|
||||
|
||||
WARN_ON(delta < 0);
|
||||
return (u64)delta <= ndp + 1;
|
||||
delta -= ndp + 1;
|
||||
|
||||
return delta > 0 ? delta : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dccp_loss_free - Evaluate condition for data loss from RFC 4340, 7.7.1
|
||||
*/
|
||||
static inline bool dccp_loss_free(const u64 s1, const u64 s2, const u64 ndp)
|
||||
{
|
||||
return dccp_loss_count(s1, s2, ndp) == 0;
|
||||
}
|
||||
|
||||
enum {
|
||||
|
|
@ -246,7 +255,6 @@ static inline void dccp_clear_xmit_timers(struct sock *sk)
|
|||
extern unsigned int dccp_sync_mss(struct sock *sk, u32 pmtu);
|
||||
|
||||
extern const char *dccp_packet_name(const int type);
|
||||
extern const char *dccp_state_name(const int state);
|
||||
|
||||
extern void dccp_set_state(struct sock *sk, const int state);
|
||||
extern void dccp_done(struct sock *sk);
|
||||
|
|
@ -415,6 +423,23 @@ static inline void dccp_update_gsr(struct sock *sk, u64 seq)
|
|||
dp->dccps_gsr = seq;
|
||||
/* Sequence validity window depends on remote Sequence Window (7.5.1) */
|
||||
dp->dccps_swl = SUB48(ADD48(dp->dccps_gsr, 1), dp->dccps_r_seq_win / 4);
|
||||
/*
|
||||
* Adjust SWL so that it is not below ISR. In contrast to RFC 4340,
|
||||
* 7.5.1 we perform this check beyond the initial handshake: W/W' are
|
||||
* always > 32, so for the first W/W' packets in the lifetime of a
|
||||
* connection we always have to adjust SWL.
|
||||
* A second reason why we are doing this is that the window depends on
|
||||
* the feature-remote value of Sequence Window: nothing stops the peer
|
||||
* from updating this value while we are busy adjusting SWL for the
|
||||
* first W packets (we would have to count from scratch again then).
|
||||
* Therefore it is safer to always make sure that the Sequence Window
|
||||
* is not artificially extended by a peer who grows SWL downwards by
|
||||
* continually updating the feature-remote Sequence-Window.
|
||||
* If sequence numbers wrap it is bad luck. But that will take a while
|
||||
* (48 bit), and this measure prevents Sequence-number attacks.
|
||||
*/
|
||||
if (before48(dp->dccps_swl, dp->dccps_isr))
|
||||
dp->dccps_swl = dp->dccps_isr;
|
||||
dp->dccps_swh = ADD48(dp->dccps_gsr, (3 * dp->dccps_r_seq_win) / 4);
|
||||
}
|
||||
|
||||
|
|
@ -425,14 +450,16 @@ static inline void dccp_update_gss(struct sock *sk, u64 seq)
|
|||
dp->dccps_gss = seq;
|
||||
/* Ack validity window depends on local Sequence Window value (7.5.1) */
|
||||
dp->dccps_awl = SUB48(ADD48(dp->dccps_gss, 1), dp->dccps_l_seq_win);
|
||||
/* Adjust AWL so that it is not below ISS - see comment above for SWL */
|
||||
if (before48(dp->dccps_awl, dp->dccps_iss))
|
||||
dp->dccps_awl = dp->dccps_iss;
|
||||
dp->dccps_awh = dp->dccps_gss;
|
||||
}
|
||||
|
||||
static inline int dccp_ack_pending(const struct sock *sk)
|
||||
{
|
||||
const struct dccp_sock *dp = dccp_sk(sk);
|
||||
return dp->dccps_timestamp_echo != 0 ||
|
||||
(dp->dccps_hc_rx_ackvec != NULL &&
|
||||
return (dp->dccps_hc_rx_ackvec != NULL &&
|
||||
dccp_ackvec_pending(dp->dccps_hc_rx_ackvec)) ||
|
||||
inet_csk_ack_scheduled(sk);
|
||||
}
|
||||
|
|
@ -449,7 +476,6 @@ extern int dccp_insert_options_rsk(struct dccp_request_sock*, struct sk_buff*);
|
|||
extern int dccp_insert_option_elapsed_time(struct sk_buff *skb, u32 elapsed);
|
||||
extern u32 dccp_timestamp(void);
|
||||
extern void dccp_timestamping_init(void);
|
||||
extern int dccp_insert_option_timestamp(struct sk_buff *skb);
|
||||
extern int dccp_insert_option(struct sk_buff *skb, unsigned char option,
|
||||
const void *value, unsigned char len);
|
||||
|
||||
|
|
|
|||
|
|
@ -730,16 +730,6 @@ int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local,
|
|||
0, list, len);
|
||||
}
|
||||
|
||||
/* Analogous to dccp_feat_register_sp(), but for non-negotiable values */
|
||||
int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val)
|
||||
{
|
||||
/* any changes must be registered before establishing the connection */
|
||||
if (sk->sk_state != DCCP_CLOSED)
|
||||
return -EISCONN;
|
||||
if (dccp_feat_type(feat) != FEAT_NN)
|
||||
return -EINVAL;
|
||||
return __feat_register_nn(&dccp_sk(sk)->dccps_featneg, feat, 0, val);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tracking features whose value depend on the choice of CCID
|
||||
|
|
|
|||
|
|
@ -111,7 +111,6 @@ extern int dccp_feat_init(struct sock *sk);
|
|||
extern void dccp_feat_initialise_sysctls(void);
|
||||
extern int dccp_feat_register_sp(struct sock *sk, u8 feat, u8 is_local,
|
||||
u8 const *list, u8 len);
|
||||
extern int dccp_feat_register_nn(struct sock *sk, u8 feat, u64 val);
|
||||
extern int dccp_feat_parse_options(struct sock *, struct dccp_request_sock *,
|
||||
u8 mand, u8 opt, u8 feat, u8 *val, u8 len);
|
||||
extern int dccp_feat_clone_list(struct list_head const *, struct list_head *);
|
||||
|
|
|
|||
|
|
@ -259,7 +259,7 @@ static int dccp_check_seqno(struct sock *sk, struct sk_buff *skb)
|
|||
sysctl_dccp_sync_ratelimit)))
|
||||
return 0;
|
||||
|
||||
DCCP_WARN("DCCP: Step 6 failed for %s packet, "
|
||||
DCCP_WARN("Step 6 failed for %s packet, "
|
||||
"(LSWL(%llu) <= P.seqno(%llu) <= S.SWH(%llu)) and "
|
||||
"(P.ackno %s or LAWL(%llu) <= P.ackno(%llu) <= S.AWH(%llu), "
|
||||
"sending SYNC...\n", dccp_packet_name(dh->dccph_type),
|
||||
|
|
@ -441,20 +441,14 @@ static int dccp_rcv_request_sent_state_process(struct sock *sk,
|
|||
kfree_skb(sk->sk_send_head);
|
||||
sk->sk_send_head = NULL;
|
||||
|
||||
dp->dccps_isr = DCCP_SKB_CB(skb)->dccpd_seq;
|
||||
dccp_update_gsr(sk, dp->dccps_isr);
|
||||
/*
|
||||
* SWL and AWL are initially adjusted so that they are not less than
|
||||
* the initial Sequence Numbers received and sent, respectively:
|
||||
* SWL := max(GSR + 1 - floor(W/4), ISR),
|
||||
* AWL := max(GSS - W' + 1, ISS).
|
||||
* These adjustments MUST be applied only at the beginning of the
|
||||
* connection.
|
||||
*
|
||||
* AWL was adjusted in dccp_v4_connect -acme
|
||||
* Set ISR, GSR from packet. ISS was set in dccp_v{4,6}_connect
|
||||
* and GSS in dccp_transmit_skb(). Setting AWL/AWH and SWL/SWH
|
||||
* is done as part of activating the feature values below, since
|
||||
* these settings depend on the local/remote Sequence Window
|
||||
* features, which were undefined or not confirmed until now.
|
||||
*/
|
||||
dccp_set_seqno(&dp->dccps_swl,
|
||||
max48(dp->dccps_swl, dp->dccps_isr));
|
||||
dp->dccps_gsr = dp->dccps_isr = DCCP_SKB_CB(skb)->dccpd_seq;
|
||||
|
||||
dccp_sync_mss(sk, icsk->icsk_pmtu_cookie);
|
||||
|
||||
|
|
|
|||
|
|
@ -392,7 +392,7 @@ struct sock *dccp_v4_request_recv_sock(struct sock *sk, struct sk_buff *skb,
|
|||
|
||||
newsk = dccp_create_openreq_child(sk, req, skb);
|
||||
if (newsk == NULL)
|
||||
goto exit;
|
||||
goto exit_nonewsk;
|
||||
|
||||
sk_setup_caps(newsk, dst);
|
||||
|
||||
|
|
@ -409,16 +409,20 @@ struct sock *dccp_v4_request_recv_sock(struct sock *sk, struct sk_buff *skb,
|
|||
|
||||
dccp_sync_mss(newsk, dst_mtu(dst));
|
||||
|
||||
if (__inet_inherit_port(sk, newsk) < 0) {
|
||||
sock_put(newsk);
|
||||
goto exit;
|
||||
}
|
||||
__inet_hash_nolisten(newsk, NULL);
|
||||
__inet_inherit_port(sk, newsk);
|
||||
|
||||
return newsk;
|
||||
|
||||
exit_overflow:
|
||||
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
|
||||
exit_nonewsk:
|
||||
dst_release(dst);
|
||||
exit:
|
||||
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS);
|
||||
dst_release(dst);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -564,7 +564,7 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
|
|||
|
||||
newsk = dccp_create_openreq_child(sk, req, skb);
|
||||
if (newsk == NULL)
|
||||
goto out;
|
||||
goto out_nonewsk;
|
||||
|
||||
/*
|
||||
* No need to charge this sock to the relevant IPv6 refcnt debug socks
|
||||
|
|
@ -632,18 +632,22 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk,
|
|||
newinet->inet_daddr = newinet->inet_saddr = LOOPBACK4_IPV6;
|
||||
newinet->inet_rcv_saddr = LOOPBACK4_IPV6;
|
||||
|
||||
if (__inet_inherit_port(sk, newsk) < 0) {
|
||||
sock_put(newsk);
|
||||
goto out;
|
||||
}
|
||||
__inet6_hash(newsk, NULL);
|
||||
__inet_inherit_port(sk, newsk);
|
||||
|
||||
return newsk;
|
||||
|
||||
out_overflow:
|
||||
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS);
|
||||
out_nonewsk:
|
||||
dst_release(dst);
|
||||
out:
|
||||
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS);
|
||||
if (opt != NULL && opt != np->opt)
|
||||
sock_kfree_s(sk, opt, opt->tot_len);
|
||||
dst_release(dst);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -121,30 +121,18 @@ struct sock *dccp_create_openreq_child(struct sock *sk,
|
|||
*
|
||||
* Choose S.ISS (initial seqno) or set from Init Cookies
|
||||
* Initialize S.GAR := S.ISS
|
||||
* Set S.ISR, S.GSR, S.SWL, S.SWH from packet or Init Cookies
|
||||
* Set S.ISR, S.GSR from packet (or Init Cookies)
|
||||
*
|
||||
* Setting AWL/AWH and SWL/SWH happens as part of the feature
|
||||
* activation below, as these windows all depend on the local
|
||||
* and remote Sequence Window feature values (7.5.2).
|
||||
*/
|
||||
newdp->dccps_gar = newdp->dccps_iss = dreq->dreq_iss;
|
||||
dccp_update_gss(newsk, dreq->dreq_iss);
|
||||
|
||||
newdp->dccps_isr = dreq->dreq_isr;
|
||||
dccp_update_gsr(newsk, dreq->dreq_isr);
|
||||
newdp->dccps_gss = newdp->dccps_iss = dreq->dreq_iss;
|
||||
newdp->dccps_gar = newdp->dccps_iss;
|
||||
newdp->dccps_gsr = newdp->dccps_isr = dreq->dreq_isr;
|
||||
|
||||
/*
|
||||
* SWL and AWL are initially adjusted so that they are not less than
|
||||
* the initial Sequence Numbers received and sent, respectively:
|
||||
* SWL := max(GSR + 1 - floor(W/4), ISR),
|
||||
* AWL := max(GSS - W' + 1, ISS).
|
||||
* These adjustments MUST be applied only at the beginning of the
|
||||
* connection.
|
||||
*/
|
||||
dccp_set_seqno(&newdp->dccps_swl,
|
||||
max48(newdp->dccps_swl, newdp->dccps_isr));
|
||||
dccp_set_seqno(&newdp->dccps_awl,
|
||||
max48(newdp->dccps_awl, newdp->dccps_iss));
|
||||
|
||||
/*
|
||||
* Activate features after initialising the sequence numbers,
|
||||
* since CCID initialisation may depend on GSS, ISR, ISS etc.
|
||||
* Activate features: initialise CCIDs, sequence windows etc.
|
||||
*/
|
||||
if (dccp_feat_activate_values(newsk, &dreq->dreq_featneg)) {
|
||||
/* It is still raw copy of parent, so invalidate
|
||||
|
|
|
|||
|
|
@ -96,18 +96,11 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq,
|
|||
}
|
||||
|
||||
/*
|
||||
* CCID-Specific Options (from RFC 4340, sec. 10.3):
|
||||
*
|
||||
* Option numbers 128 through 191 are for options sent from the
|
||||
* HC-Sender to the HC-Receiver; option numbers 192 through 255
|
||||
* are for options sent from the HC-Receiver to the HC-Sender.
|
||||
*
|
||||
* CCID-specific options are ignored during connection setup, as
|
||||
* negotiation may still be in progress (see RFC 4340, 10.3).
|
||||
* The same applies to Ack Vectors, as these depend on the CCID.
|
||||
*
|
||||
*/
|
||||
if (dreq != NULL && (opt >= 128 ||
|
||||
if (dreq != NULL && (opt >= DCCPO_MIN_RX_CCID_SPECIFIC ||
|
||||
opt == DCCPO_ACK_VECTOR_0 || opt == DCCPO_ACK_VECTOR_1))
|
||||
goto ignore_option;
|
||||
|
||||
|
|
@ -170,6 +163,8 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq,
|
|||
dccp_role(sk), ntohl(opt_val),
|
||||
(unsigned long long)
|
||||
DCCP_SKB_CB(skb)->dccpd_ack_seq);
|
||||
/* schedule an Ack in case this sender is quiescent */
|
||||
inet_csk_schedule_ack(sk);
|
||||
break;
|
||||
case DCCPO_TIMESTAMP_ECHO:
|
||||
if (len != 4 && len != 6 && len != 8)
|
||||
|
|
@ -226,23 +221,15 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq,
|
|||
dccp_pr_debug("%s rx opt: ELAPSED_TIME=%d\n",
|
||||
dccp_role(sk), elapsed_time);
|
||||
break;
|
||||
case 128 ... 191: {
|
||||
const u16 idx = value - options;
|
||||
|
||||
case DCCPO_MIN_RX_CCID_SPECIFIC ... DCCPO_MAX_RX_CCID_SPECIFIC:
|
||||
if (ccid_hc_rx_parse_options(dp->dccps_hc_rx_ccid, sk,
|
||||
opt, len, idx,
|
||||
value) != 0)
|
||||
pkt_type, opt, value, len))
|
||||
goto out_invalid_option;
|
||||
}
|
||||
break;
|
||||
case 192 ... 255: {
|
||||
const u16 idx = value - options;
|
||||
|
||||
case DCCPO_MIN_TX_CCID_SPECIFIC ... DCCPO_MAX_TX_CCID_SPECIFIC:
|
||||
if (ccid_hc_tx_parse_options(dp->dccps_hc_tx_ccid, sk,
|
||||
opt, len, idx,
|
||||
value) != 0)
|
||||
pkt_type, opt, value, len))
|
||||
goto out_invalid_option;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DCCP_CRIT("DCCP(%p): option %d(len=%d) not "
|
||||
|
|
@ -384,7 +371,7 @@ int dccp_insert_option_elapsed_time(struct sk_buff *skb, u32 elapsed_time)
|
|||
|
||||
EXPORT_SYMBOL_GPL(dccp_insert_option_elapsed_time);
|
||||
|
||||
int dccp_insert_option_timestamp(struct sk_buff *skb)
|
||||
static int dccp_insert_option_timestamp(struct sk_buff *skb)
|
||||
{
|
||||
__be32 now = htonl(dccp_timestamp());
|
||||
/* yes this will overflow but that is the point as we want a
|
||||
|
|
@ -393,8 +380,6 @@ int dccp_insert_option_timestamp(struct sk_buff *skb)
|
|||
return dccp_insert_option(skb, DCCPO_TIMESTAMP, &now, sizeof(now));
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(dccp_insert_option_timestamp);
|
||||
|
||||
static int dccp_insert_option_timestamp_echo(struct dccp_sock *dp,
|
||||
struct dccp_request_sock *dreq,
|
||||
struct sk_buff *skb)
|
||||
|
|
|
|||
|
|
@ -304,7 +304,7 @@ void dccp_write_xmit(struct sock *sk, int block)
|
|||
dcb->dccpd_type = DCCP_PKT_DATA;
|
||||
|
||||
err = dccp_transmit_skb(sk, skb);
|
||||
ccid_hc_tx_packet_sent(dp->dccps_hc_tx_ccid, sk, 0, len);
|
||||
ccid_hc_tx_packet_sent(dp->dccps_hc_tx_ccid, sk, len);
|
||||
if (err)
|
||||
DCCP_BUG("err=%d after ccid_hc_tx_packet_sent",
|
||||
err);
|
||||
|
|
@ -474,8 +474,9 @@ int dccp_send_reset(struct sock *sk, enum dccp_reset_codes code)
|
|||
/*
|
||||
* Do all connect socket setups that can be done AF independent.
|
||||
*/
|
||||
static inline void dccp_connect_init(struct sock *sk)
|
||||
int dccp_connect(struct sock *sk)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct dccp_sock *dp = dccp_sk(sk);
|
||||
struct dst_entry *dst = __sk_dst_get(sk);
|
||||
struct inet_connection_sock *icsk = inet_csk(sk);
|
||||
|
|
@ -485,22 +486,12 @@ static inline void dccp_connect_init(struct sock *sk)
|
|||
|
||||
dccp_sync_mss(sk, dst_mtu(dst));
|
||||
|
||||
/* Initialise GAR as per 8.5; AWL/AWH are set in dccp_transmit_skb() */
|
||||
dp->dccps_gar = dp->dccps_iss;
|
||||
|
||||
icsk->icsk_retransmits = 0;
|
||||
}
|
||||
|
||||
int dccp_connect(struct sock *sk)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct inet_connection_sock *icsk = inet_csk(sk);
|
||||
|
||||
/* do not connect if feature negotiation setup fails */
|
||||
if (dccp_feat_finalise_settings(dccp_sk(sk)))
|
||||
return -EPROTO;
|
||||
|
||||
dccp_connect_init(sk);
|
||||
/* Initialise GAR as per 8.5; AWL/AWH are set in dccp_transmit_skb() */
|
||||
dp->dccps_gar = dp->dccps_iss;
|
||||
|
||||
skb = alloc_skb(sk->sk_prot->max_header, sk->sk_allocation);
|
||||
if (unlikely(skb == NULL))
|
||||
|
|
@ -516,6 +507,7 @@ int dccp_connect(struct sock *sk)
|
|||
DCCP_INC_STATS(DCCP_MIB_ACTIVEOPENS);
|
||||
|
||||
/* Timer for repeating the REQUEST until an answer. */
|
||||
icsk->icsk_retransmits = 0;
|
||||
inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
|
||||
icsk->icsk_rto, DCCP_RTO_MAX);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -50,6 +50,30 @@ EXPORT_SYMBOL_GPL(dccp_hashinfo);
|
|||
/* the maximum queue length for tx in packets. 0 is no limit */
|
||||
int sysctl_dccp_tx_qlen __read_mostly = 5;
|
||||
|
||||
#ifdef CONFIG_IP_DCCP_DEBUG
|
||||
static const char *dccp_state_name(const int state)
|
||||
{
|
||||
static const char *const dccp_state_names[] = {
|
||||
[DCCP_OPEN] = "OPEN",
|
||||
[DCCP_REQUESTING] = "REQUESTING",
|
||||
[DCCP_PARTOPEN] = "PARTOPEN",
|
||||
[DCCP_LISTEN] = "LISTEN",
|
||||
[DCCP_RESPOND] = "RESPOND",
|
||||
[DCCP_CLOSING] = "CLOSING",
|
||||
[DCCP_ACTIVE_CLOSEREQ] = "CLOSEREQ",
|
||||
[DCCP_PASSIVE_CLOSE] = "PASSIVE_CLOSE",
|
||||
[DCCP_PASSIVE_CLOSEREQ] = "PASSIVE_CLOSEREQ",
|
||||
[DCCP_TIME_WAIT] = "TIME_WAIT",
|
||||
[DCCP_CLOSED] = "CLOSED",
|
||||
};
|
||||
|
||||
if (state >= DCCP_MAX_STATES)
|
||||
return "INVALID STATE!";
|
||||
else
|
||||
return dccp_state_names[state];
|
||||
}
|
||||
#endif
|
||||
|
||||
void dccp_set_state(struct sock *sk, const int state)
|
||||
{
|
||||
const int oldstate = sk->sk_state;
|
||||
|
|
@ -146,30 +170,6 @@ const char *dccp_packet_name(const int type)
|
|||
|
||||
EXPORT_SYMBOL_GPL(dccp_packet_name);
|
||||
|
||||
const char *dccp_state_name(const int state)
|
||||
{
|
||||
static const char *const dccp_state_names[] = {
|
||||
[DCCP_OPEN] = "OPEN",
|
||||
[DCCP_REQUESTING] = "REQUESTING",
|
||||
[DCCP_PARTOPEN] = "PARTOPEN",
|
||||
[DCCP_LISTEN] = "LISTEN",
|
||||
[DCCP_RESPOND] = "RESPOND",
|
||||
[DCCP_CLOSING] = "CLOSING",
|
||||
[DCCP_ACTIVE_CLOSEREQ] = "CLOSEREQ",
|
||||
[DCCP_PASSIVE_CLOSE] = "PASSIVE_CLOSE",
|
||||
[DCCP_PASSIVE_CLOSEREQ] = "PASSIVE_CLOSEREQ",
|
||||
[DCCP_TIME_WAIT] = "TIME_WAIT",
|
||||
[DCCP_CLOSED] = "CLOSED",
|
||||
};
|
||||
|
||||
if (state >= DCCP_MAX_STATES)
|
||||
return "INVALID STATE!";
|
||||
else
|
||||
return dccp_state_names[state];
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(dccp_state_name);
|
||||
|
||||
int dccp_init_sock(struct sock *sk, const __u8 ctl_sock_initialized)
|
||||
{
|
||||
struct dccp_sock *dp = dccp_sk(sk);
|
||||
|
|
@ -944,7 +944,7 @@ void dccp_close(struct sock *sk, long timeout)
|
|||
|
||||
if (data_was_unread) {
|
||||
/* Unread data was tossed, send an appropriate Reset Code */
|
||||
DCCP_WARN("DCCP: ABORT -- %u bytes unread\n", data_was_unread);
|
||||
DCCP_WARN("ABORT with %u bytes unread\n", data_was_unread);
|
||||
dccp_send_reset(sk, DCCP_RESET_CODE_ABORTED);
|
||||
dccp_set_state(sk, DCCP_CLOSED);
|
||||
} else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue