* Re: [RFC PATCH v1.1 0/2] PTP related changes for fm10k
From: Richard Cochran @ 2014-09-20 21:52 UTC (permalink / raw)
To: Alexander Duyck; +Cc: netdev
In-Reply-To: <20140920170138.25490.40273.stgit@ahduyck-bv4.jf.intel.com>
Looks ok to me, and removing the timecounter stuff makes sense.
For these two patches,
Acked-by: Richard Cochran <richardcochran@gmail.com>
^ permalink raw reply
* Re: [net-next PATCH 29/29] fm10k: Add support for PTP
From: Joe Perches @ 2014-09-20 21:37 UTC (permalink / raw)
To: Richard Cochran
Cc: Alexander Duyck, davem, nhorman, netdev, john.fastabend,
matthew.vick, jeffrey.t.kirsher, sassmann
In-Reply-To: <20140920210704.GA5258@netboy>
On Sat, 2014-09-20 at 23:07 +0200, Richard Cochran wrote:
> On Fri, Sep 19, 2014 at 11:32:24AM -0700, Alexander Duyck wrote:
> > Because doing it that way it extends over 80 characters.
>
> Obviously, but still the result is really ugly.
True.
It might be reasonable to add a new container_of_ptr
to kernel.h
Something like:
/**
* container_of_ptr - cast a member of a pointer to a structure
* out to the containing structure
* @ptr: the pointer to the member.
* @type_ptr: a pointer of type of the container struct this is embedded in.
* @member: the name of the member within the type_ptr.
*
*/
#define container_of_ptr(ptr, type_ptr, member) \
({ \
const typeof((type_ptr)->member) *__mptr = (ptr); \
(type_ptr)((char *)__mptr - \
((char *)&((ptr_type)->member) - (char *)(ptr_type))); \
})
So this could be written as:
struct fm10k_intfc *interface = container_of_ptr(hw, interface, hw);
^ permalink raw reply
* [PATCH net-next v2 4/5] net: tcp: more detailed ACK events and events for CE marked packets
From: Florian Westphal @ 2014-09-20 21:29 UTC (permalink / raw)
To: davem
Cc: hagen, lars, eric.dumazet, fontana, hannes, glenn.judd, dborkman,
netdev, Florian Westphal
In-Reply-To: <1411248562-26581-1-git-send-email-fw@strlen.de>
DataCenter TCP (DCTCP) determines cwnd growth based on ECN information
and ACK properties, e.g. ACK that updates window is treated differently
than DUPACK.
Also DCTCP needs information whether ACK was delayed ACK. Furthermore,
DCTCP also implements a CE state machine that keeps track of CE markings
of incoming packets.
Therefore, extend the congestion control framework to provide these
event types, so that DCTCP can be properly implemented as a normal
congestion algorithm module outside of the core stack.
Joint work with Daniel Borkmann and Glenn Judd.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: Glenn Judd <glenn.judd@morganstanley.com>
---
include/net/tcp.h | 9 ++++++++-
net/ipv4/tcp_input.c | 22 ++++++++++++++++++----
net/ipv4/tcp_output.c | 4 ++++
3 files changed, 30 insertions(+), 5 deletions(-)
diff --git a/include/net/tcp.h b/include/net/tcp.h
index e71884a..382714e 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -744,10 +744,17 @@ enum tcp_ca_event {
CA_EVENT_CWND_RESTART, /* congestion window restart */
CA_EVENT_COMPLETE_CWR, /* end of congestion recovery */
CA_EVENT_LOSS, /* loss timeout */
+ CA_EVENT_ECN_NO_CE, /* ECT set, but not CE marked */
+ CA_EVENT_ECN_IS_CE, /* received CE marked IP packet */
+ CA_EVENT_DELAYED_ACK, /* Delayed ack is sent */
+ CA_EVENT_NON_DELAYED_ACK,
};
+/* Information about inbound ACK, passed to cong_ops->in_ack_event() */
enum tcp_ca_ack_event_flags {
- CA_ACK_SLOWPATH = (1 << 0),
+ CA_ACK_SLOWPATH = (1 << 0), /* In slow path processing */
+ CA_ACK_WIN_UPDATE = (1 << 1), /* ACK updated window */
+ CA_ACK_ECE = (1 << 2), /* ECE bit is set on ack */
};
/*
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 14dc3ee..3ebbba8 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -233,14 +233,21 @@ static inline void TCP_ECN_check_ce(struct tcp_sock *tp, const struct sk_buff *s
tcp_enter_quickack_mode((struct sock *)tp);
break;
case INET_ECN_CE:
+ if (tcp_ca_needs_ecn((struct sock *)tp))
+ tcp_ca_event((struct sock *)tp, CA_EVENT_ECN_IS_CE);
+
if (!(tp->ecn_flags & TCP_ECN_DEMAND_CWR)) {
/* Better not delay acks, sender can have a very low cwnd */
tcp_enter_quickack_mode((struct sock *)tp);
tp->ecn_flags |= TCP_ECN_DEMAND_CWR;
}
- /* fallinto */
+ tp->ecn_flags |= TCP_ECN_SEEN;
+ break;
default:
+ if (tcp_ca_needs_ecn((struct sock *)tp))
+ tcp_ca_event((struct sock *)tp, CA_EVENT_ECN_NO_CE);
tp->ecn_flags |= TCP_ECN_SEEN;
+ break;
}
}
@@ -3428,10 +3435,12 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
tp->snd_una = ack;
flag |= FLAG_WIN_UPDATE;
- tcp_in_ack_event(sk, 0);
+ tcp_in_ack_event(sk, CA_ACK_WIN_UPDATE);
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPHPACKS);
} else {
+ u32 ack_ev_flags = CA_ACK_SLOWPATH;
+
if (ack_seq != TCP_SKB_CB(skb)->end_seq)
flag |= FLAG_DATA;
else
@@ -3443,10 +3452,15 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
flag |= tcp_sacktag_write_queue(sk, skb, prior_snd_una,
&sack_rtt_us);
- if (TCP_ECN_rcv_ecn_echo(tp, tcp_hdr(skb)))
+ if (TCP_ECN_rcv_ecn_echo(tp, tcp_hdr(skb))) {
flag |= FLAG_ECE;
+ ack_ev_flags |= CA_ACK_ECE;
+ }
+
+ if (flag & FLAG_WIN_UPDATE)
+ ack_ev_flags |= CA_ACK_WIN_UPDATE;
- tcp_in_ack_event(sk, CA_ACK_SLOWPATH);
+ tcp_in_ack_event(sk, ack_ev_flags);
}
/* We passed data and got it acked, remove any soft error
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 3a0ef04..8600641 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -3122,6 +3122,8 @@ void tcp_send_delayed_ack(struct sock *sk)
int ato = icsk->icsk_ack.ato;
unsigned long timeout;
+ tcp_ca_event(sk, CA_EVENT_DELAYED_ACK);
+
if (ato > TCP_DELACK_MIN) {
const struct tcp_sock *tp = tcp_sk(sk);
int max_ato = HZ / 2;
@@ -3178,6 +3180,8 @@ void tcp_send_ack(struct sock *sk)
if (sk->sk_state == TCP_CLOSE)
return;
+ tcp_ca_event(sk, CA_EVENT_NON_DELAYED_ACK);
+
/* We are not putting this on the write queue, so
* tcp_transmit_skb() will set the ownership to this
* sock.
--
1.7.11.7
^ permalink raw reply related
* [PATCH net-next v2 1/5] net: tcp: assign tcp cong_ops when tcp sk is created
From: Florian Westphal @ 2014-09-20 21:29 UTC (permalink / raw)
To: davem
Cc: hagen, lars, eric.dumazet, fontana, hannes, glenn.judd, dborkman,
netdev, Florian Westphal
In-Reply-To: <1411248562-26581-1-git-send-email-fw@strlen.de>
Split assignment and initialization from one into two functions.
This is required by followup patches that add Datacenter TCP
(DCTCP) congestion control algorithm - we need to be able to
determine if the connection is moderated by DCTCP before the
3WHS has finished.
As we walk the available congestion control list during the
assignment, we are always guaranteed to have Reno present as
it's fixed compiled-in. Therefore, since we're doing the
early assignment, we don't have a real use for the Reno alias
tcp_init_congestion_ops anymore and can thus remove it.
Actual usage of the congestion control operations are being
made after the 3WHS has finished, in some cases however we
can access get_info() via diag if implemented, therefore we
need to zero out the private area for those modules.
Joint work with Daniel Borkmann and Glenn Judd.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: Glenn Judd <glenn.judd@morganstanley.com>
---
include/net/tcp.h | 2 +-
net/ipv4/tcp.c | 6 ++----
net/ipv4/tcp_cong.c | 46 ++++++++++++++++++++++------------------------
net/ipv4/tcp_minisocks.c | 5 ++---
4 files changed, 27 insertions(+), 32 deletions(-)
diff --git a/include/net/tcp.h b/include/net/tcp.h
index a4201ef..1c75356 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -805,6 +805,7 @@ struct tcp_congestion_ops {
int tcp_register_congestion_control(struct tcp_congestion_ops *type);
void tcp_unregister_congestion_control(struct tcp_congestion_ops *type);
+void tcp_assign_congestion_control(struct sock *sk);
void tcp_init_congestion_control(struct sock *sk);
void tcp_cleanup_congestion_control(struct sock *sk);
int tcp_set_default_congestion_control(const char *name);
@@ -816,7 +817,6 @@ int tcp_set_congestion_control(struct sock *sk, const char *name);
int tcp_slow_start(struct tcp_sock *tp, u32 acked);
void tcp_cong_avoid_ai(struct tcp_sock *tp, u32 w);
-extern struct tcp_congestion_ops tcp_init_congestion_ops;
u32 tcp_reno_ssthresh(struct sock *sk);
void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked);
extern struct tcp_congestion_ops tcp_reno;
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 070aeff..344707f 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -405,7 +405,7 @@ void tcp_init_sock(struct sock *sk)
tp->reordering = sysctl_tcp_reordering;
tcp_enable_early_retrans(tp);
- icsk->icsk_ca_ops = &tcp_init_congestion_ops;
+ tcp_assign_congestion_control(sk);
tp->tsoffset = 0;
@@ -3258,8 +3258,6 @@ void __init tcp_init(void)
tcp_hashinfo.ehash_mask + 1, tcp_hashinfo.bhash_size);
tcp_metrics_init();
-
- tcp_register_congestion_control(&tcp_reno);
-
+ BUG_ON(tcp_register_congestion_control(&tcp_reno) != 0);
tcp_tasklet_init();
}
diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c
index 80248f5..a6c8a57 100644
--- a/net/ipv4/tcp_cong.c
+++ b/net/ipv4/tcp_cong.c
@@ -74,24 +74,34 @@ void tcp_unregister_congestion_control(struct tcp_congestion_ops *ca)
EXPORT_SYMBOL_GPL(tcp_unregister_congestion_control);
/* Assign choice of congestion control. */
-void tcp_init_congestion_control(struct sock *sk)
+void tcp_assign_congestion_control(struct sock *sk)
{
struct inet_connection_sock *icsk = inet_csk(sk);
struct tcp_congestion_ops *ca;
- /* if no choice made yet assign the current value set as default */
- if (icsk->icsk_ca_ops == &tcp_init_congestion_ops) {
- rcu_read_lock();
- list_for_each_entry_rcu(ca, &tcp_cong_list, list) {
- if (try_module_get(ca->owner)) {
- icsk->icsk_ca_ops = ca;
- break;
- }
-
- /* fallback to next available */
+ rcu_read_lock();
+ list_for_each_entry_rcu(ca, &tcp_cong_list, list) {
+ if (likely(try_module_get(ca->owner))) {
+ icsk->icsk_ca_ops = ca;
+ goto out;
}
- rcu_read_unlock();
+ /* Fallback to next available. The last really
+ * guaranteed fallback is Reno from this list.
+ */
}
+out:
+ rcu_read_unlock();
+
+ /* Clear out private data before diag gets it and
+ * the ca has not been initialized.
+ */
+ if (ca->get_info)
+ memset(icsk->icsk_ca_priv, 0, sizeof(icsk->icsk_ca_priv));
+}
+
+void tcp_init_congestion_control(struct sock *sk)
+{
+ const struct inet_connection_sock *icsk = inet_csk(sk);
if (icsk->icsk_ca_ops->init)
icsk->icsk_ca_ops->init(sk);
@@ -345,15 +355,3 @@ struct tcp_congestion_ops tcp_reno = {
.ssthresh = tcp_reno_ssthresh,
.cong_avoid = tcp_reno_cong_avoid,
};
-
-/* Initial congestion control used (until SYN)
- * really reno under another name so we can tell difference
- * during tcp_set_default_congestion_control
- */
-struct tcp_congestion_ops tcp_init_congestion_ops = {
- .name = "",
- .owner = THIS_MODULE,
- .ssthresh = tcp_reno_ssthresh,
- .cong_avoid = tcp_reno_cong_avoid,
-};
-EXPORT_SYMBOL_GPL(tcp_init_congestion_ops);
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index a058f41..47b7350 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -451,9 +451,8 @@ struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req,
newtp->snd_cwnd = TCP_INIT_CWND;
newtp->snd_cwnd_cnt = 0;
- if (newicsk->icsk_ca_ops != &tcp_init_congestion_ops &&
- !try_module_get(newicsk->icsk_ca_ops->owner))
- newicsk->icsk_ca_ops = &tcp_init_congestion_ops;
+ if (!try_module_get(newicsk->icsk_ca_ops->owner))
+ tcp_assign_congestion_control(newsk);
tcp_set_ca_state(newsk, TCP_CA_Open);
tcp_init_xmit_timers(newsk);
--
1.7.11.7
^ permalink raw reply related
* [PATCH net-next v2 3/5] net: tcp: split ack slow/fast events from cwnd_event
From: Florian Westphal @ 2014-09-20 21:29 UTC (permalink / raw)
To: davem
Cc: hagen, lars, eric.dumazet, fontana, hannes, glenn.judd, dborkman,
netdev, Florian Westphal
In-Reply-To: <1411248562-26581-1-git-send-email-fw@strlen.de>
The congestion control ops "cwnd_event" currently supports
CA_EVENT_FAST_ACK and CA_EVENT_SLOW_ACK events (among others).
Both FAST and SLOW_ACK are only used by Westwood congestion
control algorithm.
This removes both flags from cwnd_event and adds a new
in_ack_event callback for this. The goal is to be able to
provide more detailed information about ACKs, such as whether
ECE flag was set, or whether the ACK resulted in a window
update.
It is required for DataCenter TCP (DCTCP) congestion control
algorithm as it makes a different choice depending on ECE being
set or not.
Joint work with Daniel Borkmann and Glenn Judd.
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: Glenn Judd <glenn.judd@morganstanley.com>
---
include/net/tcp.h | 8 ++++++--
net/ipv4/tcp_input.c | 12 ++++++++++--
net/ipv4/tcp_westwood.c | 28 ++++++++++++++++------------
3 files changed, 32 insertions(+), 16 deletions(-)
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 82d012e..e71884a 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -744,8 +744,10 @@ enum tcp_ca_event {
CA_EVENT_CWND_RESTART, /* congestion window restart */
CA_EVENT_COMPLETE_CWR, /* end of congestion recovery */
CA_EVENT_LOSS, /* loss timeout */
- CA_EVENT_FAST_ACK, /* in sequence ack */
- CA_EVENT_SLOW_ACK, /* other ack */
+};
+
+enum tcp_ca_ack_event_flags {
+ CA_ACK_SLOWPATH = (1 << 0),
};
/*
@@ -777,6 +779,8 @@ struct tcp_congestion_ops {
void (*set_state)(struct sock *sk, u8 new_state);
/* call when cwnd event occurs (optional) */
void (*cwnd_event)(struct sock *sk, enum tcp_ca_event ev);
+ /* call when ack arrives (optional) */
+ void (*in_ack_event)(struct sock *sk, u32 flags);
/* new value of cwnd after loss (optional) */
u32 (*undo_cwnd)(struct sock *sk);
/* hook for packet ack accounting (optional) */
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 0af12a4..14dc3ee 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -3361,6 +3361,14 @@ static void tcp_process_tlp_ack(struct sock *sk, u32 ack, int flag)
}
}
+static inline void tcp_in_ack_event(struct sock *sk, u32 flags)
+{
+ const struct inet_connection_sock *icsk = inet_csk(sk);
+
+ if (icsk->icsk_ca_ops->in_ack_event)
+ icsk->icsk_ca_ops->in_ack_event(sk, flags);
+}
+
/* This routine deals with incoming acks, but not outgoing ones. */
static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
{
@@ -3420,7 +3428,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
tp->snd_una = ack;
flag |= FLAG_WIN_UPDATE;
- tcp_ca_event(sk, CA_EVENT_FAST_ACK);
+ tcp_in_ack_event(sk, 0);
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPHPACKS);
} else {
@@ -3438,7 +3446,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
if (TCP_ECN_rcv_ecn_echo(tp, tcp_hdr(skb)))
flag |= FLAG_ECE;
- tcp_ca_event(sk, CA_EVENT_SLOW_ACK);
+ tcp_in_ack_event(sk, CA_ACK_SLOWPATH);
}
/* We passed data and got it acked, remove any soft error
diff --git a/net/ipv4/tcp_westwood.c b/net/ipv4/tcp_westwood.c
index 81911a9..bb63fba 100644
--- a/net/ipv4/tcp_westwood.c
+++ b/net/ipv4/tcp_westwood.c
@@ -220,32 +220,35 @@ static u32 tcp_westwood_bw_rttmin(const struct sock *sk)
return max_t(u32, (w->bw_est * w->rtt_min) / tp->mss_cache, 2);
}
+static void tcp_westwood_ack(struct sock *sk, u32 ack_flags)
+{
+ if (ack_flags & CA_ACK_SLOWPATH) {
+ struct westwood *w = inet_csk_ca(sk);
+
+ westwood_update_window(sk);
+ w->bk += westwood_acked_count(sk);
+
+ update_rtt_min(w);
+ return;
+ }
+
+ westwood_fast_bw(sk);
+}
+
static void tcp_westwood_event(struct sock *sk, enum tcp_ca_event event)
{
struct tcp_sock *tp = tcp_sk(sk);
struct westwood *w = inet_csk_ca(sk);
switch (event) {
- case CA_EVENT_FAST_ACK:
- westwood_fast_bw(sk);
- break;
-
case CA_EVENT_COMPLETE_CWR:
tp->snd_cwnd = tp->snd_ssthresh = tcp_westwood_bw_rttmin(sk);
break;
-
case CA_EVENT_LOSS:
tp->snd_ssthresh = tcp_westwood_bw_rttmin(sk);
/* Update RTT_min when next ack arrives */
w->reset_rtt_min = 1;
break;
-
- case CA_EVENT_SLOW_ACK:
- westwood_update_window(sk);
- w->bk += westwood_acked_count(sk);
- update_rtt_min(w);
- break;
-
default:
/* don't care */
break;
@@ -274,6 +277,7 @@ static struct tcp_congestion_ops tcp_westwood __read_mostly = {
.ssthresh = tcp_reno_ssthresh,
.cong_avoid = tcp_reno_cong_avoid,
.cwnd_event = tcp_westwood_event,
+ .in_ack_event = tcp_westwood_ack,
.get_info = tcp_westwood_info,
.pkts_acked = tcp_westwood_pkts_acked,
--
1.7.11.7
^ permalink raw reply related
* [PATCH net-next v2 2/5] net: tcp: add flag for ca to indicate that ECN is required
From: Florian Westphal @ 2014-09-20 21:29 UTC (permalink / raw)
To: davem
Cc: hagen, lars, eric.dumazet, fontana, hannes, glenn.judd, dborkman,
netdev, Florian Westphal
In-Reply-To: <1411248562-26581-1-git-send-email-fw@strlen.de>
From: Daniel Borkmann <dborkman@redhat.com>
This patch adds a flag to TCP congestion algorithms that allows
for requesting to mark IPv4/IPv6 sockets with transport as ECN
capable, that is, ECT(0), when required by a congestion algorithm.
It is currently used and needed in DataCenter TCP (DCTCP), as it
requires both peers to assert ECT on all IP packets sent - it
uses ECN feedback (i.e. CE, Congestion Encountered information)
from switches inside the data center to derive feedback to the
end hosts.
Therefore, simply add a new flag to icsk_ca_ops. Note that DCTCP's
algorithm/behaviour slightly diverges from RFC3168, therefore this
is only (!) enabled iff the assigned congestion control ops module
has requested this. By that, we can tightly couple this logic really
only to the provided congestion control ops.
Joint work with Florian Westphal and Glenn Judd.
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Glenn Judd <glenn.judd@morganstanley.com>
---
v1->v2:
- Update stale comment wrt. DCTCP ECN usage
- Don't call INET_ECN_xmit for every packet
include/net/tcp.h | 61 +++++++++++++++++++++++++++++++++++++--------------
net/ipv4/tcp_input.c | 2 +-
net/ipv4/tcp_output.c | 25 +++++++++++++++------
3 files changed, 63 insertions(+), 25 deletions(-)
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 1c75356..82d012e 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -724,23 +724,6 @@ struct tcp_skb_cb {
#define TCP_SKB_CB(__skb) ((struct tcp_skb_cb *)&((__skb)->cb[0]))
-/* RFC3168 : 6.1.1 SYN packets must not have ECT/ECN bits set
- *
- * If we receive a SYN packet with these bits set, it means a network is
- * playing bad games with TOS bits. In order to avoid possible false congestion
- * notifications, we disable TCP ECN negociation.
- */
-static inline void
-TCP_ECN_create_request(struct request_sock *req, const struct sk_buff *skb,
- struct net *net)
-{
- const struct tcphdr *th = tcp_hdr(skb);
-
- if (net->ipv4.sysctl_tcp_ecn && th->ece && th->cwr &&
- INET_ECN_is_not_ect(TCP_SKB_CB(skb)->ip_dsfield))
- inet_rsk(req)->ecn_ok = 1;
-}
-
/* Due to TSO, an SKB can be composed of multiple actual
* packets. To keep these tracked properly, we use this.
*/
@@ -772,7 +755,10 @@ enum tcp_ca_event {
#define TCP_CA_MAX 128
#define TCP_CA_BUF_MAX (TCP_CA_NAME_MAX*TCP_CA_MAX)
+/* Algorithm can be set on socket without CAP_NET_ADMIN privileges */
#define TCP_CONG_NON_RESTRICTED 0x1
+/* Requires ECN/ECT set on all packets */
+#define TCP_CONG_NEEDS_ECN 0x2
struct tcp_congestion_ops {
struct list_head list;
@@ -821,6 +807,13 @@ u32 tcp_reno_ssthresh(struct sock *sk);
void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 acked);
extern struct tcp_congestion_ops tcp_reno;
+static inline bool tcp_ca_needs_ecn(const struct sock *sk)
+{
+ const struct inet_connection_sock *icsk = inet_csk(sk);
+
+ return icsk->icsk_ca_ops->flags & TCP_CONG_NEEDS_ECN;
+}
+
static inline void tcp_set_ca_state(struct sock *sk, const u8 ca_state)
{
struct inet_connection_sock *icsk = inet_csk(sk);
@@ -838,6 +831,40 @@ static inline void tcp_ca_event(struct sock *sk, const enum tcp_ca_event event)
icsk->icsk_ca_ops->cwnd_event(sk, event);
}
+/* RFC3168 : 6.1.1 SYN packets must not have ECT/ECN bits set
+ *
+ * If we receive a SYN packet with these bits set, it means a
+ * network is playing bad games with TOS bits. In order to
+ * avoid possible false congestion notifications, we disable
+ * TCP ECN negociation.
+ *
+ * Exception: tcp_ca wants ECN. This is required for DCTCP
+ * congestion control; it requires setting ECT on all packets,
+ * including SYN. We inverse the test in this case: If our
+ * local socket wants ECN, but peer only set ece/cwr (but not
+ * ECT in IP header) its probably a non-DCTCP aware sender.
+ */
+static inline void
+TCP_ECN_create_request(struct request_sock *req, const struct sk_buff *skb,
+ const struct sock *listen_sk)
+{
+ const struct tcphdr *th = tcp_hdr(skb);
+ const struct net *net = sock_net(listen_sk);
+ bool th_ecn = th->ece && th->cwr;
+ bool ect, need_ecn;
+
+ if (!th_ecn)
+ return;
+
+ ect = !INET_ECN_is_not_ect(TCP_SKB_CB(skb)->ip_dsfield);
+ need_ecn = tcp_ca_needs_ecn(listen_sk);
+
+ if (!ect && !need_ecn && net->ipv4.sysctl_tcp_ecn)
+ inet_rsk(req)->ecn_ok = 1;
+ else if (ect && need_ecn)
+ inet_rsk(req)->ecn_ok = 1;
+}
+
/* These functions determine how the current flow behaves in respect of SACK
* handling. SACK is negotiated with the peer, and therefore it can vary
* between different flows.
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index ea92f23..0af12a4 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -5944,7 +5944,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
goto drop_and_free;
if (!want_cookie || tmp_opt.tstamp_ok)
- TCP_ECN_create_request(req, skb, sock_net(sk));
+ TCP_ECN_create_request(req, skb, sk);
if (want_cookie) {
isn = cookie_init_sequence(af_ops, sk, skb, &req->mss);
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 7f1280d..3a0ef04 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -318,11 +318,15 @@ static u16 tcp_select_window(struct sock *sk)
}
/* Packet ECN state for a SYN-ACK */
-static inline void TCP_ECN_send_synack(const struct tcp_sock *tp, struct sk_buff *skb)
+static inline void TCP_ECN_send_synack(struct sock *sk, struct sk_buff *skb)
{
+ const struct tcp_sock *tp = tcp_sk(sk);
+
TCP_SKB_CB(skb)->tcp_flags &= ~TCPHDR_CWR;
if (!(tp->ecn_flags & TCP_ECN_OK))
TCP_SKB_CB(skb)->tcp_flags &= ~TCPHDR_ECE;
+ else if (tcp_ca_needs_ecn(sk))
+ INET_ECN_xmit(sk);
}
/* Packet ECN state for a SYN. */
@@ -331,17 +335,24 @@ static inline void TCP_ECN_send_syn(struct sock *sk, struct sk_buff *skb)
struct tcp_sock *tp = tcp_sk(sk);
tp->ecn_flags = 0;
- if (sock_net(sk)->ipv4.sysctl_tcp_ecn == 1) {
+ if (sock_net(sk)->ipv4.sysctl_tcp_ecn == 1 ||
+ tcp_ca_needs_ecn(sk)) {
TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_ECE | TCPHDR_CWR;
tp->ecn_flags = TCP_ECN_OK;
+ if (tcp_ca_needs_ecn(sk))
+ INET_ECN_xmit(sk);
}
}
static __inline__ void
-TCP_ECN_make_synack(const struct request_sock *req, struct tcphdr *th)
+TCP_ECN_make_synack(const struct request_sock *req, struct tcphdr *th,
+ struct sock *sk)
{
- if (inet_rsk(req)->ecn_ok)
+ if (inet_rsk(req)->ecn_ok) {
th->ece = 1;
+ if (tcp_ca_needs_ecn(sk))
+ INET_ECN_xmit(sk);
+ }
}
/* Set up ECN state for a packet on a ESTABLISHED socket that is about to
@@ -362,7 +373,7 @@ static inline void TCP_ECN_send(struct sock *sk, struct sk_buff *skb,
tcp_hdr(skb)->cwr = 1;
skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN;
}
- } else {
+ } else if (!tcp_ca_needs_ecn(sk)) {
/* ACK or retransmitted segment: clear ECT|CE */
INET_ECN_dontxmit(sk);
}
@@ -2781,7 +2792,7 @@ int tcp_send_synack(struct sock *sk)
}
TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_ACK;
- TCP_ECN_send_synack(tcp_sk(sk), skb);
+ TCP_ECN_send_synack(sk, skb);
}
return tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC);
}
@@ -2840,7 +2851,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
memset(th, 0, sizeof(struct tcphdr));
th->syn = 1;
th->ack = 1;
- TCP_ECN_make_synack(req, th);
+ TCP_ECN_make_synack(req, th, sk);
th->source = htons(ireq->ir_num);
th->dest = ireq->ir_rmt_port;
/* Setting of flags are superfluous here for callers (and ECE is
--
1.7.11.7
^ permalink raw reply related
* [PATCH net-next v2 0/5] net: tcp: DCTCP congestion control algorithm
From: Florian Westphal @ 2014-09-20 21:29 UTC (permalink / raw)
To: davem
Cc: hagen, lars, eric.dumazet, fontana, hannes, glenn.judd, dborkman,
netdev
This patch series adds support for the DataCenter TCP (DCTCP) congestion
control algorithm. Please see individual patches for the details.
Over the last couple of months, we have resolved all outstanding issues
from last time in the background and as agreed with Dave, we're now
sending v2 of the set.
The last patch adds DCTCP as a congestion control module, and previous
ones add needed infrastructure to extend the congestion control framework.
Joint work between Florian Westphal, Daniel Borkmann and Glenn Judd.
v1 -> v2:
- Rebased to latest net-next
- Addressed Eric's feedback, thanks!
- Update stale comment wrt. DCTCP ECN usage
- Don't call INET_ECN_xmit for every packet
- Add dctcp ss/inetdiag support to expose internal stats to userspace
Thanks!
Daniel Borkmann (2):
net: tcp: add flag for ca to indicate that ECN is required
net: tcp: add DCTCP congestion control algorithm
Florian Westphal (3):
net: tcp: assign tcp cong_ops when tcp sk is created
net: tcp: split ack slow/fast events from cwnd_event
net: tcp: more detailed ACK events and events for CE marked packets
Documentation/networking/dctcp.txt | 43 ++++
include/net/tcp.h | 80 ++++++--
include/uapi/linux/inet_diag.h | 13 +
net/ipv4/Kconfig | 26 ++
net/ipv4/Makefile | 1
net/ipv4/tcp.c | 6
net/ipv4/tcp_cong.c | 46 ++--
net/ipv4/tcp_dctcp.c | 344 +++++++++++++++++++++++++++++++++++++
net/ipv4/tcp_input.c | 36 +++
net/ipv4/tcp_minisocks.c | 5
net/ipv4/tcp_output.c | 30 ++-
net/ipv4/tcp_westwood.c | 28 +--
12 files changed, 577 insertions(+), 81 deletions(-)
^ permalink raw reply
* [PATCH net-next v2 5/5] net: tcp: add DCTCP congestion control algorithm
From: Florian Westphal @ 2014-09-20 21:29 UTC (permalink / raw)
To: davem
Cc: hagen, lars, eric.dumazet, fontana, hannes, glenn.judd, dborkman,
netdev, Florian Westphal
In-Reply-To: <1411248562-26581-1-git-send-email-fw@strlen.de>
From: Daniel Borkmann <dborkman@redhat.com>
This work adds the DataCenter TCP (DCTCP) congestion control
algorithm [1], which has been first published at SIGCOMM 2010 [2],
resp. follow-up analysis at SIGMETRICS 2011 [3] (and also, more
recently as an informational IETF draft available at [4]).
DCTCP is an enhancement to the TCP congestion control algorithm for
data center networks. Typical data center workloads are i.e.
i) partition/aggregate (queries; bursty, delay sensitive), ii) short
messages e.g. 50KB-1MB (for coordination and control state; delay
sensitive), and iii) large flows e.g. 1MB-100MB (data update;
throughput sensitive). DCTCP has therefore been designed for such
environments to provide/achieve the following three requirements:
* High burst tolerance (incast due to partition/aggregate)
* Low latency (short flows, queries)
* High throughput (continuous data updates, large file
transfers) with commodity, shallow buffered switches
The basic idea of its design consists of two fundamentals: i) on the
switch side, packets are being marked when its internal queue
length > threshold K (K is chosen so that a large enough headroom
for marked traffic is still available in the switch queue); ii) the
sender/host side maintains a moving average of the fraction of marked
packets, so each RTT, F is being updated as follows:
F := X / Y, where X is # of marked ACKs, Y is total # of ACKs
alpha := (1 - g) * alpha + g * F, where g is a smoothing constant
The resulting alpha (iow: probability that switch queue is congested)
is then being used in order to adaptively decrease the congestion
window W:
W := (1 - (alpha / 2)) * W
The means for receiving marked packets resp. marking them on switch
side in DCTCP is the use of ECN.
RFC3168 describes a mechanism for using Explicit Congestion Notification
from the switch for early detection of congestion, rather than waiting
for segment loss to occur.
However, this method only detects the presence of congestion, not
the *extent*. In the presence of mild congestion, it reduces the TCP
congestion window too aggressively and unnecessarily affects the
throughput of long flows [4].
DCTCP, as mentioned, enhances Explicit Congestion Notification (ECN)
processing to estimate the fraction of bytes that encounter congestion,
rather than simply detecting that some congestion has occurred. DCTCP
then scales the TCP congestion window based on this estimate [4],
thus it can derive multibit feedback from the information present in
the single-bit sequence of marks in its control law. And thus act in
*proportion* to the extent of congestion, not its *presence*.
Switches therefore set the Congestion Experienced (CE) codepoint in
packets when internal queue lengths exceed threshold K. Resulting,
DCTCP delivers the same or better throughput than normal TCP, while
using 90% less buffer space.
It was found in [2] that DCTCP enables the applications to handle 10x
the current background traffic, without impacting foreground traffic.
Moreover, a 10x increase in foreground traffic did not cause any
timeouts, and thus largely eliminates TCP incast collapse problems.
The algorithm itself has already seen deployments in large production
data centers since then.
We did a long-term stress-test and analysis in a data center, short
summary of our TCP incast tests with iperf compared to cubic:
This test measured DCTCP throughput and latency and compared it with
CUBIC throughput and latency for an incast scenario. In this test, 19
senders sent at maximum rate to a single receiver. The receiver simply
ran iperf -s.
The senders ran iperf -c <receiver> -t 30. All senders started
simultaneously (using local clocks synchronized by ntp).
This test was repeated multiple times. Below shows the results from a
single test. Other tests are similar. (DCTCP results were extremely
consistent, CUBIC results show some variance induced by the TCP timeouts
that CUBIC encountered.)
For this test, we report statistics on the number of TCP timeouts,
flow throughput, and traffic latency.
1) Timeouts (total over all flows, and per flow summaries):
CUBIC DCTCP
Total 3227 25
Mean 169.842 1.316
Median 183 1
Max 207 5
Min 123 0
Stddev 28.991 1.600
Timeout data is taken by measuring the net change in netstat -s
"other TCP timeouts" reported. As a result, the timeout measurements
above are not restricted to the test traffic, and we believe that it
is likely that all of the "DCTCP timeouts" are actually timeouts for
non-test traffic. We report them nevertheless. CUBIC will also include
some non-test timeouts, but they are drawfed by bona fide test traffic
timeouts for CUBIC. Clearly DCTCP does an excellent job of preventing
TCP timeouts. DCTCP reduces timeouts by at least two orders of
magnitude and may well have eliminated them in this scenario.
2) Throughput (per flow in Mbps):
CUBIC DCTCP
Mean 521.684 521.895
Median 464 523
Max 776 527
Min 403 519
Stddev 105.891 2.601
Fairness 0.962 0.999
Throughput data was simply the average throughput for each flow
reported by iperf. By avoiding TCP timeouts, DCTCP is able to
achieve much better per-flow results. In CUBIC, many flows
experience TCP timeouts which makes flow throughput unpredictable and
unfair. DCTCP, on the other hand, provides very clean predictable
throughput without incurring TCP timeouts. Thus, the standard deviation
of CUBIC throughput is dramatically higher than the standard deviation
of DCTCP throughput.
Mean throughput is nearly identical because even though cubic flows
suffer TCP timeouts, other flows will step in and fill the unused
bandwidth. Note that this test is something of a best case scenario
for incast under CUBIC: it allows other flows to fill in for flows
experiencing a timeout. Under situations where the receiver is issuing
requests and then waiting for all flows to complete, flows cannot fill
in for timed out flows and throughput will drop dramatically.
3) Latency (in ms):
CUBIC DCTCP
Mean 4.0088 0.04219
Median 4.055 0.0395
Max 4.2 0.085
Min 3.32 0.028
Stddev 0.1666 0.01064
Latency for each protocol was computed by running "ping -i 0.2
<receiver>" from a single sender to the receiver during the incast
test. For DCTCP, "ping -Q 0x6 -i 0.2 <receiver>" was used to ensure
that traffic traversed the DCTCP queue and was not dropped when the
queue size was greater than the marking threshold. The summary
statistics above are over all ping metrics measured between the single
sender, receiver pair.
The latency results for this test show a dramatic difference between
CUBIC and DCTCP. CUBIC intentionally overflows the switch buffer
which incurs the maximum queue latency (more buffer memory will lead
to high latency.) DCTCP, on the other hand, deliberately attempts to
keep queue occupancy low. The result is a two orders of magnitude
reduction of latency with DCTCP - even with a switch with relatively
little RAM. Switches with larger amounts of RAM will incur increasing
amounts of latency for CUBIC, but not for DCTCP.
4) Convergence and stability test:
This test measured the time that DCTCP took to fairly redistribute
bandwidth when a new flow commences. It also measured DCTCP's ability
to remain stable at a fair bandwidth distribution. DCTCP is compared
with CUBIC for this test.
At the commencement of this test, a single flow is sending at maximum
rate (near 10 Gbps) to a single receiver. One second after that first
flow commences, a new flow from a distinct server begins sending to
the same receiver as the first flow. After the second flow has sent
data for 10 seconds, the second flow is terminated. The first flow
sends for an additional second. Ideally, the bandwidth would be evenly
shared as soon as the second flow starts, and recover as soon as it
stops.
The results of this test are shown below. Note that the flow bandwidth
for the two flows was measured near the same time, but not
simultaneously.
DCTCP performs nearly perfectly within the measurement limitations
of this test: bandwidth is quickly distributed fairly between the two
flows, remains stable throughout the duration of the test, and
recovers quickly. CUBIC, in contrast, is slow to divide the bandwidth
fairly, and has trouble remaining stable.
CUBIC DCTCP
Seconds Flow 1 Flow 2 Seconds Flow 1 Flow 2
0 9.93 0 0 9.92 0
0.5 9.87 0 0.5 9.86 0
1 8.73 2.25 1 6.46 4.88
1.5 7.29 2.8 1.5 4.9 4.99
2 6.96 3.1 2 4.92 4.94
2.5 6.67 3.34 2.5 4.93 5
3 6.39 3.57 3 4.92 4.99
3.5 6.24 3.75 3.5 4.94 4.74
4 6 3.94 4 5.34 4.71
4.5 5.88 4.09 4.5 4.99 4.97
5 5.27 4.98 5 4.83 5.01
5.5 4.93 5.04 5.5 4.89 4.99
6 4.9 4.99 6 4.92 5.04
6.5 4.93 5.1 6.5 4.91 4.97
7 4.28 5.8 7 4.97 4.97
7.5 4.62 4.91 7.5 4.99 4.82
8 5.05 4.45 8 5.16 4.76
8.5 5.93 4.09 8.5 4.94 4.98
9 5.73 4.2 9 4.92 5.02
9.5 5.62 4.32 9.5 4.87 5.03
10 6.12 3.2 10 4.91 5.01
10.5 6.91 3.11 10.5 4.87 5.04
11 8.48 0 11 8.49 4.94
11.5 9.87 0 11.5 9.9 0
SYN/ACK ECT test:
This test demonstrates the importance of ECT on SYN and SYN-ACK packets
by measuring the connection probability in the presence of competing
flows for a DCTCP connection attempt *without* ECT in the SYN packet.
The test was repeated five times for each number of competing flows.
Competing Flows 1 | 2 | 4 | 8 | 16
------------------------------
Mean Connection Probability 1 | 0.67 | 0.45 | 0.28 | 0
Median Connection Probability 1 | 0.65 | 0.45 | 0.25 | 0
As the number of competing flows moves beyond 1, the connection
probability drops rapidly.
Enabling DCTCP with this patch requires the following steps:
DCTCP must be running both on the sender and receiver side in your
data center, i.e.:
sysctl -w net.ipv4.tcp_congestion_control=dctcp
Also, ECN functionality must be enabled on all switches in your
data center for DCTCP to work. The default ECN marking threshold (K)
heuristic on the switch for DCTCP is e.g., 20 packets (30KB) at
1Gbps, and 65 packets (~100KB) at 10Gbps (K > 1/7 * C * RTT, [4]).
In above tests, for each switch port, traffic was segregated into two
queues. For any packet with a DSCP of 0x01 - or equivalently a TOS of
0x04 - the packet was placed into the DCTCP queue. All other packets
were placed into the default drop-tail queue. For the DCTCP queue,
RED/ECN marking was enabled, here, with a marking threshold of 75 KB.
More details however, we refer you to the paper [2] under section 3).
There are no code changes required to applications running in user
space. DCTCP has been implemented in full *isolation* of the rest of
the TCP code as its own congestion control module, so that it can run
without a need to expose code to the core of the TCP stack, and thus
nothing changes for non-DCTCP users.
Changes in the CA framework code are minimal, and DCTCP algorithm
operates on mechanisms that are already available in most Silicon.
The gain (dctcp_shift_g) is currently a fixed constant (1/16) from
the paper, but we leave the option that it can be chosen carefully
to a different value by the user.
In case DCTCP is being used and ECN support on peer site is off,
DCTCP falls back after 3WHS to operate in normal TCP Reno mode.
ss {-4,-6} -t -i diag interface:
... dctcp wscale:7,7 rto:203 rtt:2.349/0.026 mss:1448 cwnd:2054
ssthresh:1102 ce_state 0 alpha 15 ab_ecn 0 ab_tot 735584
send 10129.2Mbps pacing_rate 20254.1Mbps unacked:1822 retrans:0/15
reordering:101 rcv_space:29200
... dctcp-reno wscale:7,7 rto:201 rtt:0.711/1.327 ato:40 mss:1448
cwnd:10 ssthresh:1102 fallback_mode send 162.9Mbps pacing_rate
325.5Mbps rcv_rtt:1.5 rcv_space:29200
More information about DCTCP can be found in [1-4].
[1] http://simula.stanford.edu/~alizade/Site/DCTCP.html
[2] http://simula.stanford.edu/~alizade/Site/DCTCP_files/dctcp-final.pdf
[3] http://simula.stanford.edu/~alizade/Site/DCTCP_files/dctcp_analysis-full.pdf
[4] http://tools.ietf.org/html/draft-bensley-tcpm-dctcp-00
Joint work with Florian Westphal and Glenn Judd.
Signed-off-by: Daniel Borkmann <dborkman@redhat.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Glenn Judd <glenn.judd@morganstanley.com>
---
Documentation/networking/dctcp.txt | 43 +++++
include/uapi/linux/inet_diag.h | 13 +-
net/ipv4/Kconfig | 26 ++-
net/ipv4/Makefile | 1 +
net/ipv4/tcp_dctcp.c | 344 +++++++++++++++++++++++++++++++++++++
net/ipv4/tcp_output.c | 1 +
6 files changed, 425 insertions(+), 3 deletions(-)
create mode 100644 Documentation/networking/dctcp.txt
create mode 100644 net/ipv4/tcp_dctcp.c
diff --git a/Documentation/networking/dctcp.txt b/Documentation/networking/dctcp.txt
new file mode 100644
index 0000000..0d5dfbc
--- /dev/null
+++ b/Documentation/networking/dctcp.txt
@@ -0,0 +1,43 @@
+DCTCP (DataCenter TCP)
+----------------------
+
+DCTCP is an enhancement to the TCP congestion control algorithm for data
+center networks and leverages Explicit Congestion Notification (ECN) in
+the data center network to provide multi-bit feedback to the end hosts.
+
+To enable it on end hosts:
+
+ sysctl -w net.ipv4.tcp_congestion_control=dctcp
+
+All switches in the data center network running DCTCP must support ECN
+marking and be configured for marking when reaching defined switch buffer
+thresholds. The default ECN marking threshold heuristic for DCTCP on
+switches is 20 packets (30KB) at 1Gbps, and 65 packets (~100KB) at 10Gbps,
+but might need further careful tweaking.
+
+For more details, see below documents:
+
+Paper:
+
+The algorithm is further described in detail in the following two
+SIGCOMM/SIGMETRICS papers:
+
+ i) Mohammad Alizadeh, Albert Greenberg, David A. Maltz, Jitendra Padhye,
+ Parveen Patel, Balaji Prabhakar, Sudipta Sengupta, and Murari Sridharan:
+ "Data Center TCP (DCTCP)", Data Center Networks session
+ Proc. ACM SIGCOMM, New Delhi, 2010.
+ http://simula.stanford.edu/~alizade/Site/DCTCP_files/dctcp-final.pdf
+ http://www.sigcomm.org/ccr/papers/2010/October/1851275.1851192
+
+ii) Mohammad Alizadeh, Adel Javanmard, and Balaji Prabhakar:
+ "Analysis of DCTCP: Stability, Convergence, and Fairness"
+ Proc. ACM SIGMETRICS, San Jose, 2011.
+ http://simula.stanford.edu/~alizade/Site/DCTCP_files/dctcp_analysis-full.pdf
+
+IETF informational draft:
+
+ http://tools.ietf.org/html/draft-bensley-tcpm-dctcp-00
+
+DCTCP site:
+
+ http://simula.stanford.edu/~alizade/Site/DCTCP.html
diff --git a/include/uapi/linux/inet_diag.h b/include/uapi/linux/inet_diag.h
index bbde90f..d65c0a0 100644
--- a/include/uapi/linux/inet_diag.h
+++ b/include/uapi/linux/inet_diag.h
@@ -110,10 +110,10 @@ enum {
INET_DIAG_TCLASS,
INET_DIAG_SKMEMINFO,
INET_DIAG_SHUTDOWN,
+ INET_DIAG_DCTCPINFO,
};
-#define INET_DIAG_MAX INET_DIAG_SHUTDOWN
-
+#define INET_DIAG_MAX INET_DIAG_DCTCPINFO
/* INET_DIAG_MEM */
@@ -133,5 +133,14 @@ struct tcpvegas_info {
__u32 tcpv_minrtt;
};
+/* INET_DIAG_DCTCPINFO */
+
+struct tcp_dctcp_info {
+ __u16 dctcp_enabled;
+ __u16 dctcp_ce_state;
+ __u32 dctcp_alpha;
+ __u32 dctcp_ab_ecn;
+ __u32 dctcp_ab_tot;
+};
#endif /* _UAPI_INET_DIAG_H_ */
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index dbc10d8..f9cf567 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -560,6 +560,27 @@ config TCP_CONG_ILLINOIS
For further details see:
http://www.ews.uiuc.edu/~shaoliu/tcpillinois/index.html
+config TCP_CONG_DCTCP
+ tristate "DataCenter TCP (DCTCP)"
+ default n
+ ---help---
+ DCTCP leverages Explicit Congestion Notification (ECN) in the network to
+ provide multi-bit feedback to the end hosts. It is designed to provide:
+
+ - High burst tolerance (incast due to partition/aggregate),
+ - Low latency (short flows, queries),
+ - High throughput (continuous data updates, large file transfers) with
+ commodity, shallow-buffered switches.
+
+ All switches in the data center network running DCTCP must support
+ ECN marking and be configured for marking when reaching defined switch
+ buffer thresholds. The default ECN marking threshold heuristic for
+ DCTCP on switches is 20 packets (30KB) at 1Gbps, and 65 packets
+ (~100KB) at 10Gbps, but might need further careful tweaking.
+
+ For further details see:
+ http://simula.stanford.edu/~alizade/Site/DCTCP_files/dctcp-final.pdf
+
choice
prompt "Default TCP congestion control"
default DEFAULT_CUBIC
@@ -588,9 +609,11 @@ choice
config DEFAULT_WESTWOOD
bool "Westwood" if TCP_CONG_WESTWOOD=y
+ config DEFAULT_DCTCP
+ bool "DCTCP" if TCP_CONG_DCTCP=y
+
config DEFAULT_RENO
bool "Reno"
-
endchoice
endif
@@ -610,6 +633,7 @@ config DEFAULT_TCP_CONG
default "westwood" if DEFAULT_WESTWOOD
default "veno" if DEFAULT_VENO
default "reno" if DEFAULT_RENO
+ default "dctcp" if DEFAULT_DCTCP
default "cubic"
config TCP_MD5SIG
diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile
index 8ee1cd4..6f2ec1a 100644
--- a/net/ipv4/Makefile
+++ b/net/ipv4/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_INET_UDP_DIAG) += udp_diag.o
obj-$(CONFIG_NET_TCPPROBE) += tcp_probe.o
obj-$(CONFIG_TCP_CONG_BIC) += tcp_bic.o
obj-$(CONFIG_TCP_CONG_CUBIC) += tcp_cubic.o
+obj-$(CONFIG_TCP_CONG_DCTCP) += tcp_dctcp.o
obj-$(CONFIG_TCP_CONG_WESTWOOD) += tcp_westwood.o
obj-$(CONFIG_TCP_CONG_HSTCP) += tcp_highspeed.o
obj-$(CONFIG_TCP_CONG_HYBLA) += tcp_hybla.o
diff --git a/net/ipv4/tcp_dctcp.c b/net/ipv4/tcp_dctcp.c
new file mode 100644
index 0000000..d45d1fa
--- /dev/null
+++ b/net/ipv4/tcp_dctcp.c
@@ -0,0 +1,344 @@
+/* DataCenter TCP (DCTCP) congestion control.
+ *
+ * http://simula.stanford.edu/~alizade/Site/DCTCP.html
+ *
+ * This is an implementation of DCTCP over Reno, an enhancement to the
+ * TCP congestion control algorithm designed for data centers. DCTCP
+ * leverages Explicit Congestion Notification (ECN) in the network to
+ * provide multi-bit feedback to the end hosts. DCTCP's goal is to meet
+ * the following three data center transport requirements:
+ *
+ * - High burst tolerance (incast due to partition/aggregate)
+ * - Low latency (short flows, queries)
+ * - High throughput (continuous data updates, large file transfers)
+ * with commodity shallow buffered switches
+ *
+ * The algorithm is described in detail in the following two papers:
+ *
+ * 1) Mohammad Alizadeh, Albert Greenberg, David A. Maltz, Jitendra Padhye,
+ * Parveen Patel, Balaji Prabhakar, Sudipta Sengupta, and Murari Sridharan:
+ * "Data Center TCP (DCTCP)", Data Center Networks session
+ * Proc. ACM SIGCOMM, New Delhi, 2010.
+ * http://simula.stanford.edu/~alizade/Site/DCTCP_files/dctcp-final.pdf
+ *
+ * 2) Mohammad Alizadeh, Adel Javanmard, and Balaji Prabhakar:
+ * "Analysis of DCTCP: Stability, Convergence, and Fairness"
+ * Proc. ACM SIGMETRICS, San Jose, 2011.
+ * http://simula.stanford.edu/~alizade/Site/DCTCP_files/dctcp_analysis-full.pdf
+ *
+ * Initial prototype from Abdul Kabbani, Masato Yasuda and Mohammad Alizadeh.
+ *
+ * Authors:
+ *
+ * Daniel Borkmann <dborkman@redhat.com>
+ * Florian Westphal <fw@strlen.de>
+ * Glenn Judd <glenn.judd@morganstanley.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <net/tcp.h>
+#include <linux/inet_diag.h>
+
+#define DCTCP_MAX_ALPHA 1024U
+
+struct dctcp {
+ u32 acked_bytes_ecn;
+ u32 acked_bytes_total;
+ u32 prior_snd_una;
+ u32 prior_rcv_nxt;
+ u32 dctcp_alpha;
+ u32 next_seq;
+ u32 ce_state;
+ u32 delayed_ack_reserved;
+};
+
+static unsigned int dctcp_shift_g __read_mostly = 4; /* g = 1/2^4 */
+module_param(dctcp_shift_g, uint, 0644);
+MODULE_PARM_DESC(dctcp_shift_g, "parameter g for updating dctcp_alpha");
+
+static unsigned int dctcp_alpha_on_init __read_mostly = DCTCP_MAX_ALPHA;
+module_param(dctcp_alpha_on_init, uint, 0644);
+MODULE_PARM_DESC(dctcp_alpha_on_init, "parameter for initial alpha value");
+
+static unsigned int dctcp_clamp_alpha_on_loss __read_mostly;
+module_param(dctcp_clamp_alpha_on_loss, uint, 0644);
+MODULE_PARM_DESC(dctcp_clamp_alpha_on_loss,
+ "parameter for clamping alpha on loss");
+
+static struct tcp_congestion_ops dctcp_reno;
+
+static void dctcp_reset(const struct tcp_sock *tp, struct dctcp *ca)
+{
+ ca->next_seq = tp->snd_nxt;
+
+ ca->acked_bytes_ecn = 0;
+ ca->acked_bytes_total = 0;
+}
+
+static void dctcp_init(struct sock *sk)
+{
+ const struct tcp_sock *tp = tcp_sk(sk);
+
+ if ((tp->ecn_flags & TCP_ECN_OK) ||
+ (sk->sk_state == TCP_LISTEN ||
+ sk->sk_state == TCP_CLOSE)) {
+ struct dctcp *ca = inet_csk_ca(sk);
+
+ ca->prior_snd_una = tp->snd_una;
+ ca->prior_rcv_nxt = tp->rcv_nxt;
+
+ ca->dctcp_alpha = min(dctcp_alpha_on_init, DCTCP_MAX_ALPHA);
+
+ ca->delayed_ack_reserved = 0;
+ ca->ce_state = 0;
+
+ dctcp_reset(tp, ca);
+ return;
+ }
+
+ /* No ECN support? Fall back to Reno. Also need to clear
+ * ECT from sk since it is set during 3WHS for DCTCP.
+ */
+ inet_csk(sk)->icsk_ca_ops = &dctcp_reno;
+ INET_ECN_dontxmit(sk);
+}
+
+static u32 dctcp_ssthresh(struct sock *sk)
+{
+ const struct dctcp *ca = inet_csk_ca(sk);
+ struct tcp_sock *tp = tcp_sk(sk);
+
+ return max(tp->snd_cwnd - ((tp->snd_cwnd * ca->dctcp_alpha) >> 11U), 2U);
+}
+
+/* Minimal DCTP CE state machine:
+ *
+ * S: 0 <- last pkt was non-CE
+ * 1 <- last pkt was CE
+ */
+
+static void dctcp_ce_state_0_to_1(struct sock *sk)
+{
+ struct dctcp *ca = inet_csk_ca(sk);
+ struct tcp_sock *tp = tcp_sk(sk);
+
+ /* State has changed from CE=0 to CE=1 and delayed
+ * ACK has not sent yet.
+ */
+ if (!ca->ce_state && ca->delayed_ack_reserved) {
+ u32 tmp_rcv_nxt;
+
+ /* Save current rcv_nxt. */
+ tmp_rcv_nxt = tp->rcv_nxt;
+
+ /* Generate previous ack with CE=0. */
+ tp->ecn_flags &= ~TCP_ECN_DEMAND_CWR;
+ tp->rcv_nxt = ca->prior_rcv_nxt;
+
+ tcp_send_ack(sk);
+
+ /* Recover current rcv_nxt. */
+ tp->rcv_nxt = tmp_rcv_nxt;
+ }
+
+ ca->prior_rcv_nxt = tp->rcv_nxt;
+ ca->ce_state = 1;
+
+ tp->ecn_flags |= TCP_ECN_DEMAND_CWR;
+}
+
+static void dctcp_ce_state_1_to_0(struct sock *sk)
+{
+ struct dctcp *ca = inet_csk_ca(sk);
+ struct tcp_sock *tp = tcp_sk(sk);
+
+ /* State has changed from CE=1 to CE=0 and delayed
+ * ACK has not sent yet.
+ */
+ if (ca->ce_state && ca->delayed_ack_reserved) {
+ u32 tmp_rcv_nxt;
+
+ /* Save current rcv_nxt. */
+ tmp_rcv_nxt = tp->rcv_nxt;
+
+ /* Generate previous ack with CE=1. */
+ tp->ecn_flags |= TCP_ECN_DEMAND_CWR;
+ tp->rcv_nxt = ca->prior_rcv_nxt;
+
+ tcp_send_ack(sk);
+
+ /* Recover current rcv_nxt. */
+ tp->rcv_nxt = tmp_rcv_nxt;
+ }
+
+ ca->prior_rcv_nxt = tp->rcv_nxt;
+ ca->ce_state = 0;
+
+ tp->ecn_flags &= ~TCP_ECN_DEMAND_CWR;
+}
+
+static void dctcp_update_alpha(struct sock *sk, u32 flags)
+{
+ const struct tcp_sock *tp = tcp_sk(sk);
+ struct dctcp *ca = inet_csk_ca(sk);
+ u32 acked_bytes = tp->snd_una - ca->prior_snd_una;
+
+ /* If ack did not advance snd_una, count dupack as MSS size.
+ * If ack did update window, do not count it at all.
+ */
+ if (acked_bytes == 0 && !(flags & CA_ACK_WIN_UPDATE))
+ acked_bytes = inet_csk(sk)->icsk_ack.rcv_mss;
+ if (acked_bytes) {
+ ca->acked_bytes_total += acked_bytes;
+ ca->prior_snd_una = tp->snd_una;
+
+ if (flags & CA_ACK_ECE)
+ ca->acked_bytes_ecn += acked_bytes;
+ }
+
+ /* Expired RTT */
+ if (!before(tp->snd_una, ca->next_seq)) {
+ /* For avoiding denominator == 1. */
+ if (ca->acked_bytes_total == 0)
+ ca->acked_bytes_total = 1;
+
+ /* alpha = (1 - g) * alpha + g * F */
+ ca->dctcp_alpha = ca->dctcp_alpha -
+ (ca->dctcp_alpha >> dctcp_shift_g) +
+ (ca->acked_bytes_ecn << (10U - dctcp_shift_g)) /
+ ca->acked_bytes_total;
+
+ if (ca->dctcp_alpha > DCTCP_MAX_ALPHA)
+ /* Clamp dctcp_alpha to max. */
+ ca->dctcp_alpha = DCTCP_MAX_ALPHA;
+
+ dctcp_reset(tp, ca);
+ }
+}
+
+static void dctcp_state(struct sock *sk, u8 new_state)
+{
+ if (dctcp_clamp_alpha_on_loss && new_state == TCP_CA_Loss) {
+ struct dctcp *ca = inet_csk_ca(sk);
+
+ /* If this extension is enabled, we clamp dctcp_alpha to
+ * max on packet loss; the motivation is that dctcp_alpha
+ * is an indicator to the extend of congestion and packet
+ * loss is an indicator of extreme congestion; setting
+ * this in practice turned out to be beneficial, and
+ * effectively assumes total congestion which reduces the
+ * window by half.
+ */
+ ca->dctcp_alpha = DCTCP_MAX_ALPHA;
+ }
+}
+
+static void dctcp_update_ack_reserved(struct sock *sk, enum tcp_ca_event ev)
+{
+ struct dctcp *ca = inet_csk_ca(sk);
+
+ switch (ev) {
+ case CA_EVENT_DELAYED_ACK:
+ if (!ca->delayed_ack_reserved)
+ ca->delayed_ack_reserved = 1;
+ break;
+ case CA_EVENT_NON_DELAYED_ACK:
+ if (ca->delayed_ack_reserved)
+ ca->delayed_ack_reserved = 0;
+ break;
+ default:
+ /* Don't care for the rest. */
+ break;
+ }
+}
+
+static void dctcp_cwnd_event(struct sock *sk, enum tcp_ca_event ev)
+{
+ switch (ev) {
+ case CA_EVENT_ECN_IS_CE:
+ dctcp_ce_state_0_to_1(sk);
+ break;
+ case CA_EVENT_ECN_NO_CE:
+ dctcp_ce_state_1_to_0(sk);
+ break;
+ case CA_EVENT_DELAYED_ACK:
+ case CA_EVENT_NON_DELAYED_ACK:
+ dctcp_update_ack_reserved(sk, ev);
+ break;
+ default:
+ /* Don't care for the rest. */
+ break;
+ }
+}
+
+static void dctcp_get_info(struct sock *sk, u32 ext, struct sk_buff *skb)
+{
+ const struct dctcp *ca = inet_csk_ca(sk);
+
+ /* Fill it also in case of VEGASINFO due to req struct limits.
+ * We can still correctly retrieve it later.
+ */
+ if (ext & (1 << (INET_DIAG_DCTCPINFO - 1)) ||
+ ext & (1 << (INET_DIAG_VEGASINFO - 1))) {
+ struct tcp_dctcp_info info;
+
+ memset(&info, 0, sizeof(info));
+ if (inet_csk(sk)->icsk_ca_ops != &dctcp_reno) {
+ info.dctcp_enabled = 1;
+ info.dctcp_ce_state = (u16) ca->ce_state;
+ info.dctcp_alpha = ca->dctcp_alpha;
+ info.dctcp_ab_ecn = ca->acked_bytes_ecn;
+ info.dctcp_ab_tot = ca->acked_bytes_total;
+ }
+
+ nla_put(skb, INET_DIAG_DCTCPINFO, sizeof(info), &info);
+ }
+}
+
+static struct tcp_congestion_ops dctcp __read_mostly = {
+ .init = dctcp_init,
+ .in_ack_event = dctcp_update_alpha,
+ .cwnd_event = dctcp_cwnd_event,
+ .ssthresh = dctcp_ssthresh,
+ .cong_avoid = tcp_reno_cong_avoid,
+ .set_state = dctcp_state,
+ .get_info = dctcp_get_info,
+ .flags = TCP_CONG_NEEDS_ECN,
+ .owner = THIS_MODULE,
+ .name = "dctcp",
+};
+
+static struct tcp_congestion_ops dctcp_reno __read_mostly = {
+ .ssthresh = tcp_reno_ssthresh,
+ .cong_avoid = tcp_reno_cong_avoid,
+ .get_info = dctcp_get_info,
+ .owner = THIS_MODULE,
+ .name = "dctcp-reno",
+};
+
+static int __init dctcp_register(void)
+{
+ BUILD_BUG_ON(sizeof(struct dctcp) > ICSK_CA_PRIV_SIZE);
+ return tcp_register_congestion_control(&dctcp);
+}
+
+static void __exit dctcp_unregister(void)
+{
+ tcp_unregister_congestion_control(&dctcp);
+}
+
+module_init(dctcp_register);
+module_exit(dctcp_unregister);
+
+MODULE_AUTHOR("Daniel Borkmann <dborkman@redhat.com>");
+MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
+MODULE_AUTHOR("Glenn Judd <glenn.judd@morganstanley.com>");
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("DataCenter TCP (DCTCP)");
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 8600641..e8cbea2 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -3203,6 +3203,7 @@ void tcp_send_ack(struct sock *sk)
skb_mstamp_get(&buff->skb_mstamp);
tcp_transmit_skb(sk, buff, 0, sk_gfp_atomic(sk, GFP_ATOMIC));
}
+EXPORT_SYMBOL_GPL(tcp_send_ack);
/* This routine sends a packet with an out of date sequence
* number. It assumes the other end will try to ack it.
--
1.7.11.7
^ permalink raw reply related
* Re: [net-next PATCH 29/29] fm10k: Add support for PTP
From: Richard Cochran @ 2014-09-20 21:16 UTC (permalink / raw)
To: Alexander Duyck
Cc: davem, nhorman, netdev, john.fastabend, matthew.vick,
jeffrey.t.kirsher, sassmann
In-Reply-To: <541C76B8.6040708@intel.com>
On Fri, Sep 19, 2014 at 11:32:24AM -0700, Alexander Duyck wrote:
>
> Because the value cannot be adjusted directly. The timesource for the
> switch is shared by all ports and host interfaces. As such we have to
> maintain a software offset per host to avoid corrupting the other clocks.
So any host can set the time, but only one may adjust the frequency?
Regarding the overflow, probably you can remove the daily check, since
you only need a single clock read every 300 years, and that is
reasonable to expect in any case.
Thanks,
Richard
^ permalink raw reply
* Re: [net-next PATCH 29/29] fm10k: Add support for PTP
From: Richard Cochran @ 2014-09-20 21:07 UTC (permalink / raw)
To: Alexander Duyck
Cc: davem, nhorman, netdev, john.fastabend, matthew.vick,
jeffrey.t.kirsher, sassmann
In-Reply-To: <541C76B8.6040708@intel.com>
On Fri, Sep 19, 2014 at 11:32:24AM -0700, Alexander Duyck wrote:
> Because doing it that way it extends over 80 characters.
Obviously, but still the result is really ugly.
Thanks,
Richard
^ permalink raw reply
* Re:
From: Richard Wong @ 2014-09-20 19:19 UTC (permalink / raw)
To: netdev
Hello,
I have a business proposal I'd like to share with you, on your response I'll email you with more details.
I await your prompt reply on this.
Kind regards
Richard Wong
^ permalink raw reply
* Re: [PATCH net-next] tcp: avoid possible arithmetic overflows
From: Joe Perches @ 2014-09-20 20:19 UTC (permalink / raw)
To: Eric Dumazet; +Cc: Yuchung Cheng, David Miller, netdev, Neal Cardwell
In-Reply-To: <1411242956.26859.81.camel@edumazet-glaptop2.roam.corp.google.com>
On Sat, 2014-09-20 at 12:55 -0700, Eric Dumazet wrote:
> On Sat, 2014-09-20 at 12:46 -0700, Yuchung Cheng wrote:
> > On Sat, Sep 20, 2014 at 11:01 AM, Joe Perches <joe@perches.com> wrote:
> > > On Sat, 2014-09-20 at 10:19 -0700, Eric Dumazet wrote:
> > >> From: Eric Dumazet <edumazet@google.com>
> > >>
> > >> icsk_rto is an 32bit field, and icsk_backoff can reach 15 by default,
> > >> or more if some sysctl (eg tcp_retries2) are changed.
> > >>
> > >> Better use 64bit to perform icsk_rto << icsk_backoff operations
> > >
> > > Maybe better to use a helper function for this?
> > >
> > > something like:
> > >
> > > static inline u64 icsk_rto_backoff(const struct inet_connection_sock *icsk)
> > > {
> > > u64 when = (u64)icsk->icsk_rto;
> > >
> > > return when << icsk->icsk_backoff;
> > > }
> > Thanks for the fix Eric. I second Joe's idea to use a helper function.
> >
>
> Yep.
>
> Given the timeout functions in the kernel use 'unsigned long', I prefer
> to keep the u64 magic private to this helper.
>
> I will probably use
>
> static inline unsigned long icsk_rto_backoff(const struct inet_connection_sock *icsk)
> {
> u64 when = (u64)icsk->icsk_rto << icsk->icsk_backoff;
>
> return min_t(u64, when, ~0UL);
OK.
I think an explicit cast to unsigned long after the min_t
to avoid the implicit downcast would be better
return (unsigned long)min_t(etc...)
so that no warning is produced if someone does make W=3
^ permalink raw reply
* Re: [PATCH net-next v4 2/2] bonding: Simplify the xmit function for modes that use xmit_hash
From: Mahesh Bandewar @ 2014-09-20 20:04 UTC (permalink / raw)
To: Nikolay Aleksandrov
Cc: Jay Vosburgh, Veaceslav Falico, Andy Gospodarek, David Miller,
netdev, Eric Dumazet, Maciej Zenczykowski
In-Reply-To: <541D54CD.5030206@redhat.com>
On Sat, Sep 20, 2014 at 3:19 AM, Nikolay Aleksandrov <nikolay@redhat.com> wrote:
> On 09/20/2014 02:09 AM, Mahesh Bandewar wrote:
>> On Fri, Sep 19, 2014 at 4:06 AM, Nikolay Aleksandrov <nikolay@redhat.com> wrote:
>>>
>>> On 09/19/2014 12:00 PM, Nikolay Aleksandrov wrote:
>>>> On 09/18/2014 11:53 PM, Mahesh Bandewar wrote:
>>>>> Earlier change to use usable slave array for TLB mode had an additional
>>>>> performance advantage. So extending the same logic to all other modes
>>>>> that use xmit-hash for slave selection (viz 802.3AD, and XOR modes).
>>>>> Also consolidating this with the earlier TLB change.
>>>>>
>>>>> The main idea is to build the usable slaves array in the control path
>>>>> and use that array for slave selection during xmit operation.
>>>>>
>>>>> Measured performance in a setup with a bond of 4x1G NICs with 200
>>>>> instances of netperf for the modes involved (3ad, xor, tlb)
>>>>> cmd: netperf -t TCP_RR -H <TargetHost> -l 60 -s 5
>>>>>
>>>>> Mode TPS-Before TPS-After
>>>>>
>>>>> 802.3ad : 468,694 493,101
>>>>> TLB (lb=0): 392,583 392,965
>>>>> XOR : 475,696 484,517
>>>>>
>>>>> Signed-off-by: Mahesh Bandewar <maheshb@google.com>
>>>>> ---
>>>>> v1:
>>>>> (a) If bond_update_slave_arr() fails to allocate memory, it will overwrite
>>>>> the slave that need to be removed.
>>>>> (b) Freeing of array will assign NULL (to handle bond->down to bond->up
>>>>> transition gracefully.
>>>>> (c) Change from pr_debug() to pr_err() if bond_update_slave_arr() returns
>>>>> failure.
>>>>> (d) XOR: bond_update_slave_arr() will consider mii-mon, arp-mon cases and
>>>>> will populate the array even if these parameters are not used.
>>>>> (e) 3AD: Should handle the ad_agg_selection_logic correctly.
>>>>> v2:
>>>>> (a) Removed rcu_read_{un}lock() calls from array manipulation code.
>>>>> (b) Slave link-events now refresh array for all these modes.
>>>>> (c) Moved free-array call from bond_close() to bond_uninit().
>>>>> v3:
>>>>> (a) Fixed null pointer dereference.
>>>>> (b) Removed bond->lock lockdep dependency.
>>>>> v4:
>>>>> (a) Made to changes to comply with Nikolay's locking changes
>>>>> (b) Added a work-queue to refresh slave-array when RTNL is not held
>>>>> (c) Array refresh happens ONLY with RTNL now.
>>>>> (d) alloc changed from GFP_ATOMIC to GFP_KERNEL
>>>>>
>>> <<<snip>>>
>>>>> @@ -3839,6 +4003,7 @@ static void bond_uninit(struct net_device *bond_dev)
>>>>> struct bonding *bond = netdev_priv(bond_dev);
>>>>> struct list_head *iter;
>>>>> struct slave *slave;
>>>>> + struct bond_up_slave *arr;
>>>>>
>>>>> bond_netpoll_cleanup(bond_dev);
>>>>>
>>>>> @@ -3847,6 +4012,12 @@ static void bond_uninit(struct net_device *bond_dev)
>>>>> __bond_release_one(bond_dev, slave->dev, true);
>>>>> netdev_info(bond_dev, "Released all slaves\n");
>>>>>
>>> Sorry but I just spotted a major problem, bond_3ad_unbind_slave() (called
>>> from __bond_release_one) calls ad_agg_selection_logic() which can re-arm
>>> the slave_arr work after it's supposed to be stopped here (i.e. the bond
>>> device has been closed so all works should've been stopped) so we might
>>> leak memory and access freed memory after all since it'll keep
>>> re-scheduling itself until it can acquire rtnl which is after the bond
>>> device has been destroyed.
>>>
>> This should not be a problem. ndo_close (bond_close()) is called
>> before ndo_uninit(bond_uninit()), so the work-queues get cancelled
>> there so if rearm tries to schedule some work after queue gets
>> cancelled, it can't do much and wont harm anything.
>> Hence there wont be any arrays built once it's free-ed completely and
>> therefore no memory leak. I addded some instrumentation and tried
>> following sequence -
>>
>> # modprobe bonding mode=4
>> # ip link set bond0 up
>> # [Add ip]
>> # [Add default route]
>> # ifenslave bond0 eth0 eth1 eth2 eth3
>> ....
>> [Run some backgound traffic. I used netperf.]
>>
>> # ip link bond0 down
>>
>> I did not see anything "bad" happening. Did your trial produced
>> something unpleasant?
>>
> The test you've done is irrelevant to the situation that I described
> because ndo_uninit() is called when the device is being destroyed. Moreover
> the case I told you about would require to have an active aggregator and an
> inactive one (i.e. so agg selection logic will get called), here is the result:
> [ 428.916586] bond1 (unregistering): Removing an active aggregator
> [ 428.916589] Failed to build slave-array.
> [ 428.916849] bond1 (unregistering): Releasing active interface eth1
> [ 428.920342] bond1 (unregistering): Released all slaves
> [ 428.923043] Failed to update slave array from WT
> [ 428.924098] Failed to update slave array from WT
> [ 428.925125] Failed to update slave array from WT
> [ 428.926120] Failed to update slave array from WT
> [ 428.927096] Failed to update slave array from WT
> [ 428.928101] Failed to update slave array from WT
> [ 428.929120] Failed to update slave array from WT
> [ 428.930086] BUG: unable to handle kernel NULL pointer dereference at
> (null)
> [ 428.930644] IP: [<ffffffff810aa37b>] __queue_work+0x7b/0x350
> [ 428.930946] PGD 0
> [ 428.931053] Oops: 0000 [#1] SMP
> [ 428.931053] Modules linked in: sfc ptp pps_core mdio i2c_algo_bit mtd
> bonding(O) snd_hda_codec_generic joydev crct10dif_pclmul crc32_pclmul
> i2c_piix4 ppdev crc32c_intel ghash_clmulni_intel parport_pc snd_hda_intel
> snd_hda_controller snd_hda_codec snd_hwdep snd_pcm snd_timer 9pnet_virtio
> snd 9pnet pcspkr parport i2ccore serio_raw virtio_console virtio_balloon
> pvpanic soundcore virtio_blk virtio_net ata_generic floppy pata_acpi
> virtio_pci virtio_ring virtio
> [ 428.935022] CPU: 0 PID: 0 Comm: swapper/0 Tainted: G O
> 3.17.0-rc4+ #30
> [ 428.935022] Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011
> [ 428.935022] task: ffffffff81c1b460 ti: ffffffff81c00000 task.ti:
> ffffffff81c00000
> [ 428.935022] RIP: 0010:[<ffffffff810aa37b>] [<ffffffff810aa37b>]
> __queue_work+0x7b/0x350
> [ 428.935022] RSP: 0018:ffff88005f003e28 EFLAGS: 00010086
> [ 428.935022] RAX: ffff88005c05c800 RBX: 0000000000000000 RCX:
> 0000000000000000
> [ 428.935022] RDX: 0000000000000000 RSI: 0000000000000006 RDI:
> ffff88005a4fbd58
> [ 428.935022] RBP: ffff88005f003e60 R08: 0000000000000046 R09:
> ffffffff8225abc2
> [ 428.935022] R10: 0000000000000004 R11: 0000000000000005 R12:
> ffff88005a4fbd58
> [ 428.935022] R13: 0000000000000008 R14: ffff88004b211800 R15:
> 00000000000102f0
> [ 428.935022] FS: 0000000000000000(0000) GS:ffff88005f000000(0000)
> knlGS:0000000000000000
> [ 428.935022] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> [ 428.935022] CR2: 0000000000000000 CR3: 000000004abde000 CR4:
> 00000000000406f0
> [ 428.935022] Stack:
> [ 428.935022] 0a19522f72b12222 0000000081c1b460 ffffffff8225abc0
> ffff88005a4fbd78
> [ 428.935022] 0000000000000101 ffffffff810aa650 ffff88005a4fbd58
> ffff88005f003e70
> [ 428.935022] ffffffff810aa668 ffff88005f003ea8 ffffffff810f3536
> ffffffff8225abc0
> [ 428.935022] Call Trace:
> [ 428.935022] <IRQ>
> [ 428.935022]
> [ 428.935022] [<ffffffff810aa650>] ? __queue_work+0x350/0x350
> [ 428.935022] [<ffffffff810aa668>] delayed_work_timer_fn+0x18/0x20
> [ 428.935022] [<ffffffff810f3536>] call_timer_fn+0x36/0x120
> [ 428.935022] [<ffffffff810aa650>] ? __queue_work+0x350/0x350
> [ 428.935022] [<ffffffff810f38f5>] run_timer_softirq+0x1a5/0x320
> [ 428.935022] [<ffffffff81096dc5>] __do_softirq+0xf5/0x2b0
> [ 428.935022] [<ffffffff810971fd>] irq_exit+0xbd/0xd0
> [ 428.935022] [<ffffffff8173b715>] smp_apic_timer_interrupt+0x45/0x60
> [ 428.935022] [<ffffffff8173981d>] apic_timer_interrupt+0x6d/0x80
> [ 428.935022] <EOI>
> [ 428.935022]
> [ 428.935022] [<ffffffff810581c6>] ? native_safe_halt+0x6/0x10
> [ 428.935022] [<ffffffff8101f36f>] default_idle+0x1f/0xe0
> [ 428.935022] [<ffffffff8101fd8f>] arch_cpu_idle+0xf/0x20
> [ 428.935022] [<ffffffff810d25dd>] cpu_startup_entry+0x38d/0x3c0
> [ 428.935022] [<ffffffff81722927>] rest_init+0x87/0x90
> [ 428.935022] [<ffffffff81d3510e>] start_kernel+0x482/0x4a3
> [ 428.935022] [<ffffffff81d34a85>] ? set_init_arg+0x53/0x53
> [ 428.935022] [<ffffffff81d34120>] ? early_idt_handlers+0x120/0x120
> [ 428.935022] [<ffffffff81d345ee>] x86_64_start_reservations+0x2a/0x2c
> [ 428.935022] [<ffffffff81d3473d>] x86_64_start_kernel+0x14d/0x170
> [ 428.935022] Code: 84 bb 01 00 00 a8 02 0f 85 eb 00 00 00 48 63 45 d4 49
> 8b 9e 08 01 00 00 48 03 1c c5 60 fa d0 81 4c 89 e7 e8 18 f5 ff ff 48 85 c0
> <48> 8b 3b 0f 84 7c 01 00 00 48 39 c7 0f 84 73 01 00 00 48 89 c7
> [ 428.935022] RIP [<ffffffff810aa37b>] __queue_work+0x7b/0x350
> [ 428.935022] RSP <ffff88005f003e28>
> [ 428.935022] CR2: 0000000000000000
>
> This is because it keeps trying to re-schedule even though the interface's
> memory has been freed.
>
Hmm, how do we handle this?
> While testing this I spotted another issue as well - Failed to build
> slave_arr message has been printed too many times because you print it in
> 3ad mode when there's no active aggregator (bond_3ad_get_active_agg_info
> check in bond_update_slave_arr) which leads to re-scheduling which also
> lead to a deadlock.
>
I think this can be corrected with pr_ratelimited() call.
>>>>> + arr = rtnl_dereference(bond->slave_arr);
>>>>> + if (arr) {
>>>>> + kfree_rcu(arr, rcu);
>>>>> + RCU_INIT_POINTER(bond->slave_arr, NULL);
>>>>> + }
>>>>> +
>>>>> list_del(&bond->bond_list);
>>>>>
>>>>> bond_debug_unregister(bond);
>>>>> diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
>>>>> index 98dc0d7ad731..4635b175256a 100644
>>>>> --- a/drivers/net/bonding/bonding.h
>>>>> +++ b/drivers/net/bonding/bonding.h
>>>>> @@ -177,6 +177,12 @@ struct slave {
>>>>> struct kobject kobj;
>>>>> };
>>>>>
>>>>> +struct bond_up_slave {
>>>>> + unsigned int count;
>>>>> + struct rcu_head rcu;
>>>>> + struct slave *arr[0];
>>>>> +};
>>>>> +
>>>>> /*
>>>>> * Link pseudo-state only used internally by monitors
>>>>> */
>>>>> @@ -191,6 +197,7 @@ struct bonding {
>>>>> struct slave __rcu *curr_active_slave;
>>>>> struct slave __rcu *current_arp_slave;
>>>>> struct slave __rcu *primary_slave;
>>>>> + struct bond_up_slave __rcu *slave_arr; /* Array of usable slaves */
>>>>> bool force_primary;
>>>>> s32 slave_cnt; /* never change this value outside the attach/detach wrappers */
>>>>> int (*recv_probe)(const struct sk_buff *, struct bonding *,
>>>>> @@ -220,6 +227,7 @@ struct bonding {
>>>>> struct delayed_work alb_work;
>>>>> struct delayed_work ad_work;
>>>>> struct delayed_work mcast_work;
>>>>> + struct delayed_work slave_arr_work;
>>>>> #ifdef CONFIG_DEBUG_FS
>>>>> /* debugging support via debugfs */
>>>>> struct dentry *debug_dir;
>>>>> @@ -531,6 +539,8 @@ const char *bond_slave_link_status(s8 link);
>>>>> struct bond_vlan_tag *bond_verify_device_path(struct net_device *start_dev,
>>>>> struct net_device *end_dev,
>>>>> int level);
>>>>> +int bond_update_slave_arr(struct bonding *bond, struct slave *skipslave);
>>>>> +void bond_slave_arr_work_rearm(struct bonding *bond);
>>>>>
>>>>> #ifdef CONFIG_PROC_FS
>>>>> void bond_create_proc_entry(struct bonding *bond);
>>>>>
>>>>
>>>> --
>>>> To unsubscribe from this list: send the line "unsubscribe netdev" in
>>>> the body of a message to majordomo@vger.kernel.org
>>>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>>>>
>>>
>
^ permalink raw reply
* Re: [PATCH net-next] tcp: avoid possible arithmetic overflows
From: Eric Dumazet @ 2014-09-20 19:55 UTC (permalink / raw)
To: Yuchung Cheng; +Cc: Joe Perches, David Miller, netdev, Neal Cardwell
In-Reply-To: <CAK6E8=etvB4MUBu96ck+7yucbFLgbxvEPJ3TM8X5qBsZLTnhDg@mail.gmail.com>
On Sat, 2014-09-20 at 12:46 -0700, Yuchung Cheng wrote:
> On Sat, Sep 20, 2014 at 11:01 AM, Joe Perches <joe@perches.com> wrote:
> > On Sat, 2014-09-20 at 10:19 -0700, Eric Dumazet wrote:
> >> From: Eric Dumazet <edumazet@google.com>
> >>
> >> icsk_rto is an 32bit field, and icsk_backoff can reach 15 by default,
> >> or more if some sysctl (eg tcp_retries2) are changed.
> >>
> >> Better use 64bit to perform icsk_rto << icsk_backoff operations
> >
> > Maybe better to use a helper function for this?
> >
> > something like:
> >
> > static inline u64 icsk_rto_backoff(const struct inet_connection_sock *icsk)
> > {
> > u64 when = (u64)icsk->icsk_rto;
> >
> > return when << icsk->icsk_backoff;
> > }
> Thanks for the fix Eric. I second Joe's idea to use a helper function.
>
Yep.
Given the timeout functions in the kernel use 'unsigned long', I prefer
to keep the u64 magic private to this helper.
I will probably use
static inline unsigned long icsk_rto_backoff(const struct inet_connection_sock *icsk)
{
u64 when = (u64)icsk->icsk_rto << icsk->icsk_backoff;
return min_t(u64, when, ~0UL);
}
On 64bit arches, the min_t() should be a nop.
^ permalink raw reply
* Re: [PATCH net-next] tcp: avoid possible arithmetic overflows
From: Yuchung Cheng @ 2014-09-20 19:46 UTC (permalink / raw)
To: Joe Perches; +Cc: Eric Dumazet, David Miller, netdev, Neal Cardwell
In-Reply-To: <1411236071.8612.6.camel@joe-AO725>
On Sat, Sep 20, 2014 at 11:01 AM, Joe Perches <joe@perches.com> wrote:
> On Sat, 2014-09-20 at 10:19 -0700, Eric Dumazet wrote:
>> From: Eric Dumazet <edumazet@google.com>
>>
>> icsk_rto is an 32bit field, and icsk_backoff can reach 15 by default,
>> or more if some sysctl (eg tcp_retries2) are changed.
>>
>> Better use 64bit to perform icsk_rto << icsk_backoff operations
>
> Maybe better to use a helper function for this?
>
> something like:
>
> static inline u64 icsk_rto_backoff(const struct inet_connection_sock *icsk)
> {
> u64 when = (u64)icsk->icsk_rto;
>
> return when << icsk->icsk_backoff;
> }
Thanks for the fix Eric. I second Joe's idea to use a helper function.
>
>> diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
> []
>> @@ -3208,9 +3208,12 @@ static void tcp_ack_probe(struct sock *sk)
>> * This function is not for random using!
>> */
>> } else {
>> + unsigned long when;
>> +
>> + when = min((u64)icsk->icsk_rto << icsk->icsk_backoff,
>> + (u64)TCP_RTO_MAX);
>
> Maybe:
> u32 when = (u32)min_t(u64, icsk_rto_backoff(icsk), TCP_RTO_MAX);
>
>
^ permalink raw reply
* Re: Fwd: [Bug 84661] New: tc filter show from iproute2 does not work correctly on 32bit x86
From: Anton Farygin @ 2014-09-20 17:54 UTC (permalink / raw)
To: Eric Dumazet, Daniel Borkmann
Cc: yangyingliang, rider, netdev, Anton V. Boyarshinov
In-Reply-To: <1411231830.26859.69.camel@edumazet-glaptop2.roam.corp.google.com>
On 20.09.2014 20:50, Eric Dumazet wrote:
> On Sat, 2014-09-20 at 17:39 +0200, Daniel Borkmann wrote:
>> [ Cc'ing Yang ]
>>
>> -------- Original Message --------
>> Subject: [Bug 84661] New: tc filter show from iproute2 does not work correctly on 32bit x86
>> Date: Tue, 16 Sep 2014 11:47:20 +0000
>> From: bugzilla-daemon@bugzilla.kernel.org
>> To: dborkman@redhat.com
>>
>> https://bugzilla.kernel.org/show_bug.cgi?id=84661
>>
>> Bug ID: 84661
>> Summary: tc filter show from iproute2 does not work correctly
>> on 32bit x86
>> Product: Networking
>> Version: 2.5
>> Kernel Version: 3.14.17 and 3.16.1
>> Hardware: All
>> OS: Linux
>> Tree: Mainline
>> Status: NEW
>> Severity: normal
>> Priority: P1
>> Component: Other
>> Assignee: shemminger@linux-foundation.org
>> Reporter: rider@altlinux.org
>> Regression: No
>>
>> i tested this issue with kernels 3.14.x and 3.16.x. and iproute2-3.16.0
>>
>> This commit:
>> https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/commit/?id=6a031f67c83aa175aedd10d4ae64750415ab57b0
>> breaking output tc filter show on 32bit systems.
>>
>> This simple script shows the difference in the work on i586 and x86_64:
>> -------------------------
>> #!/bin/sh
>> dev=$1
>> tc qdisc add dev $dev root handle 1: htb default fffe
>> tc filter add dev $dev parent 1:0 protocol ip pref 10 u32
>> tc filter add dev $dev parent 1:0 protocol ip pref 10 handle 100: u32 divisor 8
>> tc filter add dev $dev parent 1:0 protocol ip pref 10 u32 ht 800:: match ip dst
>> 10.21.10.0/21 hashkey mask 0x00000700 at 16 link 100:
>> tc filter show dev $dev
>> tc filter del dev $dev parent 1:0 protocol ip pref 10 u32 ht 800:: match ip dst
>> 10.21.10.0/21 hashkey mask 0x00000700 at 16 link 100:
>> tc qdisc del dev $dev root handle 1: htb default fffe
>> ---------------------------
>>
>> output on i586:
>>
>> #./script enp0s25
>> filter parent 1: protocol ip pref 10 u32
>> filter parent 1: protocol ip pref 10 u32 fh 100: ht divisor 8
>> filter parent 1: protocol ip pref 10 u32 fh 800: ht divisor 1
>>
>>
>> output on x86_64:
>> # ./script eth0
>> filter parent 1: protocol ip pref 10 u32
>> filter parent 1: protocol ip pref 10 u32 fh 100: ht divisor 8
>> filter parent 1: protocol ip pref 10 u32 fh 800: ht divisor 1
>> filter parent 1: protocol ip pref 10 u32 fh 800::800 order 2048 key ht 800 bkt
>> 0 link 100:
>> match 0a150800/fffff800 at 16
>> hash mask 00000700 at 16
>>
> I do not understand how a netem patch could change something for this
> case ?
No, Anton Boyarshinov checked this commit, and the problem is not in it.
the bug had appearedbetween the last and the first release of the 3.13
and 3.14 kernel.
^ permalink raw reply
* Re: [PATCH net-next] tcp: avoid possible arithmetic overflows
From: Joe Perches @ 2014-09-20 18:01 UTC (permalink / raw)
To: Eric Dumazet; +Cc: David Miller, netdev, Neal Cardwell, Yuchung Cheng
In-Reply-To: <1411233550.26859.76.camel@edumazet-glaptop2.roam.corp.google.com>
On Sat, 2014-09-20 at 10:19 -0700, Eric Dumazet wrote:
> From: Eric Dumazet <edumazet@google.com>
>
> icsk_rto is an 32bit field, and icsk_backoff can reach 15 by default,
> or more if some sysctl (eg tcp_retries2) are changed.
>
> Better use 64bit to perform icsk_rto << icsk_backoff operations
Maybe better to use a helper function for this?
something like:
static inline u64 icsk_rto_backoff(const struct inet_connection_sock *icsk)
{
u64 when = (u64)icsk->icsk_rto;
return when << icsk->icsk_backoff;
}
> diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
[]
> @@ -3208,9 +3208,12 @@ static void tcp_ack_probe(struct sock *sk)
> * This function is not for random using!
> */
> } else {
> + unsigned long when;
> +
> + when = min((u64)icsk->icsk_rto << icsk->icsk_backoff,
> + (u64)TCP_RTO_MAX);
Maybe:
u32 when = (u32)min_t(u64, icsk_rto_backoff(icsk), TCP_RTO_MAX);
^ permalink raw reply
* Re: [patch net-next v2 8/9] switchdev: introduce Netlink API
From: Jiri Pirko @ 2014-09-20 17:38 UTC (permalink / raw)
To: Scott Feldman
Cc: Roopa Prabhu, Jamal Hadi Salim, netdev, David Miller, Neil Horman,
Andy Gospodarek, Thomas Graf, dborkman, ogerlitz, jesse, pshelar,
azhou, Ben Hutchings, Stephen Hemminger, Jeff Kirsher,
Vlad Yasevich, Cong Wang, John Fastabend, Eric Dumazet,
Florian Fainelli, John W. Linville, dev, jasowang, ebiederm,
nicolas.dichtel, Sergey
In-Reply-To: <DCCFAE52-F4BD-4877-A6FC-3C3AE180E871@cumulusnetworks.com>
Sat, Sep 20, 2014 at 07:21:10PM CEST, sfeldma@cumulusnetworks.com wrote:
>
>On Sep 20, 2014, at 5:51 AM, Roopa Prabhu <roopa@cumulusnetworks.com> wrote:
>
>> On 9/20/14, 1:10 AM, Scott Feldman wrote:
>>> On Sep 19, 2014, at 8:41 PM, Roopa Prabhu <roopa@cumulusnetworks.com>
>>> wrote:
>>>
>>>
>>>> On 9/19/14, 8:49 AM, Jiri Pirko wrote:
>>>>
>>>>> Fri, Sep 19, 2014 at 05:25:48PM CEST, jhs@mojatatu.com
>>>>> wrote:
>>>>>
>>>>>> On 09/19/14 09:49, Jiri Pirko wrote:
>>>>>>
>>>>>>> This patch exposes switchdev API using generic Netlink.
>>>>>>> Example userspace utility is here:
>>>>>>>
>>>>>>> https://github.com/jpirko/switchdev
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>> Is this just a temporary test tool? Otherwise i dont see reason
>>>>>> for its existence (or the API that it feeds on).
>>>>>>
>>>>> Please read the conversation I had with Pravin and Jesse in v1 thread.
>>>>> Long story short they like to have the api separated from ovs datapath
>>>>> so ovs daemon can use it to directly communicate with driver. Also John
>>>>> Fastabend requested a way to work with driver flows without using ovs ->
>>>>> that was the original reason I created switchdev genl api.
>>>>>
>>>>> Regarding the "sw" tool, yes it is for testing purposes now. ovs daemon
>>>>> will use directly switchdev genl api.
>>>>>
>>>>> I hope I cleared this out.
>>>>>
>>>> We already have all the needed rtnetlink kernel api and userspace tools around it to support all
>>>> switching asic features. ie, the rtnetlink api is the switchdev api. We can do l2, l3, acl's with it.
>>>> Its unclear to me why we need another new netlink api. Which will mean none of the existing tools to
>>>> create bridges etc will work on a switchdev.
>>>> Which seems like going in the direction exactly opposite to what we had discussed earlier.
>>>>
>>> Existing rtnetlink isn’t available to swdev without some kind of snooping the echoes from the various kernel components (bridge, fib, etc). With swdev_flow, as Jiri has defined it, there is an additional conversion needed to bridge the gap (bad expression, I know) between rtnetlink and swdev_flow. This conversion happens in the kernel components. For example, the bridge module, still driven from userspace by existing rtnetlink, will formulate the necessary swdev_flow insert/remove calls to the swdev driver such that HW will offload the fwd path.
>>>
>>> You have:
>>> user -> rtnetlink -> kernel -> netlink echo -> [some process] -> [some driver] -> HW
>>>
>>>
>>> Jiri has:
>>> user -> rtnetlink -> kernel -> swdev_* -> swdev driver -> HW
>>>
>>>
>> Keeping the goal to not change or not add a new userspace API in mind,
>> I have :
>> user -> rtnetlink -> kernel -> ndo_op -> swdev driver -> HW
>>
>
>Then you have the same as Jiri, for the traditional L2/L3 case.
>
>> Jiri has:
>> user -> genl (newapi) -> kernel -> swdev_* -> swdev driver -> HW
>
>Jiri’s genl is for userspace apps that are talking rtnetlink, like OVS. It’s not a substitute for rtnetlink, it’s an alternative. The complete picture is:
Not an alternative, an addition.
>
>user -> swdev genl -----
> \
> \
> -------> kernel -> ndo_swdev_* -> swdev driver -> HW
> /
> /
>user -> rtnetlink ------
True is that, as Thomas pointed out, we can probably nest this into
rtnl_link messages. That might work.
^ permalink raw reply
* [RFC PATCH v1.1 2/2] fm10k: Add support for PTP
From: Alexander Duyck @ 2014-09-20 17:15 UTC (permalink / raw)
To: richardcochran; +Cc: netdev
In-Reply-To: <20140920170138.25490.40273.stgit@ahduyck-bv4.jf.intel.com>
This change adds support for the Linux PTP Hardware clock and timestamping
functionality provided by the hardware. There are actually two cases that
this timestamping is meant to support.
The first case would be an ordinary clock scenario. In this configuration
the host interface does not have access to BAR 4. However all of the host
interfaces should be locked into the same boundary clock region and as such
they are all on the same clock anyway. With this being the case they can
synchronize among themselves and only need to adjust the offset since they
are all on the same clock with the same frequency.
The second case is a boundary clock scenario. This is a special case and
would require both BAR 4 access, and a means of presenting a netdev per
boundary region. The current plan is to use DSA at some point in the
future to provide these interfaces, but the DSA portion is still under
development.
Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
CC: Richard Cochran <richardcochran@gmail.com>
---
v2: Moved use of container_of macro out of interface declaration.
Dropped use of timecounter and cyclecounter in favor of s64 offset.
Added comment on reason we cannot directly adjust the clock value.
Added comment on the math involved in determining max_adj.
Pushed code for reading systime register into previous patch.
drivers/net/ethernet/intel/fm10k/Makefile | 2
drivers/net/ethernet/intel/fm10k/fm10k.h | 37 ++
drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c | 30 +
drivers/net/ethernet/intel/fm10k/fm10k_main.c | 20 +
drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | 20 +
drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 112 +++++
drivers/net/ethernet/intel/fm10k/fm10k_ptp.c | 463 ++++++++++++++++++++++
7 files changed, 683 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/ethernet/intel/fm10k/fm10k_ptp.c
diff --git a/drivers/net/ethernet/intel/fm10k/Makefile b/drivers/net/ethernet/intel/fm10k/Makefile
index fbc0e09..08859dd 100644
--- a/drivers/net/ethernet/intel/fm10k/Makefile
+++ b/drivers/net/ethernet/intel/fm10k/Makefile
@@ -30,4 +30,4 @@ obj-$(CONFIG_FM10K) += fm10k.o
fm10k-objs := fm10k_main.o fm10k_common.o fm10k_pci.o \
fm10k_netdev.o fm10k_ethtool.o fm10k_pf.o fm10k_vf.o \
fm10k_mbx.o fm10k_iov.o fm10k_tlv.o \
- fm10k_debugfs.o fm10k_dcbnl.o
+ fm10k_debugfs.o fm10k_ptp.o fm10k_dcbnl.o
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k.h b/drivers/net/ethernet/intel/fm10k/fm10k.h
index 307335a..11da1f6 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k.h
+++ b/drivers/net/ethernet/intel/fm10k/fm10k.h
@@ -26,6 +26,9 @@
#include <linux/rtnetlink.h>
#include <linux/if_vlan.h>
#include <linux/pci.h>
+#include <linux/net_tstamp.h>
+#include <linux/clocksource.h>
+#include <linux/ptp_clock_kernel.h>
#include "fm10k_pf.h"
#include "fm10k_vf.h"
@@ -293,6 +296,7 @@ struct fm10k_intfc {
struct fm10k_hw_stats stats;
struct fm10k_hw hw;
u32 __iomem *uc_addr;
+ u32 __iomem *sw_addr;
u16 msg_enable;
u16 tx_ring_count;
u16 rx_ring_count;
@@ -314,6 +318,20 @@ struct fm10k_intfc {
struct dentry *dbg_intfc;
#endif /* CONFIG_DEBUG_FS */
+ struct ptp_clock_info ptp_caps;
+ struct ptp_clock *ptp_clock;
+
+ struct sk_buff_head ts_tx_skb_queue;
+ u32 tx_hwtstamp_timeouts;
+
+ struct hwtstamp_config ts_config;
+ /* We are unable to actually adjust the clock beyond the frequency
+ * value. Once the clock is started there is no resetting it. As
+ * such we maintain a separate offset from the actual hardware clock
+ * to allow for offset adjustment.
+ */
+ s64 ptp_adjust;
+ rwlock_t systime_lock;
#ifdef CONFIG_DCB
u8 pfc_en;
#endif
@@ -411,6 +429,10 @@ union fm10k_ftag_info {
};
struct fm10k_cb {
+ union {
+ __le64 tstamp;
+ unsigned long ts_tx_timeout;
+ };
union fm10k_ftag_info fi;
};
@@ -492,6 +514,21 @@ static inline void fm10k_dbg_init(void) {}
static inline void fm10k_dbg_exit(void) {}
#endif /* CONFIG_DEBUG_FS */
+/* Time Stamping */
+void fm10k_systime_to_hwtstamp(struct fm10k_intfc *interface,
+ struct skb_shared_hwtstamps *hwtstamp,
+ u64 systime);
+void fm10k_ts_tx_enqueue(struct fm10k_intfc *interface, struct sk_buff *skb);
+void fm10k_ts_tx_hwtstamp(struct fm10k_intfc *interface, __le16 dglort,
+ u64 systime);
+void fm10k_ts_reset(struct fm10k_intfc *interface);
+void fm10k_ts_init(struct fm10k_intfc *interface);
+void fm10k_ts_tx_subtask(struct fm10k_intfc *interface);
+void fm10k_ptp_register(struct fm10k_intfc *interface);
+void fm10k_ptp_unregister(struct fm10k_intfc *interface);
+int fm10k_get_ts_config(struct net_device *netdev, struct ifreq *ifr);
+int fm10k_set_ts_config(struct net_device *netdev, struct ifreq *ifr);
+
/* DCB */
void fm10k_dcbnl_set_ops(struct net_device *dev);
#endif /* _FM10K_H_ */
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
index 42beb89..a9bbe60 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
@@ -91,6 +91,8 @@ static const struct fm10k_stats fm10k_gstrings_stats[] = {
FM10K_STAT("mbx_rx_messages", hw.mbx.rx_messages),
FM10K_STAT("mbx_rx_dwords", hw.mbx.rx_dwords),
FM10K_STAT("mbx_rx_parse_err", hw.mbx.rx_parse_err),
+
+ FM10K_STAT("tx_hwtstamp_timeouts", tx_hwtstamp_timeouts),
};
#define FM10K_GLOBAL_STATS_LEN ARRAY_SIZE(fm10k_gstrings_stats)
@@ -1006,6 +1008,33 @@ static int fm10k_set_channels(struct net_device *dev,
return fm10k_setup_tc(dev, netdev_get_num_tc(dev));
}
+static int fm10k_get_ts_info(struct net_device *dev,
+ struct ethtool_ts_info *info)
+{
+ struct fm10k_intfc *interface = netdev_priv(dev);
+
+ info->so_timestamping =
+ SOF_TIMESTAMPING_TX_SOFTWARE |
+ SOF_TIMESTAMPING_RX_SOFTWARE |
+ SOF_TIMESTAMPING_SOFTWARE |
+ SOF_TIMESTAMPING_TX_HARDWARE |
+ SOF_TIMESTAMPING_RX_HARDWARE |
+ SOF_TIMESTAMPING_RAW_HARDWARE;
+
+ if (interface->ptp_clock)
+ info->phc_index = ptp_clock_index(interface->ptp_clock);
+ else
+ info->phc_index = -1;
+
+ info->tx_types = (1 << HWTSTAMP_TX_OFF) |
+ (1 << HWTSTAMP_TX_ON);
+
+ info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) |
+ (1 << HWTSTAMP_FILTER_ALL);
+
+ return 0;
+}
+
static const struct ethtool_ops fm10k_ethtool_ops = {
.get_strings = fm10k_get_strings,
.get_sset_count = fm10k_get_sset_count,
@@ -1031,6 +1060,7 @@ static const struct ethtool_ops fm10k_ethtool_ops = {
.set_rxfh = fm10k_set_rssh,
.get_channels = fm10k_get_channels,
.set_channels = fm10k_set_channels,
+ .get_ts_info = fm10k_get_ts_info,
};
void fm10k_set_ethtool_ops(struct net_device *dev)
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_main.c b/drivers/net/ethernet/intel/fm10k/fm10k_main.c
index 33da585..c8e15bd 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_main.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_main.c
@@ -398,6 +398,19 @@ static inline void fm10k_rx_hash(struct fm10k_ring *ring,
PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_L3);
}
+static void fm10k_rx_hwtstamp(struct fm10k_ring *rx_ring,
+ union fm10k_rx_desc *rx_desc,
+ struct sk_buff *skb)
+{
+ struct fm10k_intfc *interface = rx_ring->q_vector->interface;
+
+ FM10K_CB(skb)->tstamp = rx_desc->q.timestamp;
+
+ if (unlikely(interface->flags & FM10K_FLAG_RX_TS_ENABLED))
+ fm10k_systime_to_hwtstamp(interface, skb_hwtstamps(skb),
+ le64_to_cpu(rx_desc->q.timestamp));
+}
+
static void fm10k_type_trans(struct fm10k_ring *rx_ring,
union fm10k_rx_desc *rx_desc,
struct sk_buff *skb)
@@ -447,6 +460,8 @@ static unsigned int fm10k_process_skb_fields(struct fm10k_ring *rx_ring,
fm10k_rx_checksum(rx_ring, rx_desc, skb);
+ fm10k_rx_hwtstamp(rx_ring, rx_desc, skb);
+
FM10K_CB(skb)->fi.w.vlan = rx_desc->w.vlan;
skb_record_rx_queue(skb, rx_ring->queue_index);
@@ -885,6 +900,11 @@ static u8 fm10k_tx_desc_flags(struct sk_buff *skb, u32 tx_flags)
/* set type for advanced descriptor with frame checksum insertion */
u32 desc_flags = 0;
+ /* set timestamping bits */
+ if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) &&
+ likely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS))
+ desc_flags |= FM10K_TXD_FLAG_TIME;
+
/* set checksum offload bits */
desc_flags |= FM10K_SET_FLAG(tx_flags, FM10K_TX_FLAGS_CSUM,
FM10K_TXD_FLAG_CSUM);
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c
index 4fbe46c..a7f806c 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c
@@ -242,6 +242,9 @@ void fm10k_clean_all_tx_rings(struct fm10k_intfc *interface)
for (i = 0; i < interface->num_tx_queues; i++)
fm10k_clean_tx_ring(interface->tx_ring[i]);
+
+ /* remove any stale timestamp buffers and free them */
+ skb_queue_purge(&interface->ts_tx_skb_queue);
}
/**
@@ -650,6 +653,10 @@ static netdev_tx_t fm10k_xmit_frame(struct sk_buff *skb, struct net_device *dev)
__skb_put(skb, pad_len);
}
+ /* prepare packet for hardware time stamping */
+ if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP))
+ fm10k_ts_tx_enqueue(interface, skb);
+
if (r_idx >= interface->num_tx_queues)
r_idx %= interface->num_tx_queues;
@@ -1176,6 +1183,18 @@ int fm10k_setup_tc(struct net_device *dev, u8 tc)
return 0;
}
+static int fm10k_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
+{
+ switch (cmd) {
+ case SIOCGHWTSTAMP:
+ return fm10k_get_ts_config(netdev, ifr);
+ case SIOCSHWTSTAMP:
+ return fm10k_set_ts_config(netdev, ifr);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
static void fm10k_assign_l2_accel(struct fm10k_intfc *interface,
struct fm10k_l2_accel *l2_accel)
{
@@ -1344,6 +1363,7 @@ static const struct net_device_ops fm10k_netdev_ops = {
.ndo_get_vf_config = fm10k_ndo_get_vf_config,
.ndo_add_vxlan_port = fm10k_add_vxlan_port,
.ndo_del_vxlan_port = fm10k_del_vxlan_port,
+ .ndo_do_ioctl = fm10k_ioctl,
.ndo_dfwd_add_station = fm10k_dfwd_add_station,
.ndo_dfwd_del_station = fm10k_dfwd_del_station,
};
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c
index 77c2d36..b7514cc 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c
@@ -170,6 +170,9 @@ static void fm10k_reinit(struct fm10k_intfc *interface)
/* reassociate interrupts */
fm10k_mbx_request_irq(interface);
+ /* reset clock */
+ fm10k_ts_reset(interface);
+
if (netif_running(netdev))
fm10k_open(netdev);
@@ -490,6 +493,7 @@ static void fm10k_service_task(struct work_struct *work)
/* tasks only run when interface is up */
fm10k_watchdog_subtask(interface);
fm10k_check_hang_subtask(interface);
+ fm10k_ts_tx_subtask(interface);
/* release lock on service events to allow scheduling next event */
fm10k_service_event_complete(interface);
@@ -1064,6 +1068,25 @@ static s32 fm10k_mbx_mac_addr(struct fm10k_hw *hw, u32 **results,
return 0;
}
+static s32 fm10k_1588_msg_vf(struct fm10k_hw *hw, u32 **results,
+ struct fm10k_mbx_info *mbx)
+{
+ struct fm10k_intfc *interface;
+ u64 timestamp;
+ s32 err;
+
+ err = fm10k_tlv_attr_get_u64(results[FM10K_1588_MSG_TIMESTAMP],
+ ×tamp);
+ if (err)
+ return err;
+
+ interface = container_of(hw, struct fm10k_intfc, hw);
+
+ fm10k_ts_tx_hwtstamp(interface, 0, timestamp);
+
+ return 0;
+}
+
/* generic error handler for mailbox issues */
static s32 fm10k_mbx_error(struct fm10k_hw *hw, u32 **results,
struct fm10k_mbx_info *mbx)
@@ -1084,6 +1107,7 @@ static const struct fm10k_msg_data vf_mbx_data[] = {
FM10K_TLV_MSG_TEST_HANDLER(fm10k_tlv_msg_test),
FM10K_VF_MSG_MAC_VLAN_HANDLER(fm10k_mbx_mac_addr),
FM10K_VF_MSG_LPORT_STATE_HANDLER(fm10k_msg_lport_state_vf),
+ FM10K_VF_MSG_1588_HANDLER(fm10k_1588_msg_vf),
FM10K_TLV_MSG_ERROR_HANDLER(fm10k_mbx_error),
};
@@ -1181,6 +1205,68 @@ static s32 fm10k_update_pvid(struct fm10k_hw *hw, u32 **results,
return 0;
}
+static s32 fm10k_1588_msg_pf(struct fm10k_hw *hw, u32 **results,
+ struct fm10k_mbx_info *mbx)
+{
+ struct fm10k_swapi_1588_timestamp timestamp;
+ struct fm10k_iov_data *iov_data;
+ struct fm10k_intfc *interface;
+ u16 sglort, vf_idx;
+ s32 err;
+
+ err = fm10k_tlv_attr_get_le_struct(
+ results[FM10K_PF_ATTR_ID_1588_TIMESTAMP],
+ ×tamp, sizeof(timestamp));
+ if (err)
+ return err;
+
+ interface = container_of(hw, struct fm10k_intfc, hw);
+
+ if (timestamp.dglort) {
+ fm10k_ts_tx_hwtstamp(interface, timestamp.dglort,
+ le64_to_cpu(timestamp.egress));
+ return 0;
+ }
+
+ /* either dglort or sglort must be set */
+ if (!timestamp.sglort)
+ return FM10K_ERR_PARAM;
+
+ /* verify GLORT is at least one of the ones we own */
+ sglort = le16_to_cpu(timestamp.sglort);
+ if (!fm10k_glort_valid_pf(hw, sglort))
+ return FM10K_ERR_PARAM;
+
+ if (sglort == interface->glort) {
+ fm10k_ts_tx_hwtstamp(interface, 0,
+ le64_to_cpu(timestamp.ingress));
+ return 0;
+ }
+
+ /* if there is no iov_data then there is no mailboxes to process */
+ if (!ACCESS_ONCE(interface->iov_data))
+ return FM10K_ERR_PARAM;
+
+ rcu_read_lock();
+
+ /* notify VF if this timestamp belongs to it */
+ iov_data = interface->iov_data;
+ vf_idx = (hw->mac.dglort_map & FM10K_DGLORTMAP_NONE) - sglort;
+
+ if (!iov_data || vf_idx >= iov_data->num_vfs) {
+ err = FM10K_ERR_PARAM;
+ goto err_unlock;
+ }
+
+ err = hw->iov.ops.report_timestamp(hw, &iov_data->vf_info[vf_idx],
+ le64_to_cpu(timestamp.ingress));
+
+err_unlock:
+ rcu_read_unlock();
+
+ return err;
+}
+
static const struct fm10k_msg_data pf_mbx_data[] = {
FM10K_PF_MSG_ERR_HANDLER(XCAST_MODES, fm10k_msg_err_pf),
FM10K_PF_MSG_ERR_HANDLER(UPDATE_MAC_FWD_RULE, fm10k_msg_err_pf),
@@ -1188,6 +1274,7 @@ static const struct fm10k_msg_data pf_mbx_data[] = {
FM10K_PF_MSG_ERR_HANDLER(LPORT_CREATE, fm10k_msg_err_pf),
FM10K_PF_MSG_ERR_HANDLER(LPORT_DELETE, fm10k_msg_err_pf),
FM10K_PF_MSG_UPDATE_PVID_HANDLER(fm10k_update_pvid),
+ FM10K_PF_MSG_1588_TIMESTAMP_HANDLER(fm10k_1588_msg_pf),
FM10K_TLV_MSG_ERROR_HANDLER(fm10k_mbx_error),
};
@@ -1549,6 +1636,12 @@ static int fm10k_sw_init(struct fm10k_intfc *interface,
return -EIO;
}
+ /* assign BAR 4 resources for use with PTP */
+ if (fm10k_read_reg(hw, FM10K_CTRL) & FM10K_CTRL_BAR4_ALLOWED)
+ interface->sw_addr = ioremap(pci_resource_start(pdev, 4),
+ pci_resource_len(pdev, 4));
+ hw->sw_addr = interface->sw_addr;
+
/* Only the PF can support VXLAN and NVGRE offloads */
if (hw->mac.type != fm10k_mac_pf) {
netdev->hw_enc_features = 0;
@@ -1565,6 +1658,9 @@ static int fm10k_sw_init(struct fm10k_intfc *interface,
(unsigned long)interface);
INIT_WORK(&interface->service_task, fm10k_service_task);
+ /* Intitialize timestamp data */
+ fm10k_ts_init(interface);
+
/* set default ring sizes */
interface->tx_ring_count = FM10K_DEFAULT_TXD;
interface->rx_ring_count = FM10K_DEFAULT_RXD;
@@ -1716,6 +1812,9 @@ static int fm10k_probe(struct pci_dev *pdev,
/* stop all the transmit queues from transmitting until link is up */
netif_tx_stop_all_queues(netdev);
+ /* Register PTP interface */
+ fm10k_ptp_register(interface);
+
/* print bus type/speed/width info */
dev_info(&pdev->dev, "(PCI Express:%s Width: %s Payload: %s)\n",
(hw->bus.speed == fm10k_bus_speed_8000 ? "8.0GT/s" :
@@ -1747,6 +1846,8 @@ err_register:
err_mbx_interrupt:
fm10k_clear_queueing_scheme(interface);
err_sw_init:
+ if (interface->sw_addr)
+ iounmap(interface->sw_addr);
iounmap(interface->uc_addr);
err_ioremap:
free_netdev(netdev);
@@ -1780,6 +1881,9 @@ static void fm10k_remove(struct pci_dev *pdev)
if (netdev->reg_state == NETREG_REGISTERED)
unregister_netdev(netdev);
+ /* cleanup timestamp handling */
+ fm10k_ptp_unregister(interface);
+
/* release VFs */
fm10k_iov_disable(pdev);
@@ -1792,6 +1896,8 @@ static void fm10k_remove(struct pci_dev *pdev)
/* remove any debugfs interfaces */
fm10k_dbg_intfc_exit(interface);
+ if (interface->sw_addr)
+ iounmap(interface->sw_addr);
iounmap(interface->uc_addr);
free_netdev(netdev);
@@ -1848,6 +1954,9 @@ static int fm10k_resume(struct pci_dev *pdev)
/* reset statistics starting values */
hw->mac.ops.rebind_hw_stats(hw, &interface->stats);
+ /* reset clock */
+ fm10k_ts_reset(interface);
+
rtnl_lock();
err = fm10k_init_queueing_scheme(interface);
@@ -2004,6 +2113,9 @@ static void fm10k_io_resume(struct pci_dev *pdev)
/* reassociate interrupts */
fm10k_mbx_request_irq(interface);
+ /* reset clock */
+ fm10k_ts_reset(interface);
+
if (netif_running(netdev))
err = fm10k_open(netdev);
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ptp.c b/drivers/net/ethernet/intel/fm10k/fm10k_ptp.c
new file mode 100644
index 0000000..7822809
--- /dev/null
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_ptp.c
@@ -0,0 +1,463 @@
+/* Intel Ethernet Switch Host Interface Driver
+ * Copyright(c) 2013 - 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Contact Information:
+ * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ */
+
+#include <linux/ptp_classify.h>
+#include <linux/ptp_clock_kernel.h>
+
+#include "fm10k.h"
+
+#define FM10K_TS_TX_TIMEOUT (HZ * 15)
+
+void fm10k_systime_to_hwtstamp(struct fm10k_intfc *interface,
+ struct skb_shared_hwtstamps *hwtstamp,
+ u64 systime)
+{
+ unsigned long flags;
+
+ read_lock_irqsave(&interface->systime_lock, flags);
+ systime += interface->ptp_adjust;
+ read_unlock_irqrestore(&interface->systime_lock, flags);
+
+ hwtstamp->hwtstamp = ns_to_ktime(systime);
+}
+
+static struct sk_buff *fm10k_ts_tx_skb(struct fm10k_intfc *interface,
+ __le16 dglort)
+{
+ struct sk_buff_head *list = &interface->ts_tx_skb_queue;
+ struct sk_buff *skb;
+
+ skb_queue_walk(list, skb) {
+ if (FM10K_CB(skb)->fi.w.dglort == dglort)
+ return skb;
+ }
+
+ return NULL;
+}
+
+void fm10k_ts_tx_enqueue(struct fm10k_intfc *interface, struct sk_buff *skb)
+{
+ struct sk_buff_head *list = &interface->ts_tx_skb_queue;
+ struct sk_buff *clone;
+ unsigned long flags;
+ __le16 dglort;
+
+ /* create clone for us to return on the Tx path */
+ clone = skb_clone_sk(skb);
+ if (!clone)
+ return;
+
+ FM10K_CB(clone)->ts_tx_timeout = jiffies + FM10K_TS_TX_TIMEOUT;
+ dglort = FM10K_CB(clone)->fi.w.dglort;
+
+ spin_lock_irqsave(&list->lock, flags);
+
+ /* attempt to locate any buffers with the same dglort,
+ * if none are present then insert skb in tail of list
+ */
+ skb = fm10k_ts_tx_skb(interface, FM10K_CB(clone)->fi.w.dglort);
+ if (!skb)
+ __skb_queue_tail(list, clone);
+
+ spin_unlock_irqrestore(&list->lock, flags);
+
+ /* if list is already has one then we just free the clone */
+ if (skb)
+ kfree_skb(skb);
+ else
+ skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS;
+}
+
+void fm10k_ts_tx_hwtstamp(struct fm10k_intfc *interface, __le16 dglort,
+ u64 systime)
+{
+ struct skb_shared_hwtstamps shhwtstamps;
+ struct sk_buff_head *list = &interface->ts_tx_skb_queue;
+ struct sk_buff *skb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&list->lock, flags);
+
+ /* attempt to locate and pull the sk_buff out of the list */
+ skb = fm10k_ts_tx_skb(interface, dglort);
+ if (skb)
+ __skb_unlink(skb, list);
+
+ spin_unlock_irqrestore(&list->lock, flags);
+
+ /* if not found do nothing */
+ if (!skb)
+ return;
+
+ /* timestamp the sk_buff and return it to the socket */
+ fm10k_systime_to_hwtstamp(interface, &shhwtstamps, systime);
+ skb_complete_tx_timestamp(skb, &shhwtstamps);
+}
+
+void fm10k_ts_tx_subtask(struct fm10k_intfc *interface)
+{
+ struct sk_buff_head *list = &interface->ts_tx_skb_queue;
+ struct sk_buff *skb, *tmp;
+ unsigned long flags;
+
+ /* If we're down or resetting, just bail */
+ if (test_bit(__FM10K_DOWN, &interface->state) ||
+ test_bit(__FM10K_RESETTING, &interface->state))
+ return;
+
+ spin_lock_irqsave(&list->lock, flags);
+
+ /* walk though the list and flush any expired timestamp packets */
+ skb_queue_walk_safe(list, skb, tmp) {
+ if (!time_is_after_jiffies(FM10K_CB(skb)->ts_tx_timeout))
+ continue;
+ __skb_unlink(skb, list);
+ kfree_skb(skb);
+ interface->tx_hwtstamp_timeouts++;
+ }
+
+ spin_unlock_irqrestore(&list->lock, flags);
+}
+
+static u64 fm10k_systime_read(struct fm10k_intfc *interface)
+{
+ struct fm10k_hw *hw = &interface->hw;
+
+ return hw->mac.ops.read_systime(hw);
+}
+
+void fm10k_ts_reset(struct fm10k_intfc *interface)
+{
+ s64 ns = ktime_to_ns(ktime_get_real());
+ unsigned long flags;
+
+ /* reinitialize the clock */
+ write_lock_irqsave(&interface->systime_lock, flags);
+ interface->ptp_adjust = fm10k_systime_read(interface) - ns;
+ write_unlock_irqrestore(&interface->systime_lock, flags);
+}
+
+void fm10k_ts_init(struct fm10k_intfc *interface)
+{
+ /* Initialize lock protecting systime access */
+ rwlock_init(&interface->systime_lock);
+
+ /* Initialize skb queue for pending timestamp requests */
+ skb_queue_head_init(&interface->ts_tx_skb_queue);
+
+ /* reset the clock to current kernel time */
+ fm10k_ts_reset(interface);
+}
+
+/**
+ * fm10k_get_ts_config - get current hardware timestamping configuration
+ * @netdev: network interface device structure
+ * @ifreq: ioctl data
+ *
+ * This function returns the current timestamping settings. Rather than
+ * attempt to deconstruct registers to fill in the values, simply keep a copy
+ * of the old settings around, and return a copy when requested.
+ */
+int fm10k_get_ts_config(struct net_device *netdev, struct ifreq *ifr)
+{
+ struct fm10k_intfc *interface = netdev_priv(netdev);
+ struct hwtstamp_config *config = &interface->ts_config;
+
+ return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ?
+ -EFAULT : 0;
+}
+
+/**
+ * fm10k_set_ts_config - control hardware time stamping
+ * @netdev: network interface device structure
+ * @ifreq: ioctl data
+ *
+ * Outgoing time stamping can be enabled and disabled. Play nice and
+ * disable it when requested, although it shouldn't cause any overhead
+ * when no packet needs it. At most one packet in the queue may be
+ * marked for time stamping, otherwise it would be impossible to tell
+ * for sure to which packet the hardware time stamp belongs.
+ *
+ * Incoming time stamping has to be configured via the hardware
+ * filters. Not all combinations are supported, in particular event
+ * type has to be specified. Matching the kind of event packet is
+ * not supported, with the exception of "all V2 events regardless of
+ * level 2 or 4".
+ *
+ * Since hardware always timestamps Path delay packets when timestamping V2
+ * packets, regardless of the type specified in the register, only use V2
+ * Event mode. This more accurately tells the user what the hardware is going
+ * to do anyways.
+ */
+int fm10k_set_ts_config(struct net_device *netdev, struct ifreq *ifr)
+{
+ struct fm10k_intfc *interface = netdev_priv(netdev);
+ struct hwtstamp_config ts_config;
+
+ if (copy_from_user(&ts_config, ifr->ifr_data, sizeof(ts_config)))
+ return -EFAULT;
+
+ /* reserved for future extensions */
+ if (ts_config.flags)
+ return -EINVAL;
+
+ switch (ts_config.tx_type) {
+ case HWTSTAMP_TX_OFF:
+ break;
+ case HWTSTAMP_TX_ON:
+ /* we likely need some check here to see if this is supported */
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ switch (ts_config.rx_filter) {
+ case HWTSTAMP_FILTER_NONE:
+ interface->flags &= ~FM10K_FLAG_RX_TS_ENABLED;
+ break;
+ case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+ case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+ case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
+ case HWTSTAMP_FILTER_PTP_V2_EVENT:
+ case HWTSTAMP_FILTER_PTP_V2_SYNC:
+ case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+ case HWTSTAMP_FILTER_ALL:
+ interface->flags |= FM10K_FLAG_RX_TS_ENABLED;
+ ts_config.rx_filter = HWTSTAMP_FILTER_ALL;
+ break;
+ default:
+ return -ERANGE;
+ }
+
+ /* save these settings for future reference */
+ interface->ts_config = ts_config;
+
+ return copy_to_user(ifr->ifr_data, &ts_config, sizeof(ts_config)) ?
+ -EFAULT : 0;
+}
+
+static int fm10k_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
+{
+ struct fm10k_intfc *interface;
+ struct fm10k_hw *hw;
+ int err;
+
+ interface = container_of(ptp, struct fm10k_intfc, ptp_caps);
+ hw = &interface->hw;
+
+ err = hw->mac.ops.adjust_systime(hw, ppb);
+
+ /* the only error we should see is if the value is out of range */
+ return (err == FM10K_ERR_PARAM) ? -ERANGE : err;
+}
+
+static int fm10k_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
+{
+ struct fm10k_intfc *interface;
+ unsigned long flags;
+
+ interface = container_of(ptp, struct fm10k_intfc, ptp_caps);
+
+ write_lock_irqsave(&interface->systime_lock, flags);
+ interface->ptp_adjust += delta;
+ write_unlock_irqrestore(&interface->systime_lock, flags);
+
+ return 0;
+}
+
+static int fm10k_ptp_gettime(struct ptp_clock_info *ptp, struct timespec *ts)
+{
+ struct fm10k_intfc *interface;
+ unsigned long flags;
+ u64 now;
+
+ interface = container_of(ptp, struct fm10k_intfc, ptp_caps);
+
+ read_lock_irqsave(&interface->systime_lock, flags);
+ now = fm10k_systime_read(interface) + interface->ptp_adjust;
+ read_unlock_irqrestore(&interface->systime_lock, flags);
+
+ *ts = ns_to_timespec(now);
+
+ return 0;
+}
+
+static int fm10k_ptp_settime(struct ptp_clock_info *ptp,
+ const struct timespec *ts)
+{
+ struct fm10k_intfc *interface;
+ unsigned long flags;
+ u64 ns = timespec_to_ns(ts);
+
+ interface = container_of(ptp, struct fm10k_intfc, ptp_caps);
+
+ write_lock_irqsave(&interface->systime_lock, flags);
+ interface->ptp_adjust = fm10k_systime_read(interface) - ns;
+ write_unlock_irqrestore(&interface->systime_lock, flags);
+
+ return 0;
+}
+
+static int fm10k_ptp_enable(struct ptp_clock_info *ptp,
+ struct ptp_clock_request *rq, int on)
+{
+ struct ptp_clock_time *t = &rq->perout.period;
+ struct fm10k_intfc *interface;
+ struct fm10k_hw *hw;
+ u64 period;
+ u32 step;
+
+ /* we can only support periodic output */
+ if (rq->type != PTP_CLK_REQ_PEROUT)
+ return -EINVAL;
+
+ /* verify the requested channel is there */
+ if (rq->perout.index >= ptp->n_per_out)
+ return -EINVAL;
+
+ /* we cannot enforce start time as there is no
+ * mechanism for that in the hardware, we can only control
+ * the period.
+ */
+
+ /* we cannot support periods greater than 4 seconds due to reg limit */
+ if (t->sec > 4 || t->sec < 0)
+ return -ERANGE;
+
+ interface = container_of(ptp, struct fm10k_intfc, ptp_caps);
+ hw = &interface->hw;
+
+ /* we simply cannot support the operation if we don't have BAR4 */
+ if (!hw->sw_addr)
+ return -ENOTSUPP;
+
+ /* convert to unsigned 64b ns, verify we can put it in a 32b register */
+ period = t->sec * 1000000000LL + t->nsec;
+
+ /* determine the minimum size for period */
+ step = 2 * (fm10k_read_reg(hw, FM10K_SYSTIME_CFG) &
+ FM10K_SYSTIME_CFG_STEP_MASK);
+
+ /* verify the value is in range supported by hardware */
+ if ((period && (period < step)) || (period > U32_MAX))
+ return -ERANGE;
+
+ /* notify hardware of request to being sending pulses */
+ fm10k_write_sw_reg(hw, FM10K_SW_SYSTIME_PULSE(rq->perout.index),
+ (u32)period);
+
+ return 0;
+}
+
+static struct ptp_pin_desc fm10k_ptp_pd[2] = {
+ {
+ .name = "IEEE1588_PULSE0",
+ .index = 0,
+ .func = PTP_PF_PEROUT,
+ .chan = 0
+ },
+ {
+ .name = "IEEE1588_PULSE1",
+ .index = 1,
+ .func = PTP_PF_PEROUT,
+ .chan = 1
+ }
+};
+
+static int fm10k_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin,
+ enum ptp_pin_function func, unsigned int chan)
+{
+ /* verify the requested pin is there */
+ if (pin >= ptp->n_pins || !ptp->pin_config)
+ return -EINVAL;
+
+ /* enforce locked channels, no changing them */
+ if (chan != ptp->pin_config[pin].chan)
+ return -EINVAL;
+
+ /* we want to keep the functions locked as well */
+ if (func != ptp->pin_config[pin].func)
+ return -EINVAL;
+
+ return 0;
+}
+
+void fm10k_ptp_register(struct fm10k_intfc *interface)
+{
+ struct ptp_clock_info *ptp_caps = &interface->ptp_caps;
+ struct device *dev = &interface->pdev->dev;
+ struct ptp_clock *ptp_clock;
+
+ snprintf(ptp_caps->name, sizeof(ptp_caps->name),
+ "%s", interface->netdev->name);
+ ptp_caps->owner = THIS_MODULE;
+ /* This math is simply the inverse of the math in
+ * fm10k_adjust_systime_pf applied to an adjustment value
+ * of 2^30 - 1 which is the maximum value of the register:
+ * max_ppb == ((2^30 - 1) * 5^9) / 2^31
+ */
+ ptp_caps->max_adj = 976562;
+ ptp_caps->adjfreq = fm10k_ptp_adjfreq;
+ ptp_caps->adjtime = fm10k_ptp_adjtime;
+ ptp_caps->gettime = fm10k_ptp_gettime;
+ ptp_caps->settime = fm10k_ptp_settime;
+
+ /* provide pins if BAR4 is accessible */
+ if (interface->sw_addr) {
+ /* enable periodic outputs */
+ ptp_caps->n_per_out = 2;
+ ptp_caps->enable = fm10k_ptp_enable;
+
+ /* enable clock pins */
+ ptp_caps->verify = fm10k_ptp_verify;
+ ptp_caps->n_pins = 2;
+ ptp_caps->pin_config = fm10k_ptp_pd;
+ }
+
+ ptp_clock = ptp_clock_register(ptp_caps, dev);
+ if (IS_ERR(ptp_clock)) {
+ ptp_clock = NULL;
+ dev_err(dev, "ptp_clock_register failed\n");
+ } else {
+ dev_info(dev, "registered PHC device %s\n", ptp_caps->name);
+ }
+
+ interface->ptp_clock = ptp_clock;
+}
+
+void fm10k_ptp_unregister(struct fm10k_intfc *interface)
+{
+ struct ptp_clock *ptp_clock = interface->ptp_clock;
+ struct device *dev = &interface->pdev->dev;
+
+ if (!ptp_clock)
+ return;
+
+ interface->ptp_clock = NULL;
+
+ ptp_clock_unregister(ptp_clock);
+ dev_info(dev, "removed PHC %s\n", interface->ptp_caps.name);
+}
^ permalink raw reply related
* [RFC PATCH v1.1 1/2] fm10k: Add support for ptp to hw specific files
From: Alexander Duyck @ 2014-09-20 17:14 UTC (permalink / raw)
To: richardcochran; +Cc: netdev
In-Reply-To: <20140920170138.25490.40273.stgit@ahduyck-bv4.jf.intel.com>
This change adds the messaging support needed to support PTP. In the case
of Tx timestamps it is necessary for the Switch Management entity to return
the frames via the mailbox as the host interface cannot know which port the
timestamp will be delivered to. In addition there is only one clock on the
entire switch, as such the entity that has BAR 4 access is the only one who
can actually update the frequency as it is the only one with access.
Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
CC: Richard Cochran <richardcochran@gmail.com>
---
v2: Updated comment about how we compute the adjustment value.
Added read_systime function to MAC operations for PF and VF.
drivers/net/ethernet/intel/fm10k/fm10k_common.h | 8 ++
drivers/net/ethernet/intel/fm10k/fm10k_pf.c | 99 +++++++++++++++++++++++
drivers/net/ethernet/intel/fm10k/fm10k_type.h | 14 +++
drivers/net/ethernet/intel/fm10k/fm10k_vf.c | 55 +++++++++++++
drivers/net/ethernet/intel/fm10k/fm10k_vf.h | 10 ++
5 files changed, 186 insertions(+)
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_common.h b/drivers/net/ethernet/intel/fm10k/fm10k_common.h
index 8250e14..45e4e5b 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_common.h
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_common.h
@@ -39,6 +39,14 @@ do { \
writel((val), &hw_addr[(reg)]); \
} while (0)
+/* Switch register write operations, index using DWORDS */
+#define fm10k_write_sw_reg(hw, reg, val) \
+do { \
+ u32 __iomem *sw_addr = ACCESS_ONCE((hw)->sw_addr); \
+ if (!FM10K_REMOVED(sw_addr)) \
+ writel((val), &sw_addr[(reg)]); \
+} while (0)
+
/* read ctrl register which has no clear on read fields as PCIe flush */
#define fm10k_write_flush(hw) fm10k_read_reg((hw), FM10K_CTRL)
s32 fm10k_get_bus_info_generic(struct fm10k_hw *hw);
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c
index 0b6ce10..275423d 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_pf.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_pf.c
@@ -1123,6 +1123,19 @@ static void fm10k_iov_update_stats_pf(struct fm10k_hw *hw,
fm10k_update_hw_stats_q(hw, q, idx, qpp);
}
+static s32 fm10k_iov_report_timestamp_pf(struct fm10k_hw *hw,
+ struct fm10k_vf_info *vf_info,
+ u64 timestamp)
+{
+ u32 msg[4];
+
+ /* generate port state response to notify VF it is not ready */
+ fm10k_tlv_msg_init(msg, FM10K_VF_MSG_ID_1588);
+ fm10k_tlv_attr_put_u64(msg, FM10K_1588_MSG_TIMESTAMP, timestamp);
+
+ return vf_info->mbx.ops.enqueue_tx(hw, &vf_info->mbx, msg);
+}
+
/**
* fm10k_iov_msg_msix_pf - Message handler for MSI-X request from VF
* @hw: Pointer to hardware structure
@@ -1723,6 +1736,89 @@ s32 fm10k_msg_err_pf(struct fm10k_hw *hw, u32 **results,
return 0;
}
+const struct fm10k_tlv_attr fm10k_1588_timestamp_msg_attr[] = {
+ FM10K_TLV_ATTR_LE_STRUCT(FM10K_PF_ATTR_ID_1588_TIMESTAMP,
+ sizeof(struct fm10k_swapi_1588_timestamp)),
+ FM10K_TLV_ATTR_LAST
+};
+
+/* currently there is no shared 1588 timestamp handler */
+
+/**
+ * fm10k_adjust_systime_pf - Adjust systime frequency
+ * @hw: pointer to hardware structure
+ * @ppb: adjustment rate in parts per billion
+ *
+ * This function will adjust the SYSTIME_CFG register contained in BAR 4
+ * if this function is supported for BAR 4 access. The adjustment amount
+ * is based on the parts per billion value provided and adjusted to a
+ * value based on parts per 2^48 clock cycles.
+ *
+ * If adjustment is not supported or the requested value is too large
+ * we will return an error.
+ **/
+static s32 fm10k_adjust_systime_pf(struct fm10k_hw *hw, s32 ppb)
+{
+ u64 systime_adjust;
+
+ /* if sw_addr is not set we don't have switch register access */
+ if (!hw->sw_addr)
+ return ppb ? FM10K_ERR_PARAM : 0;
+
+ /* we must convert the value from parts per billion to parts per
+ * 2^48 cycles. In addition I have opted to only use the 30 most
+ * significant bits of the adjustment value as the 8 least
+ * significant bits are located in another register and represent
+ * a value significantly less than a part per billion, the result
+ * of dropping the 8 least significant bits is that the adjustment
+ * value is effectively multiplied by 2^8 when we write it.
+ *
+ * As a result of all this the math for this breaks down as follows:
+ * ppb / 10^9 == adjust * 2^8 / 2^48
+ * If we solve this for adjust, and simplify it comes out as:
+ * ppb * 2^31 / 5^9 == adjust
+ */
+ systime_adjust = (ppb < 0) ? -ppb : ppb;
+ systime_adjust <<= 31;
+ do_div(systime_adjust, 1953125);
+
+ /* verify the requested adjustment value is in range */
+ if (systime_adjust > FM10K_SW_SYSTIME_ADJUST_MASK)
+ return FM10K_ERR_PARAM;
+
+ if (ppb < 0)
+ systime_adjust |= FM10K_SW_SYSTIME_ADJUST_DIR_NEGATIVE;
+
+ fm10k_write_sw_reg(hw, FM10K_SW_SYSTIME_ADJUST, (u32)systime_adjust);
+
+ return 0;
+}
+
+/**
+ * fm10k_read_systime_pf - Reads value of systime registers
+ * @hw: pointer to the hardware structure
+ *
+ * Function reads the content of 2 registers, combined to represent a 64 bit
+ * value measured in nanosecods. In order to guarantee the value is accurate
+ * we check the 32 most significant bits both before and after reading the
+ * 32 least significant bits to verify they didn't change as we were reading
+ * the registers.
+ **/
+static u64 fm10k_read_systime_pf(struct fm10k_hw *hw)
+{
+ u32 systime_l, systime_h, systime_tmp;
+
+ systime_h = fm10k_read_reg(hw, FM10K_SYSTIME + 1);
+
+ do {
+ systime_tmp = systime_h;
+ systime_l = fm10k_read_reg(hw, FM10K_SYSTIME);
+ systime_h = fm10k_read_reg(hw, FM10K_SYSTIME + 1);
+ } while (systime_tmp != systime_h);
+
+ return ((u64)systime_h << 32) | systime_l;
+}
+
static const struct fm10k_msg_data fm10k_msg_data_pf[] = {
FM10K_PF_MSG_ERR_HANDLER(XCAST_MODES, fm10k_msg_err_pf),
FM10K_PF_MSG_ERR_HANDLER(UPDATE_MAC_FWD_RULE, fm10k_msg_err_pf),
@@ -1753,6 +1849,8 @@ static struct fm10k_mac_ops mac_ops_pf = {
.set_dma_mask = &fm10k_set_dma_mask_pf,
.get_fault = &fm10k_get_fault_pf,
.get_host_state = &fm10k_get_host_state_pf,
+ .adjust_systime = &fm10k_adjust_systime_pf,
+ .read_systime = &fm10k_read_systime_pf,
};
static struct fm10k_iov_ops iov_ops_pf = {
@@ -1764,6 +1862,7 @@ static struct fm10k_iov_ops iov_ops_pf = {
.set_lport = &fm10k_iov_set_lport_pf,
.reset_lport = &fm10k_iov_reset_lport_pf,
.update_stats = &fm10k_iov_update_stats_pf,
+ .report_timestamp = &fm10k_iov_report_timestamp_pf,
};
static s32 fm10k_get_invariants_pf(struct fm10k_hw *hw)
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_type.h b/drivers/net/ethernet/intel/fm10k/fm10k_type.h
index ecaf93a..280296f 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_type.h
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_type.h
@@ -224,6 +224,11 @@ struct fm10k_hw;
#define FM10K_STATS_LOOPBACK_DROP 0x3806
#define FM10K_STATS_NODESC_DROP 0x3807
+/* Timesync registers */
+#define FM10K_SYSTIME 0x3814
+#define FM10K_SYSTIME_CFG 0x3818
+#define FM10K_SYSTIME_CFG_STEP_MASK 0x0000000F
+
/* PCIe state registers */
#define FM10K_PHYADDR 0x381C
@@ -358,6 +363,12 @@ struct fm10k_hw;
#define FM10K_VFSYSTIME 0x00040
#define FM10K_VFITR(_n) ((_n) + 0x00060)
+/* Registers contained in BAR 4 for Switch management */
+#define FM10K_SW_SYSTIME_ADJUST 0x0224D
+#define FM10K_SW_SYSTIME_ADJUST_MASK 0x3FFFFFFF
+#define FM10K_SW_SYSTIME_ADJUST_DIR_NEGATIVE 0x80000000
+#define FM10K_SW_SYSTIME_PULSE(_n) ((_n) + 0x02252)
+
enum fm10k_int_source {
fm10k_int_Mailbox = 0,
fm10k_int_PCIeFault = 1,
@@ -524,6 +535,7 @@ struct fm10k_mac_ops {
s32 (*get_fault)(struct fm10k_hw *, int, struct fm10k_fault *);
void (*request_lport_map)(struct fm10k_hw *);
s32 (*adjust_systime)(struct fm10k_hw *, s32 ppb);
+ u64 (*read_systime)(struct fm10k_hw *);
};
enum fm10k_mac_type {
@@ -614,6 +626,7 @@ struct fm10k_iov_ops {
s32 (*set_lport)(struct fm10k_hw *, struct fm10k_vf_info *, u16, u8);
void (*reset_lport)(struct fm10k_hw *, struct fm10k_vf_info *);
void (*update_stats)(struct fm10k_hw *, struct fm10k_hw_stats_q *, u16);
+ s32 (*report_timestamp)(struct fm10k_hw *, struct fm10k_vf_info *, u64);
};
struct fm10k_iov_info {
@@ -637,6 +650,7 @@ struct fm10k_info {
struct fm10k_hw {
u32 __iomem *hw_addr;
+ u32 __iomem *sw_addr;
void *back;
struct fm10k_mac_info mac;
struct fm10k_bus_info bus;
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_vf.c b/drivers/net/ethernet/intel/fm10k/fm10k_vf.c
index 25c23fc..f0aa0f9 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_vf.c
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_vf.c
@@ -431,6 +431,13 @@ static s32 fm10k_update_xcast_mode_vf(struct fm10k_hw *hw, u16 glort, u8 mode)
return mbx->ops.enqueue_tx(hw, mbx, msg);
}
+const struct fm10k_tlv_attr fm10k_1588_msg_attr[] = {
+ FM10K_TLV_ATTR_U64(FM10K_1588_MSG_TIMESTAMP),
+ FM10K_TLV_ATTR_LAST
+};
+
+/* currently there is no shared 1588 timestamp handler */
+
/**
* fm10k_update_hw_stats_vf - Updates hardware related statistics of VF
* @hw: pointer to hardware structure
@@ -482,6 +489,52 @@ static s32 fm10k_configure_dglort_map_vf(struct fm10k_hw *hw,
return 0;
}
+/**
+ * fm10k_adjust_systime_vf - Adjust systime frequency
+ * @hw: pointer to hardware structure
+ * @ppb: adjustment rate in parts per billion
+ *
+ * This function takes an adjustment rate in parts per billion and will
+ * verify that this value is 0 as the VF cannot support adjusting the
+ * systime clock.
+ *
+ * If the ppb value is non-zero the return is ERR_PARAM else success
+ **/
+static s32 fm10k_adjust_systime_vf(struct fm10k_hw *hw, s32 ppb)
+{
+ /* The VF cannot adjust the clock frequency, however it should
+ * already have a syntonic clock with whichever host interface is
+ * running as the master for the host interface clock domain so
+ * there should be not frequency adjustment necessary.
+ */
+ return ppb ? FM10K_ERR_PARAM : 0;
+}
+
+/**
+ * fm10k_read_systime_vf - Reads value of systime registers
+ * @hw: pointer to the hardware structure
+ *
+ * Function reads the content of 2 registers, combined to represent a 64 bit
+ * value measured in nanosecods. In order to guarantee the value is accurate
+ * we check the 32 most significant bits both before and after reading the
+ * 32 least significant bits to verify they didn't change as we were reading
+ * the registers.
+ **/
+static u64 fm10k_read_systime_vf(struct fm10k_hw *hw)
+{
+ u32 systime_l, systime_h, systime_tmp;
+
+ systime_h = fm10k_read_reg(hw, FM10K_VFSYSTIME + 1);
+
+ do {
+ systime_tmp = systime_h;
+ systime_l = fm10k_read_reg(hw, FM10K_VFSYSTIME);
+ systime_h = fm10k_read_reg(hw, FM10K_VFSYSTIME + 1);
+ } while (systime_tmp != systime_h);
+
+ return ((u64)systime_h << 32) | systime_l;
+}
+
static const struct fm10k_msg_data fm10k_msg_data_vf[] = {
FM10K_TLV_MSG_TEST_HANDLER(fm10k_tlv_msg_test),
FM10K_VF_MSG_MAC_VLAN_HANDLER(fm10k_msg_mac_vlan_vf),
@@ -507,6 +560,8 @@ static struct fm10k_mac_ops mac_ops_vf = {
.rebind_hw_stats = &fm10k_rebind_hw_stats_vf,
.configure_dglort_map = &fm10k_configure_dglort_map_vf,
.get_host_state = &fm10k_get_host_state_generic,
+ .adjust_systime = &fm10k_adjust_systime_vf,
+ .read_systime = &fm10k_read_systime_vf,
};
static s32 fm10k_get_invariants_vf(struct fm10k_hw *hw)
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_vf.h b/drivers/net/ethernet/intel/fm10k/fm10k_vf.h
index 8e96ee5..06a99d7 100644
--- a/drivers/net/ethernet/intel/fm10k/fm10k_vf.h
+++ b/drivers/net/ethernet/intel/fm10k/fm10k_vf.h
@@ -29,6 +29,7 @@ enum fm10k_vf_tlv_msg_id {
FM10K_VF_MSG_ID_MSIX,
FM10K_VF_MSG_ID_MAC_VLAN,
FM10K_VF_MSG_ID_LPORT_STATE,
+ FM10K_VF_MSG_ID_1588,
FM10K_VF_MSG_ID_MAX,
};
@@ -48,6 +49,11 @@ enum fm10k_tlv_lport_state_attr_id {
FM10K_LPORT_STATE_MSG_MAX
};
+enum fm10k_tlv_1588_attr_id {
+ FM10K_1588_MSG_TIMESTAMP,
+ FM10K_1588_MSG_MAX
+};
+
#define FM10K_VF_MSG_MSIX_HANDLER(func) \
FM10K_MSG_HANDLER(FM10K_VF_MSG_ID_MSIX, NULL, func)
@@ -64,5 +70,9 @@ extern const struct fm10k_tlv_attr fm10k_lport_state_msg_attr[];
FM10K_MSG_HANDLER(FM10K_VF_MSG_ID_LPORT_STATE, \
fm10k_lport_state_msg_attr, func)
+extern const struct fm10k_tlv_attr fm10k_1588_msg_attr[];
+#define FM10K_VF_MSG_1588_HANDLER(func) \
+ FM10K_MSG_HANDLER(FM10K_VF_MSG_ID_1588, fm10k_1588_msg_attr, func)
+
extern struct fm10k_info fm10k_vf_info;
#endif /* _FM10K_VF_H */
^ permalink raw reply related
* [RFC PATCH v1.1 0/2] PTP related changes for fm10k
From: Alexander Duyck @ 2014-09-20 17:14 UTC (permalink / raw)
To: richardcochran; +Cc: netdev
I'm looking for feedback on what will be the v2 of PTP patches I will be
submitting for the fm10k driver. I thought I would spare the list the job
of reviewing the other 27 patches since any changes there are trivial.
I believe I have addressed most of the original concerns with it. The only
thing I wasn't able to specifically address is the fact that we cannot
change what the actual hardware clock value is. If I hadn't made it clear
before the problem is the hardware clock is shared between all PCIe
functions on the switch so stopping it or drastically altering the value
would significantly throw off any attempt at keeping the clock sane across
the 585 PCIe functions (9 PF, and 9x64 VFs) that this switch can support,
all of which share the same SYSTIME clock reference.
---
Alexander Duyck (2):
fm10k: Add support for ptp to hw specific files
fm10k: Add support for PTP
drivers/net/ethernet/intel/fm10k/Makefile | 2
drivers/net/ethernet/intel/fm10k/fm10k.h | 37 ++
drivers/net/ethernet/intel/fm10k/fm10k_common.h | 8
drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c | 30 +
drivers/net/ethernet/intel/fm10k/fm10k_main.c | 20 +
drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | 20 +
drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 112 +++++
drivers/net/ethernet/intel/fm10k/fm10k_pf.c | 99 +++++
drivers/net/ethernet/intel/fm10k/fm10k_ptp.c | 463 ++++++++++++++++++++++
drivers/net/ethernet/intel/fm10k/fm10k_type.h | 14 +
drivers/net/ethernet/intel/fm10k/fm10k_vf.c | 55 +++
drivers/net/ethernet/intel/fm10k/fm10k_vf.h | 10
12 files changed, 869 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/ethernet/intel/fm10k/fm10k_ptp.c
--
^ permalink raw reply
* Re: [patch net-next v2 8/9] switchdev: introduce Netlink API
From: Scott Feldman @ 2014-09-20 17:21 UTC (permalink / raw)
To: Roopa Prabhu
Cc: Jiri Pirko, Jamal Hadi Salim, netdev, David Miller, Neil Horman,
Andy Gospodarek, Thomas Graf, dborkman, ogerlitz, jesse, pshelar,
azhou, Ben Hutchings, Stephen Hemminger, Jeff Kirsher,
Vlad Yasevich, Cong Wang, John Fastabend, Eric Dumazet,
Florian Fainelli, John W. Linville, dev, jasowang, ebiederm,
nicolas.dichtel, Sergey Ryazanov <ryazanov
In-Reply-To: <541D784B.6030302@cumulusnetworks.com>
On Sep 20, 2014, at 5:51 AM, Roopa Prabhu <roopa@cumulusnetworks.com> wrote:
> On 9/20/14, 1:10 AM, Scott Feldman wrote:
>> On Sep 19, 2014, at 8:41 PM, Roopa Prabhu <roopa@cumulusnetworks.com>
>> wrote:
>>
>>
>>> On 9/19/14, 8:49 AM, Jiri Pirko wrote:
>>>
>>>> Fri, Sep 19, 2014 at 05:25:48PM CEST, jhs@mojatatu.com
>>>> wrote:
>>>>
>>>>> On 09/19/14 09:49, Jiri Pirko wrote:
>>>>>
>>>>>> This patch exposes switchdev API using generic Netlink.
>>>>>> Example userspace utility is here:
>>>>>>
>>>>>> https://github.com/jpirko/switchdev
>>>>>>
>>>>>>
>>>>>>
>>>>> Is this just a temporary test tool? Otherwise i dont see reason
>>>>> for its existence (or the API that it feeds on).
>>>>>
>>>> Please read the conversation I had with Pravin and Jesse in v1 thread.
>>>> Long story short they like to have the api separated from ovs datapath
>>>> so ovs daemon can use it to directly communicate with driver. Also John
>>>> Fastabend requested a way to work with driver flows without using ovs ->
>>>> that was the original reason I created switchdev genl api.
>>>>
>>>> Regarding the "sw" tool, yes it is for testing purposes now. ovs daemon
>>>> will use directly switchdev genl api.
>>>>
>>>> I hope I cleared this out.
>>>>
>>> We already have all the needed rtnetlink kernel api and userspace tools around it to support all
>>> switching asic features. ie, the rtnetlink api is the switchdev api. We can do l2, l3, acl's with it.
>>> Its unclear to me why we need another new netlink api. Which will mean none of the existing tools to
>>> create bridges etc will work on a switchdev.
>>> Which seems like going in the direction exactly opposite to what we had discussed earlier.
>>>
>> Existing rtnetlink isn’t available to swdev without some kind of snooping the echoes from the various kernel components (bridge, fib, etc). With swdev_flow, as Jiri has defined it, there is an additional conversion needed to bridge the gap (bad expression, I know) between rtnetlink and swdev_flow. This conversion happens in the kernel components. For example, the bridge module, still driven from userspace by existing rtnetlink, will formulate the necessary swdev_flow insert/remove calls to the swdev driver such that HW will offload the fwd path.
>>
>> You have:
>> user -> rtnetlink -> kernel -> netlink echo -> [some process] -> [some driver] -> HW
>>
>>
>> Jiri has:
>> user -> rtnetlink -> kernel -> swdev_* -> swdev driver -> HW
>>
>>
> Keeping the goal to not change or not add a new userspace API in mind,
> I have :
> user -> rtnetlink -> kernel -> ndo_op -> swdev driver -> HW
>
Then you have the same as Jiri, for the traditional L2/L3 case.
> Jiri has:
> user -> genl (newapi) -> kernel -> swdev_* -> swdev driver -> HW
Jiri’s genl is for userspace apps that are talking rtnetlink, like OVS. It’s not a substitute for rtnetlink, it’s an alternative. The complete picture is:
user -> swdev genl -----
\
\
-------> kernel -> ndo_swdev_* -> swdev driver -> HW
/
/
user -> rtnetlink ------
-scott
^ permalink raw reply
* [PATCH net-next] tcp: avoid possible arithmetic overflows
From: Eric Dumazet @ 2014-09-20 17:19 UTC (permalink / raw)
To: David Miller; +Cc: netdev, Neal Cardwell, Yuchung Cheng
From: Eric Dumazet <edumazet@google.com>
icsk_rto is an 32bit field, and icsk_backoff can reach 15 by default,
or more if some sysctl (eg tcp_retries2) are changed.
Better use 64bit to perform icsk_rto << icsk_backoff operations
From: Eric Dumazet <edumazet@google.com>
---
net/ipv4/tcp_input.c | 7 +++++--
net/ipv4/tcp_output.c | 13 ++++++-------
net/ipv4/tcp_timer.c | 5 +++--
3 files changed, 14 insertions(+), 11 deletions(-)
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 02fb66d4a018..1ea3847c62fc 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -3208,9 +3208,12 @@ static void tcp_ack_probe(struct sock *sk)
* This function is not for random using!
*/
} else {
+ unsigned long when;
+
+ when = min((u64)icsk->icsk_rto << icsk->icsk_backoff,
+ (u64)TCP_RTO_MAX);
inet_csk_reset_xmit_timer(sk, ICSK_TIME_PROBE0,
- min(icsk->icsk_rto << icsk->icsk_backoff, TCP_RTO_MAX),
- TCP_RTO_MAX);
+ when, TCP_RTO_MAX);
}
}
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 7f1280dcad57..2231b400f3ce 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -3279,6 +3279,7 @@ void tcp_send_probe0(struct sock *sk)
{
struct inet_connection_sock *icsk = inet_csk(sk);
struct tcp_sock *tp = tcp_sk(sk);
+ unsigned long when;
int err;
err = tcp_write_wakeup(sk);
@@ -3294,9 +3295,8 @@ void tcp_send_probe0(struct sock *sk)
if (icsk->icsk_backoff < sysctl_tcp_retries2)
icsk->icsk_backoff++;
icsk->icsk_probes_out++;
- inet_csk_reset_xmit_timer(sk, ICSK_TIME_PROBE0,
- min(icsk->icsk_rto << icsk->icsk_backoff, TCP_RTO_MAX),
- TCP_RTO_MAX);
+ when = min((u64)icsk->icsk_rto << icsk->icsk_backoff,
+ (u64)TCP_RTO_MAX);
} else {
/* If packet was not sent due to local congestion,
* do not backoff and do not remember icsk_probes_out.
@@ -3306,11 +3306,10 @@ void tcp_send_probe0(struct sock *sk)
*/
if (!icsk->icsk_probes_out)
icsk->icsk_probes_out = 1;
- inet_csk_reset_xmit_timer(sk, ICSK_TIME_PROBE0,
- min(icsk->icsk_rto << icsk->icsk_backoff,
- TCP_RESOURCE_PROBE_INTERVAL),
- TCP_RTO_MAX);
+ when = min((u64)icsk->icsk_rto << icsk->icsk_backoff,
+ (u64)TCP_RESOURCE_PROBE_INTERVAL);
}
+ inet_csk_reset_xmit_timer(sk, ICSK_TIME_PROBE0, when, TCP_RTO_MAX);
}
int tcp_rtx_synack(struct sock *sk, struct request_sock *req)
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index a339e7ba05a4..05e1d0723233 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -180,7 +180,7 @@ static int tcp_write_timeout(struct sock *sk)
retry_until = sysctl_tcp_retries2;
if (sock_flag(sk, SOCK_DEAD)) {
- const int alive = (icsk->icsk_rto < TCP_RTO_MAX);
+ const int alive = icsk->icsk_rto < TCP_RTO_MAX;
retry_until = tcp_orphan_retries(sk, alive);
do_reset = alive ||
@@ -294,7 +294,8 @@ static void tcp_probe_timer(struct sock *sk)
max_probes = sysctl_tcp_retries2;
if (sock_flag(sk, SOCK_DEAD)) {
- const int alive = ((icsk->icsk_rto << icsk->icsk_backoff) < TCP_RTO_MAX);
+ u64 exp_rto = (u64)icsk->icsk_rto << icsk->icsk_backoff;
+ const int alive = exp_rto < TCP_RTO_MAX;
max_probes = tcp_orphan_retries(sk, alive);
^ permalink raw reply related
* Re: Fwd: [Bug 84661] New: tc filter show from iproute2 does not work correctly on 32bit x86
From: Eric Dumazet @ 2014-09-20 16:50 UTC (permalink / raw)
To: Daniel Borkmann; +Cc: yangyingliang, rider, netdev
In-Reply-To: <541D9F9B.9040404@redhat.com>
On Sat, 2014-09-20 at 17:39 +0200, Daniel Borkmann wrote:
> [ Cc'ing Yang ]
>
> -------- Original Message --------
> Subject: [Bug 84661] New: tc filter show from iproute2 does not work correctly on 32bit x86
> Date: Tue, 16 Sep 2014 11:47:20 +0000
> From: bugzilla-daemon@bugzilla.kernel.org
> To: dborkman@redhat.com
>
> https://bugzilla.kernel.org/show_bug.cgi?id=84661
>
> Bug ID: 84661
> Summary: tc filter show from iproute2 does not work correctly
> on 32bit x86
> Product: Networking
> Version: 2.5
> Kernel Version: 3.14.17 and 3.16.1
> Hardware: All
> OS: Linux
> Tree: Mainline
> Status: NEW
> Severity: normal
> Priority: P1
> Component: Other
> Assignee: shemminger@linux-foundation.org
> Reporter: rider@altlinux.org
> Regression: No
>
> i tested this issue with kernels 3.14.x and 3.16.x. and iproute2-3.16.0
>
> This commit:
> https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/commit/?id=6a031f67c83aa175aedd10d4ae64750415ab57b0
> breaking output tc filter show on 32bit systems.
>
> This simple script shows the difference in the work on i586 and x86_64:
> -------------------------
> #!/bin/sh
> dev=$1
> tc qdisc add dev $dev root handle 1: htb default fffe
> tc filter add dev $dev parent 1:0 protocol ip pref 10 u32
> tc filter add dev $dev parent 1:0 protocol ip pref 10 handle 100: u32 divisor 8
> tc filter add dev $dev parent 1:0 protocol ip pref 10 u32 ht 800:: match ip dst
> 10.21.10.0/21 hashkey mask 0x00000700 at 16 link 100:
> tc filter show dev $dev
> tc filter del dev $dev parent 1:0 protocol ip pref 10 u32 ht 800:: match ip dst
> 10.21.10.0/21 hashkey mask 0x00000700 at 16 link 100:
> tc qdisc del dev $dev root handle 1: htb default fffe
> ---------------------------
>
> output on i586:
>
> #./script enp0s25
> filter parent 1: protocol ip pref 10 u32
> filter parent 1: protocol ip pref 10 u32 fh 100: ht divisor 8
> filter parent 1: protocol ip pref 10 u32 fh 800: ht divisor 1
>
>
> output on x86_64:
> # ./script eth0
> filter parent 1: protocol ip pref 10 u32
> filter parent 1: protocol ip pref 10 u32 fh 100: ht divisor 8
> filter parent 1: protocol ip pref 10 u32 fh 800: ht divisor 1
> filter parent 1: protocol ip pref 10 u32 fh 800::800 order 2048 key ht 800 bkt
> 0 link 100:
> match 0a150800/fffff800 at 16
> hash mask 00000700 at 16
>
I do not understand how a netem patch could change something for this
case ?
^ permalink raw reply
* Fwd: [Bug 84661] New: tc filter show from iproute2 does not work correctly on 32bit x86
From: Daniel Borkmann @ 2014-09-20 15:39 UTC (permalink / raw)
To: yangyingliang; +Cc: rider, netdev
In-Reply-To: <bug-84661-65011@https.bugzilla.kernel.org/>
[ Cc'ing Yang ]
-------- Original Message --------
Subject: [Bug 84661] New: tc filter show from iproute2 does not work correctly on 32bit x86
Date: Tue, 16 Sep 2014 11:47:20 +0000
From: bugzilla-daemon@bugzilla.kernel.org
To: dborkman@redhat.com
https://bugzilla.kernel.org/show_bug.cgi?id=84661
Bug ID: 84661
Summary: tc filter show from iproute2 does not work correctly
on 32bit x86
Product: Networking
Version: 2.5
Kernel Version: 3.14.17 and 3.16.1
Hardware: All
OS: Linux
Tree: Mainline
Status: NEW
Severity: normal
Priority: P1
Component: Other
Assignee: shemminger@linux-foundation.org
Reporter: rider@altlinux.org
Regression: No
i tested this issue with kernels 3.14.x and 3.16.x. and iproute2-3.16.0
This commit:
https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/commit/?id=6a031f67c83aa175aedd10d4ae64750415ab57b0
breaking output tc filter show on 32bit systems.
This simple script shows the difference in the work on i586 and x86_64:
-------------------------
#!/bin/sh
dev=$1
tc qdisc add dev $dev root handle 1: htb default fffe
tc filter add dev $dev parent 1:0 protocol ip pref 10 u32
tc filter add dev $dev parent 1:0 protocol ip pref 10 handle 100: u32 divisor 8
tc filter add dev $dev parent 1:0 protocol ip pref 10 u32 ht 800:: match ip dst
10.21.10.0/21 hashkey mask 0x00000700 at 16 link 100:
tc filter show dev $dev
tc filter del dev $dev parent 1:0 protocol ip pref 10 u32 ht 800:: match ip dst
10.21.10.0/21 hashkey mask 0x00000700 at 16 link 100:
tc qdisc del dev $dev root handle 1: htb default fffe
---------------------------
output on i586:
#./script enp0s25
filter parent 1: protocol ip pref 10 u32
filter parent 1: protocol ip pref 10 u32 fh 100: ht divisor 8
filter parent 1: protocol ip pref 10 u32 fh 800: ht divisor 1
output on x86_64:
# ./script eth0
filter parent 1: protocol ip pref 10 u32
filter parent 1: protocol ip pref 10 u32 fh 100: ht divisor 8
filter parent 1: protocol ip pref 10 u32 fh 800: ht divisor 1
filter parent 1: protocol ip pref 10 u32 fh 800::800 order 2048 key ht 800 bkt
0 link 100:
match 0a150800/fffff800 at 16
hash mask 00000700 at 16
--
You are receiving this mail because:
You are watching the assignee of the bug.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox