* [PATCH net-next v2 7/8] net: tun: extend link mode support to 48 bits
From: David Decotigny @ 2015-01-06 2:54 UTC (permalink / raw)
To: Amir Vadai, Florian Fainelli, netdev, linux-kernel, linux-api
Cc: David Decotigny, David S. Miller, Jason Wang, Michael S. Tsirkin,
Herbert Xu, Al Viro, Ben Hutchings, Masatake YAMATO, Xi Wang,
Neil Horman, WANG Cong, Flavio Leitner, Tom Gundersen, Jiri Pirko,
Vlad Yasevich, Eric W. Biederman, Saeed Mahameed, Venkata Duvvuru,
Govindarajulu Varadarajan
In-Reply-To: <1420512850-24699-1-git-send-email-ddecotig@gmail.com>
From: David Decotigny <decot@googlers.com>
Signed-off-by: David Decotigny <decot@googlers.com>
---
drivers/net/tun.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index c0df872..88f9078 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -2257,8 +2257,8 @@ static struct miscdevice tun_miscdev = {
static int tun_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
- cmd->supported = 0;
- cmd->advertising = 0;
+ ethtool_cmd_supported_set(cmd, 0);
+ ethtool_cmd_advertising_set(cmd, 0);
ethtool_cmd_speed_set(cmd, SPEED_10);
cmd->duplex = DUPLEX_FULL;
cmd->port = PORT_TP;
--
2.2.0.rc0.207.ga3a616c
^ permalink raw reply related
* Re: [net-next PATCH v1 01/11] net: flow_table: create interface for hw match/action tables
From: Simon Horman @ 2015-01-06 2:54 UTC (permalink / raw)
To: John Fastabend; +Cc: Thomas Graf, sfeldma, jiri, jhs, netdev, davem, andy
In-Reply-To: <20150106020514.GA24057@vergenet.net>
On Tue, Jan 06, 2015 at 11:05:14AM +0900, Simon Horman wrote:
> On Mon, Jan 05, 2015 at 05:19:26PM -0800, John Fastabend wrote:
> > On 01/05/2015 05:09 PM, Simon Horman wrote:
> > >On Mon, Jan 05, 2015 at 04:45:50PM -0800, John Fastabend wrote:
> > >>[...]
> > >>
> > >>>>>+/**
> > >>>>>+ * @struct net_flow_field_ref
> > >>>>>+ * @brief uniquely identify field as header:field tuple
> > >>>>>+ */
> > >>>>>+struct net_flow_field_ref {
> > >>>>>+ int instance;
> > >>>>>+ int header;
> > >>>>>+ int field;
> > >>>>>+ int mask_type;
> > >>>>>+ int type;
> > >>>>>+ union { /* Are these all the required data types */
> > >>>>>+ __u8 value_u8;
> > >>>>>+ __u16 value_u16;
> > >>>>>+ __u32 value_u32;
> > >>>>>+ __u64 value_u64;
> > >>>>>+ };
> > >>>>>+ union { /* Are these all the required data types */
> > >>>>>+ __u8 mask_u8;
> > >>>>>+ __u16 mask_u16;
> > >>>>>+ __u32 mask_u32;
> > >>>>>+ __u64 mask_u64;
> > >>>>>+ };
> > >>>>>+};
> > >>>>
> > >>>>Does it make sense to write this as follows?
> > >>>
> > >>>Yes. I'll make this update it helps make it clear value/mask pairs are
> > >>>needed.
> > >>>
> > >>>>
> > >>>>union {
> > >>>> struct {
> > >>>> __u8 value_u8;
> > >>>> __u8 mask_u8;
> > >>>> };
> > >>>> struct {
> > >>>> __u16 value_u16;
> > >>>> __u16 mask_u16;
> > >>>> };
> > >>>> ...
> > >>>>};
> > >>
> > >>Another thought is to pull this entirely out of the structure and hide
> > >>it from the UAPI so we can add more value/mask types as needed without
> > >>having to spin versions of net_flow_field_ref. On the other hand I've
> > >>been able to fit all my fields in these types so far and I can't think
> > >>of any additions we need at the moment.
> > >
> > >FWIW, I think it would be cleaner to break both field_ref and action_args
> > >out into attributes and not expose the structures to user-space. But
> > >perhaps there is an advantage to dealing with structures directly that
> > >I am missing.
> > >
> >
> > I came to the same conclusion just now as well. I'm reworking it now
> > for v2.
>
> Thanks.
>
> BTW, I think there are a few problems with net_flow_put_flow_action().
>
> I am not quite to the bottom of it but it seems that:
> * It loops over a->args[i] and then calls net_flow_put_act_types()
> which performs a similar loop. This outer-loop appears to be incorrect.
> * It passes a[i].args instead of a->args[i] to net_flow_put_act_types()
>
> I can post a fix once I've got it working to my satisfaction.
> But if you are reworking that code anyway perhaps it is easier for
> you to handle it then.
FWIW this got the current scheme working for me:
diff --git a/net/core/flow_table.c b/net/core/flow_table.c
index 5dbdc13..598afa2 100644
--- a/net/core/flow_table.c
+++ b/net/core/flow_table.c
@@ -946,7 +946,7 @@ static int net_flow_put_flow_action(struct sk_buff *skb,
struct net_flow_action *a)
{
struct nlattr *action, *sigs;
- int i, err = 0;
+ int err = 0;
action = nla_nest_start(skb, NET_FLOW_ACTION);
if (!action)
@@ -958,21 +958,19 @@ static int net_flow_put_flow_action(struct sk_buff *skb,
if (!a->args)
goto done;
- for (i = 0; a->args[i].type; i++) {
- sigs = nla_nest_start(skb, NET_FLOW_ACTION_ATTR_SIGNATURE);
- if (!sigs) {
- nla_nest_cancel(skb, action);
- return -EMSGSIZE;
- }
+ sigs = nla_nest_start(skb, NET_FLOW_ACTION_ATTR_SIGNATURE);
+ if (!sigs) {
+ nla_nest_cancel(skb, action);
+ return -EMSGSIZE;
+ }
- err = net_flow_put_act_types(skb, a[i].args);
- if (err) {
- nla_nest_cancel(skb, sigs);
- nla_nest_cancel(skb, action);
- return err;
- }
- nla_nest_end(skb, sigs);
+ err = net_flow_put_act_types(skb, a->args);
+ if (err) {
+ nla_nest_cancel(skb, sigs);
+ nla_nest_cancel(skb, action);
+ return err;
}
+ nla_nest_end(skb, sigs);
done:
nla_nest_end(skb, action);
@@ -1103,6 +1101,7 @@ static int net_flow_get_action(struct net_flow_action *a, struct nlattr *attr)
}
a->args[count] = *(struct net_flow_action_arg *)nla_data(args);
+ count++;
}
return 0;
}
^ permalink raw reply related
* [Question]when work == weight in net_rx_action
From: Dennis Chen @ 2015-01-06 3:28 UTC (permalink / raw)
To: netdev
in net_rx_action() function, I found that if the work done returned
from the n->poll() equal the weight, then it will enter the following
path:
if (unlikely(work == weight)) {
if (unlikely(napi_disable_pending(n))) {
local_irq_enable();
napi_complete(n);
local_irq_disable();
} else {
if (n->gro_list) {
/* flush too old packets
* If HZ < 1000, flush all packets.
*/
local_irq_enable();
napi_gro_flush(n, HZ >= 1000);
local_irq_disable();
}
list_move_tail(&n->poll_list, &sd->poll_list);
}
}
suppose the napi instance is not disabled and n->gro_list = NULL, so
this function just moves the napi instance node to the tail of the
sd->poll_list, because the context here is device interrupt disabled,
is there any chance for the net_rx_action will be called again?
--
Den
^ permalink raw reply
* Re: [net-next PATCH v1 01/11] net: flow_table: create interface for hw match/action tables
From: John Fastabend @ 2015-01-06 3:31 UTC (permalink / raw)
To: Simon Horman; +Cc: Thomas Graf, sfeldma, jiri, jhs, netdev, davem, andy
In-Reply-To: <20150106025456.GB24057@vergenet.net>
[...]
>>
>> BTW, I think there are a few problems with net_flow_put_flow_action().
>>
>> I am not quite to the bottom of it but it seems that:
>> * It loops over a->args[i] and then calls net_flow_put_act_types()
>> which performs a similar loop. This outer-loop appears to be incorrect.
>> * It passes a[i].args instead of a->args[i] to net_flow_put_act_types()
>>
>> I can post a fix once I've got it working to my satisfaction.
>> But if you are reworking that code anyway perhaps it is easier for
>> you to handle it then.
>
> FWIW this got the current scheme working for me:
>
Thanks Simon. I'll roll this in as well.
> diff --git a/net/core/flow_table.c b/net/core/flow_table.c
> index 5dbdc13..598afa2 100644
> --- a/net/core/flow_table.c
> +++ b/net/core/flow_table.c
> @@ -946,7 +946,7 @@ static int net_flow_put_flow_action(struct sk_buff *skb,
> struct net_flow_action *a)
> {
> struct nlattr *action, *sigs;
> - int i, err = 0;
> + int err = 0;
>
> action = nla_nest_start(skb, NET_FLOW_ACTION);
> if (!action)
> @@ -958,21 +958,19 @@ static int net_flow_put_flow_action(struct sk_buff *skb,
> if (!a->args)
> goto done;
>
> - for (i = 0; a->args[i].type; i++) {
> - sigs = nla_nest_start(skb, NET_FLOW_ACTION_ATTR_SIGNATURE);
> - if (!sigs) {
> - nla_nest_cancel(skb, action);
> - return -EMSGSIZE;
> - }
> + sigs = nla_nest_start(skb, NET_FLOW_ACTION_ATTR_SIGNATURE);
> + if (!sigs) {
> + nla_nest_cancel(skb, action);
> + return -EMSGSIZE;
> + }
>
> - err = net_flow_put_act_types(skb, a[i].args);
> - if (err) {
> - nla_nest_cancel(skb, sigs);
> - nla_nest_cancel(skb, action);
> - return err;
> - }
> - nla_nest_end(skb, sigs);
> + err = net_flow_put_act_types(skb, a->args);
> + if (err) {
> + nla_nest_cancel(skb, sigs);
> + nla_nest_cancel(skb, action);
> + return err;
> }
> + nla_nest_end(skb, sigs);
>
> done:
> nla_nest_end(skb, action);
> @@ -1103,6 +1101,7 @@ static int net_flow_get_action(struct net_flow_action *a, struct nlattr *attr)
> }
>
> a->args[count] = *(struct net_flow_action_arg *)nla_data(args);
> + count++;
> }
> return 0;
> }
>
--
John Fastabend Intel Corporation
^ permalink raw reply
* Re: [PATCH V2] net: eth: xgene: change APM X-Gene SoC platform ethernet to support ACPI
From: David Miller @ 2015-01-06 3:41 UTC (permalink / raw)
To: fkan; +Cc: patches, netdev, linux-kernel
In-Reply-To: <1420508576-10480-1-git-send-email-fkan@apm.com>
From: Feng Kan <fkan@apm.com>
Date: Mon, 5 Jan 2015 17:42:56 -0800
> This adds support for APM X-Gene ethernet driver to use ACPI table to derive
> ethernet driver parameter.
>
> Signed-off-by: Feng Kan <fkan@apm.com>
Applied.
^ permalink raw reply
* Re: [PATCH V2] net: eth: xgene: change APM X-Gene SoC platform ethernet to support ACPI
From: David Miller @ 2015-01-06 3:42 UTC (permalink / raw)
To: fkan; +Cc: patches, netdev, linux-kernel
In-Reply-To: <20150105.224137.894770670718334587.davem@davemloft.net>
From: David Miller <davem@davemloft.net>
Date: Mon, 05 Jan 2015 22:41:37 -0500 (EST)
> From: Feng Kan <fkan@apm.com>
> Date: Mon, 5 Jan 2015 17:42:56 -0800
>
>> This adds support for APM X-Gene ethernet driver to use ACPI table to derive
>> ethernet driver parameter.
>>
>> Signed-off-by: Feng Kan <fkan@apm.com>
>
> Applied.
Actually, reverted, this breaks the build:
drivers/net/ethernet/apm/xgene/xgene_enet_main.c:1031:28: warning: ‘xgene_enet_of_match’ defined but not used [-Wunused-variable]
static struct of_device_id xgene_enet_of_match[] = {
^
In file included from drivers/net/ethernet/apm/xgene/xgene_enet_main.h:32:0,
from drivers/net/ethernet/apm/xgene/xgene_enet_main.c:22:
include/linux/module.h:138:40: error: ‘__mod_of__xgene_enet_match_device_table’ aliased to undefined symbol ‘xgene_enet_match’
extern const struct type##_device_id __mod_##type##__##name##_device_table \
^
drivers/net/ethernet/apm/xgene/xgene_enet_main.c:1036:1: note: in expansion of macro ‘MODULE_DEVICE_TABLE’
MODULE_DEVICE_TABLE(of, xgene_enet_match);
^
^ permalink raw reply
* Re: [PATCH net-next v3 0/4] ip: Support checksum returned in csmg
From: David Miller @ 2015-01-06 3:47 UTC (permalink / raw)
To: therbert; +Cc: netdev
In-Reply-To: <1420494977-15026-1-git-send-email-therbert@google.com>
From: Tom Herbert <therbert@google.com>
Date: Mon, 5 Jan 2015 13:56:13 -0800
> This patch set allows the packet checksum for a datagram socket
> to be returned in csum data in recvmsg. This allows userspace
> to implement its own checksum over the data, for instance if an
> IP tunnel was be implemented in user space, the inner checksum
> could be validated.
>
> Changes in this patch set:
> - Move checksum conversion to inet_sock from udp_sock. This
> generalizes checksum conversion for use with other protocols.
> - Move IP cmsg constants to a header file and make processing
> of the flags more efficient in ip_cmsg_recv
> - Return checksum value in cmsg. This is specifically the unfolded
> 32 bit checksum of the full packet starting from the first byte
> returned in recvmsg
>
> Tested: Wrote a little server to get checksums in cmsg for UDP and
> verfied correct checksum is returned.
Series applied, thanks Tom.
^ permalink raw reply
* Re: [PATCH net-next v4] net: Do not call ndo_dflt_fdb_dump if ndo_fdb_dump is defined
From: David Miller @ 2015-01-06 3:52 UTC (permalink / raw)
To: hubert.sokolowski; +Cc: netdev, ray.kinsella
In-Reply-To: <54AAC9F1.9060409@intel.com>
From: Hubert Sokolowski <hubert.sokolowski@intel.com>
Date: Mon, 05 Jan 2015 17:29:21 +0000
> Add checking whether the call to ndo_dflt_fdb_dump is needed.
> It is not expected to call ndo_dflt_fdb_dump unconditionally
> by some drivers (i.e. qlcnic or macvlan) that defines
> own ndo_fdb_dump. Other drivers define own ndo_fdb_dump
> and don't want ndo_dflt_fdb_dump to be called at all.
> At the same time it is desirable to call the default dump
> function on a bridge device.
> Fix attributes that are passed to dev->netdev_ops->ndo_fdb_dump.
> Add extra checking in br_fdb_dump to avoid duplicate entries
> as now filter_dev can be NULL.
>
> Following tests for filtering have been performed before
> the change and after the patch was applied to make sure
> they are the same and it doesn't break the filtering algorithm.
...
> Signed-off-by: Hubert Sokolowski <hubert.sokolowski@intel.com>
Applied, thank you.
^ permalink raw reply
* Re: [PATCH net-next v3 0/6] net: allow setting congctl via routing table
From: David Miller @ 2015-01-06 3:55 UTC (permalink / raw)
To: dborkman; +Cc: hannes, fw, netdev
In-Reply-To: <1420498668-4660-1-git-send-email-dborkman@redhat.com>
From: Daniel Borkmann <dborkman@redhat.com>
Date: Mon, 5 Jan 2015 23:57:42 +0100
> This is the second part of our work and allows for setting the congestion
> control algorithm via routing table. For details, please see individual
> patches.
>
> Since patch 1 is a bug fix, we suggest applying patch 1 to net, and then
> merging net into net-next, for example, and following up with the remaining
> feature patches wrt dependencies.
>
> Joint work with Florian Westphal, suggested by Hannes Frederic Sowa.
>
> Patch for iproute2 is available under [1], but will be reposted with along
> with the man-page update when this set hits net-next.
>
> [1] http://patchwork.ozlabs.org/patch/418149/
Looks good, series applied, thanks!
^ permalink raw reply
* Re: [PATCH V2] net: eth: xgene: change APM X-Gene SoC platform ethernet to support ACPI
From: Feng Kan @ 2015-01-06 4:05 UTC (permalink / raw)
To: David Miller; +Cc: patches, netdev, linux-kernel@vger.kernel.org
In-Reply-To: <20150105.224235.1941007145649816230.davem@davemloft.net>
On Mon, Jan 5, 2015 at 7:42 PM, David Miller <davem@davemloft.net> wrote:
> From: David Miller <davem@davemloft.net>
> Date: Mon, 05 Jan 2015 22:41:37 -0500 (EST)
>
>> From: Feng Kan <fkan@apm.com>
>> Date: Mon, 5 Jan 2015 17:42:56 -0800
>>
>>> This adds support for APM X-Gene ethernet driver to use ACPI table to derive
>>> ethernet driver parameter.
>>>
>>> Signed-off-by: Feng Kan <fkan@apm.com>
>>
>> Applied.
>
> Actually, reverted, this breaks the build:
>
> drivers/net/ethernet/apm/xgene/xgene_enet_main.c:1031:28: warning: 'xgene_enet_of_match' defined but not used [-Wunused-variable]
> static struct of_device_id xgene_enet_of_match[] = {
> ^
> In file included from drivers/net/ethernet/apm/xgene/xgene_enet_main.h:32:0,
> from drivers/net/ethernet/apm/xgene/xgene_enet_main.c:22:
> include/linux/module.h:138:40: error: '__mod_of__xgene_enet_match_device_table' aliased to undefined symbol 'xgene_enet_match'
> extern const struct type##_device_id __mod_##type##__##name##_device_table \
> ^
> drivers/net/ethernet/apm/xgene/xgene_enet_main.c:1036:1: note: in expansion of macro 'MODULE_DEVICE_TABLE'
> MODULE_DEVICE_TABLE(of, xgene_enet_match);
> ^
I see, I initially tried on the net-next tree which did not build for
me, failed somewhere else but builds xgene enet ok.
Then I tested on net/master which builds fine for me.
Would you please point me to the branch you are using?
^ permalink raw reply
* Re: [PATCH V2] net: eth: xgene: change APM X-Gene SoC platform ethernet to support ACPI
From: David Miller @ 2015-01-06 4:10 UTC (permalink / raw)
To: fkan; +Cc: patches, netdev, linux-kernel
In-Reply-To: <CAL85gmCGK54yK3sThVhGsatMVUS8+76LRwdDt4w9PUfppMJ3og@mail.gmail.com>
From: Feng Kan <fkan@apm.com>
Date: Mon, 5 Jan 2015 20:05:28 -0800
> I see, I initially tried on the net-next tree which did not build for
> me, failed somewhere else but builds xgene enet ok.
> Then I tested on net/master which builds fine for me.
> Would you please point me to the branch you are using?
net-next
^ permalink raw reply
* Re: [net-next PATCH v1 01/11] net: flow_table: create interface for hw match/action tables
From: Scott Feldman @ 2015-01-06 5:25 UTC (permalink / raw)
To: John Fastabend
Cc: Thomas Graf, Jiří Pírko, Jamal Hadi Salim,
simon.horman@netronome.com, Netdev, David S. Miller,
Andy Gospodarek
In-Reply-To: <20141231194544.31070.30335.stgit@nitbit.x32>
Nice work John. Some nits inline...
On Wed, Dec 31, 2014 at 11:45 AM, John Fastabend
<john.fastabend@gmail.com> wrote:
>
> diff --git a/include/linux/if_flow.h b/include/linux/if_flow.h
> new file mode 100644
> index 0000000..1b6c1ea
> --- /dev/null
> +++ b/include/linux/if_flow.h
> @@ -0,0 +1,93 @@
> +/*
> + * include/linux/net/if_flow.h - Flow table interface for Switch devices
> + * Copyright (c) 2014 John Fastabend <john.r.fastabend@intel.com>
> + *
> + *
> + * 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".
> + *
> + * Author: John Fastabend <john.r.fastabend@intel.com>
> + */
> +
> +#ifndef _IF_FLOW_H
> +#define _IF_FLOW_H
> +
> +#include <uapi/linux/if_flow.h>
> +
> +/**
> + * @struct net_flow_header
> + * @brief defines a match (header/field) an endpoint can use
> + *
> + * @uid unique identifier for header
> + * @field_sz number of fields are in the set
> + * @fields the set of fields in the net_flow_header
> + */
> +struct net_flow_header {
> + char name[NET_FLOW_NAMSIZ];
> + int uid;
> + int field_sz;
> + struct net_flow_field *fields;
> +};
> +
> +/**
> + * @struct net_flow_action
> + * @brief a description of a endpoint defined action
> + *
> + * @name printable name
> + * @uid unique action identifier
> + * @types NET_FLOW_ACTION_TYPE_NULL terminated list of action types
s/types/args?
> + */
> +struct net_flow_action {
> + char name[NET_FLOW_NAMSIZ];
> + int uid;
> + struct net_flow_action_arg *args;
> +};
> +
> +/**
> + * @struct net_flow_table
> + * @brief define flow table with supported match/actions
> + *
> + * @uid unique identifier for table
> + * @source uid of parent table
Is parent table the table previous in the pipeline? If so, what if
you can get to table from N different parent tables, what goes in
source?
> + * @size max number of entries for table or -1 for unbounded
> + * @matches null terminated set of supported match types given by match uid
> + * @actions null terminated set of supported action types given by action uid
> + * @flows set of flows
> + */
> +struct net_flow_table {
> + char name[NET_FLOW_NAMSIZ];
> + int uid;
> + int source;
> + int size;
> + struct net_flow_field_ref *matches;
> + int *actions;
> +};
> +
> +/* net_flow_hdr_node: node in a header graph of header fields.
> + *
> + * @uid : unique id of the graph node
> + * @flwo_header_ref : identify the hdrs that can handled by this node
s/flwo_header_ref/hdrs?
> + * @net_flow_jump_table : give a case jump statement
s/net_flow_jump_table/jump
> + */
> +struct net_flow_hdr_node {
> + char name[NET_FLOW_NAMSIZ];
> + int uid;
> + int *hdrs;
> + struct net_flow_jump_table *jump;
> +};
> +
> +struct net_flow_tbl_node {
> + int uid;
> + __u32 flags;
> + struct net_flow_jump_table *jump;
> +};
> +#endif
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index 29c92ee..3c3c856 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -52,6 +52,11 @@
> #include <linux/neighbour.h>
> #include <uapi/linux/netdevice.h>
>
> +#ifdef CONFIG_NET_FLOW_TABLES
> +#include <linux/if_flow.h>
> +#include <uapi/linux/if_flow.h>
linux/if_flow.h already included uapi file
> +#endif
> +
> struct netpoll_info;
> struct device;
> struct phy_device;
> @@ -1186,6 +1191,13 @@ struct net_device_ops {
> int (*ndo_switch_port_stp_update)(struct net_device *dev,
> u8 state);
> #endif
> +#ifdef CONFIG_NET_FLOW_TABLES
> + struct net_flow_action **(*ndo_flow_get_actions)(struct net_device *dev);
> + struct net_flow_table **(*ndo_flow_get_tables)(struct net_device *dev);
> + struct net_flow_header **(*ndo_flow_get_headers)(struct net_device *dev);
> + struct net_flow_hdr_node **(*ndo_flow_get_hdr_graph)(struct net_device *dev);
hdr or header? pick one, probably hdr.
> + struct net_flow_tbl_node **(*ndo_flow_get_tbl_graph)(struct net_device *dev);
move this up next to get_tables
> +#endif
> };
>
> /**
> diff --git a/include/uapi/linux/if_flow.h b/include/uapi/linux/if_flow.h
> new file mode 100644
> index 0000000..2acdb38
> --- /dev/null
> +++ b/include/uapi/linux/if_flow.h
> @@ -0,0 +1,363 @@
> +/*
> + * include/uapi/linux/if_flow.h - Flow table interface for Switch devices
> + * Copyright (c) 2014 John Fastabend <john.r.fastabend@intel.com>
> + *
> + *
> + * 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".
> + *
> + * Author: John Fastabend <john.r.fastabend@intel.com>
> + */
> +
> +/* Netlink description:
> + *
> + * Table definition used to describe running tables. The following
> + * describes the netlink message returned from a flow API messages.
message?
> +
> +enum {
> + NET_FLOW_MASK_TYPE_UNSPEC,
> + NET_FLOW_MASK_TYPE_EXACT,
> + NET_FLOW_MASK_TYPE_LPM,
As discussed in another thread, need third mask type that's not LPM;
e.g. 0b0101.
> +#define NET_FLOW_TABLE_GRAPH_NODE_MAX (__NET_FLOW_TABLE_GRAPH_NODE_MAX - 1)
> +
> +enum {
> + NET_FLOW_TABLE_GRAPH_UNSPEC,
> + NET_FLOW_TABLE_GRAPH_NODE,
> + __NET_FLOW_TABLE_GRAPH_MAX,
> +};
> +#define NET_FLOW_TABLE_GRAPH_MAX (__NET_FLOW_TABLE_GRAPH_MAX - 1)
> +
> +enum {
> + NET_FLOW_IDENTIFIER_IFINDEX, /* net_device ifindex */
Maybe add an NET_FLOW_IDENTIFIER_UNSPEC so NET_FLOW_IDENTIFIER_IFINDEX
isn't zero.
> diff --git a/net/Kconfig b/net/Kconfig
> index ff9ffc1..8380bfe 100644
> --- a/net/Kconfig
> +++ b/net/Kconfig
> @@ -293,6 +293,13 @@ config NET_FLOW_LIMIT
> with many clients some protection against DoS by a single (spoofed)
> flow that greatly exceeds average workload.
>
> +config NET_FLOW_TABLES
> + boolean "Support network flow tables"
> + ---help---
> + This feature provides an interface for device drivers to report
> + flow tables and supported matches and actions. If you do not
> + want to support hardware offloads for flow tables, say N here.
> +
> menu "Network testing"
>
> config NET_PKTGEN
> diff --git a/net/core/Makefile b/net/core/Makefile
> index 235e6c5..1eea785 100644
> --- a/net/core/Makefile
> +++ b/net/core/Makefile
> @@ -23,3 +23,4 @@ obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += timestamping.o
> obj-$(CONFIG_NET_PTP_CLASSIFY) += ptp_classifier.o
> obj-$(CONFIG_CGROUP_NET_PRIO) += netprio_cgroup.o
> obj-$(CONFIG_CGROUP_NET_CLASSID) += netclassid_cgroup.o
> +obj-$(CONFIG_NET_FLOW_TABLES) += flow_table.o
> diff --git a/net/core/flow_table.c b/net/core/flow_table.c
> new file mode 100644
> index 0000000..ec3f06d
> --- /dev/null
> +++ b/net/core/flow_table.c
> @@ -0,0 +1,837 @@
> +/*
> + * include/uapi/linux/if_flow.h - Flow table interface for Switch devices
> + * Copyright (c) 2014 John Fastabend <john.r.fastabend@intel.com>
> + *
> + *
> + * 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".
> + *
> + * Author: John Fastabend <john.r.fastabend@intel.com>
> + */
> +
> +#include <uapi/linux/if_flow.h>
> +#include <linux/if_flow.h>
> +#include <linux/if_bridge.h>
> +#include <linux/types.h>
> +#include <net/netlink.h>
> +#include <net/genetlink.h>
> +#include <net/rtnetlink.h>
> +#include <linux/module.h>
> +
> +static struct genl_family net_flow_nl_family = {
> + .id = GENL_ID_GENERATE,
> + .name = NET_FLOW_GENL_NAME,
> + .version = NET_FLOW_GENL_VERSION,
> + .maxattr = NET_FLOW_MAX,
> + .netnsok = true,
> +};
> +
> +static struct net_device *net_flow_get_dev(struct genl_info *info)
> +{
> + struct net *net = genl_info_net(info);
> + int type, ifindex;
> +
> + if (!info->attrs[NET_FLOW_IDENTIFIER_TYPE] ||
> + !info->attrs[NET_FLOW_IDENTIFIER])
> + return NULL;
> +
> + type = nla_get_u32(info->attrs[NET_FLOW_IDENTIFIER_TYPE]);
> + switch (type) {
> + case NET_FLOW_IDENTIFIER_IFINDEX:
> + ifindex = nla_get_u32(info->attrs[NET_FLOW_IDENTIFIER]);
> + break;
> + default:
> + return NULL;
> + }
> +
> + return dev_get_by_index(net, ifindex);
> +}
> +
> +static int net_flow_put_act_types(struct sk_buff *skb,
> + struct net_flow_action_arg *args)
> +{
> + int i, err;
> +
> + for (i = 0; args[i].type; i++) {
> + err = nla_put(skb, NET_FLOW_ACTION_ARG,
> + sizeof(struct net_flow_action_arg), &args[i]);
> + if (err)
> + return -EMSGSIZE;
> + }
> + return 0;
> +}
> +
> +static const
> +struct nla_policy net_flow_action_policy[NET_FLOW_ACTION_ATTR_MAX + 1] = {
> + [NET_FLOW_ACTION_ATTR_NAME] = {.type = NLA_STRING,
> + .len = NET_FLOW_NAMSIZ-1 },
> + [NET_FLOW_ACTION_ATTR_UID] = {.type = NLA_U32 },
> + [NET_FLOW_ACTION_ATTR_SIGNATURE] = {.type = NLA_NESTED },
> +};
> +
> +static int net_flow_put_action(struct sk_buff *skb, struct net_flow_action *a)
> +{
> + struct net_flow_action_arg *this;
> + struct nlattr *nest;
> + int err, args = 0;
> +
> + if (a->name && nla_put_string(skb, NET_FLOW_ACTION_ATTR_NAME, a->name))
> + return -EMSGSIZE;
> +
> + if (nla_put_u32(skb, NET_FLOW_ACTION_ATTR_UID, a->uid))
> + return -EMSGSIZE;
> +
> + if (!a->args)
> + return 0;
> +
> + for (this = &a->args[0]; strlen(this->name) > 0; this++)
> + args++;
> +
Since you only need to know that there are > 0 args, but don't need
the actual count, can you simplify test with something like:
bool has_args = strlen(a->args->name) > 0;
or
bool has_args = !!a->args->type;
> + if (args) {
> + nest = nla_nest_start(skb, NET_FLOW_ACTION_ATTR_SIGNATURE);
> + if (!nest)
> + goto nest_put_failure;
Maybe just return -EMSGSIZE here and skip goto.
> +
> + err = net_flow_put_act_types(skb, a->args);
> + if (err) {
> + nla_nest_cancel(skb, nest);
> + return err;
> + }
> + nla_nest_end(skb, nest);
> + }
> +
> + return 0;
> +nest_put_failure:
> + return -EMSGSIZE;
> +}
> +
> +static int net_flow_put_actions(struct sk_buff *skb,
> + struct net_flow_action **acts)
> +{
> + struct nlattr *actions;
> + int err, i;
> +
> + actions = nla_nest_start(skb, NET_FLOW_ACTIONS);
> + if (!actions)
> + return -EMSGSIZE;
> +
> + for (i = 0; acts[i]->uid; i++) {
Using for(act = acts; act->udi; act++) will make code a little nicer.
> + struct nlattr *action = nla_nest_start(skb, NET_FLOW_ACTION);
> +
> + if (!action)
> + goto action_put_failure;
> +
> + err = net_flow_put_action(skb, acts[i]);
> + if (err)
> + goto action_put_failure;
> + nla_nest_end(skb, action);
> + }
> + nla_nest_end(skb, actions);
> +
> + return 0;
> +action_put_failure:
> + nla_nest_cancel(skb, actions);
> + return -EMSGSIZE;
> +}
> +
> +struct sk_buff *net_flow_build_actions_msg(struct net_flow_action **a,
> + struct net_device *dev,
> + u32 portid, int seq, u8 cmd)
> +{
> + struct genlmsghdr *hdr;
> + struct sk_buff *skb;
> + int err = -ENOBUFS;
> +
> + skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
> + if (!skb)
> + return ERR_PTR(-ENOBUFS);
> +
> + hdr = genlmsg_put(skb, portid, seq, &net_flow_nl_family, 0, cmd);
> + if (!hdr)
> + goto out;
> +
> + if (nla_put_u32(skb,
> + NET_FLOW_IDENTIFIER_TYPE,
> + NET_FLOW_IDENTIFIER_IFINDEX) ||
> + nla_put_u32(skb, NET_FLOW_IDENTIFIER, dev->ifindex)) {
> + err = -ENOBUFS;
> + goto out;
> + }
> +
> + err = net_flow_put_actions(skb, a);
> + if (err < 0)
> + goto out;
> +
> + err = genlmsg_end(skb, hdr);
> + if (err < 0)
> + goto out;
> +
> + return skb;
> +out:
> + nlmsg_free(skb);
> + return ERR_PTR(err);
> +}
> +
> +static int net_flow_cmd_get_actions(struct sk_buff *skb,
> + struct genl_info *info)
> +{
> + struct net_flow_action **a;
> + struct net_device *dev;
> + struct sk_buff *msg;
> +
> + dev = net_flow_get_dev(info);
> + if (!dev)
> + return -EINVAL;
> +
> + if (!dev->netdev_ops->ndo_flow_get_actions) {
> + dev_put(dev);
> + return -EOPNOTSUPP;
> + }
> +
> + a = dev->netdev_ops->ndo_flow_get_actions(dev);
> + if (!a)
> + return -EBUSY;
Is it assumed ndo_flow_get_actions() returns a pointer to a static
list of actions? What if the device wants to give up a dynamic list
of actions? I'm trying to understand the lifetime of pointer 'a'.
What would cause -EBUSY condition?
> +
> + msg = net_flow_build_actions_msg(a, dev,
> + info->snd_portid,
> + info->snd_seq,
> + NET_FLOW_TABLE_CMD_GET_ACTIONS);
> + dev_put(dev);
> +
> + if (IS_ERR(msg))
> + return PTR_ERR(msg);
> +
> + return genlmsg_reply(msg, info);
> +}
> +
> +static int net_flow_put_table(struct net_device *dev,
> + struct sk_buff *skb,
> + struct net_flow_table *t)
> +{
> + struct nlattr *matches, *actions;
> + int i;
> +
> + if (nla_put_string(skb, NET_FLOW_TABLE_ATTR_NAME, t->name) ||
> + nla_put_u32(skb, NET_FLOW_TABLE_ATTR_UID, t->uid) ||
> + nla_put_u32(skb, NET_FLOW_TABLE_ATTR_SOURCE, t->source) ||
> + nla_put_u32(skb, NET_FLOW_TABLE_ATTR_SIZE, t->size))
> + return -EMSGSIZE;
> +
> + matches = nla_nest_start(skb, NET_FLOW_TABLE_ATTR_MATCHES);
> + if (!matches)
> + return -EMSGSIZE;
> +
> + for (i = 0; t->matches[i].instance; i++)
pointer-based loop better than i-based? my personal preference, i guess.
> + nla_put(skb, NET_FLOW_FIELD_REF,
> + sizeof(struct net_flow_field_ref),
> + &t->matches[i]);
> + nla_nest_end(skb, matches);
> +
^ permalink raw reply
* [PATCH v4 net-net 0/2] Increase the limit of tuntap queues
From: Pankaj Gupta @ 2015-01-06 5:39 UTC (permalink / raw)
To: linux-kernel, netdev
Cc: davem, jasowang, mst, dgibson, vfalico, edumazet, vyasevic, hkchu,
wuzhy, xemul, therbert, bhutchings, xii, stephen, jiri,
sergei.shtylyov, Pankaj Gupta
Networking under KVM works best if we allocate a per-vCPU rx and tx
queue in a virtual NIC. This requires a per-vCPU queue on the host side.
Modern physical NICs have multiqueue support for large number of queues.
To scale vNIC to run multiple queues parallel to maximum number of vCPU's
we need to increase number of queues support in tuntap.
Changes from v3:
PATCH1: Michael.S.Tsirkin - Some cleanups and updated commit message.
Changes from v2:
PATCH 3: David Miller - flex array adds extra level of indirection
for preallocated array.(dropped, as flow array
is allocated using kzalloc with failover to zalloc).
Changes from v1:
PATCH 2: David Miller - sysctl changes to limit number of queues
not required for unprivileged users(dropped).
Changes from RFC
PATCH 1: Sergei Shtylyov - Add an empty line after declarations.
PATCH 2: Jiri Pirko - Do not introduce new module paramaters.
Michael.S.Tsirkin- We can use sysctl for limiting max number
of queues.
This series is to increase the number of tuntap queues. Original work is being
done by 'jasowang@redhat.com'. I am taking this 'https://lkml.org/lkml/2013/6/19/29'
patch series as a reference. As per discussion in the patch series:
There were two reasons which prevented us from increasing number of tun queues:
- The netdev_queue array in netdevice were allocated through kmalloc, which may
cause a high order memory allocation too when we have several queues.
E.g. sizeof(netdev_queue) is 320, which means a high order allocation would
happens when the device has more than 16 queues.
- We store the hash buckets in tun_struct which results a very large size of
tun_struct, this high order memory allocation fail easily when the memory is
fragmented.
The patch 60877a32bce00041528576e6b8df5abe9251fa73 increases the number of tx
queues. Memory allocation fallback to vzalloc() when kmalloc() fails.
This series tries to address following issues:
- Increase the number of netdev_queue queues for rx similarly its done for tx
queues by falling back to vzalloc() when memory allocation with kmalloc() fails.
- Increase number of queues to 256, maximum number is equal to maximum number
of vCPUS allowed in a guest.
I have also done testing with multiple parallel Netperf sessions for different
combination of queues and CPU's. It seems to be working fine without much increase
in cpu load with increase in number of queues. I also see good increase in throughput
with increase in number of queues. Though i had limitation of 8 physical CPU's.
For this test: Two Hosts(Host1 & Host2) are directly connected with cable
Host1 is running Guest1. Data is sent from Host2 to Guest1 via Host1.
Host kernel: 3.19.0-rc2+, AMD Opteron(tm) Processor 6320
NIC : Emulex Corporation OneConnect 10Gb NIC (be3)
Patch Applied %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle throughput
Single Queue, 2 vCPU's
-------------
Before Patch :all 0.19 0.00 0.16 0.07 0.04 0.10 0.00 0.18 0.00 99.26 57864.18
After Patch :all 0.99 0.00 0.64 0.69 0.07 0.26 0.00 1.58 0.00 95.77 57735.77
With 2 Queues, 2 vCPU's
---------------
Before Patch :all 0.19 0.00 0.19 0.10 0.04 0.11 0.00 0.28 0.00 99.08 63083.09
After Patch :all 0.87 0.00 0.73 0.78 0.09 0.35 0.00 2.04 0.00 95.14 62917.03
With 4 Queues, 4 vCPU's
--------------
Before Patch :all 0.20 0.00 0.21 0.11 0.04 0.12 0.00 0.32 0.00 99.00 80865.06
After Patch :all 0.71 0.00 0.93 0.85 0.11 0.51 0.00 2.62 0.00 94.27 86463.19
With 8 Queues, 8 vCPU's
--------------
Before Patch :all 0.19 0.00 0.18 0.09 0.04 0.11 0.00 0.23 0.00 99.17 86795.31
After Patch :all 0.65 0.00 1.18 0.93 0.13 0.68 0.00 3.38 0.00 93.05 89459.93
With 16 Queues, 8 vCPU's
--------------
After Patch :all 0.61 0.00 1.59 0.97 0.18 0.92 0.00 4.32 0.00 91.41 120951.60
Patches Summary:
net: allow large number of rx queues
tuntap: Increase the number of queues in tun
drivers/net/tun.c | 9 +++++----
net/core/dev.c | 13 ++++++++-----
2 files changed, 13 insertions(+), 9 deletions(-)
^ permalink raw reply
* [PATCH v4 net-next 1/2] net: allow large number of rx queues
From: Pankaj Gupta @ 2015-01-06 5:39 UTC (permalink / raw)
To: linux-kernel, netdev
Cc: davem, jasowang, mst, dgibson, vfalico, edumazet, vyasevic, hkchu,
wuzhy, xemul, therbert, bhutchings, xii, stephen, jiri,
sergei.shtylyov, Pankaj Gupta
In-Reply-To: <1420522756-15127-1-git-send-email-pagupta@redhat.com>
netif_alloc_rx_queues() uses kcalloc() to allocate memory
for "struct netdev_queue *_rx" array.
If we are doing large rx queue allocation kcalloc() might
fail, so this patch does a fallback to vzalloc().
Similar implementation is done for tx queue allocation in
netif_alloc_netdev_queues().
We avoid failure of high order memory allocation
with the help of vzalloc(), this allows us to do large
rx and tx queue allocation which in turn helps us to
increase the number of queues in tun.
As vmalloc() adds overhead on a critical network path,
__GFP_REPEAT flag is used with kzalloc() to do this fallback
only when really needed.
Signed-off-by: Pankaj Gupta <pagupta@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: David Gibson <dgibson@redhat.com>
---
net/core/dev.c | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/net/core/dev.c b/net/core/dev.c
index 1ab168e..954591a 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -6145,13 +6145,16 @@ static int netif_alloc_rx_queues(struct net_device *dev)
{
unsigned int i, count = dev->num_rx_queues;
struct netdev_rx_queue *rx;
+ size_t sz = count * sizeof(*rx);
BUG_ON(count < 1);
- rx = kcalloc(count, sizeof(struct netdev_rx_queue), GFP_KERNEL);
- if (!rx)
- return -ENOMEM;
-
+ rx = kzalloc(sz, GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT);
+ if (!rx) {
+ rx = vzalloc(sz);
+ if (!rx)
+ return -ENOMEM;
+ }
dev->_rx = rx;
for (i = 0; i < count; i++)
@@ -6781,7 +6784,7 @@ void free_netdev(struct net_device *dev)
netif_free_tx_queues(dev);
#ifdef CONFIG_SYSFS
- kfree(dev->_rx);
+ kvfree(dev->_rx);
#endif
kfree(rcu_dereference_protected(dev->ingress_queue, 1));
--
1.8.3.1
^ permalink raw reply related
* [PATCH v4 net-next 2/2 tuntap: Increase the number of queues in tun.
From: Pankaj Gupta @ 2015-01-06 5:39 UTC (permalink / raw)
To: linux-kernel, netdev
Cc: davem, jasowang, mst, dgibson, vfalico, edumazet, vyasevic, hkchu,
wuzhy, xemul, therbert, bhutchings, xii, stephen, jiri,
sergei.shtylyov, Pankaj Gupta
In-Reply-To: <1420522756-15127-1-git-send-email-pagupta@redhat.com>
Networking under kvm works best if we allocate a per-vCPU RX and TX
queue in a virtual NIC. This requires a per-vCPU queue on the host side.
It is now safe to increase the maximum number of queues.
Preceding patch: 'net: allow large number of rx queues'
made sure this won't cause failures due to high order memory
allocations. Increase it to 256: this is the max number of vCPUs
KVM supports.
Size of tun_struct changes from 8512 to 10496 after this patch. This keeps
pages allocated for tun_struct before and after the patch to 3.
Signed-off-by: Pankaj Gupta <pagupta@redhat.com>
Reviewed-by: David Gibson <dgibson@redhat.com>
---
drivers/net/tun.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index e3fa65a..a19dc5f8 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -113,10 +113,11 @@ struct tap_filter {
unsigned char addr[FLT_EXACT_COUNT][ETH_ALEN];
};
-/* DEFAULT_MAX_NUM_RSS_QUEUES were chosen to let the rx/tx queues allocated for
- * the netdevice to be fit in one page. So we can make sure the success of
- * memory allocation. TODO: increase the limit. */
-#define MAX_TAP_QUEUES DEFAULT_MAX_NUM_RSS_QUEUES
+/* MAX_TAP_QUEUES 256 is chosen to allow rx/tx queues to be equal
+ * to max number of vCPUS in guest. Also, we are making sure here
+ * queue memory allocation do not fail.
+ */
+#define MAX_TAP_QUEUES 256
#define MAX_TAP_FLOWS 4096
#define TUN_FLOW_EXPIRE (3 * HZ)
--
1.8.3.1
^ permalink raw reply related
* Re: [net-next PATCH v1 01/11] net: flow_table: create interface for hw match/action tables
From: John Fastabend @ 2015-01-06 6:04 UTC (permalink / raw)
To: Scott Feldman
Cc: Thomas Graf, Jiří Pírko, Jamal Hadi Salim,
simon.horman@netronome.com, Netdev, David S. Miller,
Andy Gospodarek
In-Reply-To: <CAE4R7bBeCbq1yiRHQaQSX4JwKMWxn2hMoOptyOZe3Ak=9WzrVw@mail.gmail.com>
[...]
>> +/**
>> + * @struct net_flow_action
>> + * @brief a description of a endpoint defined action
>> + *
>> + * @name printable name
>> + * @uid unique action identifier
>> + * @types NET_FLOW_ACTION_TYPE_NULL terminated list of action types
>
> s/types/args?
>
yep typo fixed in upcoming v2.
>> + */
>> +struct net_flow_action {
>> + char name[NET_FLOW_NAMSIZ];
>> + int uid;
>> + struct net_flow_action_arg *args;
>> +};
>> +
>> +/**
>> + * @struct net_flow_table
>> + * @brief define flow table with supported match/actions
>> + *
>> + * @uid unique identifier for table
>> + * @source uid of parent table
>
> Is parent table the table previous in the pipeline? If so, what if
> you can get to table from N different parent tables, what goes in
> source?
No, you can get the layout of tables from the table graph ops.
Source is used when a single tcam or other implementation mechanism
is sliced into a set of tables. The current rocker world doesn't use
this very much at the moment because its static and I just assumed
every table came out of the same virtual hardware namespace.
A simple example world would be to come up with a set of large virtual
TCAMs. Any given TCAM maybe sliced into a set of tables. Users may
organize these either via some out of band configuration at init or
power on time. In the rocker case we could specify this when we load
qemu. For now it is just informational. But if we start allowing users
to create delete tables at runtime it is important to "know" where the
slices are being allocated/free'd from. The source gives you this
information.
The hardware devices I'm working on have multiple sources we can
allocate/free tables from. The source values would provide a way to
track down which tables are in which hardware namespaces.
Hope that helps?
>
>> + * @size max number of entries for table or -1 for unbounded
>> + * @matches null terminated set of supported match types given by match uid
>> + * @actions null terminated set of supported action types given by action uid
>> + * @flows set of flows
>> + */
>> +struct net_flow_table {
>> + char name[NET_FLOW_NAMSIZ];
>> + int uid;
>> + int source;
>> + int size;
>> + struct net_flow_field_ref *matches;
>> + int *actions;
>> +};
>> +
>> +/* net_flow_hdr_node: node in a header graph of header fields.
>> + *
>> + * @uid : unique id of the graph node
>> + * @flwo_header_ref : identify the hdrs that can handled by this node
>
> s/flwo_header_ref/hdrs?
>
>> + * @net_flow_jump_table : give a case jump statement
>
> s/net_flow_jump_table/jump
yep thanks.
>
>> + */
>> +struct net_flow_hdr_node {
>> + char name[NET_FLOW_NAMSIZ];
>> + int uid;
>> + int *hdrs;
>> + struct net_flow_jump_table *jump;
>> +};
>> +
>> +struct net_flow_tbl_node {
>> + int uid;
>> + __u32 flags;
>> + struct net_flow_jump_table *jump;
>> +};
>> +#endif
>> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
>> index 29c92ee..3c3c856 100644
>> --- a/include/linux/netdevice.h
>> +++ b/include/linux/netdevice.h
>> @@ -52,6 +52,11 @@
>> #include <linux/neighbour.h>
>> #include <uapi/linux/netdevice.h>
>>
>> +#ifdef CONFIG_NET_FLOW_TABLES
>> +#include <linux/if_flow.h>
>> +#include <uapi/linux/if_flow.h>
>
> linux/if_flow.h already included uapi file
>
fixed.
>> +#endif
>> +
>> struct netpoll_info;
>> struct device;
>> struct phy_device;
>> @@ -1186,6 +1191,13 @@ struct net_device_ops {
>> int (*ndo_switch_port_stp_update)(struct net_device *dev,
>> u8 state);
>> #endif
>> +#ifdef CONFIG_NET_FLOW_TABLES
>> + struct net_flow_action **(*ndo_flow_get_actions)(struct net_device *dev);
>> + struct net_flow_table **(*ndo_flow_get_tables)(struct net_device *dev);
>> + struct net_flow_header **(*ndo_flow_get_headers)(struct net_device *dev);
>> + struct net_flow_hdr_node **(*ndo_flow_get_hdr_graph)(struct net_device *dev);
>
> hdr or header? pick one, probably hdr.
hdr is shorter and doesn't lose any clarity IMO I'll use net_flow_hdr
and net_flow_hdr_node
>
>> + struct net_flow_tbl_node **(*ndo_flow_get_tbl_graph)(struct net_device *dev);
>
> move this up next to get_tables
sure also what do you think tbl instead of table.
>
>> +#endif
>> };
>>
>> /**
>> diff --git a/include/uapi/linux/if_flow.h b/include/uapi/linux/if_flow.h
>> new file mode 100644
>> index 0000000..2acdb38
>> --- /dev/null
>> +++ b/include/uapi/linux/if_flow.h
>> @@ -0,0 +1,363 @@
>> +/*
>> + * include/uapi/linux/if_flow.h - Flow table interface for Switch devices
>> + * Copyright (c) 2014 John Fastabend <john.r.fastabend@intel.com>
>> + *
>> + *
>> + * 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".
>> + *
>> + * Author: John Fastabend <john.r.fastabend@intel.com>
>> + */
>> +
>> +/* Netlink description:
>> + *
>> + * Table definition used to describe running tables. The following
>> + * describes the netlink message returned from a flow API messages.
>
> message?
>
That sentence is a bit awkward all around. Changed it to
"The following describes the netlink format used by the flow API."
maybe that is better.
>
>> +
>> +enum {
>> + NET_FLOW_MASK_TYPE_UNSPEC,
>> + NET_FLOW_MASK_TYPE_EXACT,
>> + NET_FLOW_MASK_TYPE_LPM,
>
> As discussed in another thread, need third mask type that's not LPM;
> e.g. 0b0101.
>
yep.
>
>> +#define NET_FLOW_TABLE_GRAPH_NODE_MAX (__NET_FLOW_TABLE_GRAPH_NODE_MAX - 1)
>> +
>> +enum {
>> + NET_FLOW_TABLE_GRAPH_UNSPEC,
>> + NET_FLOW_TABLE_GRAPH_NODE,
>> + __NET_FLOW_TABLE_GRAPH_MAX,
>> +};
>> +#define NET_FLOW_TABLE_GRAPH_MAX (__NET_FLOW_TABLE_GRAPH_MAX - 1)
>> +
>> +enum {
>> + NET_FLOW_IDENTIFIER_IFINDEX, /* net_device ifindex */
>
> Maybe add an NET_FLOW_IDENTIFIER_UNSPEC so NET_FLOW_IDENTIFIER_IFINDEX
> isn't zero.
>
agreed I tend to like being able to test things with if (foo) { ... }
[...]
>> +
>> +static int net_flow_put_action(struct sk_buff *skb, struct net_flow_action *a)
>> +{
>> + struct net_flow_action_arg *this;
>> + struct nlattr *nest;
>> + int err, args = 0;
>> +
>> + if (a->name && nla_put_string(skb, NET_FLOW_ACTION_ATTR_NAME, a->name))
>> + return -EMSGSIZE;
>> +
>> + if (nla_put_u32(skb, NET_FLOW_ACTION_ATTR_UID, a->uid))
>> + return -EMSGSIZE;
>> +
>> + if (!a->args)
>> + return 0;
>> +
>> + for (this = &a->args[0]; strlen(this->name) > 0; this++)
>> + args++;
>> +
>
> Since you only need to know that there are > 0 args, but don't need
> the actual count, can you simplify test with something like:
>
good catch, this is a hold over from some code I rewrote I'll clean
this up like,
static int net_flow_put_action(struct sk_buff *skb, struct
net_flow_action *a)
{
struct net_flow_action_arg *this;
struct nlattr *nest;
int err;
if (a->name && nla_put_string(skb, NET_FLOW_ACTION_ATTR_NAME,
a->name))
return -EMSGSIZE;
if (nla_put_u32(skb, NET_FLOW_ACTION_ATTR_UID, a->uid))
return -EMSGSIZE;
if (a->args && a->args[0].type) {
nest = nla_nest_start(skb,
NET_FLOW_ACTION_ATTR_SIGNATURE);
if (!nest)
return -EMSGSIZE;
err = net_flow_put_act_types(skb, a->args);
if (err) {
nla_nest_cancel(skb, nest);
return err;
}
nla_nest_end(skb, nest);
}
return 0;
}
I think that should probably work. Of course I'll compile it and test
it.
> bool has_args = strlen(a->args->name) > 0;
>
> or
>
> bool has_args = !!a->args->type;
>
>> + if (args) {
>> + nest = nla_nest_start(skb, NET_FLOW_ACTION_ATTR_SIGNATURE);
>> + if (!nest)
>> + goto nest_put_failure;
>
> Maybe just return -EMSGSIZE here and skip goto.
>
>> +
>> + err = net_flow_put_act_types(skb, a->args);
>> + if (err) {
>> + nla_nest_cancel(skb, nest);
>> + return err;
>> + }
>> + nla_nest_end(skb, nest);
>> + }
>> +
>> + return 0;
>> +nest_put_failure:
>> + return -EMSGSIZE;
>> +}
>> +
>> +static int net_flow_put_actions(struct sk_buff *skb,
>> + struct net_flow_action **acts)
>> +{
>> + struct nlattr *actions;
>> + int err, i;
>> +
>> + actions = nla_nest_start(skb, NET_FLOW_ACTIONS);
>> + if (!actions)
>> + return -EMSGSIZE;
>> +
>> + for (i = 0; acts[i]->uid; i++) {
>
> Using for(act = acts; act->udi; act++) will make code a little nicer.
>
not entirely convinced its any nicer that way but sure I'll convert it.
[...]
>> +static int net_flow_cmd_get_actions(struct sk_buff *skb,
>> + struct genl_info *info)
>> +{
>> + struct net_flow_action **a;
>> + struct net_device *dev;
>> + struct sk_buff *msg;
>> +
>> + dev = net_flow_get_dev(info);
>> + if (!dev)
>> + return -EINVAL;
>> +
>> + if (!dev->netdev_ops->ndo_flow_get_actions) {
>> + dev_put(dev);
>> + return -EOPNOTSUPP;
>> + }
>> +
>> + a = dev->netdev_ops->ndo_flow_get_actions(dev);
>> + if (!a)
>> + return -EBUSY;
>
> Is it assumed ndo_flow_get_actions() returns a pointer to a static
> list of actions? What if the device wants to give up a dynamic list
> of actions? I'm trying to understand the lifetime of pointer 'a'.
> What would cause -EBUSY condition?
>
Ah this is a good point. At the moment if a driver dynamically changes
a structure then its going to break because there is no locking
involved. I think the best way to do this is to use RCU here. We can
return rcu dereferenced pointers and then drivers will need to wait a
grace period before free'ing the old pointer. To simplify drivers we
can do this from helper calls and document the semantics.
Currently rocker is static so we don't have any issues. If no one minds
I would like to do this in a follow up series.
>> +
>> + msg = net_flow_build_actions_msg(a, dev,
>> + info->snd_portid,
>> + info->snd_seq,
>> + NET_FLOW_TABLE_CMD_GET_ACTIONS);
>> + dev_put(dev);
>> +
>> + if (IS_ERR(msg))
>> + return PTR_ERR(msg);
>> +
>> + return genlmsg_reply(msg, info);
>> +}
>> +
>> +static int net_flow_put_table(struct net_device *dev,
>> + struct sk_buff *skb,
>> + struct net_flow_table *t)
>> +{
>> + struct nlattr *matches, *actions;
>> + int i;
>> +
>> + if (nla_put_string(skb, NET_FLOW_TABLE_ATTR_NAME, t->name) ||
>> + nla_put_u32(skb, NET_FLOW_TABLE_ATTR_UID, t->uid) ||
>> + nla_put_u32(skb, NET_FLOW_TABLE_ATTR_SOURCE, t->source) ||
>> + nla_put_u32(skb, NET_FLOW_TABLE_ATTR_SIZE, t->size))
>> + return -EMSGSIZE;
>> +
>> + matches = nla_nest_start(skb, NET_FLOW_TABLE_ATTR_MATCHES);
>> + if (!matches)
>> + return -EMSGSIZE;
>> +
>> + for (i = 0; t->matches[i].instance; i++)
>
> pointer-based loop better than i-based? my personal preference, i guess.
hmm I guess I tended to write these with indices. I might leave them
for now but can change them if the consensus is pointer loops are easier
to read.
>
>> + nla_put(skb, NET_FLOW_FIELD_REF,
>> + sizeof(struct net_flow_field_ref),
>> + &t->matches[i]);
>> + nla_nest_end(skb, matches);
>> +
--
John Fastabend Intel Corporation
^ permalink raw reply
* Re: [PATCH 1/1] bridge: remove BR_GROUPFWD_RESTRICTED for arbitrary forwarding of reserved addresses
From: Stephen Hemminger @ 2015-01-06 6:10 UTC (permalink / raw)
To: Bernhard Thaler; +Cc: netdev, bridge, davem
In-Reply-To: <1420505776-26827-1-git-send-email-bernhard.thaler@wvnet.at>
On Tue, 6 Jan 2015 01:56:15 +0100
Bernhard Thaler <bernhard.thaler@wvnet.at> wrote:
> BR_GROUPFWD_RESTRICTED bitmask restricts users from setting values to
> /sys/class/net/brX/bridge/group_fwd_mask that allow forwarding of
> some IEEE 802.1D Table 7-10 Reserved addresses:
> (MAC Control) 802.3 01-80-C2-00-00-01
> (Link Aggregation) 802.3 01-80-C2-00-00-02
> 802.1AB LLDP 01-80-C2-00-00-0E
> BR_GROUPFWD_RESTRICTED may have been set as an extra protection against
> forwarding these control frames as forwarding 802.1X PAE (01-80-C2-00-00-03)
> in 802.1X setups satisfies most common use-cases.
> Other situations, such as placing a software based bridge as a "TAP" between two
> devices may require to forward e.g. LLDP frames while debugging network problems
> or actively changing/filtering traffic with ebtables.
>
> This patch allows to set e.g.:
> echo 65535 > /sys/class/net/brX/bridge/group_fwd_mask
> which sets no restrictions on the forwardable reserved addresses.
>
> - the default value 0 will still comply with 802.1D and not forward any
> reserved addresses
> - values such as 8 for forwarding 802.1X related frames will behave the
> same way as with BR_GROUPFWD_RESTRICTED currently in place, so backward
> compatibility to current scripts using group_fwd_masks shoudl be possible
>
> Administrators and network engineers however will be able to arbitrarily
> forward any reserved addresses without BR_GROUPFWD_RESTRICTED. This will
> be non-standard compliant behavior, but forwarding of any reserved address
> right from the beginning is. Users should be aware of this anyway and
> know what/why they are doing when setting values such as 65535, 32768, 16384,
> 4, 2 for group_fwd_mask
>
> This patch was tested on a bridge with two interfaces created with bridge-utils.
>
> Signed-off-by: Bernhard Thaler <bernhard.thaler@wvnet.at>
I am ok with forwarding LLDP because some people need it.
But allowing forwarding STP or PAUSE frames is bad.
We don't let people do things that break networks. Other examples
already exist like set all 0 ethernet addresses, or the restrictions
on allowing net 127 in IP addresses.
^ permalink raw reply
* Re: [net-next PATCH v1 02/11] net: flow_table: add flow, delete flow
From: Scott Feldman @ 2015-01-06 6:19 UTC (permalink / raw)
To: John Fastabend
Cc: Thomas Graf, Jiří Pírko, Jamal Hadi Salim,
simon.horman@netronome.com, Netdev, David S. Miller,
Andy Gospodarek
In-Reply-To: <20141231194615.31070.18038.stgit@nitbit.x32>
On Wed, Dec 31, 2014 at 11:46 AM, John Fastabend
<john.fastabend@gmail.com> wrote:
> Now that the device capabilities are exposed we can add support to
> add and delete flows from the tables.
>
> The two operations are
>
> table_set_flows :
>
> The set flow operations is used to program a set of flows into a
> hardware device table. The message is consumed via netlink encoded
> message which is then decoded into a null terminated array of
> flow entry structures. A flow entry structure is defined as
>
> struct net_flow_flow {
> int table_id;
> int uid;
> int priority;
> struct net_flow_field_ref *matches;
> struct net_flow_action *actions;
> }
>
> The table id is the _uid_ returned from 'get_tables' operatoins.
> Matches is a set of match criteria for packets with a logical AND
> operation done on the set so packets match the entire criteria.
> Actions provide a set of actions to perform when the flow rule is
> hit. Both matches and actions are null terminated arrays.
>
> The flows are configured in hardware using an ndo op. We do not
> provide a commit operation at the moment and expect hardware
> commits the flows one at a time. Future work may require a commit
> operation to tell the hardware we are done loading flow rules. On
> some hardware this will help bulk updates.
>
> Its possible for hardware to return an error from a flow set
> operation. This can occur for many reasons both transient and
> resource constraints. We have different error handling strategies
> built in and listed here,
>
> *_ERROR_ABORT abort on first error with errmsg
>
> *_ERROR_CONTINUE continue programming flows no errmsg
>
> *_ERROR_ABORT_LOG abort on first error and return flow that
> failed to user space in reply msg
>
> *_ERROR_CONT_LOG continue programming flows and return a list
> of flows that failed to user space in a reply
> msg.
>
> notably missing is a rollback error strategy. I don't have a
> use for this in software yet but the strategy can be added with
> *_ERROR_ROLLBACK for example.
>
> table_del_flows
>
> The delete flow operation uses the same structures and error
> handling strategies as the table_set_flows operations. Although on
> delete messges ommit the matches/actions arrays because they are
> not needed to lookup the flow.
>
> Also thanks to Simon Horman for fixes and other help.
>
> Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
> ---
> include/linux/if_flow.h | 21 ++
> include/linux/netdevice.h | 8 +
> include/uapi/linux/if_flow.h | 49 ++++
> net/core/flow_table.c | 501 ++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 579 insertions(+)
>
> diff --git a/include/linux/if_flow.h b/include/linux/if_flow.h
> index 1b6c1ea..20fa752 100644
> --- a/include/linux/if_flow.h
> +++ b/include/linux/if_flow.h
> @@ -90,4 +90,25 @@ struct net_flow_tbl_node {
> __u32 flags;
> struct net_flow_jump_table *jump;
> };
> +
> +/**
> + * @struct net_flow_flow
> + * @brief describes the match/action entry
> + *
> + * @uid unique identifier for flow
> + * @priority priority to execute flow match/action in table
What is the convention on priority? 0 is lowest priority or highest?
> + * @match null terminated set of match uids match criteria
> + * @actoin null terminated set of action uids to apply to match
> + *
> + * Flows must match all entries in match set.
> + */
> +struct net_flow_flow {
> + int table_id;
> + int uid;
> + int priority;
> + struct net_flow_field_ref *matches;
> + struct net_flow_action *actions;
> +};
> +
> +int net_flow_put_flow(struct sk_buff *skb, struct net_flow_flow *flow);
> #endif
> diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
> index 3c3c856..be8d4e4 100644
> --- a/include/linux/netdevice.h
> +++ b/include/linux/netdevice.h
> @@ -1197,6 +1197,14 @@ struct net_device_ops {
> struct net_flow_header **(*ndo_flow_get_headers)(struct net_device *dev);
> struct net_flow_hdr_node **(*ndo_flow_get_hdr_graph)(struct net_device *dev);
> struct net_flow_tbl_node **(*ndo_flow_get_tbl_graph)(struct net_device *dev);
> + int (*ndo_flow_get_flows)(struct sk_buff *skb,
> + struct net_device *dev,
> + int table,
> + int min, int max);
> + int (*ndo_flow_set_flows)(struct net_device *dev,
> + struct net_flow_flow *f);
> + int (*ndo_flow_del_flows)(struct net_device *dev,
> + struct net_flow_flow *f);
Need doc for these in BIG comment block above this struct. Same for
ndo_flow_xxx added in previous patch.
> #endif
> };
>
> diff --git a/include/uapi/linux/if_flow.h b/include/uapi/linux/if_flow.h
> index 2acdb38..125cdc6 100644
> --- a/include/uapi/linux/if_flow.h
> +++ b/include/uapi/linux/if_flow.h
> @@ -329,6 +329,48 @@ enum {
> #define NET_FLOW_TABLE_GRAPH_MAX (__NET_FLOW_TABLE_GRAPH_MAX - 1)
>
> enum {
> + NET_FLOW_NET_FLOW_UNSPEC,
> + NET_FLOW_FLOW,
> + __NET_FLOW_NET_FLOW_MAX,
> +};
> +#define NET_FLOW_NET_FLOW_MAX (__NET_FLOW_NET_FLOW_MAX - 1)
> +
> +enum {
> + NET_FLOW_TABLE_FLOWS_UNSPEC,
> + NET_FLOW_TABLE_FLOWS_TABLE,
> + NET_FLOW_TABLE_FLOWS_MINPRIO,
> + NET_FLOW_TABLE_FLOWS_MAXPRIO,
> + NET_FLOW_TABLE_FLOWS_FLOWS,
> + __NET_FLOW_TABLE_FLOWS_MAX,
> +};
> +#define NET_FLOW_TABLE_FLOWS_MAX (__NET_FLOW_TABLE_FLOWS_MAX - 1)
> +
> +enum {
NET_FLOW_FLOWS_ERROR_UNSPEC?
> + /* Abort with normal errmsg */
> + NET_FLOW_FLOWS_ERROR_ABORT,
> + /* Ignore errors and continue without logging */
> + NET_FLOW_FLOWS_ERROR_CONTINUE,
> + /* Abort and reply with invalid flow fields */
> + NET_FLOW_FLOWS_ERROR_ABORT_LOG,
> + /* Continue and reply with list of invalid flows */
> + NET_FLOW_FLOWS_ERROR_CONT_LOG,
> + __NET_FLOWS_FLOWS_ERROR_MAX,
> +};
> +#define NET_FLOWS_FLOWS_ERROR_MAX (__NET_FLOWS_FLOWS_ERROR_MAX - 1)
> +
> +enum {
> + NET_FLOW_ATTR_UNSPEC,
> + NET_FLOW_ATTR_ERROR,
> + NET_FLOW_ATTR_TABLE,
> + NET_FLOW_ATTR_UID,
> + NET_FLOW_ATTR_PRIORITY,
> + NET_FLOW_ATTR_MATCHES,
> + NET_FLOW_ATTR_ACTIONS,
> + __NET_FLOW_ATTR_MAX,
> +};
> +#define NET_FLOW_ATTR_MAX (__NET_FLOW_ATTR_MAX - 1)
> +
> +enum {
> NET_FLOW_IDENTIFIER_IFINDEX, /* net_device ifindex */
> };
>
> @@ -343,6 +385,9 @@ enum {
> NET_FLOW_HEADER_GRAPH,
> NET_FLOW_TABLE_GRAPH,
>
> + NET_FLOW_FLOWS,
> + NET_FLOW_FLOWS_ERROR,
> +
> __NET_FLOW_MAX,
> NET_FLOW_MAX = (__NET_FLOW_MAX - 1),
> };
> @@ -354,6 +399,10 @@ enum {
> NET_FLOW_TABLE_CMD_GET_HDR_GRAPH,
> NET_FLOW_TABLE_CMD_GET_TABLE_GRAPH,
>
> + NET_FLOW_TABLE_CMD_GET_FLOWS,
> + NET_FLOW_TABLE_CMD_SET_FLOWS,
> + NET_FLOW_TABLE_CMD_DEL_FLOWS,
> +
> __NET_FLOW_CMD_MAX,
> NET_FLOW_CMD_MAX = (__NET_FLOW_CMD_MAX - 1),
> };
> diff --git a/net/core/flow_table.c b/net/core/flow_table.c
> index ec3f06d..f4cf293 100644
> --- a/net/core/flow_table.c
> +++ b/net/core/flow_table.c
> @@ -774,6 +774,489 @@ static int net_flow_cmd_get_table_graph(struct sk_buff *skb,
> return genlmsg_reply(msg, info);
> }
>
> +static struct sk_buff *net_flow_build_flows_msg(struct net_device *dev,
> + u32 portid, int seq, u8 cmd,
> + int min, int max, int table)
> +{
> + struct genlmsghdr *hdr;
> + struct nlattr *flows;
> + struct sk_buff *skb;
> + int err = -ENOBUFS;
> +
> + skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Does netlink msg size limit the number of flows we can return? If I
have 10K unique prefix routes, resulting in 10K flows in my L3 table,
all at same priority, can it be dumped?
I'm wondering (out loud) if get_flows is something that should be
supported at the driver level, or should it be managed above the
driver. In other words, whoever calls set_flows and del_flows knows
what's what and could return get_flows rather than calling down to
driver/device to get_flows. Unless driver/device could create flows
independent of set_flows and del_flows.
> + if (!skb)
> + return ERR_PTR(-ENOBUFS);
> +
> + hdr = genlmsg_put(skb, portid, seq, &net_flow_nl_family, 0, cmd);
> + if (!hdr)
> + goto out;
> +
> + if (nla_put_u32(skb,
> + NET_FLOW_IDENTIFIER_TYPE,
> + NET_FLOW_IDENTIFIER_IFINDEX) ||
> + nla_put_u32(skb, NET_FLOW_IDENTIFIER, dev->ifindex)) {
> + err = -ENOBUFS;
> + goto out;
> + }
> +
> + flows = nla_nest_start(skb, NET_FLOW_FLOWS);
> + if (!flows) {
> + err = -EMSGSIZE;
> + goto out;
> + }
> +
> + err = dev->netdev_ops->ndo_flow_get_flows(skb, dev, table, min, max);
> + if (err < 0)
> + goto out_cancel;
> +
> + nla_nest_end(skb, flows);
> +
> + err = genlmsg_end(skb, hdr);
> + if (err < 0)
> + goto out;
> +
> + return skb;
> +out_cancel:
> + nla_nest_cancel(skb, flows);
> +out:
> + nlmsg_free(skb);
> + return ERR_PTR(err);
> +}
> +
> +static const
> +struct nla_policy net_flow_table_flows_policy[NET_FLOW_TABLE_FLOWS_MAX + 1] = {
> + [NET_FLOW_TABLE_FLOWS_TABLE] = { .type = NLA_U32,},
> + [NET_FLOW_TABLE_FLOWS_MINPRIO] = { .type = NLA_U32,},
> + [NET_FLOW_TABLE_FLOWS_MAXPRIO] = { .type = NLA_U32,},
> + [NET_FLOW_TABLE_FLOWS_FLOWS] = { .type = NLA_NESTED,},
> +};
> +
> +static int net_flow_table_cmd_get_flows(struct sk_buff *skb,
> + struct genl_info *info)
> +{
> + struct nlattr *tb[NET_FLOW_TABLE_FLOWS_MAX+1];
> + int table, min = -1, max = -1;
> + struct net_device *dev;
> + struct sk_buff *msg;
> + int err = -EINVAL;
> +
> + dev = net_flow_get_dev(info);
> + if (!dev)
> + return -EINVAL;
> +
> + if (!dev->netdev_ops->ndo_flow_get_flows) {
> + dev_put(dev);
> + return -EOPNOTSUPP;
> + }
> +
> + if (!info->attrs[NET_FLOW_IDENTIFIER_TYPE] ||
> + !info->attrs[NET_FLOW_IDENTIFIER] ||
> + !info->attrs[NET_FLOW_FLOWS])
> + goto out;
> +
> + err = nla_parse_nested(tb, NET_FLOW_TABLE_FLOWS_MAX,
> + info->attrs[NET_FLOW_FLOWS],
> + net_flow_table_flows_policy);
> + if (err)
> + goto out;
> +
> + if (!tb[NET_FLOW_TABLE_FLOWS_TABLE])
> + goto out;
> +
> + table = nla_get_u32(tb[NET_FLOW_TABLE_FLOWS_TABLE]);
> +
> + if (tb[NET_FLOW_TABLE_FLOWS_MINPRIO])
> + min = nla_get_u32(tb[NET_FLOW_TABLE_FLOWS_MINPRIO]);
> + if (tb[NET_FLOW_TABLE_FLOWS_MAXPRIO])
> + max = nla_get_u32(tb[NET_FLOW_TABLE_FLOWS_MAXPRIO]);
Just curious, what is the intended use of min/max prio? Was it to
reduce number of flows returned in one get_flows call?
> + msg = net_flow_build_flows_msg(dev,
> + info->snd_portid,
> + info->snd_seq,
> + NET_FLOW_TABLE_CMD_GET_FLOWS,
> + min, max, table);
> + dev_put(dev);
> +
> + if (IS_ERR(msg))
> + return PTR_ERR(msg);
> +
> + return genlmsg_reply(msg, info);
> +out:
> + dev_put(dev);
> + return err;
> +}
> +
> +static struct sk_buff *net_flow_start_errmsg(struct net_device *dev,
> + struct genlmsghdr **hdr,
> + u32 portid, int seq, u8 cmd)
> +{
> + struct genlmsghdr *h;
> + struct sk_buff *skb;
> +
> + skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
> + if (!skb)
> + return ERR_PTR(-EMSGSIZE);
> +
> + h = genlmsg_put(skb, portid, seq, &net_flow_nl_family, 0, cmd);
> + if (!h)
> + return ERR_PTR(-EMSGSIZE);
> +
> + if (nla_put_u32(skb,
> + NET_FLOW_IDENTIFIER_TYPE,
> + NET_FLOW_IDENTIFIER_IFINDEX) ||
> + nla_put_u32(skb, NET_FLOW_IDENTIFIER, dev->ifindex))
> + return ERR_PTR(-EMSGSIZE);
> +
> + *hdr = h;
> + return skb;
> +}
> +
> +static struct sk_buff *net_flow_end_flow_errmsg(struct sk_buff *skb,
> + struct genlmsghdr *hdr)
> +{
> + int err;
> +
> + err = genlmsg_end(skb, hdr);
> + if (err < 0) {
> + nlmsg_free(skb);
> + return ERR_PTR(err);
> + }
> +
> + return skb;
> +}
> +
> +static int net_flow_put_flow_action(struct sk_buff *skb,
> + struct net_flow_action *a)
> +{
> + struct nlattr *action, *sigs;
> + int i, err = 0;
> +
> + action = nla_nest_start(skb, NET_FLOW_ACTION);
> + if (!action)
> + return -EMSGSIZE;
> +
> + if (nla_put_u32(skb, NET_FLOW_ACTION_ATTR_UID, a->uid))
> + return -EMSGSIZE;
> +
> + if (!a->args)
> + goto done;
> +
> + for (i = 0; a->args[i].type; i++) {
> + sigs = nla_nest_start(skb, NET_FLOW_ACTION_ATTR_SIGNATURE);
> + if (!sigs) {
> + nla_nest_cancel(skb, action);
> + return -EMSGSIZE;
> + }
> +
> + err = net_flow_put_act_types(skb, a[i].args);
> + if (err) {
> + nla_nest_cancel(skb, action);
> + nla_nest_cancel(skb, sigs);
order seems backwards. i think you can just cancel outer.
> + return err;
> + }
> + nla_nest_end(skb, sigs);
> + }
> +
> +done:
> + nla_nest_end(skb, action);
> + return 0;
> +}
> +
> +int net_flow_put_flow(struct sk_buff *skb, struct net_flow_flow *flow)
> +{
> + struct nlattr *flows, *matches;
> + struct nlattr *actions = NULL; /* must be null to unwind */
> + int err, j, i = 0;
> +
> + flows = nla_nest_start(skb, NET_FLOW_FLOW);
> + if (!flows)
> + goto put_failure;
> +
> + if (nla_put_u32(skb, NET_FLOW_ATTR_TABLE, flow->table_id) ||
> + nla_put_u32(skb, NET_FLOW_ATTR_UID, flow->uid) ||
> + nla_put_u32(skb, NET_FLOW_ATTR_PRIORITY, flow->priority))
> + goto flows_put_failure;
> +
> + if (flow->matches) {
> + matches = nla_nest_start(skb, NET_FLOW_ATTR_MATCHES);
> + if (!matches)
> + goto flows_put_failure;
> +
> + for (j = 0; flow->matches && flow->matches[j].header; j++) {
for(match = flow->matches; match->header; match++)
> + struct net_flow_field_ref *f = &flow->matches[j];
> +
> + if (!f->header)
> + continue;
Already checked in for loop?
> +
> + nla_put(skb, NET_FLOW_FIELD_REF, sizeof(*f), f);
no err check
> + }
> + nla_nest_end(skb, matches);
> + }
> +
> + if (flow->actions) {
> + actions = nla_nest_start(skb, NET_FLOW_ATTR_ACTIONS);
> + if (!actions)
> + goto flows_put_failure;
> +
> + for (i = 0; flow->actions && flow->actions[i].uid; i++) {
> + err = net_flow_put_flow_action(skb, &flow->actions[i]);
> + if (err) {
> + nla_nest_cancel(skb, actions);
just cancel outer (I think)
> + goto flows_put_failure;
> + }
> + }
> + nla_nest_end(skb, actions);
> + }
> +
> + nla_nest_end(skb, flows);
> + return 0;
> +
> +flows_put_failure:
> + nla_nest_cancel(skb, flows);
> +put_failure:
> + return -EMSGSIZE;
> +}
> +EXPORT_SYMBOL(net_flow_put_flow);
> +
> +static int net_flow_get_field(struct net_flow_field_ref *field,
> + struct nlattr *nla)
> +{
> + if (nla_type(nla) != NET_FLOW_FIELD_REF)
> + return -EINVAL;
> +
> + if (nla_len(nla) < sizeof(*field))
> + return -EINVAL;
> +
> + *field = *(struct net_flow_field_ref *)nla_data(nla);
> + return 0;
> +}
maybe return struct net_flow_field_ref * to simplify return logic.
> +
> +static int net_flow_get_action(struct net_flow_action *a, struct nlattr *attr)
> +{
> + struct nlattr *act[NET_FLOW_ACTION_ATTR_MAX+1];
> + struct nlattr *args;
> + int rem;
> + int err, count = 0;
> +
> + if (nla_type(attr) != NET_FLOW_ACTION) {
> + pr_warn("%s: expected NET_FLOW_ACTION\n", __func__);
> + return 0;
> + }
> +
> + err = nla_parse_nested(act, NET_FLOW_ACTION_ATTR_MAX,
> + attr, net_flow_action_policy);
> + if (err < 0)
> + return err;
> +
> + if (!act[NET_FLOW_ACTION_ATTR_UID] ||
> + !act[NET_FLOW_ACTION_ATTR_SIGNATURE])
> + return -EINVAL;
> +
> + a->uid = nla_get_u32(act[NET_FLOW_ACTION_ATTR_UID]);
> +
> + nla_for_each_nested(args, act[NET_FLOW_ACTION_ATTR_SIGNATURE], rem)
> + count++; /* unoptimized max possible */
> +
> + a->args = kcalloc(count + 1,
> + sizeof(struct net_flow_action_arg),
> + GFP_KERNEL);
kcalloc failure?
> + count = 0;
> +
> + nla_for_each_nested(args, act[NET_FLOW_ACTION_ATTR_SIGNATURE], rem) {
> + if (nla_type(args) != NET_FLOW_ACTION_ARG)
> + continue;
> +
> + if (nla_len(args) < sizeof(struct net_flow_action_arg)) {
> + kfree(a->args);
> + return -EINVAL;
> + }
> +
> + a->args[count] = *(struct net_flow_action_arg *)nla_data(args);
count++?
^ permalink raw reply
* RE: [PATCH v4] can: Convert to runtime_pm
From: Appana Durga Kedareswara Rao @ 2015-01-06 6:23 UTC (permalink / raw)
To: Soren Brinkmann, mkl@pengutronix.de
Cc: wg@grandegger.com, mkl@pengutronix.de, Michal Simek,
grant.likely@linaro.org, linux-can@vger.kernel.org,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <20141223224308.GC4611@xsjandreislx>
Hi Marc,
Could you please provide your feedback on this patch ?
@Soren : Will update the driver with your comments during next version of the patch.
Regards,
Kedar.
-----Original Message-----
From: Sören Brinkmann [mailto:soren.brinkmann@xilinx.com]
Sent: Wednesday, December 24, 2014 4:13 AM
To: Appana Durga Kedareswara Rao
Cc: wg@grandegger.com; mkl@pengutronix.de; Michal Simek; grant.likely@linaro.org; linux-can@vger.kernel.org; netdev@vger.kernel.org; linux-kernel@vger.kernel.org; Appana Durga Kedareswara Rao
Subject: Re: [PATCH v4] can: Convert to runtime_pm
On Tue, 2014-12-23 at 05:55PM +0530, Kedareswara rao Appana wrote:
> Instead of enabling/disabling clocks at several locations in the
> driver, use the runtime_pm framework. This consolidates the actions
> for runtime PM in the appropriate callbacks and makes the driver more
> readable and mantainable.
>
> Signed-off-by: Soren Brinkmann <soren.brinkmann@xilinx.com>
> Signed-off-by: Kedareswara rao Appana <appanad@xilinx.com>
> ---
> Chnages for v4:
> - Updated with the review comments.
> Changes for v3:
> - Converted the driver to use runtime_pm.
> Changes for v2:
> - Removed the struct platform_device* from suspend/resume
> as suggest by Lothar.
>
> drivers/net/can/xilinx_can.c | 123
> +++++++++++++++++++++++++-----------------
> 1 files changed, 74 insertions(+), 49 deletions(-)
>
> diff --git a/drivers/net/can/xilinx_can.c
> b/drivers/net/can/xilinx_can.c index 6c67643..c71f683 100644
> --- a/drivers/net/can/xilinx_can.c
> +++ b/drivers/net/can/xilinx_can.c
> @@ -32,6 +32,7 @@
> #include <linux/can/dev.h>
> #include <linux/can/error.h>
> #include <linux/can/led.h>
> +#include <linux/pm_runtime.h>
>
> #define DRIVER_NAME "xilinx_can"
>
> @@ -138,7 +139,7 @@ struct xcan_priv {
> u32 (*read_reg)(const struct xcan_priv *priv, enum xcan_reg reg);
> void (*write_reg)(const struct xcan_priv *priv, enum xcan_reg reg,
> u32 val);
> - struct net_device *dev;
> + struct device *dev;
> void __iomem *reg_base;
> unsigned long irq_flags;
> struct clk *bus_clk;
> @@ -842,6 +843,13 @@ static int xcan_open(struct net_device *ndev)
> struct xcan_priv *priv = netdev_priv(ndev);
> int ret;
>
> + ret = pm_runtime_get_sync(priv->dev);
> + if (ret < 0) {
> + netdev_err(ndev, "%s: pm_runtime_get failed\r(%d)\n\r",
Does this create the intended output? I haven't seen '\r' anywhere else.
Shouldn't this simply be:
netdev_err(ndev, "%s: pm_runtime_get failed (%d)\n",
[...]
> @@ -934,27 +927,20 @@ static int xcan_get_berr_counter(const struct net_device *ndev,
> struct xcan_priv *priv = netdev_priv(ndev);
> int ret;
>
> - ret = clk_prepare_enable(priv->can_clk);
> - if (ret)
> - goto err;
> -
> - ret = clk_prepare_enable(priv->bus_clk);
> - if (ret)
> - goto err_clk;
> + ret = pm_runtime_get_sync(priv->dev);
> + if (ret < 0) {
> + netdev_err(ndev, "%s: pm_runtime_get failed\r(%d)\n\r",
ditto
Sören
This email and any attachments are intended for the sole use of the named recipient(s) and contain(s) confidential information that may be proprietary, privileged or copyrighted under applicable law. If you are not the intended recipient, do not read, copy, or forward this email message or any attachments. Delete this email message and any attachments immediately.
^ permalink raw reply
* Re: [net-next PATCH v1 01/11] net: flow_table: create interface for hw match/action tables
From: Scott Feldman @ 2015-01-06 6:40 UTC (permalink / raw)
To: John Fastabend
Cc: Thomas Graf, Jiří Pírko, Jamal Hadi Salim,
simon.horman@netronome.com, Netdev, David S. Miller,
Andy Gospodarek
In-Reply-To: <54AB7AD3.7050704@gmail.com>
On Mon, Jan 5, 2015 at 10:04 PM, John Fastabend
<john.fastabend@gmail.com> wrote:
>>> + * @uid unique identifier for table
>>> + * @source uid of parent table
>>
>>
>> Is parent table the table previous in the pipeline? If so, what if
>> you can get to table from N different parent tables, what goes in
>> source?
>
>
> No, you can get the layout of tables from the table graph ops.
>
> Source is used when a single tcam or other implementation mechanism
> is sliced into a set of tables. The current rocker world doesn't use
> this very much at the moment because its static and I just assumed
> every table came out of the same virtual hardware namespace.
>
> A simple example world would be to come up with a set of large virtual
> TCAMs. Any given TCAM maybe sliced into a set of tables. Users may
> organize these either via some out of band configuration at init or
> power on time. In the rocker case we could specify this when we load
> qemu. For now it is just informational. But if we start allowing users
> to create delete tables at runtime it is important to "know" where the
> slices are being allocated/free'd from. The source gives you this
> information.
>
> The hardware devices I'm working on have multiple sources we can
> allocate/free tables from. The source values would provide a way to
> track down which tables are in which hardware namespaces.
>
> Hope that helps?
Got it, thanks.
Can source be encoded in tbl_id?
>> hdr or header? pick one, probably hdr.
>
>
> hdr is shorter and doesn't lose any clarity IMO I'll use net_flow_hdr
> and net_flow_hdr_node
>
>>
>>> + struct net_flow_tbl_node **(*ndo_flow_get_tbl_graph)(struct
>>> net_device *dev);
>>
>>
>> move this up next to get_tables
>
>
> sure also what do you think tbl instead of table.
+1 for tbl.
^ permalink raw reply
* Re: [PATCH net-next v3 0/5]: ixgbevf: Allow querying VFs RSS indirection table and key
From: Gleb Natapov @ 2015-01-06 6:55 UTC (permalink / raw)
To: Greg Rose; +Cc: Vlad Zolotarov, netdev, avi, jeffrey.t.kirsher
In-Reply-To: <CALgkqUojkfTwhsoAnPZdJv-oMg7PMB5m8Q2=k5=QqXKxSJgq7w@mail.gmail.com>
On Mon, Jan 05, 2015 at 03:54:52PM -0800, Greg Rose wrote:
> On Mon, Jan 5, 2015 at 6:15 AM, Vlad Zolotarov
> <vladz@cloudius-systems.com> wrote:
> > Add the ethtool ops to VF driver to allow querying the RSS indirection table
> > and RSS Random Key.
> >
> > - PF driver: Add new VF-PF channel commands.
> > - VF driver: Utilize these new commands and add the corresponding
> > ethtool callbacks.
> >
> > New in v3:
> > - Added a missing support for x550 devices.
> > - Mask the indirection table values according to PSRTYPE[n].RQPL.
> > - Minimized the number of added VF-PF commands.
> >
> > New in v2:
> > - Added a detailed description to patches 4 and 5.
> >
> > New in v1 (compared to RFC):
> > - Use "if-else" statement instead of a "switch-case" for a single option case.
> > More specifically: in cases where the newly added API version is the only one
> > allowed. We may consider using a "switch-case" back again when the list of
> > allowed API versions in these specific places grows up.
> >
> > Vlad Zolotarov (5):
> > ixgbe: Add a RETA query command to VF-PF channel API
> > ixgbevf: Add a RETA query code
> > ixgbe: Add GET_RSS_KEY command to VF-PF channel commands set
> > ixgbevf: Add RSS Key query code
> > ixgbevf: Add the appropriate ethtool ops to query RSS indirection
> > table and key
> >
> > drivers/net/ethernet/intel/ixgbe/ixgbe_mbx.h | 10 ++
> > drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c | 91 +++++++++++++++
> > drivers/net/ethernet/intel/ixgbevf/ethtool.c | 43 +++++++
> > drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c | 4 +-
> > drivers/net/ethernet/intel/ixgbevf/mbx.h | 10 ++
> > drivers/net/ethernet/intel/ixgbevf/vf.c | 132 ++++++++++++++++++++++
> > drivers/net/ethernet/intel/ixgbevf/vf.h | 2 +
> > 7 files changed, 291 insertions(+), 1 deletion(-)
>
> I've given this code a review and I don't see a way to
> set a policy in the PF driver as to whether this request should be
> allowed or not. We cannot enable this query by default - it is a
> security risk. To make this acceptable you need to do a
> couple of things.
>
Can you please elaborate on the security risk this information poses?
Is toeplitz hash function cryptographically strong enough so that VF
cannot reconstruct the hash key from hash result provided in packet
descriptor? The abstract of this paper [1] claims it is not, but I do
not have access to the full article unfortunately hence the question.
[1] http://ieeexplore.ieee.org/xpl/login.jsp?tp=&arnumber=5503170&url=http%3A%2F%2Fieeexplore.ieee.org%2Fxpls%2Fabs_all.jsp%3Farnumber%3D5503170
--
Gleb.
^ permalink raw reply
* Re: [net-next PATCH v1 04/11] rocker: add pipeline model for rocker switch
From: Scott Feldman @ 2015-01-06 7:01 UTC (permalink / raw)
To: John Fastabend
Cc: Thomas Graf, Jiří Pírko, Jamal Hadi Salim,
simon.horman@netronome.com, Netdev, David S. Miller,
Andy Gospodarek
In-Reply-To: <20141231194709.31070.16657.stgit@nitbit.x32>
On Wed, Dec 31, 2014 at 11:47 AM, John Fastabend
<john.fastabend@gmail.com> wrote:
> This adds rocker support for the net_flow_get_* operations. With this
> we can interrogate rocker.
>
> Here we see that for static configurations enabling the get operations
> is simply a matter of defining a pipeline model and returning the
> structures for the core infrastructure to encapsulate into netlink
> messages.
>
> Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
> ---
> drivers/net/ethernet/rocker/rocker.c | 35 +
> drivers/net/ethernet/rocker/rocker_pipeline.h | 673 +++++++++++++++++++++++++
> 2 files changed, 708 insertions(+)
> create mode 100644 drivers/net/ethernet/rocker/rocker_pipeline.h
>
> diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c
> index fded127..4c6787a 100644
> --- a/drivers/net/ethernet/rocker/rocker.c
> +++ b/drivers/net/ethernet/rocker/rocker.c
> @@ -36,6 +36,7 @@
> #include <generated/utsrelease.h>
>
> #include "rocker.h"
> +#include "rocker_pipeline.h"
>
> static const char rocker_driver_name[] = "rocker";
>
> @@ -3780,6 +3781,33 @@ static int rocker_port_switch_port_stp_update(struct net_device *dev, u8 state)
> return rocker_port_stp_update(rocker_port, state);
> }
>
> +#ifdef CONFIG_NET_FLOW_TABLES
Can this #ifdef test be moved out of driver? The if_flow core code
can stub out operations if CONFIG_NET_FLOW_TABLES isn't defined.
> +static struct net_flow_table **rocker_get_tables(struct net_device *d)
> +{
> + return rocker_table_list;
> +}
> +
> +static struct net_flow_header **rocker_get_headers(struct net_device *d)
> +{
> + return rocker_header_list;
> +}
> +
> +static struct net_flow_action **rocker_get_actions(struct net_device *d)
> +{
> + return rocker_action_list;
> +}
> +
> +static struct net_flow_tbl_node **rocker_get_tgraph(struct net_device *d)
> +{
> + return rocker_table_nodes;
> +}
> +
> +static struct net_flow_hdr_node **rocker_get_hgraph(struct net_device *d)
> +{
> + return rocker_header_nodes;
> +}
> +#endif
> +
> static const struct net_device_ops rocker_port_netdev_ops = {
> .ndo_open = rocker_port_open,
> .ndo_stop = rocker_port_stop,
> @@ -3794,6 +3822,13 @@ static const struct net_device_ops rocker_port_netdev_ops = {
> .ndo_bridge_getlink = rocker_port_bridge_getlink,
> .ndo_switch_parent_id_get = rocker_port_switch_parent_id_get,
> .ndo_switch_port_stp_update = rocker_port_switch_port_stp_update,
> +#ifdef CONFIG_NET_FLOW_TABLES
same comment here
> + .ndo_flow_get_tables = rocker_get_tables,
> + .ndo_flow_get_headers = rocker_get_headers,
> + .ndo_flow_get_actions = rocker_get_actions,
> + .ndo_flow_get_tbl_graph = rocker_get_tgraph,
> + .ndo_flow_get_hdr_graph = rocker_get_hgraph,
> +#endif
> };
>
> /********************
> diff --git a/drivers/net/ethernet/rocker/rocker_pipeline.h b/drivers/net/ethernet/rocker/rocker_pipeline.h
> new file mode 100644
> index 0000000..9544339
> --- /dev/null
> +++ b/drivers/net/ethernet/rocker/rocker_pipeline.h
Add standard header info...copyright/license.
> @@ -0,0 +1,673 @@
> +#ifndef _MY_PIPELINE_H_
> +#define _MY_PIPELINE_H_
_ROCKER_PIPELINE_H_
> +
> +#include <linux/if_flow.h>
> +
> +/* header definition */
> +#define HEADER_ETHERNET_SRC_MAC 1
> +#define HEADER_ETHERNET_DST_MAC 2
> +#define HEADER_ETHERNET_ETHERTYPE 3
Use enum?
> +struct net_flow_field ethernet_fields[3] = {
ethernet_fields[] = ... // let compiler size it
> + { .name = "src_mac", .uid = HEADER_ETHERNET_SRC_MAC, .bitwidth = 48},
> + { .name = "dst_mac", .uid = HEADER_ETHERNET_DST_MAC, .bitwidth = 48},
> + { .name = "ethertype",
> + .uid = HEADER_ETHERNET_ETHERTYPE,
> + .bitwidth = 16},
> +};
> +
> +#define HEADER_ETHERNET 1
> +struct net_flow_header ethernet = {
> + .name = "ethernet",
> + .uid = HEADER_ETHERNET,
> + .field_sz = 3,
ARRAY_SIZE()?
> + .fields = ethernet_fields,
> +};
> +
> +#define HEADER_VLAN_PCP 1
> +#define HEADER_VLAN_CFI 2
> +#define HEADER_VLAN_VID 3
> +#define HEADER_VLAN_ETHERTYPE 4
> +struct net_flow_field vlan_fields[4] = {
[] = ...
> + { .name = "pcp", .uid = HEADER_VLAN_PCP, .bitwidth = 3,},
> + { .name = "cfi", .uid = HEADER_VLAN_CFI, .bitwidth = 1,},
> + { .name = "vid", .uid = HEADER_VLAN_VID, .bitwidth = 12,},
> + { .name = "ethertype", .uid = HEADER_VLAN_ETHERTYPE, .bitwidth = 16,},
> +};
> +
> +#define HEADER_VLAN 2
> +struct net_flow_header vlan = {
> + .name = "vlan",
> + .uid = HEADER_VLAN,
> + .field_sz = 4,
ARRAY_SIZE()
> + .fields = vlan_fields,
> +};
> +
> +#define HEADER_IPV4_VERSION 1
> +#define HEADER_IPV4_IHL 2
> +#define HEADER_IPV4_DSCP 3
> +#define HEADER_IPV4_ECN 4
> +#define HEADER_IPV4_LENGTH 5
> +#define HEADER_IPV4_IDENTIFICATION 6
> +#define HEADER_IPV4_FLAGS 7
> +#define HEADER_IPV4_FRAGMENT_OFFSET 8
> +#define HEADER_IPV4_TTL 9
> +#define HEADER_IPV4_PROTOCOL 10
> +#define HEADER_IPV4_CSUM 11
> +#define HEADER_IPV4_SRC_IP 12
> +#define HEADER_IPV4_DST_IP 13
> +#define HEADER_IPV4_OPTIONS 14
> +struct net_flow_field ipv4_fields[14] = {
> + { .name = "version",
> + .uid = HEADER_IPV4_VERSION,
> + .bitwidth = 4,},
> + { .name = "ihl",
> + .uid = HEADER_IPV4_IHL,
> + .bitwidth = 4,},
> + { .name = "dscp",
> + .uid = HEADER_IPV4_DSCP,
> + .bitwidth = 6,},
> + { .name = "ecn",
> + .uid = HEADER_IPV4_ECN,
> + .bitwidth = 2,},
> + { .name = "length",
> + .uid = HEADER_IPV4_LENGTH,
> + .bitwidth = 8,},
> + { .name = "identification",
> + .uid = HEADER_IPV4_IDENTIFICATION,
> + .bitwidth = 8,},
> + { .name = "flags",
> + .uid = HEADER_IPV4_FLAGS,
> + .bitwidth = 3,},
> + { .name = "fragment_offset",
> + .uid = HEADER_IPV4_FRAGMENT_OFFSET,
> + .bitwidth = 13,},
> + { .name = "ttl",
> + .uid = HEADER_IPV4_TTL,
> + .bitwidth = 1,},
> + { .name = "protocol",
> + .uid = HEADER_IPV4_PROTOCOL,
> + .bitwidth = 8,},
> + { .name = "csum",
> + .uid = HEADER_IPV4_CSUM,
> + .bitwidth = 8,},
> + { .name = "src_ip",
> + .uid = HEADER_IPV4_SRC_IP,
> + .bitwidth = 32,},
> + { .name = "dst_ip",
> + .uid = HEADER_IPV4_DST_IP,
> + .bitwidth = 32,},
> + { .name = "options",
> + .uid = HEADER_IPV4_OPTIONS,
> + .bitwidth = -1,},
> +};
> +
> +#define HEADER_IPV4 3
> +struct net_flow_header ipv4 = {
> + .name = "ipv4",
> + .uid = HEADER_IPV4,
> + .field_sz = 14,
> + .fields = ipv4_fields,
> +};
> +
> +#define HEADER_METADATA_IN_LPORT 1
> +#define HEADER_METADATA_GOTO_TBL 2
> +#define HEADER_METADATA_GROUP_ID 3
> +struct net_flow_field metadata_fields[3] = {
> + { .name = "in_lport",
> + .uid = HEADER_METADATA_IN_LPORT,
> + .bitwidth = 32,},
> + { .name = "goto_tbl",
> + .uid = HEADER_METADATA_GOTO_TBL,
> + .bitwidth = 16,},
> + { .name = "group_id",
> + .uid = HEADER_METADATA_GROUP_ID,
> + .bitwidth = 32,},
> +};
> +
> +#define HEADER_METADATA 4
> +struct net_flow_header metadata_t = {
> + .name = "metadata_t",
> + .uid = HEADER_METADATA,
> + .field_sz = 3,
> + .fields = metadata_fields,
> +};
> +
> +struct net_flow_header null_hdr = {.name = "",
> + .uid = 0,
> + .field_sz = 0,
> + .fields = NULL};
> +
> +struct net_flow_header *rocker_header_list[8] = {
[] = ...
Not sure where the [8] comes from
> + ðernet,
> + &vlan,
> + &ipv4,
> + &metadata_t,
> + &null_hdr,
Seems just setting last entry to NULL would be cleaner:
struct foo *foo[] = {
&one,
&two,
&three,
NULL,
};
> +};
> +
> +/* action definitions */
> +struct net_flow_action_arg null_args[1] = {
> + {
> + .name = "",
> + .type = NET_FLOW_ACTION_ARG_TYPE_NULL,
> + },
> +};
> +
> +struct net_flow_action null_action = {
> + .name = "", .uid = 0, .args = NULL,
> +};
> +
> +struct net_flow_action_arg set_goto_table_args[2] = {
> + {
> + .name = "table",
> + .type = NET_FLOW_ACTION_ARG_TYPE_U16,
> + .value_u16 = 0,
> + },
> + {
> + .name = "",
> + .type = NET_FLOW_ACTION_ARG_TYPE_NULL,
> + },
> +};
> +
> +#define ACTION_SET_GOTO_TABLE 1
> +struct net_flow_action set_goto_table = {
> + .name = "set_goto_table",
> + .uid = ACTION_SET_GOTO_TABLE,
> + .args = set_goto_table_args,
> +};
> +
> +struct net_flow_action_arg set_vlan_id_args[2] = {
> + {
> + .name = "vlan_id",
> + .type = NET_FLOW_ACTION_ARG_TYPE_U16,
> + .value_u16 = 0,
> + },
> + {
> + .name = "",
> + .type = NET_FLOW_ACTION_ARG_TYPE_NULL,
> + },
> +};
> +
> +#define ACTION_SET_VLAN_ID 2
> +struct net_flow_action set_vlan_id = {
> + .name = "set_vlan_id",
> + .uid = ACTION_SET_VLAN_ID,
> + .args = set_vlan_id_args,
> +};
> +
> +/* TBD: what is the untagged bool about in vlan table */
> +#define ACTION_COPY_TO_CPU 3
> +struct net_flow_action copy_to_cpu = {
> + .name = "copy_to_cpu",
> + .uid = ACTION_COPY_TO_CPU,
> + .args = null_args,
> +};
> +
> +struct net_flow_action_arg set_group_id_args[2] = {
> + {
> + .name = "group_id",
> + .type = NET_FLOW_ACTION_ARG_TYPE_U32,
> + .value_u32 = 0,
> + },
> + {
> + .name = "",
> + .type = NET_FLOW_ACTION_ARG_TYPE_NULL,
> + },
> +};
> +
> +#define ACTION_SET_GROUP_ID 4
> +struct net_flow_action set_group_id = {
> + .name = "set_group_id",
> + .uid = ACTION_SET_GROUP_ID,
> + .args = set_group_id_args,
> +};
> +
> +#define ACTION_POP_VLAN 5
> +struct net_flow_action pop_vlan = {
> + .name = "pop_vlan",
> + .uid = ACTION_POP_VLAN,
> + .args = null_args,
> +};
> +
> +struct net_flow_action_arg set_eth_src_args[2] = {
> + {
> + .name = "eth_src",
> + .type = NET_FLOW_ACTION_ARG_TYPE_U64,
> + .value_u64 = 0,
> + },
> + {
> + .name = "",
> + .type = NET_FLOW_ACTION_ARG_TYPE_NULL,
> + },
> +};
> +
> +#define ACTION_SET_ETH_SRC 6
> +struct net_flow_action set_eth_src = {
> + .name = "set_eth_src",
> + .uid = ACTION_SET_ETH_SRC,
> + .args = set_eth_src_args,
> +};
> +
> +struct net_flow_action_arg set_eth_dst_args[2] = {
> + {
> + .name = "eth_dst",
> + .type = NET_FLOW_ACTION_ARG_TYPE_U64,
> + .value_u64 = 0,
> + },
> + {
> + .name = "",
> + .type = NET_FLOW_ACTION_ARG_TYPE_NULL,
> + },
> +};
> +
> +#define ACTION_SET_ETH_DST 7
> +struct net_flow_action set_eth_dst = {
> + .name = "set_eth_dst",
> + .uid = ACTION_SET_ETH_DST,
> + .args = set_eth_dst_args,
> +};
> +
> +struct net_flow_action_arg set_out_port_args[2] = {
> + {
> + .name = "set_out_port",
> + .type = NET_FLOW_ACTION_ARG_TYPE_U32,
> + .value_u32 = 0,
> + },
> + {
> + .name = "",
> + .type = NET_FLOW_ACTION_ARG_TYPE_NULL,
> + },
> +};
> +
> +#define ACTION_SET_OUT_PORT 8
> +struct net_flow_action set_out_port = {
> + .name = "set_out_port",
> + .uid = ACTION_SET_OUT_PORT,
> + .args = set_out_port_args,
> +};
> +
> +struct net_flow_action *rocker_action_list[8] = {
> + &set_goto_table,
> + &set_vlan_id,
> + ©_to_cpu,
> + &set_group_id,
> + &pop_vlan,
> + &set_eth_src,
> + &set_eth_dst,
> + &null_action,
> +};
> +
> +/* headers graph */
> +#define HEADER_INSTANCE_ETHERNET 1
> +#define HEADER_INSTANCE_VLAN_OUTER 2
> +#define HEADER_INSTANCE_IPV4 3
> +#define HEADER_INSTANCE_IN_LPORT 4
> +#define HEADER_INSTANCE_GOTO_TABLE 5
> +#define HEADER_INSTANCE_GROUP_ID 6
> +
> +struct net_flow_jump_table parse_ethernet[3] = {
> + {
> + .field = {
> + .header = HEADER_ETHERNET,
> + .field = HEADER_ETHERNET_ETHERTYPE,
> + .type = NET_FLOW_FIELD_REF_ATTR_TYPE_U16,
> + .value_u16 = 0x0800,
How is htons/ntohs conversions happening here?
Since these are network header fields, seems you want htons(0x0800).
> + },
> + .node = HEADER_INSTANCE_IPV4,
> + },
> + {
> + .field = {
> + .header = HEADER_ETHERNET,
> + .field = HEADER_ETHERNET_ETHERTYPE,
> + .type = NET_FLOW_FIELD_REF_ATTR_TYPE_U16,
> + .value_u16 = 0x8100,
> + },
> + .node = HEADER_INSTANCE_VLAN_OUTER,
> + },
> + {
> + .field = {0},
> + .node = 0,
> + },
just use NULL,
> +};
> +
> +int ethernet_headers[2] = {HEADER_ETHERNET, 0};
> +
> +struct net_flow_hdr_node ethernet_header_node = {
> + .name = "ethernet",
> + .uid = HEADER_INSTANCE_ETHERNET,
> + .hdrs = ethernet_headers,
> + .jump = parse_ethernet,
> +};
> +
> +struct net_flow_jump_table parse_vlan[2] = {
> + {
> + .field = {
> + .header = HEADER_VLAN,
> + .field = HEADER_VLAN_ETHERTYPE,
> + .type = NET_FLOW_FIELD_REF_ATTR_TYPE_U16,
> + .value_u16 = 0x0800,
> + },
> + .node = HEADER_INSTANCE_IPV4,
> + },
> + {
> + .field = {0},
> + .node = 0,
> + },
> +};
> +
> +int vlan_headers[2] = {HEADER_VLAN, 0};
> +struct net_flow_hdr_node vlan_header_node = {
> + .name = "vlan",
> + .uid = HEADER_INSTANCE_VLAN_OUTER,
> + .hdrs = vlan_headers,
> + .jump = parse_vlan,
> +};
> +
> +struct net_flow_jump_table terminal_headers[2] = {
> + {
> + .field = {0},
> + .node = NET_FLOW_JUMP_TABLE_DONE,
> + },
> + {
> + .field = {0},
> + .node = 0,
> + },
> +};
> +
> +int ipv4_headers[2] = {HEADER_IPV4, 0};
> +struct net_flow_hdr_node ipv4_header_node = {
> + .name = "ipv4",
> + .uid = HEADER_INSTANCE_IPV4,
> + .hdrs = ipv4_headers,
> + .jump = terminal_headers,
> +};
> +
> +int metadata_headers[2] = {HEADER_METADATA, 0};
> +struct net_flow_hdr_node in_lport_header_node = {
> + .name = "in_lport",
> + .uid = HEADER_INSTANCE_IN_LPORT,
> + .hdrs = metadata_headers,
> + .jump = terminal_headers,
> +};
> +
> +struct net_flow_hdr_node goto_table_header_node = {
> + .name = "goto_table",
> + .uid = HEADER_INSTANCE_GOTO_TABLE,
> + .hdrs = metadata_headers,
> + .jump = terminal_headers,
> +};
> +
> +struct net_flow_hdr_node group_id_header_node = {
> + .name = "group_id",
> + .uid = HEADER_INSTANCE_GROUP_ID,
> + .hdrs = metadata_headers,
> + .jump = terminal_headers,
> +};
> +
> +struct net_flow_hdr_node null_header = {.name = "", .uid = 0,};
> +
> +struct net_flow_hdr_node *rocker_header_nodes[7] = {
> + ðernet_header_node,
> + &vlan_header_node,
> + &ipv4_header_node,
> + &in_lport_header_node,
> + &goto_table_header_node,
> + &group_id_header_node,
> + &null_header,
> +};
> +
> +/* table definition */
> +struct net_flow_field_ref matches_ig_port[2] = {
> + { .instance = HEADER_INSTANCE_IN_LPORT,
> + .header = HEADER_METADATA,
> + .field = HEADER_METADATA_IN_LPORT,
> + .mask_type = NET_FLOW_MASK_TYPE_LPM},
Need other mask type, not LPM.
> +struct net_flow_table *rocker_table_list[7] = {
> + &ingress_port_table,
> + &vlan_table,
> + &term_mac_table,
> + &ucast_routing_table,
> + &bridge_table,
> + &acl_table,
> + &null_table,
> +};
cool stuff
> +
> +/* Define the table graph layout */
> +struct net_flow_jump_table table_node_ig_port_next[2] = {
> + { .field = {0}, .node = ROCKER_FLOW_TABLE_ID_VLAN},
> + { .field = {0}, .node = 0},
> +};
> +
> +struct net_flow_tbl_node table_node_ingress_port = {
> + .uid = ROCKER_FLOW_TABLE_ID_INGRESS_PORT,
> + .jump = table_node_ig_port_next};
> +
> +struct net_flow_jump_table table_node_vlan_next[2] = {
> + { .field = {0}, .node = ROCKER_FLOW_TABLE_ID_TERMINATION_MAC},
> + { .field = {0}, .node = 0},
> +};
> +
> +struct net_flow_tbl_node table_node_vlan = {
> + .uid = ROCKER_FLOW_TABLE_ID_VLAN,
> + .jump = table_node_vlan_next};
> +
> +struct net_flow_jump_table table_node_term_mac_next[2] = {
> + { .field = {0}, .node = ROCKER_FLOW_TABLE_ID_UNICAST_ROUTING},
> + { .field = {0}, .node = 0},
> +};
> +
> +struct net_flow_tbl_node table_node_term_mac = {
> + .uid = ROCKER_FLOW_TABLE_ID_TERMINATION_MAC,
> + .jump = table_node_term_mac_next};
> +
> +struct net_flow_jump_table table_node_bridge_next[2] = {
> + { .field = {0}, .node = ROCKER_FLOW_TABLE_ID_ACL_POLICY},
> + { .field = {0}, .node = 0},
> +};
> +
> +struct net_flow_tbl_node table_node_bridge = {
> + .uid = ROCKER_FLOW_TABLE_ID_BRIDGING,
> + .jump = table_node_bridge_next};
> +
> +struct net_flow_jump_table table_node_ucast_routing_next[2] = {
> + { .field = {0}, .node = ROCKER_FLOW_TABLE_ID_ACL_POLICY},
> + { .field = {0}, .node = 0},
> +};
> +
> +struct net_flow_tbl_node table_node_ucast_routing = {
> + .uid = ROCKER_FLOW_TABLE_ID_UNICAST_ROUTING,
> + .jump = table_node_ucast_routing_next};
> +
> +struct net_flow_jump_table table_node_acl_next[1] = {
> + { .field = {0}, .node = 0},
> +};
> +
> +struct net_flow_tbl_node table_node_acl = {
> + .uid = ROCKER_FLOW_TABLE_ID_ACL_POLICY,
> + .jump = table_node_acl_next};
> +
> +struct net_flow_tbl_node table_node_nil = {.uid = 0, .jump = NULL};
> +
> +struct net_flow_tbl_node *rocker_table_nodes[7] = {
> + &table_node_ingress_port,
> + &table_node_vlan,
> + &table_node_term_mac,
> + &table_node_ucast_routing,
> + &table_node_bridge,
> + &table_node_acl,
> + &table_node_nil,
> +};
Cool...getting tired but will review this again in v2
> +#endif /*_MY_PIPELINE_H*/
ROCKER
>
^ permalink raw reply
* Re: [PATCH] GMAC: fix simple_return.cocci warnings
From: Roger @ 2015-01-06 7:13 UTC (permalink / raw)
To: David Miller, joe
Cc: fengguang.wu, kbuild-all, peppe.cavallaro, netdev, linux-kernel
In-Reply-To: <20150104.222022.55122203599464788.davem@davemloft.net>
Hi! David
What should I do now?
On 2015/1/5 11:20, David Miller wrote:
> From: Joe Perches <joe@perches.com>
> Date: Fri, 02 Jan 2015 16:46:45 -0800
>
>> On Sat, 2015-01-03 at 08:25 +0800, kbuild test robot wrote:
>>> drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c:425:1-4: WARNING: end returns can be simpified
>>>
>>> Simplify a trivial if-return sequence. Possibly combine with a
>>> preceding function call.
>>> Generated by: scripts/coccinelle/misc/simple_return.cocci
>>>
>>> CC: Roger Chen <roger.chen@rock-chips.com>
>>> Signed-off-by: Fengguang Wu <fengguang.wu@intel.com>
>>> ---
>>>
>>> dwmac-rk.c | 6 +-----
>>> 1 file changed, 1 insertion(+), 5 deletions(-)
>>>
>>> --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
>>> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c
>>> @@ -422,11 +422,7 @@ static int rk_gmac_init(struct platform_
>>> if (ret)
>>> return ret;
>>>
>>> - ret = gmac_clk_enable(bsp_priv, true);
>>> - if (ret)
>>> - return ret;
>>> -
>>> - return 0;
>>> + return gmac_clk_enable(bsp_priv, true);
>> I think this change is not particularly better.
>>
>> When the pattern is multiply repeated like:
> ...
>> I think it's better to not change the last
>> test in the sequence just to minimize overall
>> line count.
> I think it's a wash and that both ways are about the same to me.
>
> I won't apply this, sorry.
>
>
>
^ permalink raw reply
* [PATCH net-next 0/5] Involve rhashtable_lookup_insert routine
From: Ying Xue @ 2015-01-06 7:23 UTC (permalink / raw)
To: tgraf; +Cc: jon.maloy, netdev, Paul.Gortmaker, tipc-discussion, davem
The series aims to involve rhashtable_lookup_insert() to guarantee
that the process of lookup and insertion of an object from/into hash
table is finished atomically, allowing rhashtable's users not to
introduce an extra lock during search and insertion. For example,
tipc socket is the first user benefiting from this enhancement.
But before rhashtable_lookup_insert() is involved, the following
optimizations need to be first done:
- simplify rhashtable_lookup by reusing rhashtable_lookup_compare()
- introduce rhashtable_wakeup_worker() to further reduce duplicated
code in patch #2
- fix an issue in patch #3
- involve rhashtable_lookup_insert(). But in this version, we firstly
use rhashtable_lookup() to search duplicate key in both old and new
bucket table; secondly introduce another __rhashtable_insert() helper
function to reduce the duplicated code between rhashtable_insert()
and rhashtable_lookup_insert().
- add patch #5 into the series as it depends on above patches. But in
this version, no change is made comparing with its previous version.
Ying Xue (5):
rhashtable: optimize rhashtable_lookup routine
rhashtable: introduce rhashtable_wakeup_worker helper function
rhashtable: use future table size to make expansion decision
rhashtable: involve rhashtable_lookup_insert routine
tipc: convert tipc reference table to use generic rhashtable
include/linux/rhashtable.h | 1 +
lib/rhashtable.c | 162 ++++++++++-----
net/tipc/Kconfig | 12 --
net/tipc/config.c | 24 +--
net/tipc/core.c | 10 +-
net/tipc/core.h | 3 -
net/tipc/socket.c | 480 ++++++++++++++++----------------------------
net/tipc/socket.h | 4 +-
8 files changed, 296 insertions(+), 400 deletions(-)
--
1.7.9.5
------------------------------------------------------------------------------
Dive into the World of Parallel Programming! The Go Parallel Website,
sponsored by Intel and developed in partnership with Slashdot Media, is your
hub for all things parallel software development, from weekly thought
leadership blogs to news, videos, case studies, tutorials and more. Take a
look and join the conversation now. http://goparallel.sourceforge.net
^ permalink raw reply
* [PATCH net-next 1/5] rhashtable: optimize rhashtable_lookup routine
From: Ying Xue @ 2015-01-06 7:23 UTC (permalink / raw)
To: tgraf; +Cc: jon.maloy, netdev, Paul.Gortmaker, tipc-discussion, davem
In-Reply-To: <1420529003-22244-1-git-send-email-ying.xue@windriver.com>
Define an internal compare function and relevant compare argument,
and then make use of rhashtable_lookup_compare() to lookup key in
hash table, reducing duplicated code between rhashtable_lookup()
and rhashtable_lookup_compare().
Signed-off-by: Ying Xue <ying.xue@windriver.com>
Cc: Thomas Graf <tgraf@suug.ch>
---
lib/rhashtable.c | 41 ++++++++++++++++++-----------------------
1 file changed, 18 insertions(+), 23 deletions(-)
diff --git a/lib/rhashtable.c b/lib/rhashtable.c
index cbad192..f2fdd7a 100644
--- a/lib/rhashtable.c
+++ b/lib/rhashtable.c
@@ -612,6 +612,19 @@ restart:
}
EXPORT_SYMBOL_GPL(rhashtable_remove);
+struct rhashtable_compare_arg {
+ struct rhashtable *ht;
+ const void *key;
+};
+
+static bool rhashtable_compare(void *ptr, void *arg)
+{
+ struct rhashtable_compare_arg *x = arg;
+ struct rhashtable *ht = x->ht;
+
+ return !memcmp(ptr + ht->p.key_offset, x->key, ht->p.key_len);
+}
+
/**
* rhashtable_lookup - lookup key in hash table
* @ht: hash table
@@ -627,32 +640,14 @@ EXPORT_SYMBOL_GPL(rhashtable_remove);
*/
void *rhashtable_lookup(struct rhashtable *ht, const void *key)
{
- const struct bucket_table *tbl, *old_tbl;
- struct rhash_head *he;
- u32 hash;
+ struct rhashtable_compare_arg arg = {
+ .ht = ht,
+ .key = key,
+ };
BUG_ON(!ht->p.key_len);
- rcu_read_lock();
- old_tbl = rht_dereference_rcu(ht->tbl, ht);
- tbl = rht_dereference_rcu(ht->future_tbl, ht);
- hash = key_hashfn(ht, key, ht->p.key_len);
-restart:
- rht_for_each_rcu(he, tbl, rht_bucket_index(tbl, hash)) {
- if (memcmp(rht_obj(ht, he) + ht->p.key_offset, key,
- ht->p.key_len))
- continue;
- rcu_read_unlock();
- return rht_obj(ht, he);
- }
-
- if (unlikely(tbl != old_tbl)) {
- tbl = old_tbl;
- goto restart;
- }
-
- rcu_read_unlock();
- return NULL;
+ return rhashtable_lookup_compare(ht, key, &rhashtable_compare, &arg);
}
EXPORT_SYMBOL_GPL(rhashtable_lookup);
--
1.7.9.5
------------------------------------------------------------------------------
Dive into the World of Parallel Programming! The Go Parallel Website,
sponsored by Intel and developed in partnership with Slashdot Media, is your
hub for all things parallel software development, from weekly thought
leadership blogs to news, videos, case studies, tutorials and more. Take a
look and join the conversation now. http://goparallel.sourceforge.net
^ permalink raw reply related
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