* Re: [PATCH] cls_flow: Add tunnel support to the flow classifier
From: Eric Dumazet @ 2011-10-24 3:59 UTC (permalink / raw)
To: Dan Siemon; +Cc: netdev
In-Reply-To: <1319426081.2517.20.camel@edumazet-laptop>
Le lundi 24 octobre 2011 à 05:14 +0200, Eric Dumazet a écrit :
> If you prefer, I can do the preliminary work
>
Since it is a bit tricky, I finished it.
It'll be easier for you to use existing functions without copy/paste,
since I added an "nhoff" argument that you can play with (skipping
tunnel header)
Please test it !
Thanks
[PATCH net-next] net_sched: cls_flow: use skb_header_pointer()
Dan Siemon would like to add tunnelling support to cls_flow
This preliminary patch introduces use of skb_header_pointer() to help
this task, while avoiding skb head reallocation because of deep packet
inspection.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
net/sched/cls_flow.c | 188 ++++++++++++++++++++---------------------
1 file changed, 96 insertions(+), 92 deletions(-)
diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c
index 6994214..9e087d8 100644
--- a/net/sched/cls_flow.c
+++ b/net/sched/cls_flow.c
@@ -65,132 +65,134 @@ static inline u32 addr_fold(void *addr)
return (a & 0xFFFFFFFF) ^ (BITS_PER_LONG > 32 ? a >> 32 : 0);
}
-static u32 flow_get_src(struct sk_buff *skb)
+static u32 flow_get_src(const struct sk_buff *skb, int nhoff)
{
+ __be32 *data = NULL, hdata;
+
switch (skb->protocol) {
case htons(ETH_P_IP):
- if (pskb_network_may_pull(skb, sizeof(struct iphdr)))
- return ntohl(ip_hdr(skb)->saddr);
+ data = skb_header_pointer(skb,
+ nhoff + offsetof(struct iphdr,
+ saddr),
+ 4, &hdata);
break;
case htons(ETH_P_IPV6):
- if (pskb_network_may_pull(skb, sizeof(struct ipv6hdr)))
- return ntohl(ipv6_hdr(skb)->saddr.s6_addr32[3]);
+ data = skb_header_pointer(skb,
+ nhoff + offsetof(struct ipv6hdr,
+ saddr.s6_addr32[3]),
+ 4, &hdata);
break;
}
+ if (data)
+ return ntohl(*data);
return addr_fold(skb->sk);
}
-static u32 flow_get_dst(struct sk_buff *skb)
+static u32 flow_get_dst(const struct sk_buff *skb, int nhoff)
{
+ __be32 *data = NULL, hdata;
+
switch (skb->protocol) {
case htons(ETH_P_IP):
- if (pskb_network_may_pull(skb, sizeof(struct iphdr)))
- return ntohl(ip_hdr(skb)->daddr);
+ data = skb_header_pointer(skb,
+ nhoff + offsetof(struct iphdr,
+ daddr),
+ 4, &hdata);
break;
case htons(ETH_P_IPV6):
- if (pskb_network_may_pull(skb, sizeof(struct ipv6hdr)))
- return ntohl(ipv6_hdr(skb)->daddr.s6_addr32[3]);
+ data = skb_header_pointer(skb,
+ nhoff + offsetof(struct ipv6hdr,
+ daddr.s6_addr32[3]),
+ 4, &hdata);
break;
}
+ if (data)
+ return ntohl(*data);
return addr_fold(skb_dst(skb)) ^ (__force u16)skb->protocol;
}
-static u32 flow_get_proto(struct sk_buff *skb)
+static u32 flow_get_proto(const struct sk_buff *skb, int nhoff)
{
+ __u8 *data = NULL, hdata;
+
switch (skb->protocol) {
case htons(ETH_P_IP):
- return pskb_network_may_pull(skb, sizeof(struct iphdr)) ?
- ip_hdr(skb)->protocol : 0;
+ data = skb_header_pointer(skb,
+ nhoff + offsetof(struct iphdr,
+ protocol),
+ 1, &hdata);
+ break;
case htons(ETH_P_IPV6):
- return pskb_network_may_pull(skb, sizeof(struct ipv6hdr)) ?
- ipv6_hdr(skb)->nexthdr : 0;
- default:
- return 0;
+ data = skb_header_pointer(skb,
+ nhoff + offsetof(struct ipv6hdr,
+ nexthdr),
+ 1, &hdata);
+ break;
}
+ if (data)
+ return *data;
+ return 0;
}
-static u32 flow_get_proto_src(struct sk_buff *skb)
+/* helper function to get either src or dst port */
+static __be16 *flow_get_proto_common(const struct sk_buff *skb, int nhoff,
+ __be16 *_port, int dst)
{
+ __be16 *port = NULL;
+ int poff;
+
switch (skb->protocol) {
case htons(ETH_P_IP): {
- struct iphdr *iph;
- int poff;
+ struct iphdr *iph, _iph;
- if (!pskb_network_may_pull(skb, sizeof(*iph)))
+ iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph);
+ if (!iph)
break;
- iph = ip_hdr(skb);
if (ip_is_fragment(iph))
break;
poff = proto_ports_offset(iph->protocol);
- if (poff >= 0 &&
- pskb_network_may_pull(skb, iph->ihl * 4 + 2 + poff)) {
- iph = ip_hdr(skb);
- return ntohs(*(__be16 *)((void *)iph + iph->ihl * 4 +
- poff));
- }
+ if (poff >= 0)
+ port = skb_header_pointer(skb,
+ nhoff + iph->ihl * 4 + poff + dst,
+ sizeof(*_port), _port);
break;
}
case htons(ETH_P_IPV6): {
- struct ipv6hdr *iph;
- int poff;
+ struct ipv6hdr *iph, _iph;
- if (!pskb_network_may_pull(skb, sizeof(*iph)))
+ iph = skb_header_pointer(skb, nhoff, sizeof(_iph), &_iph);
+ if (!iph)
break;
- iph = ipv6_hdr(skb);
poff = proto_ports_offset(iph->nexthdr);
- if (poff >= 0 &&
- pskb_network_may_pull(skb, sizeof(*iph) + poff + 2)) {
- iph = ipv6_hdr(skb);
- return ntohs(*(__be16 *)((void *)iph + sizeof(*iph) +
- poff));
- }
+ if (poff >= 0)
+ port = skb_header_pointer(skb,
+ nhoff + sizeof(*iph) + poff + dst,
+ sizeof(*_port), _port);
break;
}
}
- return addr_fold(skb->sk);
+ return port;
}
-static u32 flow_get_proto_dst(struct sk_buff *skb)
+static u32 flow_get_proto_src(const struct sk_buff *skb, int nhoff)
{
- switch (skb->protocol) {
- case htons(ETH_P_IP): {
- struct iphdr *iph;
- int poff;
+ __be16 _port, *port = flow_get_proto_common(skb, nhoff, &_port, 0);
- if (!pskb_network_may_pull(skb, sizeof(*iph)))
- break;
- iph = ip_hdr(skb);
- if (ip_is_fragment(iph))
- break;
- poff = proto_ports_offset(iph->protocol);
- if (poff >= 0 &&
- pskb_network_may_pull(skb, iph->ihl * 4 + 4 + poff)) {
- iph = ip_hdr(skb);
- return ntohs(*(__be16 *)((void *)iph + iph->ihl * 4 +
- 2 + poff));
- }
- break;
- }
- case htons(ETH_P_IPV6): {
- struct ipv6hdr *iph;
- int poff;
+ if (port)
+ return ntohs(*port);
- if (!pskb_network_may_pull(skb, sizeof(*iph)))
- break;
- iph = ipv6_hdr(skb);
- poff = proto_ports_offset(iph->nexthdr);
- if (poff >= 0 &&
- pskb_network_may_pull(skb, sizeof(*iph) + poff + 4)) {
- iph = ipv6_hdr(skb);
- return ntohs(*(__be16 *)((void *)iph + sizeof(*iph) +
- poff + 2));
- }
- break;
- }
- }
+ return addr_fold(skb->sk);
+}
+
+static u32 flow_get_proto_dst(const struct sk_buff *skb, int nhoff)
+{
+ __be16 _port, *port = flow_get_proto_common(skb, nhoff, &_port, 2);
+
+ if (port)
+ return ntohs(*port);
return addr_fold(skb_dst(skb)) ^ (__force u16)skb->protocol;
}
@@ -223,7 +225,7 @@ static u32 flow_get_nfct(const struct sk_buff *skb)
#define CTTUPLE(skb, member) \
({ \
enum ip_conntrack_info ctinfo; \
- struct nf_conn *ct = nf_ct_get(skb, &ctinfo); \
+ const struct nf_conn *ct = nf_ct_get(skb, &ctinfo); \
if (ct == NULL) \
goto fallback; \
ct->tuplehash[CTINFO2DIR(ctinfo)].tuple.member; \
@@ -236,7 +238,7 @@ static u32 flow_get_nfct(const struct sk_buff *skb)
})
#endif
-static u32 flow_get_nfct_src(struct sk_buff *skb)
+static u32 flow_get_nfct_src(const struct sk_buff *skb, int nhoff)
{
switch (skb->protocol) {
case htons(ETH_P_IP):
@@ -245,10 +247,10 @@ static u32 flow_get_nfct_src(struct sk_buff *skb)
return ntohl(CTTUPLE(skb, src.u3.ip6[3]));
}
fallback:
- return flow_get_src(skb);
+ return flow_get_src(skb, nhoff);
}
-static u32 flow_get_nfct_dst(struct sk_buff *skb)
+static u32 flow_get_nfct_dst(const struct sk_buff *skb, int nhoff)
{
switch (skb->protocol) {
case htons(ETH_P_IP):
@@ -257,21 +259,21 @@ static u32 flow_get_nfct_dst(struct sk_buff *skb)
return ntohl(CTTUPLE(skb, dst.u3.ip6[3]));
}
fallback:
- return flow_get_dst(skb);
+ return flow_get_dst(skb, nhoff);
}
-static u32 flow_get_nfct_proto_src(struct sk_buff *skb)
+static u32 flow_get_nfct_proto_src(const struct sk_buff *skb, int nhoff)
{
return ntohs(CTTUPLE(skb, src.u.all));
fallback:
- return flow_get_proto_src(skb);
+ return flow_get_proto_src(skb, nhoff);
}
-static u32 flow_get_nfct_proto_dst(struct sk_buff *skb)
+static u32 flow_get_nfct_proto_dst(const struct sk_buff *skb, int nhoff)
{
return ntohs(CTTUPLE(skb, dst.u.all));
fallback:
- return flow_get_proto_dst(skb);
+ return flow_get_proto_dst(skb, nhoff);
}
static u32 flow_get_rtclassid(const struct sk_buff *skb)
@@ -313,17 +315,19 @@ static u32 flow_get_rxhash(struct sk_buff *skb)
static u32 flow_key_get(struct sk_buff *skb, int key)
{
+ int nhoff = skb_network_offset(skb);
+
switch (key) {
case FLOW_KEY_SRC:
- return flow_get_src(skb);
+ return flow_get_src(skb, nhoff);
case FLOW_KEY_DST:
- return flow_get_dst(skb);
+ return flow_get_dst(skb, nhoff);
case FLOW_KEY_PROTO:
- return flow_get_proto(skb);
+ return flow_get_proto(skb, nhoff);
case FLOW_KEY_PROTO_SRC:
- return flow_get_proto_src(skb);
+ return flow_get_proto_src(skb, nhoff);
case FLOW_KEY_PROTO_DST:
- return flow_get_proto_dst(skb);
+ return flow_get_proto_dst(skb, nhoff);
case FLOW_KEY_IIF:
return flow_get_iif(skb);
case FLOW_KEY_PRIORITY:
@@ -333,13 +337,13 @@ static u32 flow_key_get(struct sk_buff *skb, int key)
case FLOW_KEY_NFCT:
return flow_get_nfct(skb);
case FLOW_KEY_NFCT_SRC:
- return flow_get_nfct_src(skb);
+ return flow_get_nfct_src(skb, nhoff);
case FLOW_KEY_NFCT_DST:
- return flow_get_nfct_dst(skb);
+ return flow_get_nfct_dst(skb, nhoff);
case FLOW_KEY_NFCT_PROTO_SRC:
- return flow_get_nfct_proto_src(skb);
+ return flow_get_nfct_proto_src(skb, nhoff);
case FLOW_KEY_NFCT_PROTO_DST:
- return flow_get_nfct_proto_dst(skb);
+ return flow_get_nfct_proto_dst(skb, nhoff);
case FLOW_KEY_RTCLASSID:
return flow_get_rtclassid(skb);
case FLOW_KEY_SKUID:
^ permalink raw reply related
* Re: [PATCH net -v2] [BUGFIX] bonding: use flush_delayed_work_sync in bond_close
From: HAYASAKA Mitsuo @ 2011-10-24 4:00 UTC (permalink / raw)
To: Jay Vosburgh
Cc: netdev, Américo Wang, Stephen Hemminger, Andy Gospodarek,
linux-kernel, yrl.pp-manager.tt
In-Reply-To: <14766.1319245142@death>
(2011/10/22 9:59), Jay Vosburgh wrote:
> Jay Vosburgh <fubar@us.ibm.com> wrote:
>
>> Américo Wang <xiyou.wangcong@gmail.com> wrote:
>>
>>> On Thu, Oct 20, 2011 at 3:09 AM, Jay Vosburgh <fubar@us.ibm.com> wrote:
>>>> Stephen Hemminger <shemminger@vyatta.com> wrote:
>>>>
>>>>> On Wed, 19 Oct 2011 11:01:02 -0700
>>>>> Jay Vosburgh <fubar@us.ibm.com> wrote:
>>>>>
>>>>>> Mitsuo Hayasaka <mitsuo.hayasaka.hu@hitachi.com> wrote:
>>>>>>
>>>>>>> The bond_close() calls cancel_delayed_work() to cancel delayed works.
>>>>>>> It, however, cannot cancel works that were already queued in workqueue.
>>>>>>> The bond_open() initializes work->data, and proccess_one_work() refers
>>>>>>> get_work_cwq(work)->wq->flags. The get_work_cwq() returns NULL when
>>>>>>> work->data has been initialized. Thus, a panic occurs.
>>>>>>>
>>>>>>> This patch uses flush_delayed_work_sync() instead of cancel_delayed_work()
>>>>>>> in bond_close(). It cancels delayed timer and waits for work to finish
>>>>>>> execution. So, it can avoid the null pointer dereference due to the
>>>>>>> parallel executions of proccess_one_work() and initializing proccess
>>>>>>> of bond_open().
>>>>>>
>>>>>> I'm setting up to test this. I have a dim recollection that we
>>>>>> tried this some years ago, and there was a different deadlock that
>>>>>> manifested through the flush path. Perhaps changes since then have
>>>>>> removed that problem.
>>>>>>
>>>>>> -J
>>>>>
>>>>> Won't this deadlock on RTNL. The problem is that:
>>>>>
>>>>> CPU0 CPU1
>>>>> rtnl_lock
>>>>> bond_close
>>>>> delayed_work
>>>>> mii_work
>>>>> read_lock(bond->lock);
>>>>> read_unlock(bond->lock);
>>>>> rtnl_lock... waiting for CPU0
>>>>> flush_delayed_work_sync
>>>>> waiting for delayed_work to finish...
>>>>
>>>> Yah, that was it. We discussed this a couple of years ago in
>>>> regards to a similar patch:
>>>>
>>>> http://lists.openwall.net/netdev/2009/12/17/3
>>>>
>>>> The short version is that we could rework the rtnl_lock inside
>>>> the montiors to be conditional and retry on failure (where "retry" means
>>>> "reschedule the work and try again later," not "spin retrying on rtnl").
>>>> That should permit the use of flush or cancel to terminate the work
>>>> items.
>>>
>>> Yes? Even if we use rtnl_trylock(), doesn't flush_delayed_work_sync()
>>> still queue the pending delayed work and wait for it to be finished?
>>
>> Yes, it does. The original patch wants to use flush instead of
>> cancel to wait for the work to finish, because there's evidently a
>> possibility of getting back into bond_open before the work item
>> executes, and bond_open would reinitialize the work queue and corrupt
>> the queued work item.
>>
>> The original patch series, and recipe for destruction, is here:
>>
>> http://www.spinics.net/lists/netdev/msg176382.html
>>
>> I've been unable to reproduce the work queue panic locally,
>> although it sounds plausible.
>>
>> Mitsuo: can you provide the precise bonding configuration you're
>> using to induce the problem? Driver options, number and type of slaves,
>> etc.
>>
>>> Maybe I am too blind, why do we need rtnl_lock for cancel_delayed_work()
>>> inside bond_close()?
>>
>> We don't need RTNL for cancel/flush. However, bond_close is an
>> ndo_stop operation, and is called in the dev_close path, which always
>> occurs under RTNL. The mii / arp monitor work functions separately
>> acquire RTNL if they need to perform various failover related
>> operations.
>>
>> I'm working on a patch that should resolve the mii / arp monitor
>> RTNL problem as I described above (if rtnl_trylock fails, punt and
>> reschedule the work). I need to rearrange the netdev_bonding_change
>> stuff a bit as well, since it acquires RTNL separately.
>>
>> Once these changes are made to mii / arp monitor, then
>> bond_close can call flush instead of cancel, which should eliminate the
>> original problem described at the top.
>
> Just an update: there are three functions that may deadlock if
> the cancel work calls are changed to flush_sync. There are two
> rtnl_lock calls in each of the bond_mii_monitor and
> bond_activebackup_arp_mon functions, and one more in the
> bond_alb_monitor.
>
> Still testing to make sure I haven't missed anything, and I
> still haven't been able to reproduce Mitsuo's original failure.
The interval of mii_mon was set to 1 to reproduce this bug easily and
the 802.3ad mode was used. Then, I executed the following command.
# while true; do ifconfig bond0 down; done &
# while true; do ifconfig bond0 up; done &
This bug rarely occurs since it is the severe timing problem.
I found that it is more easily to reproduce this bug when using guest OS.
For example, it took one to three days for me to reproduce it on host OS,
but some hours on guest OS.
Thanks.
>
> -J
>
> ---
> -Jay Vosburgh, IBM Linux Technology Center, fubar@us.ibm.com
>
>
^ permalink raw reply
* Re: Flow classifier proto-dst and TOS (and proto-src)
From: Eric Dumazet @ 2011-10-24 4:02 UTC (permalink / raw)
To: Dan Siemon; +Cc: netdev
In-Reply-To: <1319418232.20602.16.camel@ganymede>
Le dimanche 23 octobre 2011 à 21:03 -0400, Dan Siemon a écrit :
> On Mon, 2011-10-17 at 08:09 +0200, Eric Dumazet wrote:
> > Le samedi 15 octobre 2011 à 12:51 -0400, Dan Siemon a écrit :
> > > cls_flow.c: flow_get_proto_dst()
> > >
> > > The proto-dst key returns the destination port for UDP, TCP and a few
> > > other protocols [see proto_ports_offset()]. For ICMP and IPIP it falls
> > > back to:
> > >
> > > return addr_fold(skb_dst(skb)) ^ (__force u16)skb->protocol;
> > >
> > > Since Linux maintains a dst_entry for each TOS value this causes the
> > > returned value to be affected by the TOS which is unexpected and
> > > probably broken.
> >
> > Hi Dan
> >
> > I think Patrick did this on purpose, because of of the lack of
> > perturbation in cls_flow.c : If all these frames were mapped to a single
> > flow, they might interfere with an other regular flow and hurt it.
> >
> > I dont qualify existing code as buggy. Its about fallback behavior
> > anyway (I dont think its even documented)
>
> Thanks for the review Eric.
>
> Won't virtually all uses of proto-dst also use the dst key anyway? In
> which case this fallback does nothing except make the TOS effect the
> hash output because the dst will be the same and dst_entry would be the
> same if it wasn't for the different TOS (by far the common case). I
> don't see the value of the unintuitive behavior.
>
> I'm not certain this is a problem but also note that including TOS will
> mean that packets within a tunnel will be reordered if 'tos inherit' is
> set on the tunnel and only the typical src,dst,proto,proto-src,proto-dst
> is used. Again, probably not expected.
Hmm, it seems cls_flow now supports "perturbation", so my prior argument
is not true anymore. I guess your patch would be fine, but I prefer
waiting Patrick review on it ;)
^ permalink raw reply
* Re: [PATCH 9/9] make net/core/scm.c uid comparisons user namespace aware
From: Serge E. Hallyn @ 2011-10-24 4:15 UTC (permalink / raw)
To: Serge E. Hallyn
Cc: Eric W. Biederman, linux-kernel, akpm, oleg, richard, mikevs,
segoon, gregkh, dhowells, eparis, netdev
In-Reply-To: <20111020141440.GA6201@sergelap>
Quoting Serge E. Hallyn (serge.hallyn@canonical.com):
> Quoting Eric W. Biederman (ebiederm@xmission.com):
> > "Serge E. Hallyn" <serge@hallyn.com> writes:
> >
> > > Quoting Eric W. Biederman (ebiederm@xmission.com):
> > >> Serge Hallyn <serge@hallyn.com> writes:
> > >>
> > >> > From: "Serge E. Hallyn" <serge.hallyn@canonical.com>
> > >> >
> > >> > Currently uids are compared without regard for the user namespace.
> > >> > Fix that to prevent tasks in a different user namespace from
> > >> > wrongly matching on SCM_CREDENTIALS.
> > >> >
> > >> > In the past, either your uids had to match, or you had to have
> > >> > CAP_SETXID. In a namespaced world, you must either (both be in the
> > >> > same user namespace and have your uids match), or you must have
> > >> > CAP_SETXID targeted at the other user namespace. The latter can
> > >> > happen for instance if uid 500 created a new user namespace and
> > >> > now interacts with uid 0 in it.
> > >>
> > >> Serge this approach is wrong.
> > >
> > > Thanks for looking, Eric.
> > >
> > >> Because we pass the cred and the pid through the socket socket itself
> > >> is just a conduit and should be ignored in this context.
> > >
> > > Ok, that makes sense, but
> > >
> > >> The only interesting test should be are you allowed to impersonate other
> > >> users in your current userk namespace.
> > >
> > > Why in your current user namespace? Shouldn't it be in the
> > > target user ns? I understand it could be wrong to tie the
> > > user ns owning the socket to the target userns (though I still
> > > kind of like it), but just because I have CAP_SETUID in my
> > > own user_ns doesn't mean I should be able to pose as another
> > > uid in your user_ns.
> >
> > First and foremost it is important that you be able if you have the
> > capability to impersonate other users in your current user namespace.
> > That is what the capability actually controls.
> >
> > None of this allows you to impersonate any user in any other user
> > namespace. The translation between users prevents that.
> >
> > > (Now I also see that cred_to_ucred() translates to the current
> > > user_ns, so that should have been a hint to me before about
> > > your intent, but I'm not convinced I agree with your intent).
> > >
> > > And you do the same with the pid. Why is that a valid assumption?
> >
> > Yes. Basically all the code is allow you to impersonate people you
> > would have been able to impersonate before. If your target is in
> > another namespace you can not fool them.
> >
> > With pids the logic should be a lot clearer. Pretend to be a pid you can
> > see in your current pid namespace. Lookup and convert to struct pid aka
> > the namespace agnostic object. On output return the pid value that
>
> No. That conversion is happending before the user-specified pid is
> set.
Never mind, it all gets a little convoluted, but I see how it works,
and - when the time comes - how to do it right for userns. :) Sorry
about that.
thanks,
-serge
^ permalink raw reply
* Re: [RFD] Network configuration data in sysfs
From: Kirill A. Shutemov @ 2011-10-24 4:24 UTC (permalink / raw)
To: David Miller
Cc: kay.sievers, netdev, kuznet, jmorris, yoshfuji, kaber, gregkh,
gladkov.alexey
In-Reply-To: <20111023.232416.1038111296509565828.davem@davemloft.net>
On Sun, Oct 23, 2011 at 11:24:16PM -0400, David Miller wrote:
> From: "Kirill A. Shutemov" <kirill@shutemov.name>
> Date: Mon, 24 Oct 2011 04:34:07 +0300
>
> > On Sun, Oct 23, 2011 at 08:49:43PM -0400, David Miller wrote:
> >> From: "Kirill A. Shutemov" <kirill@shutemov.name>
> >> Date: Mon, 24 Oct 2011 02:35:58 +0300
> >>
> >> > Is there something fundamental preventing us to have sysfs interface for
> >> > network configuration?
> >>
> >> Netlink already provides everything sysfs would potentially provide as
> >> well as event tracking.
> >
> > Not everything. You still need special utilities to view/change the
> > configuration.
>
> You can use netlink to perform any configuration change you want, or
> to view any network configuration setting.
You need /sbin/ip or similar tool to do this, right?
--
Kirill A. Shutemov
^ permalink raw reply
* Re: [RFC v2 PATCH 5/4 PATCH] virtio-net: send gratuitous packet when needed
From: Rusty Russell @ 2011-10-24 4:24 UTC (permalink / raw)
To: Jason Wang, aliguori, quintela, jan.kiszka, mst, qemu-devel,
blauwirbel
Cc: pbonzini, kvm, netdev
In-Reply-To: <20111022054311.21798.3340.stgit@dhcp-8-146.nay.redhat.com>
On Sat, 22 Oct 2011 13:43:11 +0800, Jason Wang <jasowang@redhat.com> wrote:
> This make let virtio-net driver can send gratituous packet by a new
> config bit - VIRTIO_NET_S_ANNOUNCE in each config update
> interrupt. When this bit is set by backend, the driver would schedule
> a workqueue to send gratituous packet through NETDEV_NOTIFY_PEERS.
>
> This feature is negotiated through bit VIRTIO_NET_F_GUEST_ANNOUNCE.
>
> Signed-off-by: Jason Wang <jasowang@redhat.com>
This seems like a huge layering violation. Imagine this in real
hardware, for example.
There may be a good reason why virtual devices might want this kind of
reconfiguration cheat, which is unnecessary for normal machines, but
it'd have to be spelled out clearly in the spec to justify it...
Cheers,
Rusty.
^ permalink raw reply
* Re: [PATCH 9/9] make net/core/scm.c uid comparisons user namespace aware
From: Eric W. Biederman @ 2011-10-24 4:27 UTC (permalink / raw)
To: Serge E. Hallyn
Cc: Serge E. Hallyn, linux-kernel, akpm, oleg, richard, mikevs,
segoon, gregkh, dhowells, eparis, netdev
In-Reply-To: <20111024041529.GA23618@hallyn.com>
"Serge E. Hallyn" <serge@hallyn.com> writes:
> Never mind, it all gets a little convoluted, but I see how it works,
> and - when the time comes - how to do it right for userns. :) Sorry
> about that.
No problem.
Eric
^ permalink raw reply
* Re: [RFD] Network configuration data in sysfs
From: David Miller @ 2011-10-24 4:59 UTC (permalink / raw)
To: kirill
Cc: kay.sievers, netdev, kuznet, jmorris, yoshfuji, kaber, gregkh,
gladkov.alexey
In-Reply-To: <20111024042400.GA31344@shutemov.name>
From: "Kirill A. Shutemov" <kirill@shutemov.name>
Date: Mon, 24 Oct 2011 07:24:00 +0300
> On Sun, Oct 23, 2011 at 11:24:16PM -0400, David Miller wrote:
>> From: "Kirill A. Shutemov" <kirill@shutemov.name>
>> Date: Mon, 24 Oct 2011 04:34:07 +0300
>>
>> You can use netlink to perform any configuration change you want, or
>> to view any network configuration setting.
>
> You need /sbin/ip or similar tool to do this, right?
I'm talking about udev using netlink natively.
^ permalink raw reply
* Re: -next: NET_VENDOR_8390 dependencies
From: Jeff Kirsher @ 2011-10-24 5:07 UTC (permalink / raw)
To: Geert Uytterhoeven; +Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <CAMuHMdW6hC1JchxVL9-4f58a8Vg7cF+97BbKePxjEDHY3H8wNw@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 2060 bytes --]
On Sun, 2011-10-23 at 14:21 -0700, Geert Uytterhoeven wrote:
> drivers/net/ethernet/8390/Kconfig:
>
> config NET_VENDOR_8390
> bool "National Semi-conductor 8390 devices"
> default y
> depends on NET_VENDOR_NATSEMI && (AMIGA_PCMCIA || PCI || SUPERH || \
> ISA || MCA || EISA || MAC || M32R || MACH_TX49XX || \
> MCA_LEGACY || H8300 || ARM || MIPS || ZORRO || PCMCIA || \
> EXPERIMENTAL)
> ---help---
> If you have a network (Ethernet) card belonging to this class, say Y
> and read the Ethernet-HOWTO, available from
> <http://www.tldp.org/docs.html#howto>.
>
> Note that the answer to this question doesn't directly affect the
> kernel: saying N will just cause the configurator to skip all
> the questions about Western Digital cards. If you say Y, you will be
> asked for your specific card in the following questions.
>
> So NET_VENDOR_8390 depends on NET_VENDOR_NATSEMI.
>
> drivers/net/ethernet/natsemi/Kconfig:
>
> config NET_VENDOR_NATSEMI
> bool "National Semi-conductor devices"
> default y
> depends on MCA || MAC || MACH_JAZZ || PCI || XTENSA_PLATFORM_XT2000
> ---help---
> If you have a network (Ethernet) card belonging to this class, say Y
> and read the Ethernet-HOWTO, available from
> <http://www.tldp.org/docs.html#howto>.
>
> Note that the answer to this question doesn't directly affect the
> kernel: saying N will just cause the configurator to skip all
> the questions about National Semi-conductor devices. If you say Y,
> you will be asked for your specific card in the following questions.
>
> But NET_VENDOR_NATSEMI will never be true for several of the other
> dependencies of NET_VENDOR_8390 (e.g. AMIGA_PCMCIA, EISA, H8300, ARM,
> ZORRO, PCMCIA)?
>
You are correct, I will put together a patch to fix that. Thanks!
[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: [RFC v2 PATCH 5/4 PATCH] virtio-net: send gratuitous packet when needed
From: Michael S. Tsirkin @ 2011-10-24 5:25 UTC (permalink / raw)
To: Rusty Russell
Cc: aliguori, kvm, quintela, jan.kiszka, Jason Wang, qemu-devel,
blauwirbel, netdev, pbonzini
In-Reply-To: <87ty6z10f8.fsf@rustcorp.com.au>
On Mon, Oct 24, 2011 at 02:54:59PM +1030, Rusty Russell wrote:
> On Sat, 22 Oct 2011 13:43:11 +0800, Jason Wang <jasowang@redhat.com> wrote:
> > This make let virtio-net driver can send gratituous packet by a new
> > config bit - VIRTIO_NET_S_ANNOUNCE in each config update
> > interrupt. When this bit is set by backend, the driver would schedule
> > a workqueue to send gratituous packet through NETDEV_NOTIFY_PEERS.
> >
> > This feature is negotiated through bit VIRTIO_NET_F_GUEST_ANNOUNCE.
> >
> > Signed-off-by: Jason Wang <jasowang@redhat.com>
>
> This seems like a huge layering violation. Imagine this in real
> hardware, for example.
commits 06c4648d46d1b757d6b9591a86810be79818b60c
and 99606477a5888b0ead0284fecb13417b1da8e3af
document the need for this:
NETDEV_NOTIFY_PEERS notifier indicates that a device moved to a
different physical link.
and
In real hardware such notifications are only
generated when the device comes up or the address changes.
So hypervisor could get the same behaviour by sending link up/down
events, this is just an optimization so guest won't do
unecessary stuff like try to reconfigure an IP address.
Maybe LOCATION_CHANGE would be a better name?
> There may be a good reason why virtual devices might want this kind of
> reconfiguration cheat, which is unnecessary for normal machines,
I think yes, the difference with real hardware is guest can change
location without link getting dropped.
FWIW, Xen seems to use this capability too.
> but
> it'd have to be spelled out clearly in the spec to justify it...
>
> Cheers,
> Rusty.
Agree, and I'd like to see the spec too. The interface seems
to involve the guest clearing the status bit when it detects
an event?
Also - how does it interact with the link up event?
We probably don't want to schedule this when we detect
a link status change or during initialization, as
this patch seems to do? What if link goes down
while the work is running? Is that OK?
--
MST
^ permalink raw reply
* Re: [PATCH net-next] Add ethtool -g support to virtio_net
From: Michael S. Tsirkin @ 2011-10-24 5:31 UTC (permalink / raw)
To: David Miller; +Cc: rusty, raj, netdev, virtualization
In-Reply-To: <20111023.232604.18246342474494526.davem@davemloft.net>
On Sun, Oct 23, 2011 at 11:26:04PM -0400, David Miller wrote:
> From: Rusty Russell <rusty@rustcorp.com.au>
> Date: Thu, 20 Oct 2011 18:25:24 +1030
>
> > On Wed, 19 Oct 2011 11:10:59 -0700 (PDT), raj@tardy.cup.hp.com (Rick Jones) wrote:
> >> From: Rick Jones <rick.jones2@hp.com>
> >>
> >> Add support for reporting ring sizes via ethtool -g to the virtio_net
> >> driver.
> >>
> >> Signed-off-by: Rick Jones <rick.jones2@hp.com>
> >
> > Acked-by: Rusty Russell <rusty@rustcorp.com.au>
> >
> > MST, want me to take this, or do you?
>
> I can also take it directly, just let me know.
Please do, thanks very much.
Acked-by: Michael S. Tsirkin <mst@redhat.com>
--
MST
^ permalink raw reply
* Re: [net-next-2.6 PATCH 0/8 RFC v2] macvlan: MAC Address filtering support for passthru mode
From: Michael S. Tsirkin @ 2011-10-24 5:47 UTC (permalink / raw)
To: Roopa Prabhu
Cc: netdev, sri, dragos.tatulea, arnd, kvm, davem, mchan, dwang2,
shemminger, eric.dumazet, kaber, benve
In-Reply-To: <20111019062543.7242.3969.stgit@savbu-pc100.cisco.com>
On Tue, Oct 18, 2011 at 11:25:54PM -0700, Roopa Prabhu wrote:
> v1 version of this RFC patch was posted at http://www.spinics.net/lists/netdev/msg174245.html
>
> Today macvtap used in virtualized environment does not have support to
> propagate MAC, VLAN and interface flags from guest to lowerdev.
> Which means to be able to register additional VLANs, unicast and multicast
> addresses or change pkt filter flags in the guest, the lowerdev has to be
> put in promisocous mode. Today the only macvlan mode that supports this is
> the PASSTHRU mode and it puts the lower dev in promiscous mode.
>
> PASSTHRU mode was added primarily for the SRIOV usecase. In PASSTHRU mode
> there is a 1-1 mapping between macvtap and physical NIC or VF.
>
> There are two problems with putting the lowerdev in promiscous mode (ie SRIOV
> VF's):
> - Some SRIOV cards dont support promiscous mode today (Thread on Intel
> driver indicates that http://lists.openwall.net/netdev/2011/09/27/6)
> - For the SRIOV NICs that support it, Putting the lowerdev in
> promiscous mode leads to additional traffic being sent up to the
> guest virtio-net to filter result in extra overheads.
>
> Both the above problems can be solved by offloading filtering to the
> lowerdev hw. ie lowerdev does not need to be in promiscous mode as
> long as the guest filters are passed down to the lowerdev.
>
> This patch basically adds the infrastructure to set and get MAC and VLAN
> filters on an interface via rtnetlink. And adds support in macvlan and macvtap
> to allow set and get filter operations.
Looks sane to me. Some minor comments below.
> Earlier version of this patch provided the TUNSETTXFILTER macvtap interface
> for setting address filtering. In response to feedback, This version
> introduces a netlink interface for the same.
>
> Response to some of the questions raised during v1:
>
> - Netlink interface:
> This patch provides the following netlink interface to set mac and vlan
> filters :
> [IFLA_RX_FILTER] = {
> [IFLA_ADDR_FILTER] = {
> [IFLA_ADDR_FILTER_FLAGS]
> [IFLA_ADDR_FILTER_UC_LIST] = {
> [IFLA_ADDR_LIST_ENTRY]
> }
> [IFLA_ADDR_FILTER_MC_LIST] = {
> [IFLA_ADDR_LIST_ENTRY]
> }
> }
> [IFLA_VLAN_FILTER] = {
> [IFLA_VLAN_BITMAP]
> }
> }
>
> Note: The IFLA_VLAN_FILTER is a nested attribute and contains only
> IFLA_VLAN_BITMAP today. The idea is that the IFLA_VLAN_FILTER can
> be extended tomorrow to use a vlan list option if some implementations
> prefer a list instead.
>
> And it provides the following rtnl_link_ops to set/get MAC/VLAN filters:
>
> int (*set_rx_addr_filter)(struct net_device *dev,
> struct nlattr *tb[]);
> int (*set_rx_vlan_filter)(struct net_device *dev,
> struct nlattr *tb[]);
> size_t (*get_rx_addr_filter_size)(const struct
> net_device *dev);
> size_t (*get_rx_vlan_filter_size)(const struct
> net_device *dev);
> int (*fill_rx_addr_filter)(struct sk_buff *skb,
> const struct net_device *dev);
> int (*fill_rx_vlan_filter)(struct sk_buff *skb,
> const struct net_device *dev);
>
>
> Note: The choice of rtnl_link_ops was because I saw the use case for
> this in virtual devices that need to do filtering in sw like macvlan
> and tun. Hw devices usually have filtering in hw with netdev->uc and
> mc lists to indicate active filters. But I can move from rtnl_link_ops
> to netdev_ops if that is the preferred way to go and if there is a
> need to support this interface on all kinds of interfaces.
> Please suggest.
>
> - Protection against address spoofing:
> - This patch adds filtering support only for macvtap PASSTHRU
> Mode. PASSTHRU mode is used mainly with SRIOV VF's. And SRIOV VF's
> come with anti mac/vlan spoofing support. (Recently added
> IFLA_VF_SPOOFCHK). In 802.1Qbh case the port profile has a knob to
> enable/disable anti spoof check. Lowerdevice drivers also enforce limits
> on the number of address registrations allowed.
>
> - Support for multiqueue devices: Enable filtering on individual queues (?):
> AFAIK, there is no netdev interface to install per queue hw
> filters for a multi queue interface. And also I dont know of any hw
> that provides an interface to set hw filters on a per queue basis.
VMDq hardware would support this, no?
> A multi queue device appears as a single lowerdev (ie netdev) and
> uses the same uc and mc lists to setup unicast and multicast hw filters.
> So i dont see a huge problem with this patch coming in the way for
> multi queue devices.
>
> - Support for non-PASSTHRU mode:
> I started implementing this. But there are a couple of problems.
> - The lowerdev may not be a SRIOV VF and may not have
> anti spoof capability
Anti-spoofing a really a separate feature, isn't it?
> - Today, in non-PASSTHRU cases macvlan_handle_frame assumes that
> every macvlan device on top of the lowerdev has a single unique mac.
> And the macvlans are hashed on that single mac address.
> To support filtering for non-PASSTHRU mode in addition to this
> patch the following needs to be done:
> - non-passthru mode with a single macvlan over a lower dev
> can be treated as PASSTHRU case
> - For non-PASSTHRU mode with multiple macvlans over a single
> lower dev:
> - Multiple unicast mac's now need to be hashed to the
> same macvlan device. The macvlan hash needs to change
> for lookup based on any one of the multiple unicast
> addresses a macvlan is interested in
> - We need to consider vlans during the lookup too
> - So the macvlan device hash needs to hash on both mac
> and vlan
It might be useful to expose the filters to the device.
> - But the support for filtering in non-PASSTHRU mode can be
> built on this patch
Agree, this can be added gradually.
> This patch series implements the following
> 01/8 rtnetlink: Netlink interface for setting MAC and VLAN filters
> 02/8 rtnetlink: Add rtnl link operations for MAC address and VLAN filtering
> 03/8 rtnetlink: Add support to set MAC/VLAN filters
> 04/8 rtnetlink: Add support to get MAC/VLAN filters
> 05/8 macvlan: Add support to set MAC/VLAN filter rtnl link operations
> 06/8 macvlan: Add support to get MAC/VLAN filter rtnl link operations
> 07/8 macvtap: Add support to set MAC/VLAN filter rtnl link operations
> 08/8 macvtap: Add support to get MAC/VLAN filter rtnl link operations
>
> Please comment. Thanks.
>
> Signed-off-by: Roopa Prabhu <roprabhu@cisco.com>
> Signed-off-by: Christian Benvenuti <benve@cisco.com>
> Signed-off-by: David Wang <dwang2@cisco.com>
^ permalink raw reply
* Re: [PATCH net-next] tcp: avoid frag allocation for small frames
From: David Miller @ 2011-10-24 5:53 UTC (permalink / raw)
To: eric.dumazet; +Cc: netdev
In-Reply-To: <1319286237.6180.49.camel@edumazet-laptop>
From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Sat, 22 Oct 2011 14:23:57 +0200
> tcp_sendmsg() uses select_size() helper to choose skb head size when a
> new skb must be allocated.
>
> If GSO is enabled for the socket, current strategy is to force all
> payload data to be outside of headroom, in PAGE fragments.
>
> This strategy is not welcome for small packets, wasting memory.
>
> Experiments show that best results are obtained when using 2048 bytes
> for skb head (This includes the skb overhead and various headers)
>
> This patch provides better len/truesize ratios for packets sent to
> loopback device, and reduce memory needs for in-flight loopback packets,
> particularly on arches with big pages.
>
> If a sender sends many 1-byte packets to an unresponsive application,
> receiver rmem_alloc will grow faster and will stop queuing these packets
> sooner, or will collapse its receive queue to free excess memory.
>
> netperf -t TCP_RR results are improved by ~4 %, and many workloads are
> improved as well (tbench, mysql...)
>
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Applied, thanks Eric.
^ permalink raw reply
* Re: [net-next-2.6 PATCH 8/8 RFC v2] macvtap: Add support to get MAC/VLAN filter rtnl link operations
From: Michael S. Tsirkin @ 2011-10-24 5:56 UTC (permalink / raw)
To: Roopa Prabhu
Cc: netdev, sri, dragos.tatulea, arnd, kvm, davem, mchan, dwang2,
shemminger, eric.dumazet, kaber, benve
In-Reply-To: <20111019062636.7242.46889.stgit@savbu-pc100.cisco.com>
On Tue, Oct 18, 2011 at 11:26:36PM -0700, Roopa Prabhu wrote:
> From: Roopa Prabhu <roprabhu@cisco.com>
>
> This patch adds support to get MAC and VLAN filter rtnl_link_ops
> on a macvtap interface. It adds support for get_rx_addr_filter_size,
> get_rx_vlan_filter_size, fill_rx_addr_filter and fill_rx_vlan_filter
> rtnl link operations. Calls equivalent macvlan operations.
>
> Signed-off-by: Roopa Prabhu <roprabhu@cisco.com>
> Signed-off-by: Christian Benvenuti <benve@cisco.com>
> Signed-off-by: David Wang <dwang2@cisco.com>
> ---
> drivers/net/macvtap.c | 27 +++++++++++++++++++++++++++
> 1 files changed, 27 insertions(+), 0 deletions(-)
>
>
> diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
> index 8a2cb59..9b40de7 100644
> --- a/drivers/net/macvtap.c
> +++ b/drivers/net/macvtap.c
> @@ -285,6 +285,29 @@ static int macvtap_set_rx_vlan_filter(struct net_device *dev,
> return macvlan_set_rx_vlan_filter(dev, tb);
> }
>
> +static int macvtap_fill_rx_addr_filter(struct sk_buff *skb,
> + const struct net_device *dev)
> +{
> + return macvlan_fill_rx_addr_filter(skb, dev);
> +}
> +
> +static int macvtap_fill_rx_vlan_filter(struct sk_buff *skb,
> + const struct net_device *dev)
> +{
> + return macvlan_fill_rx_vlan_filter(skb, dev);
> +}
> +
> +static size_t macvtap_get_rx_addr_filter_size(const struct net_device *dev)
> +{
> + return macvlan_get_rx_addr_filter_size(dev);
> +}
> +
> +static size_t macvtap_get_rx_vlan_filter_size(const struct net_device *dev)
> +{
> + return macvlan_get_rx_vlan_filter_size(dev);
> +}
So why do we need the above wrappers? Can't use macvlanXXX directly?
> +
> +
don't add double emoty lines pls.
> static int macvtap_newlink(struct net *src_net,
> struct net_device *dev,
> struct nlattr *tb[],
> @@ -335,6 +358,10 @@ static struct rtnl_link_ops macvtap_link_ops __read_mostly = {
> .dellink = macvtap_dellink,
> .set_rx_addr_filter = macvtap_set_rx_addr_filter,
> .set_rx_vlan_filter = macvtap_set_rx_vlan_filter,
> + .get_rx_addr_filter_size = macvtap_get_rx_addr_filter_size,
> + .get_rx_vlan_filter_size = macvtap_get_rx_vlan_filter_size,
> + .fill_rx_addr_filter = macvtap_fill_rx_addr_filter,
> + .fill_rx_vlan_filter = macvtap_fill_rx_vlan_filter,
> };
>
>
^ permalink raw reply
* Re: [net-next-2.6 PATCH 7/8 RFC v2] macvtap: Add support to set MAC/VLAN filter rtnl link operations
From: Michael S. Tsirkin @ 2011-10-24 5:57 UTC (permalink / raw)
To: Roopa Prabhu
Cc: netdev, sri, dragos.tatulea, arnd, kvm, davem, mchan, dwang2,
shemminger, eric.dumazet, kaber, benve
In-Reply-To: <20111019062630.7242.99374.stgit@savbu-pc100.cisco.com>
On Tue, Oct 18, 2011 at 11:26:30PM -0700, Roopa Prabhu wrote:
> From: Roopa Prabhu <roprabhu@cisco.com>
>
> This patch adds support to set MAC and VLAN filter rtnl_link_ops
> on a macvtap interface. It adds support for set_rx_addr_filter and
> set_rx_vlan_filter rtnl link operations. These operations inturn call the
> equivalent operations defined in macvlan
>
> Signed-off-by: Roopa Prabhu <roprabhu@cisco.com>
> Signed-off-by: Christian Benvenuti <benve@cisco.com>
> Signed-off-by: David Wang <dwang2@cisco.com>
> ---
> drivers/net/macvtap.c | 22 ++++++++++++++++++----
> 1 files changed, 18 insertions(+), 4 deletions(-)
>
>
> diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
> index 3da5578..8a2cb59 100644
> --- a/drivers/net/macvtap.c
> +++ b/drivers/net/macvtap.c
> @@ -273,6 +273,18 @@ static int macvtap_receive(struct sk_buff *skb)
> return macvtap_forward(skb->dev, skb);
> }
>
> +static int macvtap_set_rx_addr_filter(struct net_device *dev,
> + struct nlattr *tb[])
> +{
> + return macvlan_set_rx_addr_filter(dev, tb);
> +}
> +
> +static int macvtap_set_rx_vlan_filter(struct net_device *dev,
> + struct nlattr *tb[])
> +{
> + return macvlan_set_rx_vlan_filter(dev, tb);
> +}
> +
> static int macvtap_newlink(struct net *src_net,
> struct net_device *dev,
> struct nlattr *tb[],
Can use macvlanXXX directly here? Why wrap it?
> @@ -317,10 +329,12 @@ static void macvtap_setup(struct net_device *dev)
> }
>
> static struct rtnl_link_ops macvtap_link_ops __read_mostly = {
> - .kind = "macvtap",
> - .setup = macvtap_setup,
> - .newlink = macvtap_newlink,
> - .dellink = macvtap_dellink,
> + .kind = "macvtap",
> + .setup = macvtap_setup,
> + .newlink = macvtap_newlink,
> + .dellink = macvtap_dellink,
> + .set_rx_addr_filter = macvtap_set_rx_addr_filter,
> + .set_rx_vlan_filter = macvtap_set_rx_vlan_filter,
> };
>
>
^ permalink raw reply
* Re: [PATCH net-next] Add ethtool -g support to virtio_net
From: Rusty Russell @ 2011-10-24 4:41 UTC (permalink / raw)
To: David Miller; +Cc: raj, netdev, mst, virtualization
In-Reply-To: <20111023.232604.18246342474494526.davem@davemloft.net>
On Sun, 23 Oct 2011 23:26:04 -0400 (EDT), David Miller <davem@davemloft.net> wrote:
> From: Rusty Russell <rusty@rustcorp.com.au>
> Date: Thu, 20 Oct 2011 18:25:24 +1030
>
> > On Wed, 19 Oct 2011 11:10:59 -0700 (PDT), raj@tardy.cup.hp.com (Rick Jones) wrote:
> >> From: Rick Jones <rick.jones2@hp.com>
> >>
> >> Add support for reporting ring sizes via ethtool -g to the virtio_net
> >> driver.
> >>
> >> Signed-off-by: Rick Jones <rick.jones2@hp.com>
> >
> > Acked-by: Rusty Russell <rusty@rustcorp.com.au>
> >
> > MST, want me to take this, or do you?
>
> I can also take it directly, just let me know.
I'm happy with that.
Thanks,
Rusty.
^ permalink raw reply
* Re: [PATCH net-next] Add ethtool -g support to virtio_net
From: David Miller @ 2011-10-24 6:15 UTC (permalink / raw)
To: mst; +Cc: rusty, raj, netdev, virtualization
In-Reply-To: <20111024053103.GA24528@redhat.com>
From: "Michael S. Tsirkin" <mst@redhat.com>
Date: Mon, 24 Oct 2011 07:31:05 +0200
> On Sun, Oct 23, 2011 at 11:26:04PM -0400, David Miller wrote:
>> From: Rusty Russell <rusty@rustcorp.com.au>
>> Date: Thu, 20 Oct 2011 18:25:24 +1030
>>
>> > On Wed, 19 Oct 2011 11:10:59 -0700 (PDT), raj@tardy.cup.hp.com (Rick Jones) wrote:
>> >> From: Rick Jones <rick.jones2@hp.com>
>> >>
>> >> Add support for reporting ring sizes via ethtool -g to the virtio_net
>> >> driver.
>> >>
>> >> Signed-off-by: Rick Jones <rick.jones2@hp.com>
>> >
>> > Acked-by: Rusty Russell <rusty@rustcorp.com.au>
>> >
>> > MST, want me to take this, or do you?
>>
>> I can also take it directly, just let me know.
>
> Please do, thanks very much.
>
> Acked-by: Michael S. Tsirkin <mst@redhat.com>
>
Done.
^ permalink raw reply
* Re: [PATCH 0/5] macvtap fixes.
From: Michael S. Tsirkin @ 2011-10-24 6:31 UTC (permalink / raw)
To: David Miller; +Cc: ebiederm, netdev, arnd, jasowang, ian.campbell, mashirley
In-Reply-To: <20111021.025607.2193225431127507358.davem@redhat.com>
On Fri, Oct 21, 2011 at 02:56:07AM -0400, David Miller wrote:
> From: ebiederm@xmission.com (Eric W. Biederman)
> Date: Thu, 20 Oct 2011 07:24:59 -0700
>
> >
> > This series of patches fixes a series of minor bugs in the macvtap code.
> >
> > The fixes to handle failures in newlink and the change in how we handle
> > minor device number allocations are particularly significant.
>
> Applied to net-next, thanks.
The 'Don't leak unreceived packets' patch probably makes sense in 'net'.
--
MST
^ permalink raw reply
* [PATCH net-next] tcp: md5: add more const attributes
From: Eric Dumazet @ 2011-10-24 6:35 UTC (permalink / raw)
To: David Miller; +Cc: netdev
Now tcp_md5_hash_header() has a const tcphdr argument, we can add more
const attributes to callers.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
include/net/tcp.h | 17 +++++++++--------
net/ipv4/tcp_ipv4.c | 12 ++++++------
net/ipv6/tcp_ipv6.c | 13 +++++++------
3 files changed, 22 insertions(+), 20 deletions(-)
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 910cc29..ed0e814 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1185,8 +1185,9 @@ struct tcp_md5sig_pool {
/* - functions */
extern int tcp_v4_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key,
- struct sock *sk, struct request_sock *req,
- struct sk_buff *skb);
+ const struct sock *sk,
+ const struct request_sock *req,
+ const struct sk_buff *skb);
extern struct tcp_md5sig_key * tcp_v4_md5_lookup(struct sock *sk,
struct sock *addr_sk);
extern int tcp_v4_md5_do_add(struct sock *sk, __be32 addr, u8 *newkey,
@@ -1448,9 +1449,9 @@ struct tcp_sock_af_ops {
struct sock *addr_sk);
int (*calc_md5_hash) (char *location,
struct tcp_md5sig_key *md5,
- struct sock *sk,
- struct request_sock *req,
- struct sk_buff *skb);
+ const struct sock *sk,
+ const struct request_sock *req,
+ const struct sk_buff *skb);
int (*md5_add) (struct sock *sk,
struct sock *addr_sk,
u8 *newkey,
@@ -1467,9 +1468,9 @@ struct tcp_request_sock_ops {
struct request_sock *req);
int (*calc_md5_hash) (char *location,
struct tcp_md5sig_key *md5,
- struct sock *sk,
- struct request_sock *req,
- struct sk_buff *skb);
+ const struct sock *sk,
+ const struct request_sock *req,
+ const struct sk_buff *skb);
#endif
};
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 955c925..1dad7e9 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -92,7 +92,7 @@ EXPORT_SYMBOL(sysctl_tcp_low_latency);
static struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk,
__be32 addr);
static int tcp_v4_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key,
- __be32 daddr, __be32 saddr, struct tcphdr *th);
+ __be32 daddr, __be32 saddr, const struct tcphdr *th);
#else
static inline
struct tcp_md5sig_key *tcp_v4_md5_do_lookup(struct sock *sk, __be32 addr)
@@ -1090,7 +1090,7 @@ static int tcp_v4_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp,
}
static int tcp_v4_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key,
- __be32 daddr, __be32 saddr, struct tcphdr *th)
+ __be32 daddr, __be32 saddr, const struct tcphdr *th)
{
struct tcp_md5sig_pool *hp;
struct hash_desc *desc;
@@ -1122,12 +1122,12 @@ clear_hash_noput:
}
int tcp_v4_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key,
- struct sock *sk, struct request_sock *req,
- struct sk_buff *skb)
+ const struct sock *sk, const struct request_sock *req,
+ const struct sk_buff *skb)
{
struct tcp_md5sig_pool *hp;
struct hash_desc *desc;
- struct tcphdr *th = tcp_hdr(skb);
+ const struct tcphdr *th = tcp_hdr(skb);
__be32 saddr, daddr;
if (sk) {
@@ -1172,7 +1172,7 @@ clear_hash_noput:
}
EXPORT_SYMBOL(tcp_v4_md5_hash_skb);
-static int tcp_v4_inbound_md5_hash(struct sock *sk, struct sk_buff *skb)
+static int tcp_v4_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb)
{
/*
* This gets called for each TCP segment that arrives
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index da2ada8..c8683fc 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -761,7 +761,7 @@ static int tcp_v6_md5_hash_pseudoheader(struct tcp_md5sig_pool *hp,
static int tcp_v6_md5_hash_hdr(char *md5_hash, struct tcp_md5sig_key *key,
const struct in6_addr *daddr, struct in6_addr *saddr,
- struct tcphdr *th)
+ const struct tcphdr *th)
{
struct tcp_md5sig_pool *hp;
struct hash_desc *desc;
@@ -793,13 +793,14 @@ clear_hash_noput:
}
static int tcp_v6_md5_hash_skb(char *md5_hash, struct tcp_md5sig_key *key,
- struct sock *sk, struct request_sock *req,
- struct sk_buff *skb)
+ const struct sock *sk,
+ const struct request_sock *req,
+ const struct sk_buff *skb)
{
const struct in6_addr *saddr, *daddr;
struct tcp_md5sig_pool *hp;
struct hash_desc *desc;
- struct tcphdr *th = tcp_hdr(skb);
+ const struct tcphdr *th = tcp_hdr(skb);
if (sk) {
saddr = &inet6_sk(sk)->saddr;
@@ -842,12 +843,12 @@ clear_hash_noput:
return 1;
}
-static int tcp_v6_inbound_md5_hash (struct sock *sk, struct sk_buff *skb)
+static int tcp_v6_inbound_md5_hash(struct sock *sk, const struct sk_buff *skb)
{
const __u8 *hash_location = NULL;
struct tcp_md5sig_key *hash_expected;
const struct ipv6hdr *ip6h = ipv6_hdr(skb);
- struct tcphdr *th = tcp_hdr(skb);
+ const struct tcphdr *th = tcp_hdr(skb);
int genhash;
u8 newhash[16];
^ permalink raw reply related
* Re: [PATCH net-next] tcp: md5: add more const attributes
From: David Miller @ 2011-10-24 6:51 UTC (permalink / raw)
To: eric.dumazet; +Cc: netdev
In-Reply-To: <1319438111.2517.32.camel@edumazet-laptop>
From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Mon, 24 Oct 2011 08:35:11 +0200
> Now tcp_md5_hash_header() has a const tcphdr argument, we can add more
> const attributes to callers.
>
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Applied.
^ permalink raw reply
* Re: [patch net-next V2] net: introduce ethernet teaming device
From: Jiri Pirko @ 2011-10-24 6:54 UTC (permalink / raw)
To: Or Gerlitz
Cc: netdev, davem, eric.dumazet, bhutchings, shemminger, fubar, andy,
tgraf, ebiederm, mirqus, kaber, greearb, jesse, fbl,
benjamin.poirier, jzupka
In-Reply-To: <CAJZOPZ+80kZ8-dQLJJ3OYr+fxVw=z=VCQzui2aaif1ga7W-K_g@mail.gmail.com>
Sun, Oct 23, 2011 at 11:46:32PM CEST, or.gerlitz@gmail.com wrote:
>On Fri, Oct 21, 2011 at 2:39 PM, Jiri Pirko <jpirko@redhat.com> wrote:
>> This patch introduces new network device called team. It supposes to be
>> very fast, simple, userspace-driven alternative to existing bonding driver.
>
>Jiri,
>
>Could you elaborate a little further on the motivation for this
>teaming approach/solution vs. the current bonding driver? You say that
>it suppose to be very fast, simple and user space driven, so... do
>you find bonding not to be fast enough? or too complex? or the fact
>that bonding's driving logic being in the kernel is something you
>prefer to see in user-space? anything else?
As I wrote before:
<quote>
Well as I already wrote, the key is to keep kernel code as slim and
clean as possible and do what can be done in userspace in userspace. For
example bonding has active backup slave selection, arp verification and
so on in kernel. That's not conceptional and flexible. Team allows users
to easily write/modify their own control logic based on whatever way
they want to decide to select active port. I can imagine for example
even things like communicating with smart switch and checking port
status there. Bonding is carring a lot of baggage, team does not. It's
light and it suppose to stay that way.
</quote>
Also I would add easy extensibility by writing new mode in case any
existing is not suitable for your needs.
I hope this clears it up for you.
Jirka
>
>thanks,
>
>Or.
>
>
>>
>> Userspace library called libteam with couple of demo apps is available
>> here:
>> https://github.com/jpirko/libteam
>> Note it's still in its dipers atm.
>>
>> team<->libteam use generic netlink for communication. That and rtnl
>> suppose to be the only way to configure team device, no sysfs etc.
>>
>> Python binding basis for libteam was recently introduced (some need
>> still need to be done on it though). Daemon providing arpmon/miimon
>> active-backup functionality will be introduced shortly.
>> All what's necessary is already implemented in kernel team driver.
>>
>> Signed-off-by: Jiri Pirko <jpirko@redhat.com>
>>
>> v1->v2:
>> - modes are made as modules. Makes team more modular and
>> extendable.
>> - several commenters' nitpicks found on v1 were fixed
>> - several other bugs were fixed.
>> - note I ignored Eric's comment about roundrobin port selector
>> as Eric's way may be easily implemented as another mode (mode
>> "random") in future.
>> ---
>> Documentation/networking/team.txt | 2 +
>> MAINTAINERS | 7 +
>> drivers/net/Kconfig | 2 +
>> drivers/net/Makefile | 1 +
>> drivers/net/team/Kconfig | 38 +
>> drivers/net/team/Makefile | 7 +
>> drivers/net/team/team.c | 1593 +++++++++++++++++++++++++++++
>> drivers/net/team/team_mode_activebackup.c | 152 +++
>> drivers/net/team/team_mode_roundrobin.c | 107 ++
>> include/linux/Kbuild | 1 +
>> include/linux/if.h | 1 +
>> include/linux/if_team.h | 233 +++++
>> 12 files changed, 2144 insertions(+), 0 deletions(-)
>> create mode 100644 Documentation/networking/team.txt
>> create mode 100644 drivers/net/team/Kconfig
>> create mode 100644 drivers/net/team/Makefile
>> create mode 100644 drivers/net/team/team.c
>> create mode 100644 drivers/net/team/team_mode_activebackup.c
>> create mode 100644 drivers/net/team/team_mode_roundrobin.c
>> create mode 100644 include/linux/if_team.h
>>
>> diff --git a/Documentation/networking/team.txt b/Documentation/networking/team.txt
>> new file mode 100644
>> index 0000000..5a01368
>> --- /dev/null
>> +++ b/Documentation/networking/team.txt
>> @@ -0,0 +1,2 @@
>> +Team devices are driven from userspace via libteam library which is here:
>> + https://github.com/jpirko/libteam
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 5008b08..c33400d 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -6372,6 +6372,13 @@ W: http://tcp-lp-mod.sourceforge.net/
>> S: Maintained
>> F: net/ipv4/tcp_lp.c
>>
>> +TEAM DRIVER
>> +M: Jiri Pirko <jpirko@redhat.com>
>> +L: netdev@vger.kernel.org
>> +S: Supported
>> +F: drivers/net/team/
>> +F: include/linux/if_team.h
>> +
>> TEGRA SUPPORT
>> M: Colin Cross <ccross@android.com>
>> M: Erik Gilling <konkers@android.com>
>> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
>> index 583f66c..b3020be 100644
>> --- a/drivers/net/Kconfig
>> +++ b/drivers/net/Kconfig
>> @@ -125,6 +125,8 @@ config IFB
>> 'ifb1' etc.
>> Look at the iproute2 documentation directory for usage etc
>>
>> +source "drivers/net/team/Kconfig"
>> +
>> config MACVLAN
>> tristate "MAC-VLAN support (EXPERIMENTAL)"
>> depends on EXPERIMENTAL
>> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
>> index fa877cd..4e4ebfe 100644
>> --- a/drivers/net/Makefile
>> +++ b/drivers/net/Makefile
>> @@ -17,6 +17,7 @@ obj-$(CONFIG_NET) += Space.o loopback.o
>> obj-$(CONFIG_NETCONSOLE) += netconsole.o
>> obj-$(CONFIG_PHYLIB) += phy/
>> obj-$(CONFIG_RIONET) += rionet.o
>> +obj-$(CONFIG_NET_TEAM) += team/
>> obj-$(CONFIG_TUN) += tun.o
>> obj-$(CONFIG_VETH) += veth.o
>> obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
>> diff --git a/drivers/net/team/Kconfig b/drivers/net/team/Kconfig
>> new file mode 100644
>> index 0000000..70a43a6
>> --- /dev/null
>> +++ b/drivers/net/team/Kconfig
>> @@ -0,0 +1,38 @@
>> +menuconfig NET_TEAM
>> + tristate "Ethernet team driver support (EXPERIMENTAL)"
>> + depends on EXPERIMENTAL
>> + ---help---
>> + This allows one to create virtual interfaces that teams together
>> + multiple ethernet devices.
>> +
>> + Team devices can be added using the "ip" command from the
>> + iproute2 package:
>> +
>> + "ip link add link [ address MAC ] [ NAME ] type team"
>> +
>> + To compile this driver as a module, choose M here: the module
>> + will be called team.
>> +
>> +if NET_TEAM
>> +
>> +config NET_TEAM_MODE_ROUNDROBIN
>> + tristate "Round-robin mode support"
>> + depends on NET_TEAM
>> + ---help---
>> + Basic mode where port used for transmitting packets is selected in
>> + round-robin fashion using packet counter.
>> +
>> + To compile this team mode as a module, choose M here: the module
>> + will be called team_mode_roundrobin.
>> +
>> +config NET_TEAM_MODE_ACTIVEBACKUP
>> + tristate "Active-backup mode support"
>> + depends on NET_TEAM
>> + ---help---
>> + Only one port is active at a time and the rest of ports are used
>> + for backup.
>> +
>> + To compile this team mode as a module, choose M here: the module
>> + will be called team_mode_activebackup.
>> +
>> +endif # NET_TEAM
>> diff --git a/drivers/net/team/Makefile b/drivers/net/team/Makefile
>> new file mode 100644
>> index 0000000..85f2028
>> --- /dev/null
>> +++ b/drivers/net/team/Makefile
>> @@ -0,0 +1,7 @@
>> +#
>> +# Makefile for the network team driver
>> +#
>> +
>> +obj-$(CONFIG_NET_TEAM) += team.o
>> +obj-$(CONFIG_NET_TEAM_MODE_ROUNDROBIN) += team_mode_roundrobin.o
>> +obj-$(CONFIG_NET_TEAM_MODE_ACTIVEBACKUP) += team_mode_activebackup.o
>> diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
>> new file mode 100644
>> index 0000000..398be58
>> --- /dev/null
>> +++ b/drivers/net/team/team.c
>> @@ -0,0 +1,1593 @@
>> +/*
>> + * net/drivers/team/team.c - Network team device driver
>> + * Copyright (c) 2011 Jiri Pirko <jpirko@redhat.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/types.h>
>> +#include <linux/module.h>
>> +#include <linux/init.h>
>> +#include <linux/slab.h>
>> +#include <linux/rcupdate.h>
>> +#include <linux/errno.h>
>> +#include <linux/ctype.h>
>> +#include <linux/notifier.h>
>> +#include <linux/netdevice.h>
>> +#include <linux/if_arp.h>
>> +#include <linux/socket.h>
>> +#include <linux/etherdevice.h>
>> +#include <linux/rtnetlink.h>
>> +#include <net/rtnetlink.h>
>> +#include <net/genetlink.h>
>> +#include <net/netlink.h>
>> +#include <linux/if_team.h>
>> +
>> +#define DRV_NAME "team"
>> +
>> +
>> +/**********
>> + * Helpers
>> + **********/
>> +
>> +#define team_port_exists(dev) (dev->priv_flags & IFF_TEAM_PORT)
>> +
>> +static struct team_port *team_port_get_rcu(const struct net_device *dev)
>> +{
>> + struct team_port *port = rcu_dereference(dev->rx_handler_data);
>> +
>> + return team_port_exists(dev) ? port : NULL;
>> +}
>> +
>> +static struct team_port *team_port_get_rtnl(const struct net_device *dev)
>> +{
>> + struct team_port *port = rtnl_dereference(dev->rx_handler_data);
>> +
>> + return team_port_exists(dev) ? port : NULL;
>> +}
>> +
>> +/*
>> + * Since the ability to change mac address for open port device is tested in
>> + * team_port_add, this function can be called without control of return value
>> + */
>> +static int __set_port_mac(struct net_device *port_dev,
>> + const unsigned char *dev_addr)
>> +{
>> + struct sockaddr addr;
>> +
>> + memcpy(addr.sa_data, dev_addr, ETH_ALEN);
>> + addr.sa_family = ARPHRD_ETHER;
>> + return dev_set_mac_address(port_dev, &addr);
>> +}
>> +
>> +int team_port_set_orig_mac(struct team_port *port)
>> +{
>> + return __set_port_mac(port->dev, port->orig.dev_addr);
>> +}
>> +EXPORT_SYMBOL(team_port_set_orig_mac);
>> +
>> +int team_port_set_team_mac(struct team_port *port)
>> +{
>> + return __set_port_mac(port->dev, port->team->dev->dev_addr);
>> +}
>> +EXPORT_SYMBOL(team_port_set_team_mac);
>> +
>> +
>> +/*******************
>> + * Options handling
>> + *******************/
>> +
>> +void team_options_register(struct team *team, struct team_option *option,
>> + size_t option_count)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < option_count; i++, option++)
>> + list_add_tail(&option->list, &team->option_list);
>> +}
>> +EXPORT_SYMBOL(team_options_register);
>> +
>> +static void __team_options_change_check(struct team *team,
>> + struct team_option *changed_option);
>> +
>> +static void __team_options_unregister(struct team *team,
>> + struct team_option *option,
>> + size_t option_count)
>> +{
>> + int i;
>> +
>> + for (i = 0; i < option_count; i++, option++)
>> + list_del(&option->list);
>> +}
>> +
>> +void team_options_unregister(struct team *team, struct team_option *option,
>> + size_t option_count)
>> +{
>> + __team_options_unregister(team, option, option_count);
>> + __team_options_change_check(team, NULL);
>> +}
>> +EXPORT_SYMBOL(team_options_unregister);
>> +
>> +static int team_option_get(struct team *team, struct team_option *option,
>> + void *arg)
>> +{
>> + return option->getter(team, arg);
>> +}
>> +
>> +static int team_option_set(struct team *team, struct team_option *option,
>> + void *arg)
>> +{
>> + int err;
>> +
>> + err = option->setter(team, arg);
>> + if (err)
>> + return err;
>> +
>> + __team_options_change_check(team, option);
>> + return err;
>> +}
>> +
>> +/****************
>> + * Mode handling
>> + ****************/
>> +
>> +static LIST_HEAD(mode_list);
>> +static DEFINE_SPINLOCK(mode_list_lock);
>> +
>> +static struct team_mode *__find_mode(const char *kind)
>> +{
>> + struct team_mode *mode;
>> +
>> + list_for_each_entry(mode, &mode_list, list) {
>> + if (strcmp(mode->kind, kind) == 0)
>> + return mode;
>> + }
>> + return NULL;
>> +}
>> +
>> +static bool is_good_mode_name(const char *name)
>> +{
>> + while (*name != '\0') {
>> + if (!isalpha(*name) && !isdigit(*name) && *name != '_')
>> + return false;
>> + name++;
>> + }
>> + return true;
>> +}
>> +
>> +int team_mode_register(struct team_mode *mode)
>> +{
>> + int err = 0;
>> +
>> + if (!is_good_mode_name(mode->kind) ||
>> + mode->priv_size > TEAM_MODE_PRIV_SIZE)
>> + return -EINVAL;
>> + spin_lock(&mode_list_lock);
>> + if (__find_mode(mode->kind)) {
>> + err = -EEXIST;
>> + goto unlock;
>> + }
>> + list_add_tail(&mode->list, &mode_list);
>> +unlock:
>> + spin_unlock(&mode_list_lock);
>> + return err;
>> +}
>> +EXPORT_SYMBOL(team_mode_register);
>> +
>> +int team_mode_unregister(struct team_mode *mode)
>> +{
>> + spin_lock(&mode_list_lock);
>> + list_del_init(&mode->list);
>> + spin_unlock(&mode_list_lock);
>> + return 0;
>> +}
>> +EXPORT_SYMBOL(team_mode_unregister);
>> +
>> +static struct team_mode *team_mode_get(const char *kind)
>> +{
>> + struct team_mode *mode;
>> +
>> + spin_lock(&mode_list_lock);
>> + mode = __find_mode(kind);
>> + if (!mode) {
>> + spin_unlock(&mode_list_lock);
>> + request_module("team-mode-%s", kind);
>> + spin_lock(&mode_list_lock);
>> + mode = __find_mode(kind);
>> + }
>> + if (mode)
>> + if (!try_module_get(mode->owner))
>> + mode = NULL;
>> +
>> + spin_unlock(&mode_list_lock);
>> + return mode;
>> +}
>> +
>> +static void team_mode_put(const char *kind)
>> +{
>> + struct team_mode *mode;
>> +
>> + spin_lock(&mode_list_lock);
>> + mode = __find_mode(kind);
>> + BUG_ON(!mode);
>> + module_put(mode->owner);
>> + spin_unlock(&mode_list_lock);
>> +}
>> +
>> +/*
>> + * We can benefit from the fact that it's ensured no port is present
>> + * at the time of mode change.
>> + */
>> +static int __team_change_mode(struct team *team,
>> + const struct team_mode *new_mode)
>> +{
>> + /* Check if mode was previously set and do cleanup if so */
>> + if (team->mode_kind) {
>> + void (*exit_op)(struct team *team) = team->mode_ops.exit;
>> +
>> + /* Clear ops area so no callback is called any longer */
>> + memset(&team->mode_ops, 0, sizeof(struct team_mode_ops));
>> +
>> + synchronize_rcu();
>> +
>> + if (exit_op)
>> + exit_op(team);
>> + team_mode_put(team->mode_kind);
>> + team->mode_kind = NULL;
>> + /* zero private data area */
>> + memset(&team->mode_priv, 0,
>> + sizeof(struct team) - offsetof(struct team, mode_priv));
>> + }
>> +
>> + if (!new_mode)
>> + return 0;
>> +
>> + if (new_mode->ops->init) {
>> + int err;
>> +
>> + err = new_mode->ops->init(team);
>> + if (err)
>> + return err;
>> + }
>> +
>> + team->mode_kind = new_mode->kind;
>> + memcpy(&team->mode_ops, new_mode->ops, sizeof(struct team_mode_ops));
>> +
>> + return 0;
>> +}
>> +
>> +static int team_change_mode(struct team *team, const char *kind)
>> +{
>> + struct team_mode *new_mode;
>> + struct net_device *dev = team->dev;
>> + int err;
>> +
>> + if (!list_empty(&team->port_list)) {
>> + netdev_err(dev, "No ports can be present during mode change\n");
>> + return -EBUSY;
>> + }
>> +
>> + if (team->mode_kind && strcmp(team->mode_kind, kind) == 0) {
>> + netdev_err(dev, "Unable to change to the same mode the team is in\n");
>> + return -EINVAL;
>> + }
>> +
>> + new_mode = team_mode_get(kind);
>> + if (!new_mode) {
>> + netdev_err(dev, "Mode \"%s\" not found\n", kind);
>> + return -EINVAL;
>> + }
>> +
>> + err = __team_change_mode(team, new_mode);
>> + if (err) {
>> + netdev_err(dev, "Failed to change to mode \"%s\"\n", kind);
>> + team_mode_put(kind);
>> + return err;
>> + }
>> +
>> + netdev_info(dev, "Mode changed to \"%s\"\n", kind);
>> + return 0;
>> +}
>> +
>> +
>> +/************************
>> + * Rx path frame handler
>> + ************************/
>> +
>> +/* note: already called with rcu_read_lock */
>> +static rx_handler_result_t team_handle_frame(struct sk_buff **pskb)
>> +{
>> + struct sk_buff *skb = *pskb;
>> + struct team_port *port;
>> + struct team *team;
>> + rx_handler_result_t res = RX_HANDLER_ANOTHER;
>> +
>> + skb = skb_share_check(skb, GFP_ATOMIC);
>> + if (!skb)
>> + return RX_HANDLER_CONSUMED;
>> +
>> + *pskb = skb;
>> +
>> + port = team_port_get_rcu(skb->dev);
>> + team = port->team;
>> +
>> + if (team->mode_ops.receive)
>> + res = team->mode_ops.receive(team, port, skb);
>> +
>> + if (res == RX_HANDLER_ANOTHER) {
>> + struct team_pcpu_stats *pcpu_stats;
>> +
>> + pcpu_stats = this_cpu_ptr(team->pcpu_stats);
>> + u64_stats_update_begin(&pcpu_stats->syncp);
>> + pcpu_stats->rx_packets++;
>> + pcpu_stats->rx_bytes += skb->len;
>> + if (skb->pkt_type == PACKET_MULTICAST)
>> + pcpu_stats->rx_multicast++;
>> + u64_stats_update_end(&pcpu_stats->syncp);
>> +
>> + skb->dev = team->dev;
>> + } else {
>> + this_cpu_inc(team->pcpu_stats->rx_dropped);
>> + }
>> +
>> + return res;
>> +}
>> +
>> +
>> +/****************
>> + * Port handling
>> + ****************/
>> +
>> +static bool team_port_find(const struct team *team,
>> + const struct team_port *port)
>> +{
>> + struct team_port *cur;
>> +
>> + list_for_each_entry(cur, &team->port_list, list)
>> + if (cur == port)
>> + return true;
>> + return false;
>> +}
>> +
>> +static int team_port_list_init(struct team *team)
>> +{
>> + int i;
>> + struct hlist_head *hash;
>> +
>> + hash = kmalloc(sizeof(*hash) * TEAM_PORT_HASHENTRIES, GFP_KERNEL);
>> + if (!hash)
>> + return -ENOMEM;
>> +
>> + for (i = 0; i < TEAM_PORT_HASHENTRIES; i++)
>> + INIT_HLIST_HEAD(&hash[i]);
>> + team->port_hlist = hash;
>> + INIT_LIST_HEAD(&team->port_list);
>> + return 0;
>> +}
>> +
>> +static void team_port_list_fini(struct team *team)
>> +{
>> + kfree(team->port_hlist);
>> +}
>> +
>> +/*
>> + * Add/delete port to the team port list. Write guarded by rtnl_lock.
>> + * Takes care of correct port->index setup (might be racy).
>> + */
>> +static void team_port_list_add_port(struct team *team,
>> + struct team_port *port)
>> +{
>> + port->index = team->port_count++;
>> + hlist_add_head_rcu(&port->hlist,
>> + team_port_index_hash(team, port->index));
>> + list_add_tail_rcu(&port->list, &team->port_list);
>> +}
>> +
>> +static void __reconstruct_port_hlist(struct team *team, int rm_index)
>> +{
>> + int i;
>> + struct team_port *port;
>> +
>> + for (i = rm_index + 1; i < team->port_count; i++) {
>> + port = team_get_port_by_index_rcu(team, i);
>> + hlist_del_rcu(&port->hlist);
>> + port->index--;
>> + hlist_add_head_rcu(&port->hlist,
>> + team_port_index_hash(team, port->index));
>> + }
>> +}
>> +
>> +static void team_port_list_del_port(struct team *team,
>> + struct team_port *port)
>> +{
>> + int rm_index = port->index;
>> +
>> + hlist_del_rcu(&port->hlist);
>> + list_del_rcu(&port->list);
>> + __reconstruct_port_hlist(team, rm_index);
>> + team->port_count--;
>> +}
>> +
>> +#define TEAM_VLAN_FEATURES (NETIF_F_ALL_CSUM | NETIF_F_SG | \
>> + NETIF_F_FRAGLIST | NETIF_F_ALL_TSO | \
>> + NETIF_F_HIGHDMA | NETIF_F_LRO)
>> +
>> +static void __team_compute_features(struct team *team)
>> +{
>> + struct team_port *port;
>> + u32 vlan_features = TEAM_VLAN_FEATURES;
>> + unsigned short max_hard_header_len = ETH_HLEN;
>> +
>> + list_for_each_entry(port, &team->port_list, list) {
>> + vlan_features = netdev_increment_features(vlan_features,
>> + port->dev->vlan_features,
>> + TEAM_VLAN_FEATURES);
>> +
>> + if (port->dev->hard_header_len > max_hard_header_len)
>> + max_hard_header_len = port->dev->hard_header_len;
>> + }
>> +
>> + team->dev->vlan_features = vlan_features;
>> + team->dev->hard_header_len = max_hard_header_len;
>> +
>> + netdev_change_features(team->dev);
>> +}
>> +
>> +static void team_compute_features(struct team *team)
>> +{
>> + spin_lock(&team->lock);
>> + __team_compute_features(team);
>> + spin_unlock(&team->lock);
>> +}
>> +
>> +static int team_port_enter(struct team *team, struct team_port *port)
>> +{
>> + int err = 0;
>> +
>> + dev_hold(team->dev);
>> + port->dev->priv_flags |= IFF_TEAM_PORT;
>> + if (team->mode_ops.port_enter) {
>> + err = team->mode_ops.port_enter(team, port);
>> + if (err)
>> + netdev_err(team->dev, "Device %s failed to enter team mode\n",
>> + port->dev->name);
>> + }
>> + return err;
>> +}
>> +
>> +static void team_port_leave(struct team *team, struct team_port *port)
>> +{
>> + if (team->mode_ops.port_leave)
>> + team->mode_ops.port_leave(team, port);
>> + port->dev->priv_flags &= ~IFF_TEAM_PORT;
>> + dev_put(team->dev);
>> +}
>> +
>> +static void __team_port_change_check(struct team_port *port, bool linkup);
>> +
>> +static int team_port_add(struct team *team, struct net_device *port_dev)
>> +{
>> + struct net_device *dev = team->dev;
>> + struct team_port *port;
>> + char *portname = port_dev->name;
>> + char tmp_addr[ETH_ALEN];
>> + int err;
>> +
>> + if (port_dev->flags & IFF_LOOPBACK ||
>> + port_dev->type != ARPHRD_ETHER) {
>> + netdev_err(dev, "Device %s is of an unsupported type\n",
>> + portname);
>> + return -EINVAL;
>> + }
>> +
>> + if (team_port_exists(port_dev)) {
>> + netdev_err(dev, "Device %s is already a port "
>> + "of a team device\n", portname);
>> + return -EBUSY;
>> + }
>> +
>> + if (port_dev->flags & IFF_UP) {
>> + netdev_err(dev, "Device %s is up. Set it down before adding it as a team port\n",
>> + portname);
>> + return -EBUSY;
>> + }
>> +
>> + port = kzalloc(sizeof(struct team_port), GFP_KERNEL);
>> + if (!port)
>> + return -ENOMEM;
>> +
>> + port->dev = port_dev;
>> + port->team = team;
>> +
>> + port->orig.mtu = port_dev->mtu;
>> + err = dev_set_mtu(port_dev, dev->mtu);
>> + if (err) {
>> + netdev_dbg(dev, "Error %d calling dev_set_mtu\n", err);
>> + goto err_set_mtu;
>> + }
>> +
>> + memcpy(port->orig.dev_addr, port_dev->dev_addr, ETH_ALEN);
>> + random_ether_addr(tmp_addr);
>> + err = __set_port_mac(port_dev, tmp_addr);
>> + if (err) {
>> + netdev_dbg(dev, "Device %s mac addr set failed\n",
>> + portname);
>> + goto err_set_mac_rand;
>> + }
>> +
>> + err = dev_open(port_dev);
>> + if (err) {
>> + netdev_dbg(dev, "Device %s opening failed\n",
>> + portname);
>> + goto err_dev_open;
>> + }
>> +
>> + err = team_port_set_orig_mac(port);
>> + if (err) {
>> + netdev_dbg(dev, "Device %s mac addr set failed - Device does not support addr change when it's opened\n",
>> + portname);
>> + goto err_set_mac_opened;
>> + }
>> +
>> + err = team_port_enter(team, port);
>> + if (err) {
>> + netdev_err(dev, "Device %s failed to enter team mode\n",
>> + portname);
>> + goto err_port_enter;
>> + }
>> +
>> + err = netdev_set_master(port_dev, dev);
>> + if (err) {
>> + netdev_err(dev, "Device %s failed to set master\n", portname);
>> + goto err_set_master;
>> + }
>> +
>> + err = netdev_rx_handler_register(port_dev, team_handle_frame,
>> + port);
>> + if (err) {
>> + netdev_err(dev, "Device %s failed to register rx_handler\n",
>> + portname);
>> + goto err_handler_register;
>> + }
>> +
>> + team_port_list_add_port(team, port);
>> + __team_compute_features(team);
>> + __team_port_change_check(port, !!netif_carrier_ok(port_dev));
>> +
>> + netdev_info(dev, "Port device %s added\n", portname);
>> +
>> + return 0;
>> +
>> +err_handler_register:
>> + netdev_set_master(port_dev, NULL);
>> +
>> +err_set_master:
>> + team_port_leave(team, port);
>> +
>> +err_port_enter:
>> +err_set_mac_opened:
>> + dev_close(port_dev);
>> +
>> +err_dev_open:
>> + team_port_set_orig_mac(port);
>> +
>> +err_set_mac_rand:
>> + dev_set_mtu(port_dev, port->orig.mtu);
>> +
>> +err_set_mtu:
>> + kfree(port);
>> +
>> + return err;
>> +}
>> +
>> +static int team_port_del(struct team *team, struct net_device *port_dev)
>> +{
>> + struct net_device *dev = team->dev;
>> + struct team_port *port;
>> + char *portname = port_dev->name;
>> +
>> + port = team_port_get_rtnl(port_dev);
>> + if (!port || !team_port_find(team, port)) {
>> + netdev_err(dev, "Device %s does not act as a port of this team\n",
>> + portname);
>> + return -ENOENT;
>> + }
>> +
>> + __team_port_change_check(port, false);
>> + team_port_list_del_port(team, port);
>> + netdev_rx_handler_unregister(port_dev);
>> + netdev_set_master(port_dev, NULL);
>> + team_port_leave(team, port);
>> + dev_close(port_dev);
>> + team_port_set_orig_mac(port);
>> + dev_set_mtu(port_dev, port->orig.mtu);
>> + synchronize_rcu();
>> + kfree(port);
>> + netdev_info(dev, "Port device %s removed\n", portname);
>> + __team_compute_features(team);
>> +
>> + return 0;
>> +}
>> +
>> +
>> +/*****************
>> + * Net device ops
>> + *****************/
>> +
>> +static const char team_no_mode_kind[] = "*NOMODE*";
>> +
>> +static int team_mode_option_get(struct team *team, void *arg)
>> +{
>> + const char **str = arg;
>> +
>> + *str = team->mode_kind ? team->mode_kind : team_no_mode_kind;
>> + return 0;
>> +}
>> +
>> +static int team_mode_option_set(struct team *team, void *arg)
>> +{
>> + const char **str = arg;
>> +
>> + return team_change_mode(team, *str);
>> +}
>> +
>> +static struct team_option team_options[] = {
>> + {
>> + .name = "mode",
>> + .type = TEAM_OPTION_TYPE_STRING,
>> + .getter = team_mode_option_get,
>> + .setter = team_mode_option_set,
>> + },
>> +};
>> +
>> +static int team_init(struct net_device *dev)
>> +{
>> + struct team *team = netdev_priv(dev);
>> + int err;
>> +
>> + team->dev = dev;
>> + spin_lock_init(&team->lock);
>> +
>> + team->pcpu_stats = alloc_percpu(struct team_pcpu_stats);
>> + if (!team->pcpu_stats)
>> + return -ENOMEM;
>> +
>> + err = team_port_list_init(team);
>> + if (err)
>> + goto err_port_list_init;
>> +
>> + INIT_LIST_HEAD(&team->option_list);
>> + team_options_register(team, team_options, ARRAY_SIZE(team_options));
>> + netif_carrier_off(dev);
>> +
>> + return 0;
>> +
>> +err_port_list_init:
>> +
>> + free_percpu(team->pcpu_stats);
>> +
>> + return err;
>> +}
>> +
>> +static void team_uninit(struct net_device *dev)
>> +{
>> + struct team *team = netdev_priv(dev);
>> + struct team_port *port;
>> + struct team_port *tmp;
>> +
>> + spin_lock(&team->lock);
>> + list_for_each_entry_safe(port, tmp, &team->port_list, list)
>> + team_port_del(team, port->dev);
>> +
>> + __team_change_mode(team, NULL); /* cleanup */
>> + __team_options_unregister(team, team_options, ARRAY_SIZE(team_options));
>> + spin_unlock(&team->lock);
>> +}
>> +
>> +static void team_destructor(struct net_device *dev)
>> +{
>> + struct team *team = netdev_priv(dev);
>> +
>> + team_port_list_fini(team);
>> + free_percpu(team->pcpu_stats);
>> + free_netdev(dev);
>> +}
>> +
>> +static int team_open(struct net_device *dev)
>> +{
>> + netif_carrier_on(dev);
>> + return 0;
>> +}
>> +
>> +static int team_close(struct net_device *dev)
>> +{
>> + netif_carrier_off(dev);
>> + return 0;
>> +}
>> +
>> +/*
>> + * note: already called with rcu_read_lock
>> + */
>> +static netdev_tx_t team_xmit(struct sk_buff *skb, struct net_device *dev)
>> +{
>> + struct team *team = netdev_priv(dev);
>> + bool tx_success = false;
>> + unsigned int len = skb->len;
>> +
>> + /*
>> + * Ensure transmit function is called only in case there is at least
>> + * one port present.
>> + */
>> + if (likely(!list_empty(&team->port_list) && team->mode_ops.transmit))
>> + tx_success = team->mode_ops.transmit(team, skb);
>> + if (tx_success) {
>> + struct team_pcpu_stats *pcpu_stats;
>> +
>> + pcpu_stats = this_cpu_ptr(team->pcpu_stats);
>> + u64_stats_update_begin(&pcpu_stats->syncp);
>> + pcpu_stats->tx_packets++;
>> + pcpu_stats->tx_bytes += len;
>> + u64_stats_update_end(&pcpu_stats->syncp);
>> + } else {
>> + this_cpu_inc(team->pcpu_stats->tx_dropped);
>> + }
>> +
>> + return NETDEV_TX_OK;
>> +}
>> +
>> +static void team_change_rx_flags(struct net_device *dev, int change)
>> +{
>> + struct team *team = netdev_priv(dev);
>> + struct team_port *port;
>> + int inc;
>> +
>> + rcu_read_lock();
>> + list_for_each_entry_rcu(port, &team->port_list, list) {
>> + if (change & IFF_PROMISC) {
>> + inc = dev->flags & IFF_PROMISC ? 1 : -1;
>> + dev_set_promiscuity(port->dev, inc);
>> + }
>> + if (change & IFF_ALLMULTI) {
>> + inc = dev->flags & IFF_ALLMULTI ? 1 : -1;
>> + dev_set_allmulti(port->dev, inc);
>> + }
>> + }
>> + rcu_read_unlock();
>> +}
>> +
>> +static void team_set_rx_mode(struct net_device *dev)
>> +{
>> + struct team *team = netdev_priv(dev);
>> + struct team_port *port;
>> +
>> + rcu_read_lock();
>> + list_for_each_entry_rcu(port, &team->port_list, list) {
>> + dev_uc_sync(port->dev, dev);
>> + dev_mc_sync(port->dev, dev);
>> + }
>> + rcu_read_unlock();
>> +}
>> +
>> +static int team_set_mac_address(struct net_device *dev, void *p)
>> +{
>> + struct team *team = netdev_priv(dev);
>> + struct team_port *port;
>> + struct sockaddr *addr = p;
>> +
>> + memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
>> + rcu_read_lock();
>> + list_for_each_entry_rcu(port, &team->port_list, list)
>> + if (team->mode_ops.port_change_mac)
>> + team->mode_ops.port_change_mac(team, port);
>> + rcu_read_unlock();
>> + return 0;
>> +}
>> +
>> +static int team_change_mtu(struct net_device *dev, int new_mtu)
>> +{
>> + struct team *team = netdev_priv(dev);
>> + struct team_port *port;
>> + int err;
>> +
>> + rcu_read_lock();
>> + list_for_each_entry_rcu(port, &team->port_list, list) {
>> + err = dev_set_mtu(port->dev, new_mtu);
>> + if (err) {
>> + netdev_err(dev, "Device %s failed to change mtu",
>> + port->dev->name);
>> + goto unwind;
>> + }
>> + }
>> + rcu_read_unlock();
>> +
>> + dev->mtu = new_mtu;
>> +
>> + return 0;
>> +
>> +unwind:
>> + list_for_each_entry_continue_reverse(port, &team->port_list, list)
>> + dev_set_mtu(port->dev, dev->mtu);
>> +
>> + rcu_read_unlock();
>> + return err;
>> +}
>> +
>> +static struct rtnl_link_stats64 *
>> +team_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
>> +{
>> + struct team *team = netdev_priv(dev);
>> + struct team_pcpu_stats *p;
>> + u64 rx_packets, rx_bytes, rx_multicast, tx_packets, tx_bytes;
>> + u32 rx_dropped = 0, tx_dropped = 0;
>> + unsigned int start;
>> + int i;
>> +
>> + for_each_possible_cpu(i) {
>> + p = per_cpu_ptr(team->pcpu_stats, i);
>> + do {
>> + start = u64_stats_fetch_begin_bh(&p->syncp);
>> + rx_packets = p->rx_packets;
>> + rx_bytes = p->rx_bytes;
>> + rx_multicast = p->rx_multicast;
>> + tx_packets = p->tx_packets;
>> + tx_bytes = p->tx_bytes;
>> + } while (u64_stats_fetch_retry_bh(&p->syncp, start));
>> +
>> + stats->rx_packets += rx_packets;
>> + stats->rx_bytes += rx_bytes;
>> + stats->multicast += rx_multicast;
>> + stats->tx_packets += tx_packets;
>> + stats->tx_bytes += tx_bytes;
>> + /*
>> + * rx_dropped & tx_dropped are u32, updated
>> + * without syncp protection.
>> + */
>> + rx_dropped += p->rx_dropped;
>> + tx_dropped += p->tx_dropped;
>> + }
>> + stats->rx_dropped = rx_dropped;
>> + stats->tx_dropped = tx_dropped;
>> + return stats;
>> +}
>> +
>> +static void team_vlan_rx_add_vid(struct net_device *dev, uint16_t vid)
>> +{
>> + struct team *team = netdev_priv(dev);
>> + struct team_port *port;
>> +
>> + rcu_read_lock();
>> + list_for_each_entry_rcu(port, &team->port_list, list) {
>> + const struct net_device_ops *ops = port->dev->netdev_ops;
>> +
>> + ops->ndo_vlan_rx_add_vid(port->dev, vid);
>> + }
>> + rcu_read_unlock();
>> +}
>> +
>> +static void team_vlan_rx_kill_vid(struct net_device *dev, uint16_t vid)
>> +{
>> + struct team *team = netdev_priv(dev);
>> + struct team_port *port;
>> +
>> + rcu_read_lock();
>> + list_for_each_entry_rcu(port, &team->port_list, list) {
>> + const struct net_device_ops *ops = port->dev->netdev_ops;
>> +
>> + ops->ndo_vlan_rx_kill_vid(port->dev, vid);
>> + }
>> + rcu_read_unlock();
>> +}
>> +
>> +static int team_add_slave(struct net_device *dev, struct net_device *port_dev)
>> +{
>> + struct team *team = netdev_priv(dev);
>> + int err;
>> +
>> + spin_lock(&team->lock);
>> + err = team_port_add(team, port_dev);
>> + spin_unlock(&team->lock);
>> + return err;
>> +}
>> +
>> +static int team_del_slave(struct net_device *dev, struct net_device *port_dev)
>> +{
>> + struct team *team = netdev_priv(dev);
>> + int err;
>> +
>> + spin_lock(&team->lock);
>> + err = team_port_del(team, port_dev);
>> + spin_unlock(&team->lock);
>> + return err;
>> +}
>> +
>> +static const struct net_device_ops team_netdev_ops = {
>> + .ndo_init = team_init,
>> + .ndo_uninit = team_uninit,
>> + .ndo_open = team_open,
>> + .ndo_stop = team_close,
>> + .ndo_start_xmit = team_xmit,
>> + .ndo_change_rx_flags = team_change_rx_flags,
>> + .ndo_set_rx_mode = team_set_rx_mode,
>> + .ndo_set_mac_address = team_set_mac_address,
>> + .ndo_change_mtu = team_change_mtu,
>> + .ndo_get_stats64 = team_get_stats64,
>> + .ndo_vlan_rx_add_vid = team_vlan_rx_add_vid,
>> + .ndo_vlan_rx_kill_vid = team_vlan_rx_kill_vid,
>> + .ndo_add_slave = team_add_slave,
>> + .ndo_del_slave = team_del_slave,
>> +};
>> +
>> +
>> +/***********************
>> + * rt netlink interface
>> + ***********************/
>> +
>> +static void team_setup(struct net_device *dev)
>> +{
>> + ether_setup(dev);
>> +
>> + dev->netdev_ops = &team_netdev_ops;
>> + dev->destructor = team_destructor;
>> + dev->tx_queue_len = 0;
>> + dev->flags |= IFF_MULTICAST;
>> + dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
>> +
>> + /*
>> + * Indicate we support unicast address filtering. That way core won't
>> + * bring us to promisc mode in case a unicast addr is added.
>> + * Let this up to underlay drivers.
>> + */
>> + dev->priv_flags |= IFF_UNICAST_FLT;
>> +
>> + dev->features |= NETIF_F_LLTX;
>> + dev->features |= NETIF_F_GRO;
>> + dev->hw_features = NETIF_F_HW_VLAN_TX |
>> + NETIF_F_HW_VLAN_RX |
>> + NETIF_F_HW_VLAN_FILTER;
>> +
>> + dev->features |= dev->hw_features;
>> +}
>> +
>> +static int team_newlink(struct net *src_net, struct net_device *dev,
>> + struct nlattr *tb[], struct nlattr *data[])
>> +{
>> + int err;
>> +
>> + if (tb[IFLA_ADDRESS] == NULL)
>> + random_ether_addr(dev->dev_addr);
>> +
>> + err = register_netdevice(dev);
>> + if (err)
>> + return err;
>> +
>> + return 0;
>> +}
>> +
>> +static int team_validate(struct nlattr *tb[], struct nlattr *data[])
>> +{
>> + if (tb[IFLA_ADDRESS]) {
>> + if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN)
>> + return -EINVAL;
>> + if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS])))
>> + return -EADDRNOTAVAIL;
>> + }
>> + return 0;
>> +}
>> +
>> +static struct rtnl_link_ops team_link_ops __read_mostly = {
>> + .kind = DRV_NAME,
>> + .priv_size = sizeof(struct team),
>> + .setup = team_setup,
>> + .newlink = team_newlink,
>> + .validate = team_validate,
>> +};
>> +
>> +
>> +/***********************************
>> + * Generic netlink custom interface
>> + ***********************************/
>> +
>> +static struct genl_family team_nl_family = {
>> + .id = GENL_ID_GENERATE,
>> + .name = TEAM_GENL_NAME,
>> + .version = TEAM_GENL_VERSION,
>> + .maxattr = TEAM_ATTR_MAX,
>> + .netnsok = true,
>> +};
>> +
>> +static const struct nla_policy team_nl_policy[TEAM_ATTR_MAX + 1] = {
>> + [TEAM_ATTR_UNSPEC] = { .type = NLA_UNSPEC, },
>> + [TEAM_ATTR_TEAM_IFINDEX] = { .type = NLA_U32 },
>> + [TEAM_ATTR_LIST_OPTION] = { .type = NLA_NESTED },
>> + [TEAM_ATTR_LIST_PORT] = { .type = NLA_NESTED },
>> +};
>> +
>> +static const struct nla_policy
>> +team_nl_option_policy[TEAM_ATTR_OPTION_MAX + 1] = {
>> + [TEAM_ATTR_OPTION_UNSPEC] = { .type = NLA_UNSPEC, },
>> + [TEAM_ATTR_OPTION_NAME] = {
>> + .type = NLA_STRING,
>> + .len = TEAM_STRING_MAX_LEN,
>> + },
>> + [TEAM_ATTR_OPTION_CHANGED] = { .type = NLA_FLAG },
>> + [TEAM_ATTR_OPTION_TYPE] = { .type = NLA_U8 },
>> + [TEAM_ATTR_OPTION_DATA] = {
>> + .type = NLA_BINARY,
>> + .len = TEAM_STRING_MAX_LEN,
>> + },
>> +};
>> +
>> +static int team_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info)
>> +{
>> + struct sk_buff *msg;
>> + void *hdr;
>> + int err;
>> +
>> + msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>> + if (!msg)
>> + return -ENOMEM;
>> +
>> + hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq,
>> + &team_nl_family, 0, TEAM_CMD_NOOP);
>> + if (IS_ERR(hdr)) {
>> + err = PTR_ERR(hdr);
>> + goto err_msg_put;
>> + }
>> +
>> + genlmsg_end(msg, hdr);
>> +
>> + return genlmsg_unicast(genl_info_net(info), msg, info->snd_pid);
>> +
>> +err_msg_put:
>> + nlmsg_free(msg);
>> +
>> + return err;
>> +}
>> +
>> +/*
>> + * Netlink cmd functions should be locked by following two functions.
>> + * To ensure team_uninit would not be called in between, hold rcu_read_lock
>> + * all the time.
>> + */
>> +static struct team *team_nl_team_get(struct genl_info *info)
>> +{
>> + struct net *net = genl_info_net(info);
>> + int ifindex;
>> + struct net_device *dev;
>> + struct team *team;
>> +
>> + if (!info->attrs[TEAM_ATTR_TEAM_IFINDEX])
>> + return NULL;
>> +
>> + ifindex = nla_get_u32(info->attrs[TEAM_ATTR_TEAM_IFINDEX]);
>> + rcu_read_lock();
>> + dev = dev_get_by_index_rcu(net, ifindex);
>> + if (!dev || dev->netdev_ops != &team_netdev_ops) {
>> + rcu_read_unlock();
>> + return NULL;
>> + }
>> +
>> + team = netdev_priv(dev);
>> + spin_lock(&team->lock);
>> + return team;
>> +}
>> +
>> +static void team_nl_team_put(struct team *team)
>> +{
>> + spin_unlock(&team->lock);
>> + rcu_read_unlock();
>> +}
>> +
>> +static int team_nl_send_generic(struct genl_info *info, struct team *team,
>> + int (*fill_func)(struct sk_buff *skb,
>> + struct genl_info *info,
>> + int flags, struct team *team))
>> +{
>> + struct sk_buff *skb;
>> + int err;
>> +
>> + skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>> + if (!skb)
>> + return -ENOMEM;
>> +
>> + err = fill_func(skb, info, NLM_F_ACK, team);
>> + if (err < 0)
>> + goto err_fill;
>> +
>> + err = genlmsg_unicast(genl_info_net(info), skb, info->snd_pid);
>> + return err;
>> +
>> +err_fill:
>> + nlmsg_free(skb);
>> + return err;
>> +}
>> +
>> +static int team_nl_fill_options_get_changed(struct sk_buff *skb,
>> + u32 pid, u32 seq, int flags,
>> + struct team *team,
>> + struct team_option *changed_option)
>> +{
>> + struct nlattr *option_list;
>> + void *hdr;
>> + struct team_option *option;
>> +
>> + hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags,
>> + TEAM_CMD_OPTIONS_GET);
>> + if (IS_ERR(hdr))
>> + return PTR_ERR(hdr);
>> +
>> + NLA_PUT_U32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex);
>> + option_list = nla_nest_start(skb, TEAM_ATTR_LIST_OPTION);
>> + if (!option_list)
>> + return -EMSGSIZE;
>> +
>> + list_for_each_entry(option, &team->option_list, list) {
>> + struct nlattr *option_item;
>> + long arg;
>> +
>> + option_item = nla_nest_start(skb, TEAM_ATTR_ITEM_OPTION);
>> + if (!option_item)
>> + goto nla_put_failure;
>> + NLA_PUT_STRING(skb, TEAM_ATTR_OPTION_NAME, option->name);
>> + if (option == changed_option)
>> + NLA_PUT_FLAG(skb, TEAM_ATTR_OPTION_CHANGED);
>> + switch (option->type) {
>> + case TEAM_OPTION_TYPE_U32:
>> + NLA_PUT_U8(skb, TEAM_ATTR_OPTION_TYPE, NLA_U32);
>> + team_option_get(team, option, &arg);
>> + NLA_PUT_U32(skb, TEAM_ATTR_OPTION_DATA, arg);
>> + break;
>> + case TEAM_OPTION_TYPE_STRING:
>> + NLA_PUT_U8(skb, TEAM_ATTR_OPTION_TYPE, NLA_STRING);
>> + team_option_get(team, option, &arg);
>> + NLA_PUT_STRING(skb, TEAM_ATTR_OPTION_DATA,
>> + (char *) arg);
>> + break;
>> + default:
>> + BUG();
>> + }
>> + nla_nest_end(skb, option_item);
>> + }
>> +
>> + nla_nest_end(skb, option_list);
>> + return genlmsg_end(skb, hdr);
>> +
>> +nla_put_failure:
>> + genlmsg_cancel(skb, hdr);
>> + return -EMSGSIZE;
>> +}
>> +
>> +static int team_nl_fill_options_get(struct sk_buff *skb,
>> + struct genl_info *info, int flags,
>> + struct team *team)
>> +{
>> + return team_nl_fill_options_get_changed(skb, info->snd_pid,
>> + info->snd_seq, NLM_F_ACK,
>> + team, NULL);
>> +}
>> +
>> +static int team_nl_cmd_options_get(struct sk_buff *skb, struct genl_info *info)
>> +{
>> + struct team *team;
>> + int err;
>> +
>> + team = team_nl_team_get(info);
>> + if (!team)
>> + return -EINVAL;
>> +
>> + err = team_nl_send_generic(info, team, team_nl_fill_options_get);
>> +
>> + team_nl_team_put(team);
>> +
>> + return err;
>> +}
>> +
>> +static int team_nl_cmd_options_set(struct sk_buff *skb, struct genl_info *info)
>> +{
>> + struct team *team;
>> + int err = 0;
>> + int i;
>> + struct nlattr *nl_option;
>> +
>> + team = team_nl_team_get(info);
>> + if (!team)
>> + return -EINVAL;
>> +
>> + err = -EINVAL;
>> + if (!info->attrs[TEAM_ATTR_LIST_OPTION]) {
>> + err = -EINVAL;
>> + goto team_put;
>> + }
>> +
>> + nla_for_each_nested(nl_option, info->attrs[TEAM_ATTR_LIST_OPTION], i) {
>> + struct nlattr *mode_attrs[TEAM_ATTR_OPTION_MAX + 1];
>> + enum team_option_type opt_type;
>> + struct team_option *option;
>> + char *opt_name;
>> + bool opt_found = false;
>> +
>> + if (nla_type(nl_option) != TEAM_ATTR_ITEM_OPTION) {
>> + err = -EINVAL;
>> + goto team_put;
>> + }
>> + err = nla_parse_nested(mode_attrs, TEAM_ATTR_OPTION_MAX,
>> + nl_option, team_nl_option_policy);
>> + if (err)
>> + goto team_put;
>> + if (!mode_attrs[TEAM_ATTR_OPTION_NAME] ||
>> + !mode_attrs[TEAM_ATTR_OPTION_TYPE] ||
>> + !mode_attrs[TEAM_ATTR_OPTION_DATA]) {
>> + err = -EINVAL;
>> + goto team_put;
>> + }
>> + switch (nla_get_u8(mode_attrs[TEAM_ATTR_OPTION_TYPE])) {
>> + case NLA_U32:
>> + opt_type = TEAM_OPTION_TYPE_U32;
>> + break;
>> + case NLA_STRING:
>> + opt_type = TEAM_OPTION_TYPE_STRING;
>> + break;
>> + default:
>> + goto team_put;
>> + }
>> +
>> + opt_name = nla_data(mode_attrs[TEAM_ATTR_OPTION_NAME]);
>> + list_for_each_entry(option, &team->option_list, list) {
>> + long arg;
>> + struct nlattr *opt_data_attr;
>> +
>> + if (option->type != opt_type ||
>> + strcmp(option->name, opt_name))
>> + continue;
>> + opt_found = true;
>> + opt_data_attr = mode_attrs[TEAM_ATTR_OPTION_DATA];
>> + switch (opt_type) {
>> + case TEAM_OPTION_TYPE_U32:
>> + arg = nla_get_u32(opt_data_attr);
>> + break;
>> + case TEAM_OPTION_TYPE_STRING:
>> + arg = (long) nla_data(opt_data_attr);
>> + break;
>> + default:
>> + BUG();
>> + }
>> + err = team_option_set(team, option, &arg);
>> + if (err)
>> + goto team_put;
>> + }
>> + if (!opt_found) {
>> + err = -ENOENT;
>> + goto team_put;
>> + }
>> + }
>> +
>> +team_put:
>> + team_nl_team_put(team);
>> +
>> + return err;
>> +}
>> +
>> +static int team_nl_fill_port_list_get_changed(struct sk_buff *skb,
>> + u32 pid, u32 seq, int flags,
>> + struct team *team,
>> + struct team_port *changed_port)
>> +{
>> + struct nlattr *port_list;
>> + void *hdr;
>> + struct team_port *port;
>> +
>> + hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags,
>> + TEAM_CMD_PORT_LIST_GET);
>> + if (IS_ERR(hdr))
>> + return PTR_ERR(hdr);
>> +
>> + NLA_PUT_U32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex);
>> + port_list = nla_nest_start(skb, TEAM_ATTR_LIST_PORT);
>> + if (!port_list)
>> + return -EMSGSIZE;
>> +
>> + list_for_each_entry_rcu(port, &team->port_list, list) {
>> + struct nlattr *port_item;
>> +
>> + port_item = nla_nest_start(skb, TEAM_ATTR_ITEM_PORT);
>> + if (!port_item)
>> + goto nla_put_failure;
>> + NLA_PUT_U32(skb, TEAM_ATTR_PORT_IFINDEX, port->dev->ifindex);
>> + if (port == changed_port)
>> + NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_CHANGED);
>> + if (port->linkup)
>> + NLA_PUT_FLAG(skb, TEAM_ATTR_PORT_LINKUP);
>> + NLA_PUT_U32(skb, TEAM_ATTR_PORT_SPEED, port->speed);
>> + NLA_PUT_U8(skb, TEAM_ATTR_PORT_DUPLEX, port->duplex);
>> + nla_nest_end(skb, port_item);
>> + }
>> +
>> + nla_nest_end(skb, port_list);
>> + return genlmsg_end(skb, hdr);
>> +
>> +nla_put_failure:
>> + genlmsg_cancel(skb, hdr);
>> + return -EMSGSIZE;
>> +}
>> +
>> +static int team_nl_fill_port_list_get(struct sk_buff *skb,
>> + struct genl_info *info, int flags,
>> + struct team *team)
>> +{
>> + return team_nl_fill_port_list_get_changed(skb, info->snd_pid,
>> + info->snd_seq, NLM_F_ACK,
>> + team, NULL);
>> +}
>> +
>> +static int team_nl_cmd_port_list_get(struct sk_buff *skb,
>> + struct genl_info *info)
>> +{
>> + struct team *team;
>> + int err;
>> +
>> + team = team_nl_team_get(info);
>> + if (!team)
>> + return -EINVAL;
>> +
>> + err = team_nl_send_generic(info, team, team_nl_fill_port_list_get);
>> +
>> + team_nl_team_put(team);
>> +
>> + return err;
>> +}
>> +
>> +static struct genl_ops team_nl_ops[] = {
>> + {
>> + .cmd = TEAM_CMD_NOOP,
>> + .doit = team_nl_cmd_noop,
>> + .policy = team_nl_policy,
>> + },
>> + {
>> + .cmd = TEAM_CMD_OPTIONS_SET,
>> + .doit = team_nl_cmd_options_set,
>> + .policy = team_nl_policy,
>> + .flags = GENL_ADMIN_PERM,
>> + },
>> + {
>> + .cmd = TEAM_CMD_OPTIONS_GET,
>> + .doit = team_nl_cmd_options_get,
>> + .policy = team_nl_policy,
>> + .flags = GENL_ADMIN_PERM,
>> + },
>> + {
>> + .cmd = TEAM_CMD_PORT_LIST_GET,
>> + .doit = team_nl_cmd_port_list_get,
>> + .policy = team_nl_policy,
>> + .flags = GENL_ADMIN_PERM,
>> + },
>> +};
>> +
>> +static struct genl_multicast_group team_change_event_mcgrp = {
>> + .name = TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME,
>> +};
>> +
>> +static int team_nl_send_event_options_get(struct team *team,
>> + struct team_option *changed_option)
>> +{
>> + struct sk_buff *skb;
>> + int err;
>> + struct net *net = dev_net(team->dev);
>> +
>> + skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>> + if (!skb)
>> + return -ENOMEM;
>> +
>> + err = team_nl_fill_options_get_changed(skb, 0, 0, 0, team,
>> + changed_option);
>> + if (err < 0)
>> + goto err_fill;
>> +
>> + err = genlmsg_multicast_netns(net, skb, 0, team_change_event_mcgrp.id,
>> + GFP_KERNEL);
>> + return err;
>> +
>> +err_fill:
>> + nlmsg_free(skb);
>> + return err;
>> +}
>> +
>> +static int team_nl_send_event_port_list_get(struct team_port *port)
>> +{
>> + struct sk_buff *skb;
>> + int err;
>> + struct net *net = dev_net(port->team->dev);
>> +
>> + skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
>> + if (!skb)
>> + return -ENOMEM;
>> +
>> + err = team_nl_fill_port_list_get_changed(skb, 0, 0, 0,
>> + port->team, port);
>> + if (err < 0)
>> + goto err_fill;
>> +
>> + err = genlmsg_multicast_netns(net, skb, 0, team_change_event_mcgrp.id,
>> + GFP_KERNEL);
>> + return err;
>> +
>> +err_fill:
>> + nlmsg_free(skb);
>> + return err;
>> +}
>> +
>> +static int team_nl_init(void)
>> +{
>> + int err;
>> +
>> + err = genl_register_family_with_ops(&team_nl_family, team_nl_ops,
>> + ARRAY_SIZE(team_nl_ops));
>> + if (err)
>> + return err;
>> +
>> + err = genl_register_mc_group(&team_nl_family, &team_change_event_mcgrp);
>> + if (err)
>> + goto err_change_event_grp_reg;
>> +
>> + return 0;
>> +
>> +err_change_event_grp_reg:
>> + genl_unregister_family(&team_nl_family);
>> +
>> + return err;
>> +}
>> +
>> +static void team_nl_fini(void)
>> +{
>> + genl_unregister_family(&team_nl_family);
>> +}
>> +
>> +
>> +/******************
>> + * Change checkers
>> + ******************/
>> +
>> +static void __team_options_change_check(struct team *team,
>> + struct team_option *changed_option)
>> +{
>> + int err;
>> +
>> + err = team_nl_send_event_options_get(team, changed_option);
>> + if (err)
>> + netdev_warn(team->dev, "Failed to send options change via netlink\n");
>> +}
>> +
>> +/* rtnl lock is held */
>> +static void __team_port_change_check(struct team_port *port, bool linkup)
>> +{
>> + int err;
>> +
>> + if (port->linkup == linkup)
>> + return;
>> +
>> + port->linkup = linkup;
>> + if (linkup) {
>> + struct ethtool_cmd ecmd;
>> +
>> + err = __ethtool_get_settings(port->dev, &ecmd);
>> + if (!err) {
>> + port->speed = ethtool_cmd_speed(&ecmd);
>> + port->duplex = ecmd.duplex;
>> + goto send_event;
>> + }
>> + }
>> + port->speed = 0;
>> + port->duplex = 0;
>> +
>> +send_event:
>> + err = team_nl_send_event_port_list_get(port);
>> + if (err)
>> + netdev_warn(port->team->dev, "Failed to send port change of device %s via netlink\n",
>> + port->dev->name);
>> +
>> +}
>> +
>> +static void team_port_change_check(struct team_port *port, bool linkup)
>> +{
>> + struct team *team = port->team;
>> +
>> + spin_lock(&team->lock);
>> + __team_port_change_check(port, linkup);
>> + spin_unlock(&team->lock);
>> +}
>> +
>> +/************************************
>> + * Net device notifier event handler
>> + ************************************/
>> +
>> +static int team_device_event(struct notifier_block *unused,
>> + unsigned long event, void *ptr)
>> +{
>> + struct net_device *dev = (struct net_device *) ptr;
>> + struct team_port *port;
>> +
>> + port = team_port_get_rtnl(dev);
>> + if (!port)
>> + return NOTIFY_DONE;
>> +
>> + switch (event) {
>> + case NETDEV_UP:
>> + if (netif_carrier_ok(dev))
>> + team_port_change_check(port, true);
>> + case NETDEV_DOWN:
>> + team_port_change_check(port, false);
>> + case NETDEV_CHANGE:
>> + if (netif_running(port->dev))
>> + team_port_change_check(port,
>> + !!netif_carrier_ok(port->dev));
>> + break;
>> + case NETDEV_UNREGISTER:
>> + team_del_slave(port->team->dev, dev);
>> + break;
>> + case NETDEV_FEAT_CHANGE:
>> + team_compute_features(port->team);
>> + break;
>> + case NETDEV_CHANGEMTU:
>> + /* Forbid to change mtu of underlaying device */
>> + return NOTIFY_BAD;
>> + case NETDEV_CHANGEADDR:
>> + /* Forbid to change addr of underlaying device */
>> + return NOTIFY_BAD;
>> + case NETDEV_PRE_TYPE_CHANGE:
>> + /* Forbid to change type of underlaying device */
>> + return NOTIFY_BAD;
>> + }
>> + return NOTIFY_DONE;
>> +}
>> +
>> +static struct notifier_block team_notifier_block __read_mostly = {
>> + .notifier_call = team_device_event,
>> +};
>> +
>> +
>> +/***********************
>> + * Module init and exit
>> + ***********************/
>> +
>> +static int __init team_module_init(void)
>> +{
>> + int err;
>> +
>> + register_netdevice_notifier(&team_notifier_block);
>> +
>> + err = rtnl_link_register(&team_link_ops);
>> + if (err)
>> + goto err_rtln_reg;
>> +
>> + err = team_nl_init();
>> + if (err)
>> + goto err_nl_init;
>> +
>> + return 0;
>> +
>> +err_nl_init:
>> + rtnl_link_unregister(&team_link_ops);
>> +
>> +err_rtln_reg:
>> + unregister_netdevice_notifier(&team_notifier_block);
>> +
>> + return err;
>> +}
>> +
>> +static void __exit team_module_exit(void)
>> +{
>> + team_nl_fini();
>> + rtnl_link_unregister(&team_link_ops);
>> + unregister_netdevice_notifier(&team_notifier_block);
>> +}
>> +
>> +module_init(team_module_init);
>> +module_exit(team_module_exit);
>> +
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_AUTHOR("Jiri Pirko <jpirko@redhat.com>");
>> +MODULE_DESCRIPTION("Ethernet team device driver");
>> +MODULE_ALIAS_RTNL_LINK(DRV_NAME);
>> diff --git a/drivers/net/team/team_mode_activebackup.c b/drivers/net/team/team_mode_activebackup.c
>> new file mode 100644
>> index 0000000..1aa2bfb
>> --- /dev/null
>> +++ b/drivers/net/team/team_mode_activebackup.c
>> @@ -0,0 +1,152 @@
>> +/*
>> + * net/drivers/team/team_mode_activebackup.c - Active-backup mode for team
>> + * Copyright (c) 2011 Jiri Pirko <jpirko@redhat.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/types.h>
>> +#include <linux/module.h>
>> +#include <linux/init.h>
>> +#include <linux/errno.h>
>> +#include <linux/netdevice.h>
>> +#include <net/rtnetlink.h>
>> +#include <linux/if_team.h>
>> +
>> +struct ab_priv {
>> + struct team_port __rcu *active_port;
>> +};
>> +
>> +static struct ab_priv *ab_priv(struct team *team)
>> +{
>> + return (struct ab_priv *) &team->mode_priv;
>> +}
>> +
>> +static rx_handler_result_t ab_receive(struct team *team, struct team_port *port,
>> + struct sk_buff *skb) {
>> + struct team_port *active_port;
>> +
>> + active_port = rcu_dereference(ab_priv(team)->active_port);
>> + if (active_port != port)
>> + return RX_HANDLER_EXACT;
>> + return RX_HANDLER_ANOTHER;
>> +}
>> +
>> +static bool ab_transmit(struct team *team, struct sk_buff *skb)
>> +{
>> + struct team_port *active_port;
>> +
>> + active_port = rcu_dereference(ab_priv(team)->active_port);
>> + if (unlikely(!active_port))
>> + goto drop;
>> + skb->dev = active_port->dev;
>> + if (dev_queue_xmit(skb))
>> + return false;
>> + return true;
>> +
>> +drop:
>> + dev_kfree_skb(skb);
>> + return false;
>> +}
>> +
>> +static void ab_port_leave(struct team *team, struct team_port *port)
>> +{
>> + if (ab_priv(team)->active_port == port)
>> + rcu_assign_pointer(ab_priv(team)->active_port, NULL);
>> +}
>> +
>> +static void ab_port_change_mac(struct team *team, struct team_port *port)
>> +{
>> + if (ab_priv(team)->active_port == port)
>> + team_port_set_team_mac(port);
>> +}
>> +
>> +static int ab_active_port_get(struct team *team, void *arg)
>> +{
>> + u32 *ifindex = arg;
>> +
>> + *ifindex = 0;
>> + if (ab_priv(team)->active_port)
>> + *ifindex = ab_priv(team)->active_port->dev->ifindex;
>> + return 0;
>> +}
>> +
>> +static int ab_active_port_set(struct team *team, void *arg)
>> +{
>> + u32 *ifindex = arg;
>> + struct team_port *port;
>> +
>> + list_for_each_entry_rcu(port, &team->port_list, list) {
>> + if (port->dev->ifindex == *ifindex) {
>> + struct team_port *ac_port = ab_priv(team)->active_port;
>> +
>> + /* rtnl_lock needs to be held when setting macs */
>> + rtnl_lock();
>> + if (ac_port)
>> + team_port_set_orig_mac(ac_port);
>> + rcu_assign_pointer(ab_priv(team)->active_port, port);
>> + team_port_set_team_mac(port);
>> + rtnl_unlock();
>> + return 0;
>> + }
>> + }
>> + return -ENOENT;
>> +}
>> +
>> +static struct team_option ab_options[] = {
>> + {
>> + .name = "activeport",
>> + .type = TEAM_OPTION_TYPE_U32,
>> + .getter = ab_active_port_get,
>> + .setter = ab_active_port_set,
>> + },
>> +};
>> +
>> +int ab_init(struct team *team)
>> +{
>> + team_options_register(team, ab_options, ARRAY_SIZE(ab_options));
>> + return 0;
>> +}
>> +
>> +void ab_exit(struct team *team)
>> +{
>> + team_options_unregister(team, ab_options, ARRAY_SIZE(ab_options));
>> +}
>> +
>> +static const struct team_mode_ops ab_mode_ops = {
>> + .init = ab_init,
>> + .exit = ab_exit,
>> + .receive = ab_receive,
>> + .transmit = ab_transmit,
>> + .port_leave = ab_port_leave,
>> + .port_change_mac = ab_port_change_mac,
>> +};
>> +
>> +static struct team_mode ab_mode = {
>> + .kind = "activebackup",
>> + .owner = THIS_MODULE,
>> + .priv_size = sizeof(struct ab_priv),
>> + .ops = &ab_mode_ops,
>> +};
>> +
>> +static int __init ab_init_module(void)
>> +{
>> + return team_mode_register(&ab_mode);
>> +}
>> +
>> +static void __exit ab_cleanup_module(void)
>> +{
>> + team_mode_unregister(&ab_mode);
>> +}
>> +
>> +module_init(ab_init_module);
>> +module_exit(ab_cleanup_module);
>> +
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_AUTHOR("Jiri Pirko <jpirko@redhat.com>");
>> +MODULE_DESCRIPTION("Active-backup mode for team");
>> +MODULE_ALIAS("team-mode-activebackup");
>> diff --git a/drivers/net/team/team_mode_roundrobin.c b/drivers/net/team/team_mode_roundrobin.c
>> new file mode 100644
>> index 0000000..0374052
>> --- /dev/null
>> +++ b/drivers/net/team/team_mode_roundrobin.c
>> @@ -0,0 +1,107 @@
>> +/*
>> + * net/drivers/team/team_mode_roundrobin.c - Round-robin mode for team
>> + * Copyright (c) 2011 Jiri Pirko <jpirko@redhat.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/types.h>
>> +#include <linux/module.h>
>> +#include <linux/init.h>
>> +#include <linux/errno.h>
>> +#include <linux/netdevice.h>
>> +#include <linux/if_team.h>
>> +
>> +struct rr_priv {
>> + unsigned int sent_packets;
>> +};
>> +
>> +static struct rr_priv *rr_priv(struct team *team)
>> +{
>> + return (struct rr_priv *) &team->mode_priv;
>> +}
>> +
>> +static struct team_port *__get_first_port_up(struct team *team,
>> + struct team_port *port)
>> +{
>> + struct team_port *cur;
>> +
>> + if (port->linkup)
>> + return port;
>> + cur = port;
>> + list_for_each_entry_continue_rcu(cur, &team->port_list, list)
>> + if (cur->linkup)
>> + return cur;
>> + list_for_each_entry_rcu(cur, &team->port_list, list) {
>> + if (cur == port)
>> + break;
>> + if (cur->linkup)
>> + return cur;
>> + }
>> + return NULL;
>> +}
>> +
>> +static bool rr_transmit(struct team *team, struct sk_buff *skb)
>> +{
>> + struct team_port *port;
>> + int port_index;
>> +
>> + port_index = rr_priv(team)->sent_packets++ % team->port_count;
>> + port = team_get_port_by_index_rcu(team, port_index);
>> + port = __get_first_port_up(team, port);
>> + if (unlikely(!port))
>> + goto drop;
>> + skb->dev = port->dev;
>> + if (dev_queue_xmit(skb))
>> + return false;
>> + return true;
>> +
>> +drop:
>> + dev_kfree_skb(skb);
>> + return false;
>> +}
>> +
>> +static int rr_port_enter(struct team *team, struct team_port *port)
>> +{
>> + return team_port_set_team_mac(port);
>> +}
>> +
>> +static void rr_port_change_mac(struct team *team, struct team_port *port)
>> +{
>> + team_port_set_team_mac(port);
>> +}
>> +
>> +static const struct team_mode_ops rr_mode_ops = {
>> + .transmit = rr_transmit,
>> + .port_enter = rr_port_enter,
>> + .port_change_mac = rr_port_change_mac,
>> +};
>> +
>> +static struct team_mode rr_mode = {
>> + .kind = "roundrobin",
>> + .owner = THIS_MODULE,
>> + .priv_size = sizeof(struct rr_priv),
>> + .ops = &rr_mode_ops,
>> +};
>> +
>> +static int __init rr_init_module(void)
>> +{
>> + return team_mode_register(&rr_mode);
>> +}
>> +
>> +static void __exit rr_cleanup_module(void)
>> +{
>> + team_mode_unregister(&rr_mode);
>> +}
>> +
>> +module_init(rr_init_module);
>> +module_exit(rr_cleanup_module);
>> +
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_AUTHOR("Jiri Pirko <jpirko@redhat.com>");
>> +MODULE_DESCRIPTION("Round-robin mode for team");
>> +MODULE_ALIAS("team-mode-roundrobin");
>> diff --git a/include/linux/Kbuild b/include/linux/Kbuild
>> index 619b565..0b091b3 100644
>> --- a/include/linux/Kbuild
>> +++ b/include/linux/Kbuild
>> @@ -185,6 +185,7 @@ header-y += if_pppol2tp.h
>> header-y += if_pppox.h
>> header-y += if_slip.h
>> header-y += if_strip.h
>> +header-y += if_team.h
>> header-y += if_tr.h
>> header-y += if_tun.h
>> header-y += if_tunnel.h
>> diff --git a/include/linux/if.h b/include/linux/if.h
>> index db20bd4..06b6ef6 100644
>> --- a/include/linux/if.h
>> +++ b/include/linux/if.h
>> @@ -79,6 +79,7 @@
>> #define IFF_TX_SKB_SHARING 0x10000 /* The interface supports sharing
>> * skbs on transmit */
>> #define IFF_UNICAST_FLT 0x20000 /* Supports unicast filtering */
>> +#define IFF_TEAM_PORT 0x40000 /* device used as team port */
>>
>> #define IF_GET_IFACE 0x0001 /* for querying only */
>> #define IF_GET_PROTO 0x0002
>> diff --git a/include/linux/if_team.h b/include/linux/if_team.h
>> new file mode 100644
>> index 0000000..21581a7
>> --- /dev/null
>> +++ b/include/linux/if_team.h
>> @@ -0,0 +1,233 @@
>> +/*
>> + * include/linux/if_team.h - Network team device driver header
>> + * Copyright (c) 2011 Jiri Pirko <jpirko@redhat.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + */
>> +
>> +#ifndef _LINUX_IF_TEAM_H_
>> +#define _LINUX_IF_TEAM_H_
>> +
>> +#ifdef __KERNEL__
>> +
>> +struct team_pcpu_stats {
>> + u64 rx_packets;
>> + u64 rx_bytes;
>> + u64 rx_multicast;
>> + u64 tx_packets;
>> + u64 tx_bytes;
>> + struct u64_stats_sync syncp;
>> + u32 rx_dropped;
>> + u32 tx_dropped;
>> +};
>> +
>> +struct team;
>> +
>> +struct team_port {
>> + struct net_device *dev;
>> + struct hlist_node hlist; /* node in hash list */
>> + struct list_head list; /* node in ordinary list */
>> + struct team *team;
>> + int index;
>> +
>> + /*
>> + * A place for storing original values of the device before it
>> + * become a port.
>> + */
>> + struct {
>> + unsigned char dev_addr[MAX_ADDR_LEN];
>> + unsigned int mtu;
>> + } orig;
>> +
>> + bool linkup;
>> + u32 speed;
>> + u8 duplex;
>> +
>> + struct rcu_head rcu;
>> +};
>> +
>> +struct team_mode_ops {
>> + int (*init)(struct team *team);
>> + void (*exit)(struct team *team);
>> + rx_handler_result_t (*receive)(struct team *team,
>> + struct team_port *port,
>> + struct sk_buff *skb);
>> + bool (*transmit)(struct team *team, struct sk_buff *skb);
>> + int (*port_enter)(struct team *team, struct team_port *port);
>> + void (*port_leave)(struct team *team, struct team_port *port);
>> + void (*port_change_mac)(struct team *team, struct team_port *port);
>> +};
>> +
>> +enum team_option_type {
>> + TEAM_OPTION_TYPE_U32,
>> + TEAM_OPTION_TYPE_STRING,
>> +};
>> +
>> +struct team_option {
>> + struct list_head list;
>> + const char *name;
>> + enum team_option_type type;
>> + int (*getter)(struct team *team, void *arg);
>> + int (*setter)(struct team *team, void *arg);
>> +};
>> +
>> +struct team_mode {
>> + struct list_head list;
>> + const char *kind;
>> + struct module *owner;
>> + size_t priv_size;
>> + const struct team_mode_ops *ops;
>> +};
>> +
>> +#define TEAM_MODE_PRIV_LONGS 4
>> +#define TEAM_MODE_PRIV_SIZE (sizeof(long) * TEAM_MODE_PRIV_LONGS)
>> +
>> +struct team {
>> + struct net_device *dev; /* associated netdevice */
>> + struct team_pcpu_stats __percpu *pcpu_stats;
>> +
>> + spinlock_t lock; /* used for overall locking, e.g. port lists write */
>> +
>> + /*
>> + * port lists with port count
>> + */
>> + int port_count;
>> + struct hlist_head *port_hlist;
>> + struct list_head port_list;
>> +
>> + struct list_head option_list;
>> +
>> + const char *mode_kind;
>> + struct team_mode_ops mode_ops;
>> + long mode_priv[TEAM_MODE_PRIV_LONGS];
>> +};
>> +
>> +#define TEAM_PORT_HASHBITS 4
>> +#define TEAM_PORT_HASHENTRIES (1 << TEAM_PORT_HASHBITS)
>> +
>> +static inline struct hlist_head *
>> +team_port_index_hash(const struct team *team,
>> + int port_index)
>> +{
>> + return &team->port_hlist[port_index & (TEAM_PORT_HASHENTRIES - 1)];
>> +}
>> +
>> +static inline struct team_port *
>> +team_get_port_by_index_rcu(const struct team *team,
>> + int port_index)
>> +{
>> + struct hlist_node *p;
>> + struct team_port *port;
>> + struct hlist_head *head = team_port_index_hash(team, port_index);
>> +
>> + hlist_for_each_entry_rcu(port, p, head, hlist)
>> + if (port->index == port_index)
>> + return port;
>> + return NULL;
>> +}
>> +
>> +extern int team_port_set_orig_mac(struct team_port *port);
>> +extern int team_port_set_team_mac(struct team_port *port);
>> +extern void team_options_register(struct team *team,
>> + struct team_option *option,
>> + size_t option_count);
>> +extern void team_options_unregister(struct team *team,
>> + struct team_option *option,
>> + size_t option_count);
>> +extern int team_mode_register(struct team_mode *mode);
>> +extern int team_mode_unregister(struct team_mode *mode);
>> +
>> +#endif /* __KERNEL__ */
>> +
>> +#define TEAM_STRING_MAX_LEN 32
>> +
>> +/**********************************
>> + * NETLINK_GENERIC netlink family.
>> + **********************************/
>> +
>> +enum {
>> + TEAM_CMD_NOOP,
>> + TEAM_CMD_OPTIONS_SET,
>> + TEAM_CMD_OPTIONS_GET,
>> + TEAM_CMD_PORT_LIST_GET,
>> +
>> + __TEAM_CMD_MAX,
>> + TEAM_CMD_MAX = (__TEAM_CMD_MAX - 1),
>> +};
>> +
>> +enum {
>> + TEAM_ATTR_UNSPEC,
>> + TEAM_ATTR_TEAM_IFINDEX, /* u32 */
>> + TEAM_ATTR_LIST_OPTION, /* nest */
>> + TEAM_ATTR_LIST_PORT, /* nest */
>> +
>> + __TEAM_ATTR_MAX,
>> + TEAM_ATTR_MAX = __TEAM_ATTR_MAX - 1,
>> +};
>> +
>> +/* Nested layout of get/set msg:
>> + *
>> + * [TEAM_ATTR_LIST_OPTION]
>> + * [TEAM_ATTR_ITEM_OPTION]
>> + * [TEAM_ATTR_OPTION_*], ...
>> + * [TEAM_ATTR_ITEM_OPTION]
>> + * [TEAM_ATTR_OPTION_*], ...
>> + * ...
>> + * [TEAM_ATTR_LIST_PORT]
>> + * [TEAM_ATTR_ITEM_PORT]
>> + * [TEAM_ATTR_PORT_*], ...
>> + * [TEAM_ATTR_ITEM_PORT]
>> + * [TEAM_ATTR_PORT_*], ...
>> + * ...
>> + */
>> +
>> +enum {
>> + TEAM_ATTR_ITEM_OPTION_UNSPEC,
>> + TEAM_ATTR_ITEM_OPTION, /* nest */
>> +
>> + __TEAM_ATTR_ITEM_OPTION_MAX,
>> + TEAM_ATTR_ITEM_OPTION_MAX = __TEAM_ATTR_ITEM_OPTION_MAX - 1,
>> +};
>> +
>> +enum {
>> + TEAM_ATTR_OPTION_UNSPEC,
>> + TEAM_ATTR_OPTION_NAME, /* string */
>> + TEAM_ATTR_OPTION_CHANGED, /* flag */
>> + TEAM_ATTR_OPTION_TYPE, /* u8 */
>> + TEAM_ATTR_OPTION_DATA, /* dynamic */
>> +
>> + __TEAM_ATTR_OPTION_MAX,
>> + TEAM_ATTR_OPTION_MAX = __TEAM_ATTR_OPTION_MAX - 1,
>> +};
>> +
>> +enum {
>> + TEAM_ATTR_ITEM_PORT_UNSPEC,
>> + TEAM_ATTR_ITEM_PORT, /* nest */
>> +
>> + __TEAM_ATTR_ITEM_PORT_MAX,
>> + TEAM_ATTR_ITEM_PORT_MAX = __TEAM_ATTR_ITEM_PORT_MAX - 1,
>> +};
>> +
>> +enum {
>> + TEAM_ATTR_PORT_UNSPEC,
>> + TEAM_ATTR_PORT_IFINDEX, /* u32 */
>> + TEAM_ATTR_PORT_CHANGED, /* flag */
>> + TEAM_ATTR_PORT_LINKUP, /* flag */
>> + TEAM_ATTR_PORT_SPEED, /* u32 */
>> + TEAM_ATTR_PORT_DUPLEX, /* u8 */
>> +
>> + __TEAM_ATTR_PORT_MAX,
>> + TEAM_ATTR_PORT_MAX = __TEAM_ATTR_PORT_MAX - 1,
>> +};
>> +
>> +/*
>> + * NETLINK_GENERIC related info
>> + */
>> +#define TEAM_GENL_NAME "team"
>> +#define TEAM_GENL_VERSION 0x1
>> +#define TEAM_GENL_CHANGE_EVENT_MC_GRP_NAME "change_event"
>> +
>> +#endif /* _LINUX_IF_TEAM_H_ */
>> --
>> 1.7.6
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe netdev" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>>
^ permalink raw reply
* Re: [PATCH v2 1/3] net: hold sock reference while processing tx timestamps
From: David Miller @ 2011-10-24 6:55 UTC (permalink / raw)
To: eric.dumazet; +Cc: richardcochran, netdev, johannes, stable
In-Reply-To: <1319196710.2338.26.camel@edumazet-HP-Compaq-6005-Pro-SFF-PC>
From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Fri, 21 Oct 2011 13:31:50 +0200
> Le vendredi 21 octobre 2011 à 12:49 +0200, Richard Cochran a écrit :
>> The pair of functions,
>>
>> * skb_clone_tx_timestamp()
>> * skb_complete_tx_timestamp()
>>
>> were designed to allow timestamping in PHY devices. The first
>> function, called during the MAC driver's hard_xmit method, identifies
>> PTP protocol packets, clones them, and gives them to the PHY device
>> driver. The PHY driver may hold onto the packet and deliver it at a
>> later time using the second function, which adds the packet to the
>> socket's error queue.
>>
>> As pointed out by Johannes, nothing prevents the socket from
>> disappearing while the cloned packet is sitting in the PHY driver
>> awaiting a timestamp. This patch fixes the issue by taking a reference
>> on the socket for each such packet. In addition, the comments
>> regarding the usage of these function are expanded to highlight the
>> rule that PHY drivers must use skb_complete_tx_timestamp() to release
>> the packet, in order to release the socket reference, too.
>>
>> These functions first appeared in v2.6.36.
>>
>> Reported-by: Johannes Berg <johannes@sipsolutions.net>
>> Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
>> Cc: <stable@vger.kernel.org>
>> ---
>
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Applied.
^ permalink raw reply
* Re: [PATCH net-next v3] route: fix ICMP redirect validation
From: David Miller @ 2011-10-24 6:56 UTC (permalink / raw)
To: fbl; +Cc: netdev
In-Reply-To: <1319215448-6777-1-git-send-email-fbl@redhat.com>
From: Flavio Leitner <fbl@redhat.com>
Date: Fri, 21 Oct 2011 14:44:08 -0200
> The commit f39925dbde7788cfb96419c0f092b086aa325c0f
> (ipv4: Cache learned redirect information in inetpeer.)
> removed some ICMP packet validations which are required by
> RFC 1122, section 3.2.2.2:
> ...
> A Redirect message SHOULD be silently discarded if the new
> gateway address it specifies is not on the same connected
> (sub-) net through which the Redirect arrived [INTRO:2,
> Appendix A], or if the source of the Redirect is not the
> current first-hop gateway for the specified destination (see
> Section 3.3.1).
>
> Signed-off-by: Flavio Leitner <fbl@redhat.com>
Applied, thanks.
^ permalink raw reply
* Re: [PATCH v2 2/3] dp83640: use proper function to free transmit time stamping packets
From: David Miller @ 2011-10-24 6:55 UTC (permalink / raw)
To: richardcochran; +Cc: netdev, eric.dumazet, johannes, stable
In-Reply-To: <966b346f5f73c0afc48f89686e9ed1f44457074b.1319193734.git.richard.cochran@omicron.at>
From: Richard Cochran <richardcochran@gmail.com>
Date: Fri, 21 Oct 2011 12:49:16 +0200
> The previous commit enforces a new rule for handling the cloned packets
> for transmit time stamping. These packets must not be freed using any other
> function than skb_complete_tx_timestamp. This commit fixes the one and only
> driver using this API.
>
> The driver first appeared in v3.0.
>
> Signed-off-by: Richard Cochran <richard.cochran@omicron.at>
> Acked-by: Eric Dumazet <eric.dumazet@gmail.com>
> Cc: <stable@vger.kernel.org>
In the 'net' tree, which is where you should be targetting these dp83640
driver patches, the code looks nothing like what you're patching against.
Please respin patches #2 and #3 against current sources.
^ permalink raw reply
* Re: [PATCH] jme: fix irq storm after suspend/resume
From: David Miller @ 2011-10-24 6:58 UTC (permalink / raw)
To: grawity; +Cc: linux-kernel, netdev
In-Reply-To: <j7uqst$kpc$1@dough.gmane.org>
From: "Mantas M." <grawity@gmail.com>
Date: Sat, 22 Oct 2011 19:27:09 +0300
> On 22/10/11 15:56, Clemens Buchacher wrote:
>> If the device is down during suspend/resume, interrupts are enabled
>> without a registered interrupt handler, causing a storm of
>> unhandled interrupts until the IRQ is disabled because "nobody
>> cared".
>>
>> Instead, check that the device is up before touching it in the
>> suspend/resume code.
>>
>> Fixes https://bugzilla.kernel.org/show_bug.cgi?id=39112
>
> Just tested the patch, suspend/resume works well.
Applied, thanks.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox