Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH net-next 3/3] sunvnet: generate ICMP PTMUD messages for smaller port MTUs
From: David Miller @ 2014-09-12 22:26 UTC (permalink / raw)
  To: david.stevens; +Cc: netdev
In-Reply-To: <5413723C.7040607@oracle.com>

From: David L Stevens <david.stevens@oracle.com>
Date: Fri, 12 Sep 2014 18:22:52 -0400

> +		if (skb->protocol == htons(ETH_P_IP))
> +		{

Openning braces should be on the same line as the if() statement.

^ permalink raw reply

* Re: [PATCH v11 net-next 00/12] eBPF syscall, verifier, testsuite
From: Alexei Starovoitov @ 2014-09-12 22:40 UTC (permalink / raw)
  To: Andy Lutomirski
  Cc: Alexei Starovoitov, Daniel Borkmann, David S. Miller, Ingo Molnar,
	Linus Torvalds, Steven Rostedt, Hannes Frederic Sowa,
	Chema Gonzalez, Eric Dumazet, Peter Zijlstra, Pablo Neira Ayuso,
	H. Peter Anvin, Andrew Morton, Kees Cook, Linux API,
	Network Development, LKML
In-Reply-To: <CALCETrXqwd=dp31fckMPruQMwVw+UAjaf=SSWp8wr_Cdz_tQdw@mail.gmail.com>

Hi All,

the list of things I fixed so far from V11:
- dropped patch 11 and copied few macros to libbpf.h (suggested by Daniel)
- replaced 'enum bpf_prog_type' with u32 to be safe in compat (.. Andy)
- implemented and tested compat support (.. Daniel)
- changed 'void *log_buf' to 'char *' (.. Daniel)
- combined struct bpf_work_struct and bpf_prog_info (.. Daniel)
- added better return value explanation to manpage (.. Andy)
- added log_buf/log_size explanation to manpage (.. Andy & Daniel)
- added a lot more info about prog_type and map_type and
  they relation to verifier (.. Andy)

anything else I missed from the discussion we had?

Here is updated manpage. Please take a look.

BPF(2)                     Linux Programmer's Manual                    BPF(2)



NAME
       bpf - perform a command on eBPF map or program

SYNOPSIS
       #include <linux/bpf.h>

       int bpf(int cmd, union bpf_attr *attr, unsigned int size);


DESCRIPTION
       bpf()  syscall  is a multiplexor for a range of different operations on
       eBPF  which  can  be  characterized  as  "universal  in-kernel  virtual
       machine".  eBPF  is  similar  to  original  Berkeley  Packet Filter (or
       "classic BPF") used to filter network packets. Both statically  analyze
       the  programs  before  loading  them  into  the  kernel  to ensure that
       programs cannot harm the running system.

       eBPF extends classic BPF in multiple ways including ability to call in-
       kernel  helper  functions  and  access shared data structures like eBPF
       maps.  The programs can be written in a restricted C that  is  compiled
       into  eBPF  bytecode  and executed on the eBPF virtual machine or JITed
       into native instruction set.

   eBPF Design/Architecture
       eBPF maps is a generic storage of different types.   User  process  can
       create  multiple  maps  (with key/value being opaque bytes of data) and
       access them via file descriptor. In parallel eBPF programs  can  access
       maps  from inside the kernel.  It's up to user process and eBPF program
       to decide what they store inside maps.

       eBPF programs are similar to kernel modules. They  are  loaded  by  the
       user  process  and automatically unloaded when process exits. Each eBPF
       program is a safe run-to-completion set of instructions. eBPF  verifier
       statically  determines  that  the  program  terminates  and  is safe to
       execute. During verification the program takes a hold of maps  that  it
       intends to use, so selected maps cannot be removed until the program is
       unloaded. The program can be attached to different events. These events
       can  be packets, tracepoint events and other types in the future. A new
       event triggers execution of the program  which  may  store  information
       about the event in the maps.  Beyond storing data the programs may call
       into in-kernel helper functions which may, for example, dump stack,  do
       trace_printk  or other forms of live kernel debugging. The same program
       can be attached to multiple events. Different programs can  access  the
       same map:
         tracepoint  tracepoint  tracepoint    sk_buff    sk_buff
          event A     event B     event C      on eth0    on eth1
           |             |          |            |          |
           |             |          |            |          |
           --> tracing <--      tracing       socket      socket
                prog_1           prog_2       prog_3      prog_4
                |  |               |            |
             |---  -----|  |-------|           map_3
           map_1       map_2

   Syscall Arguments
       bpf()  syscall  operation  is determined by cmd which can be one of the
       following:

       BPF_MAP_CREATE
              Create a map with given type and attributes and return map FD

       BPF_MAP_LOOKUP_ELEM
              Lookup element by key in a given map and return its value

       BPF_MAP_UPDATE_ELEM
              Create or update element (key/value pair) in a given map

       BPF_MAP_DELETE_ELEM
              Lookup and delete element by key in a given map

       BPF_MAP_GET_NEXT_KEY
              Lookup element by key in a given map  and  return  key  of  next
              element

       BPF_PROG_LOAD
              Verify and load eBPF program

       attr   is a pointer to a union of type bpf_attr as defined below.

       size   is the size of the union.

       union bpf_attr {
           struct { /* anonymous struct used by BPF_MAP_CREATE command */
               __u32             map_type;
               __u32             key_size;    /* size of key in bytes */
               __u32             value_size;  /* size of value in bytes */
               __u32             max_entries; /* max number of entries in a map */
           };

           struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */
               int map_fd;
               void *key;
               union {
                   void *value;
                   void *next_key;
               };
           };

           struct { /* anonymous struct used by BPF_PROG_LOAD command */
               __u32                 prog_type;
               __u32                 insn_cnt;
               const struct bpf_insn *insns;
               const char            *license;
               __u32                 log_level; /* verbosity level of eBPF verifier */
               __u32                 log_size;  /* size of user buffer */
               char                  *log_buf;  /* user supplied buffer */
           };
       };

   eBPF maps
       maps  is  a generic storage of different types for sharing data between
       kernel and userspace.

       Any map type has the following attributes:
         . type
         . max number of elements
         . key size in bytes
         . value size in bytes

       The following wrapper functions demonstrate how  this  syscall  can  be
       used  to  access the maps. The functions use the cmd argument to invoke
       different operations.

       BPF_MAP_CREATE
              int bpf_create_map(enum bpf_map_type map_type, int key_size,
                                 int value_size, int max_entries)
              {
                  union bpf_attr attr = {
                      .map_type = map_type,
                      .key_size = key_size,
                      .value_size = value_size,
                      .max_entries = max_entries
                  };

                  return bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
              }
              bpf()  syscall  creates  a  map  of  map_type  type  and   given
              attributes  key_size,  value_size,  max_entries.   On success it
              returns process-local file descriptor. On error, -1 is  returned
              and errno is set to EINVAL or EPERM or ENOMEM.

              The  attributes key_size and value_size will be used by verifier
              during  program  loading  to  check  that  program  is   calling
              bpf_map_*_elem() helper functions with correctly initialized key
              and  that  program  doesn't  access  map  element  value  beyond
              specified  value_size.   For  example,  when map is created with
              key_size = 8 and program does:
              bpf_map_lookup_elem(map_fd, fp - 4)
              such program will be rejected, since in-kernel  helper  function
              bpf_map_lookup_elem(map_fd,  void  *key) expects to read 8 bytes
              from 'key' pointer, but 'fp - 4' starting address will cause out
              of bounds stack access.

              Similarly,  when  map is created with value_size = 1 and program
              does:
              value = bpf_map_lookup_elem(...);
              *(u32 *)value = 1;
              such program will be rejected, since it accesses  value  pointer
              beyond specified 1 byte value_size limit.

              Currently only hash table map_type is supported:
              enum bpf_map_type {
                 BPF_MAP_TYPE_UNSPEC,
                 BPF_MAP_TYPE_HASH,
              };
              map_type  selects  one  of  the available map implementations in
              kernel. Regardless of type eBPF program accesses maps  with  the
              same      bpf_map_lookup_elem()/bpf_map_update_elem()     helper
              functions.

       BPF_MAP_LOOKUP_ELEM
              int bpf_lookup_elem(int fd, void *key, void *value)
              {
                  union bpf_attr attr = {
                      .map_fd = fd,
                      .key = key,
                      .value = value,
                  };

                  return bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
              }
              bpf() syscall looks up an element with given key in  a  map  fd.
              If  element  is found it returns zero and stores element's value
              into value.  If element is not found  it  returns  -1  and  sets
              errno to ENOENT.

       BPF_MAP_UPDATE_ELEM
              int bpf_update_elem(int fd, void *key, void *value)
              {
                  union bpf_attr attr = {
                      .map_fd = fd,
                      .key = key,
                      .value = value,
                  };

                  return bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
              }
              The  call  creates  or updates element with given key/value in a
              map fd.  On success it returns zero.  On error, -1  is  returned
              and  errno  is set to EINVAL or EPERM or ENOMEM or E2BIG.  E2BIG
              indicates that number of elements in the map reached max_entries
              limit specified at map creation time.

       BPF_MAP_DELETE_ELEM
              int bpf_delete_elem(int fd, void *key)
              {
                  union bpf_attr attr = {
                      .map_fd = fd,
                      .key = key,
                  };

                  return bpf(BPF_MAP_DELETE_ELEM, &attr, sizeof(attr));
              }
              The call deletes an element in a map fd with given key.  Returns
              zero on success. If element is not found it returns -1 and  sets
              errno to ENOENT.

       BPF_MAP_GET_NEXT_KEY
              int bpf_get_next_key(int fd, void *key, void *next_key)
              {
                  union bpf_attr attr = {
                      .map_fd = fd,
                      .key = key,
                      .next_key = next_key,
                  };

                  return bpf(BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr));
              }
              The  call  looks  up  an  element  by  key in a given map fd and
              returns key of the next element into next_key pointer. If key is
              not  found,  it return zero and returns key of the first element
              into next_key. If key is the last element,  it  returns  -1  and
              sets  errno  to  ENOENT. Other possible errno values are ENOMEM,
              EFAULT, EPERM, EINVAL.  This method can be used to iterate  over
              all elements of the map.

       close(map_fd)
              will  delete  the  map  map_fd.  Exiting process will delete all
              maps automatically.

   eBPF programs
       BPF_PROG_LOAD
              This cmd is used to load eBPF program into the kernel.

              char bpf_log_buf[LOG_BUF_SIZE];

              int bpf_prog_load(enum bpf_prog_type prog_type,
                                const struct bpf_insn *insns, int insn_cnt,
                                const char *license)
              {
                  union bpf_attr attr = {
                      .prog_type = prog_type,
                      .insns = insns,
                      .insn_cnt = insn_cnt,
                      .license = license,
                      .log_buf = bpf_log_buf,
                      .log_size = LOG_BUF_SIZE,
                      .log_level = 1,
                  };

                  return bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
              }
              prog_type is one of the available program types:
              enum bpf_prog_type {
                      BPF_PROG_TYPE_UNSPEC,
                      BPF_PROG_TYPE_SOCKET,
                      BPF_PROG_TYPE_TRACING,
              };
              By picking prog_type program author  selects  a  set  of  helper
              functions callable from eBPF program and corresponding format of
              struct bpf_context (which is  the  data  blob  passed  into  the
              program  as  the  first  argument).   For  example, the programs
              loaded with  prog_type  =  TYPE_TRACING  may  call  bpf_printk()
              helper,  whereas  TYPE_SOCKET  programs  may  not.   The  set of
              functions  available  to  the  programs  under  given  type  may
              increase in the future.

              Currently the set of functions for TYPE_TRACING is:
              bpf_map_lookup_elem(map_fd, void *key)              // lookup key in a map_fd
              bpf_map_update_elem(map_fd, void *key, void *value) // update key/value
              bpf_map_delete_elem(map_fd, void *key)              // delete key in a map_fd
              bpf_ktime_get_ns(void)                              // returns current ktime
              bpf_printk(char *fmt, int fmt_size, ...)            // prints into trace buffer
              bpf_get_current(void)                               // return current task pointer
              bpf_memcmp(void *ptr1, void *ptr2, int size)        // non-faulting memcmp
              bpf_fetch_ptr(void *ptr)    // non-faulting load pointer from any address
              bpf_fetch_u8(void *ptr)     // non-faulting 1 byte load
              bpf_fetch_u16(void *ptr)    // other non-faulting loads
              bpf_fetch_u32(void *ptr)
              bpf_fetch_u64(void *ptr)

              and bpf_context is defined as:
              struct bpf_context {
                  /* argN fields match one to one to arguments passed to trace events */
                  u64 arg1, arg2, arg3, arg4, arg5, arg6;
                  /* return value from kretprobe event or from syscall_exit event */
                  u64 ret;
              };

              The set of helper functions for TYPE_SOCKET is TBD.

              More   program   types   may   be  added  in  the  future.  Like
              BPF_PROG_TYPE_USER_TRACING for unprivileged programs.

              insns array of "struct bpf_insn" instructions

              insn_cnt number of instructions in the program

              license license string, which must be  GPL  compatible  to  call
              helper functions marked gpl_only

              log_buf user supplied buffer that in-kernel verifier is using to
              store verification log. Log is a multi-line string  that  should
              be  used  by  program  author to understand how verifier came to
              conclusion that program is unsafe. The format of the output  can
              change at any time as verifier evolves.

              log_size size of user buffer. If size of the buffer is not large
              enough to store all verifier messages, -1 is returned and  errno
              is set to ENOSPC.

              log_level  verbosity level of eBPF verifier, where zero means no
              logs provided

       close(prog_fd)
              will unload eBPF program

       The maps  are  accesible  from  programs  and  generally  tie  the  two
       together.   Programs  process  various events (like tracepoint, kprobe,
       packets) and store the data into maps. User  space  fetches  data  from
       maps.   Either the same or a different map may be used by user space as
       configuration space to alter program behavior on the fly.

   Events
       Once an eBPF program is loaded, it can be attached to an event. Various
       kernel subsystems have different ways to do so. For example:

       setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(prog_fd));
       will  attach  the  program prog_fd to socket sock which was received by
       prior call to socket().

       ioctl(event_fd, PERF_EVENT_IOC_SET_BPF, prog_fd);
       will attach the program  prog_fd  to  perf  event  event_fd  which  was
       received by prior call to perf_event_open().

       Another way to attach the program to a tracing event is:
       event_fd = open("/sys/kernel/debug/tracing/events/skb/kfree_skb/filter");
       write(event_fd, "bpf-123"); /* where 123 is eBPF program FD */
       /* here program is attached and will be triggered by events */
       close(event_fd); /* to detach from event */

EXAMPLES
       /* eBPF+sockets example:
        * 1. create map with maximum of 2 elements
        * 2. set map[6] = 0 and map[17] = 0
        * 3. load eBPF program that counts number of TCP and UDP packets received
        *    via map[skb->ip->proto]++
        * 4. attach prog_fd to raw socket via setsockopt()
        * 5. print number of received TCP/UDP packets every second
        */
       int main(int ac, char **av)
       {
           int sock, map_fd, prog_fd, key;
           long long value = 0, tcp_cnt, udp_cnt;

           map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), 2);
           if (map_fd < 0) {
               printf("failed to create map '%s'\n", strerror(errno));
               /* likely not run as root */
               return 1;
           }

           key = 6; /* ip->proto == tcp */
           assert(bpf_update_elem(map_fd, &key, &value) == 0);

           key = 17; /* ip->proto == udp */
           assert(bpf_update_elem(map_fd, &key, &value) == 0);

           struct bpf_insn prog[] = {
               BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),          /* r6 = r1 */
               BPF_LD_ABS(BPF_B, 14 + 9),                    /* r0 = ip->proto */
               BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4),/* *(u32 *)(fp - 4) = r0 */
               BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),         /* r2 = fp */
               BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),        /* r2 = r2 - 4 */
               BPF_LD_MAP_FD(BPF_REG_1, map_fd),             /* r1 = map_fd */
               BPF_CALL_FUNC(BPF_FUNC_map_lookup_elem),      /* r0 = map_lookup(r1, r2) */
               BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),        /* if (r0 == 0) goto pc+2 */
               BPF_MOV64_IMM(BPF_REG_1, 1),                  /* r1 = 1 */
               BPF_XADD(BPF_DW, BPF_REG_0, BPF_REG_1, 0, 0), /* lock *(u64 *)r0 += r1 */
               BPF_MOV64_IMM(BPF_REG_0, 0),                  /* r0 = 0 */
               BPF_EXIT_INSN(),                              /* return r0 */
           };
           prog_fd = bpf_prog_load(BPF_PROG_TYPE_SOCKET, prog, sizeof(prog), "GPL");
           assert(prog_fd >= 0);

           sock = open_raw_sock("lo");

           assert(setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd,
                             sizeof(prog_fd)) == 0);

           for (;;) {
               key = 6;
               assert(bpf_lookup_elem(map_fd, &key, &tcp_cnt) == 0);
               key = 17;
               assert(bpf_lookup_elem(map_fd, &key, &udp_cnt) == 0);
               printf("TCP %lld UDP %lld packets0, tcp_cnt, udp_cnt);
               sleep(1);
           }

           return 0;
       }

RETURN VALUE
       For a successful call, the return value depends on the operation:

       BPF_MAP_CREATE
              The new file descriptor associated with eBPF map.

       BPF_PROG_LOAD
              The new file descriptor associated with eBPF program.

       All other commands
              Zero.

       On error, -1 is returned, and errno is set appropriately.

ERRORS
       EPERM  bpf() syscall was made without sufficient privilege (without the
              CAP_SYS_ADMIN capability).

       ENOMEM Cannot allocate sufficient memory.

       EBADF  fd is not an open file descriptor

       EFAULT One of the pointers ( key or value or  log_buf  or  insns  )  is
              outside accessible address space.

       EINVAL The value specified in cmd is not recognized by this kernel.

       EINVAL For BPF_MAP_CREATE, either map_type or attributes are invalid.

       EINVAL For  BPF_MAP_*_ELEM  commands,  some  of  the  fields  of "union
              bpf_attr" unused by this command are not set to zero.

       EINVAL For BPF_PROG_LOAD, attempt to load invalid program (unrecognized
              instruction  or  uses  reserved  fields or jumps out of range or
              loop detected or calls unknown function).

       EACCES For BPF_PROG_LOAD, though program has valid instructions, it was
              rejected,  since  it  was  deemed  unsafe (may access disallowed
              memory  region  or  uninitialized  stack/register  or   function
              constraints  don't  match actual types or misaligned access). In
              such case it is recommended to call bpf() again with log_level =
              1 and examine log_buf for specific reason provided by verifier.

       ENOENT For  BPF_MAP_LOOKUP_ELEM  or BPF_MAP_DELETE_ELEM, indicates that
              element with given key was not found.

       E2BIG  program is too large or a map  reached  max_entries  limit  (max
              number of elements).

NOTES
       These commands may be used only by a privileged process (one having the
       CAP_SYS_ADMIN capability).

SEE ALSO
       eBPF   architecture   and   instruction    set    is    explained    in
       Documentation/networking/filter.txt



Linux                             2014-09-12                            BPF(2)

^ permalink raw reply

* [PATCHv2 net-next 0/3] sunvnet: add jumbo frames support
From: David L Stevens @ 2014-09-12 23:13 UTC (permalink / raw)
  To: David Miller; +Cc: netdev

This patch set updates the sunvnet driver to version 1.6 of the VIO protocol
to support per-port exchange of MTU information and allow non-standard MTU
sizes, including jumbo frames.

Using large MTUs shows a > 4X throughput improvement Linux-Solaris
and > 8X throughput improvement Linux-Linux.

Changes from v1:
	- fix brace formatting per Dave Miller <davem@davemloft.net>

David L Stevens (3):
  sunvnet: upgrade to VIO protocol version 1.6
  sunvnet: allow admin to set sunvnet MTU
  sunvnet: generate ICMP PTMUD messages for smaller port MTUs

 arch/sparc/include/asm/vio.h       |   19 +++++-
 arch/sparc/kernel/viohs.c          |   14 +++-
 drivers/net/ethernet/sun/sunvnet.c |  136 +++++++++++++++++++++++++++++++-----
 drivers/net/ethernet/sun/sunvnet.h |    1 +
 4 files changed, 149 insertions(+), 21 deletions(-)

^ permalink raw reply

* [PATCHv2 net-next 1/3] sunvnet: upgrade to VIO protocol version 1.6
From: David L Stevens @ 2014-09-12 23:18 UTC (permalink / raw)
  To: David Miller; +Cc: netdev


This patch upgrades the sunvnet driver to support VIO protocol version 1.6.
In particular, it adds per-port MTU negotiation, allowing MTUs other than
ETH_FRAMELEN with ports using newer VIO protocol versions.

Signed-off-by: David L Stevens <david.stevens@oracle.com>
---
 arch/sparc/include/asm/vio.h       |   19 ++++++-
 arch/sparc/kernel/viohs.c          |   14 +++++-
 drivers/net/ethernet/sun/sunvnet.c |   96 ++++++++++++++++++++++++++++++------
 drivers/net/ethernet/sun/sunvnet.h |    1 +
 4 files changed, 110 insertions(+), 20 deletions(-)

diff --git a/arch/sparc/include/asm/vio.h b/arch/sparc/include/asm/vio.h
index 5c0ebe7..da13e3b 100644
--- a/arch/sparc/include/asm/vio.h
+++ b/arch/sparc/include/asm/vio.h
@@ -65,6 +65,7 @@ struct vio_dring_register {
 	u16			options;
 #define VIO_TX_DRING		0x0001
 #define VIO_RX_DRING		0x0002
+#define VIO_RX_DRING_DATA	0x0004
 	u16			resv;
 	u32			num_cookies;
 	struct ldc_trans_cookie	cookies[0];
@@ -80,6 +81,8 @@ struct vio_dring_unregister {
 #define VIO_PKT_MODE		0x01 /* Packet based transfer	*/
 #define VIO_DESC_MODE		0x02 /* In-band descriptors	*/
 #define VIO_DRING_MODE		0x03 /* Descriptor rings	*/
+/* in vers >= 1.2, VIO_DRING_MODE is 0x04 and transfer mode is a bitmask */
+#define VIO_NEW_DRING_MODE	0x04

 struct vio_dring_data {
 	struct vio_msg_tag	tag;
@@ -209,10 +212,20 @@ struct vio_net_attr_info {
 	u8			addr_type;
 #define VNET_ADDR_ETHERMAC	0x01
 	u16			ack_freq;
-	u32			resv1;
+	u8			plnk_updt;
+#define PHYSLINK_UPDATE_NONE		0x00
+#define PHYSLINK_UPDATE_STATE		0x01
+#define PHYSLINK_UPDATE_STATE_ACK	0x02
+#define PHYSLINK_UPDATE_STATE_NACK	0x03
+	u8			options;
+	u16			resv1;
 	u64			addr;
 	u64			mtu;
-	u64			resv2[3];
+	u16			cflags;
+#define VNET_LSO_IPV4_CAPAB		0x0001
+	u16			ipv4_lso_maxlen;
+	u32			resv2;
+	u64			resv3[2];
 };

 #define VNET_NUM_MCAST		7
@@ -368,6 +381,8 @@ struct vio_driver_state {
 	char			*name;

 	struct vio_driver_ops	*ops;
+
+	u64			rmtu;	/* remote MTU */
 };

 #define viodbg(TYPE, f, a...) \
diff --git a/arch/sparc/kernel/viohs.c b/arch/sparc/kernel/viohs.c
index f8e7dd5..446438b 100644
--- a/arch/sparc/kernel/viohs.c
+++ b/arch/sparc/kernel/viohs.c
@@ -426,6 +426,13 @@ static int process_dreg_info(struct vio_driver_state *vio,
 	if (vio->dr_state & VIO_DR_STATE_RXREG)
 		goto send_nack;

+	/* v1.6 and higher, ACK with desired, supported mode, or NACK */
+	if (vio->ver.major <= 1 && vio->ver.minor >= 6) {
+		if (!(pkt->options & VIO_TX_DRING))
+			goto send_nack;
+		pkt->options = VIO_TX_DRING;
+	}
+
 	BUG_ON(vio->desc_buf);

 	vio->desc_buf = kzalloc(pkt->descr_size, GFP_ATOMIC);
@@ -453,8 +460,11 @@ static int process_dreg_info(struct vio_driver_state *vio,
 	pkt->tag.stype = VIO_SUBTYPE_ACK;
 	pkt->dring_ident = ++dr->ident;

-	viodbg(HS, "SEND DRING_REG ACK ident[%llx]\n",
-	       (unsigned long long) pkt->dring_ident);
+	viodbg(HS, "SEND DRING_REG ACK ident[%llx] "
+	       "ndesc[%u] dsz[%u] opt[0x%x] ncookies[%u]\n",
+	       (unsigned long long) pkt->dring_ident,
+	       pkt->num_descr, pkt->descr_size, pkt->options,
+	       pkt->num_cookies);

 	len = (sizeof(*pkt) +
 	       (dr->ncookies * sizeof(struct ldc_trans_cookie)));
diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c
index a4657a4..a6418bb 100644
--- a/drivers/net/ethernet/sun/sunvnet.c
+++ b/drivers/net/ethernet/sun/sunvnet.c
@@ -15,6 +15,7 @@
 #include <linux/ethtool.h>
 #include <linux/etherdevice.h>
 #include <linux/mutex.h>
+#include <linux/if_vlan.h>

 #include <asm/vio.h>
 #include <asm/ldc.h>
@@ -39,6 +40,7 @@ MODULE_VERSION(DRV_MODULE_VERSION);

 /* Ordered from largest major to lowest */
 static struct vio_version vnet_versions[] = {
+	{ .major = 1, .minor = 6 },
 	{ .major = 1, .minor = 0 },
 };

@@ -65,6 +67,7 @@ static int vnet_send_attr(struct vio_driver_state *vio)
 	struct vnet_port *port = to_vnet_port(vio);
 	struct net_device *dev = port->vp->dev;
 	struct vio_net_attr_info pkt;
+	int framelen = ETH_FRAME_LEN;
 	int i;

 	memset(&pkt, 0, sizeof(pkt));
@@ -72,19 +75,37 @@ static int vnet_send_attr(struct vio_driver_state *vio)
 	pkt.tag.stype = VIO_SUBTYPE_INFO;
 	pkt.tag.stype_env = VIO_ATTR_INFO;
 	pkt.tag.sid = vio_send_sid(vio);
-	pkt.xfer_mode = VIO_DRING_MODE;
+	if (vio->ver.major <= 1 && vio->ver.minor < 2)
+		pkt.xfer_mode = VIO_DRING_MODE;
+	else
+		pkt.xfer_mode = VIO_NEW_DRING_MODE;
 	pkt.addr_type = VNET_ADDR_ETHERMAC;
 	pkt.ack_freq = 0;
 	for (i = 0; i < 6; i++)
 		pkt.addr |= (u64)dev->dev_addr[i] << ((5 - i) * 8);
-	pkt.mtu = ETH_FRAME_LEN;
+	if (vio->ver.major == 1) {
+		if (vio->ver.minor > 3) {
+			if (vio->rmtu)
+				vio->rmtu = pkt.mtu = min(VNET_MAXPACKET,
+							  vio->rmtu);
+			else
+				vio->rmtu = pkt.mtu = VNET_MAXPACKET;
+		} else if (vio->ver.minor == 3)
+			pkt.mtu = framelen + VLAN_HLEN;
+		else
+			pkt.mtu = framelen;
+		if (vio->ver.minor >= 6)
+			pkt.options = VIO_TX_DRING;
+	}

 	viodbg(HS, "SEND NET ATTR xmode[0x%x] atype[0x%x] addr[%llx] "
-	       "ackfreq[%u] mtu[%llu]\n",
+	       "ackfreq[%u] plnk_updt[0x%02x] opts[0x%02x] mtu[%llu] "
+	       "cflags[0x%04x] lso_max[%u]\n",
 	       pkt.xfer_mode, pkt.addr_type,
 	       (unsigned long long) pkt.addr,
-	       pkt.ack_freq,
-	       (unsigned long long) pkt.mtu);
+	       pkt.ack_freq, pkt.plnk_updt, pkt.options,
+	       (unsigned long long) pkt.mtu, pkt.cflags, pkt.ipv4_lso_maxlen);
+

 	return vio_ldc_send(vio, &pkt, sizeof(pkt));
 }
@@ -92,18 +113,52 @@ static int vnet_send_attr(struct vio_driver_state *vio)
 static int handle_attr_info(struct vio_driver_state *vio,
 			    struct vio_net_attr_info *pkt)
 {
-	viodbg(HS, "GOT NET ATTR INFO xmode[0x%x] atype[0x%x] addr[%llx] "
-	       "ackfreq[%u] mtu[%llu]\n",
+	u64	localmtu;
+	u8	xfer_mode;
+
+	viodbg(HS, "GOT NET ATTR xmode[0x%x] atype[0x%x] addr[%llx] "
+	       "ackfreq[%u] plnk_updt[0x%02x] opts[0x%02x] mtu[%llu] "
+	       " (rmtu[%llu]) cflags[0x%04x] lso_max[%u]\n",
 	       pkt->xfer_mode, pkt->addr_type,
 	       (unsigned long long) pkt->addr,
-	       pkt->ack_freq,
-	       (unsigned long long) pkt->mtu);
+	       pkt->ack_freq, pkt->plnk_updt, pkt->options,
+	       (unsigned long long) pkt->mtu, vio->rmtu, pkt->cflags,
+	       pkt->ipv4_lso_maxlen);

 	pkt->tag.sid = vio_send_sid(vio);

-	if (pkt->xfer_mode != VIO_DRING_MODE ||
+	xfer_mode = pkt->xfer_mode;
+	/* for version < 1.2, VIO_DRING_MODE = 0x3 and no bitmask */
+	if ((vio->ver.major <= 1 &&  vio->ver.minor < 2) &&
+	    xfer_mode == VIO_DRING_MODE)
+		xfer_mode = VIO_NEW_DRING_MODE;
+
+	/* MTU negotiation:
+	 *	< v1.3 - ETH_FRAME_LEN exactly
+	 *	= v1.3 - ETH_FRAME_LEN + VLAN_HLEN exactly
+	 *	> v1.4 - MIN(pkt.mtu, VNET_MAX_PACKET, vio->rmtu) and change
+	 *			pkt->mtu for ACK
+	 */
+	if (vio->ver.major == 1 && vio->ver.minor < 3) {
+		localmtu = ETH_FRAME_LEN;
+	} else if (vio->ver.major == 1 && vio->ver.minor == 3) {
+		localmtu = ETH_FRAME_LEN + VLAN_HLEN;
+	} else {
+		localmtu = vio->rmtu ? vio->rmtu : VNET_MAXPACKET;
+		localmtu = pkt->mtu = min(pkt->mtu, localmtu);
+	}
+	vio->rmtu = localmtu;
+
+
+	/* for version >= 1.6, ACK packet mode we support */
+	if (vio->ver.major <= 1 && vio->ver.minor >= 6) {
+		pkt->xfer_mode = VIO_NEW_DRING_MODE;
+		pkt->options = VIO_TX_DRING;
+	}
+
+	if (!(xfer_mode | VIO_NEW_DRING_MODE) ||
 	    pkt->addr_type != VNET_ADDR_ETHERMAC ||
-	    pkt->mtu != ETH_FRAME_LEN) {
+	    pkt->mtu != localmtu) {
 		viodbg(HS, "SEND NET ATTR NACK\n");

 		pkt->tag.stype = VIO_SUBTYPE_NACK;
@@ -112,7 +167,14 @@ static int handle_attr_info(struct vio_driver_state *vio,

 		return -ECONNRESET;
 	} else {
-		viodbg(HS, "SEND NET ATTR ACK\n");
+		viodbg(HS, "SEND NET ATTR ACK xmode[0x%x] atype[0x%x] "
+		       "addr[%llx] ackfreq[%u] plnk_updt[0x%02x] opts[0x%02x] "
+		       "mtu[%llu] (rmtu[%llu]) cflags[0x%04x] lso_max[%u]\n",
+		       pkt->xfer_mode, pkt->addr_type,
+		       (unsigned long long) pkt->addr,
+		       pkt->ack_freq, pkt->plnk_updt, pkt->options,
+		       (unsigned long long) pkt->mtu, vio->rmtu, pkt->cflags,
+		       pkt->ipv4_lso_maxlen);

 		pkt->tag.stype = VIO_SUBTYPE_ACK;

@@ -208,7 +270,7 @@ static int vnet_rx_one(struct vnet_port *port, unsigned int len,
 	int err;

 	err = -EMSGSIZE;
-	if (unlikely(len < ETH_ZLEN || len > ETH_FRAME_LEN)) {
+	if (unlikely(len < ETH_ZLEN || len > port->vio.rmtu)) {
 		dev->stats.rx_length_errors++;
 		goto out_dropped;
 	}
@@ -528,8 +590,10 @@ static void vnet_event(void *arg, int event)
 		vio_link_state_change(vio, event);
 		spin_unlock_irqrestore(&vio->lock, flags);

-		if (event == LDC_EVENT_RESET)
+		if (event == LDC_EVENT_RESET) {
+			vio->rmtu = 0;
 			vio_port_up(vio);
+		}
 		return;
 	}

@@ -986,8 +1050,8 @@ static int vnet_port_alloc_tx_bufs(struct vnet_port *port)
 	void *dring;

 	for (i = 0; i < VNET_TX_RING_SIZE; i++) {
-		void *buf = kzalloc(ETH_FRAME_LEN + 8, GFP_KERNEL);
-		int map_len = (ETH_FRAME_LEN + 7) & ~7;
+		void *buf = kzalloc(VNET_MAXPACKET + 8, GFP_KERNEL);
+		int map_len = (VNET_MAXPACKET + 7) & ~7;

 		err = -ENOMEM;
 		if (!buf)
diff --git a/drivers/net/ethernet/sun/sunvnet.h b/drivers/net/ethernet/sun/sunvnet.h
index de5c2c6..243ae69 100644
--- a/drivers/net/ethernet/sun/sunvnet.h
+++ b/drivers/net/ethernet/sun/sunvnet.h
@@ -11,6 +11,7 @@
  */
 #define VNET_TX_TIMEOUT			(5 * HZ)

+#define VNET_MAXPACKET			1518ULL /* ETH_FRAMELEN + VLAN_HDR */
 #define VNET_TX_RING_SIZE		512
 #define VNET_TX_WAKEUP_THRESH(dr)	((dr)->pending / 4)

-- 
1.7.1

^ permalink raw reply related

* [PATCHv2 net-next 2/3] sunvnet: allow admin to set sunvnet MTU
From: David L Stevens @ 2014-09-12 23:18 UTC (permalink / raw)
  To: David Miller; +Cc: netdev

This patch allows an admin to set the MTU on a sunvnet device to arbitrary
values between the minimum (68) and maximum (65535) IPv4 packet sizes.

Signed-off-by: David L Stevens <david.stevens@oracle.com>
---
 drivers/net/ethernet/sun/sunvnet.c |    6 +++++-
 drivers/net/ethernet/sun/sunvnet.h |    2 +-
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c
index a6418bb..b557ec4 100644
--- a/drivers/net/ethernet/sun/sunvnet.c
+++ b/drivers/net/ethernet/sun/sunvnet.c
@@ -751,6 +751,10 @@ static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev)
 	if (unlikely(!port))
 		goto out_dropped;

+	if (skb->len > port->vio.rmtu) {
+		goto out_dropped;
+	}
+
 	spin_lock_irqsave(&port->vio.lock, flags);

 	dr = &port->vio.drings[VIO_DRIVER_TX_RING];
@@ -972,7 +976,7 @@ static void vnet_set_rx_mode(struct net_device *dev)

 static int vnet_change_mtu(struct net_device *dev, int new_mtu)
 {
-	if (new_mtu != ETH_DATA_LEN)
+	if (new_mtu < 68 || new_mtu > 65535)
 		return -EINVAL;

 	dev->mtu = new_mtu;
diff --git a/drivers/net/ethernet/sun/sunvnet.h b/drivers/net/ethernet/sun/sunvnet.h
index 243ae69..fcf0129 100644
--- a/drivers/net/ethernet/sun/sunvnet.h
+++ b/drivers/net/ethernet/sun/sunvnet.h
@@ -11,7 +11,7 @@
  */
 #define VNET_TX_TIMEOUT			(5 * HZ)

-#define VNET_MAXPACKET			1518ULL /* ETH_FRAMELEN + VLAN_HDR */
+#define VNET_MAXPACKET			65553ULL /* 64K-1  +ETH HDR +VLAN HDR*/
 #define VNET_TX_RING_SIZE		512
 #define VNET_TX_WAKEUP_THRESH(dr)	((dr)->pending / 4)

-- 
1.7.1

^ permalink raw reply related

* [PATCHv2 net-next 3/3] sunvnet: generate ICMP PTMUD messages for smaller port MTUs
From: David L Stevens @ 2014-09-12 23:19 UTC (permalink / raw)
  To: David Miller; +Cc: netdev

This patch sends ICMP and ICMPv6 messages for Path MTU Discovery when a remote
port MTU is smaller than the device MTU. This allows mixing newer VIO protocol
devices that support MTU negotiation with older devices that do not on the
same vswitch. It also allows Linux-Linux LDOMs to use 64K-1 data packets even
though Solaris vswitch is limited to <16K MTU.

Signed-off-by: David L Stevens <david.stevens@oracle.com>
---
 drivers/net/ethernet/sun/sunvnet.c |   34 ++++++++++++++++++++++++++++++++++
 1 files changed, 34 insertions(+), 0 deletions(-)

diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c
index b557ec4..b2381f0 100644
--- a/drivers/net/ethernet/sun/sunvnet.c
+++ b/drivers/net/ethernet/sun/sunvnet.c
@@ -17,6 +17,13 @@
 #include <linux/mutex.h>
 #include <linux/if_vlan.h>

+#if IS_ENABLED(CONFIG_IPV6)
+#include <linux/icmpv6.h>
+#endif
+
+#include <net/icmp.h>
+#include <net/route.h>
+
 #include <asm/vio.h>
 #include <asm/ldc.h>

@@ -752,6 +759,33 @@ static int vnet_start_xmit(struct sk_buff *skb, struct net_device *dev)
 		goto out_dropped;

 	if (skb->len > port->vio.rmtu) {
+		unsigned long localmtu = port->vio.rmtu - ETH_HLEN;
+
+		if (port->vio.ver.major == 1 && port->vio.ver.minor >= 3)
+			localmtu -= VLAN_HLEN;
+
+		if (skb->protocol == htons(ETH_P_IP)) {
+			struct flowi4 fl4;
+			struct rtable *rt = NULL;
+
+			memset(&fl4, 0, sizeof(fl4));
+			fl4.flowi4_oif = dev->ifindex;
+			fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos);
+			fl4.daddr = ip_hdr(skb)->daddr;
+			fl4.saddr = ip_hdr(skb)->saddr;
+
+			rt = ip_route_output_key(dev_net(dev), &fl4);
+			if (!IS_ERR(rt)) {
+				skb_dst_set(skb, &rt->dst);
+				icmp_send(skb, ICMP_DEST_UNREACH,
+					  ICMP_FRAG_NEEDED,
+					  htonl(localmtu));
+			}
+		}
+#if IS_ENABLED(CONFIG_IPV6)
+		else if (skb->protocol == htons(ETH_P_IPV6))
+			icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, localmtu);
+#endif
 		goto out_dropped;
 	}

-- 
1.7.1

^ permalink raw reply related

* Re: [PATCH] crypto: talitos: Avoid excessive loops in softirq context
From: Kim Phillips @ 2014-09-12 23:21 UTC (permalink / raw)
  To: Helmut Schaa
  Cc: linux-crypto, Herbert Xu, David Miller, Sandeep Malik,
	Horia Geanta, netdev
In-Reply-To: <CAGXE3d-EiYmJQfEwS9VeR3-5vePNzc2+N20hQTw0vY8FKhJYRA@mail.gmail.com>

[adding Sandeep, Horia and netdev]

On Fri, 12 Sep 2014 09:39:12 +0200
Helmut Schaa <helmut.schaa@googlemail.com> wrote:

> On Fri, Sep 12, 2014 at 2:49 AM, Kim Phillips
> <kim.phillips@freescale.com> wrote:
> > On Wed, 10 Sep 2014 10:34:47 +0200
> > Helmut Schaa <helmut.schaa@googlemail.com> wrote:
> >
> >> The talitos driver can cause starvation of other softirqs and as such
> >> it can also cause rcu stalls like:
> > ...
> >> Work around this by processing a maximum amount of 16 finished requests
> >> and rescheduling the done-tasklet if any work is left.
> >> This allows other softirqs to run.
> >
> > 16 sounds rather arbitrary, and application-dependent - talitos'
> > FIFO size is 24.
> 
> Yep, 16 is arbitrary, I can also do "fifo_len" if you prefer?
> 
> > IIRC, netdev's NAPI can be refactored out of just being able to work
> > on network devices, and be made to apply to crypto devices, too.  In
> > fact, some old Freescale hacks of this nature have improved
> > performance.  Can we do something like refactor NAPI instead?
> 
> That would indeed be nice but sounds like quite some more work and
> I won't have time to do so. Especially since my system was taken down
> completely by the talitos tasklet under some circumstances. If there is
> any work going on in that regard I'd be fine with just dropping that patch
> (and carrying it myself until the refactoring is done).

I'm not aware of any, but to prove whether NAPI actually fixes the
issue, can you try applying this patch:

http://patchwork.ozlabs.org/patch/146094/

For it to be upstream-acceptable, I believe it would have to port
NAPI in such a way that all crypto drivers could use it.  The
difference between net NAPI and crypto NAPI would be that the crypto
version would be reduced to only using the weight code, since that's
the only source of the performance benefit.

Thanks,

Kim

^ permalink raw reply

* Re: [net-next PATCH v5 14/16] net: sched: make bstats per cpu and estimator RCU safe
From: Eric Dumazet @ 2014-09-12 23:49 UTC (permalink / raw)
  To: John Fastabend; +Cc: xiyou.wangcong, davem, jhs, netdev, paulmck, brouer
In-Reply-To: <20140912163428.19588.52611.stgit@nitbit.x32>

On Fri, 2014-09-12 at 09:34 -0700, John Fastabend wrote:
> In order to run qdisc's without locking statistics and estimators
> need to be handled correctly.
> 
> To resolve bstats make the statistics per cpu. And because this is
> only needed for qdiscs that are running without locks which is not
> the case for most qdiscs in the near future only create percpu
> stats when qdiscs set the TCQ_F_LLQDISC flag.
> 
> Next because estimators use the bstats to calculate packets per
> second and bytes per second the estimator code paths are updated
> to use the per cpu statistics.
> 
> After this if qdiscs use the _percpu routines to update stats
> and set the TCQ_F_LLQDISC they can be made safe to run without
> locking. Any qdisc that sets the flag needs to ensure that the
> data structures it owns are safe to run without locks and
> additionally currently all the skb list routines run without
> locking so those would need to be updated in a future patch.
> 
> Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
> ---

Hmm... this is a bit too complex for my taste.

>  include/net/gen_stats.h    |   16 ++++++++++++
>  include/net/sch_generic.h  |   40 +++++++++++++++++++++++++++--
>  net/core/gen_estimator.c   |   60 ++++++++++++++++++++++++++++++++++----------
>  net/core/gen_stats.c       |   47 ++++++++++++++++++++++++++++++++++
>  net/netfilter/xt_RATEEST.c |    4 +--
>  net/sched/act_api.c        |    9 ++++---
>  net/sched/act_police.c     |    4 +--
>  net/sched/sch_api.c        |   51 ++++++++++++++++++++++++++++++++-----
>  net/sched/sch_cbq.c        |    9 ++++---
>  net/sched/sch_drr.c        |    9 ++++---
>  net/sched/sch_generic.c    |   11 +++++++-
>  net/sched/sch_hfsc.c       |   15 +++++++----
>  net/sched/sch_htb.c        |   14 +++++++---
>  net/sched/sch_ingress.c    |    2 +
>  net/sched/sch_mq.c         |   10 ++++---
>  net/sched/sch_mqprio.c     |   13 +++++-----
>  net/sched/sch_multiq.c     |    2 +
>  net/sched/sch_prio.c       |    2 +
>  net/sched/sch_qfq.c        |   12 +++++----
>  19 files changed, 258 insertions(+), 72 deletions(-)
> 
> diff --git a/include/net/gen_stats.h b/include/net/gen_stats.h
> index ea4271d..4b7ca2b 100644
> --- a/include/net/gen_stats.h
> +++ b/include/net/gen_stats.h
> @@ -6,6 +6,11 @@
>  #include <linux/rtnetlink.h>
>  #include <linux/pkt_sched.h>
>  
> +struct gnet_stats_basic_cpu {
> +	struct gnet_stats_basic_packed bstats;
> +	struct u64_stats_sync syncp;
> +};
> +
>  struct gnet_dump {
>  	spinlock_t *      lock;
>  	struct sk_buff *  skb;
> @@ -26,10 +31,17 @@ int gnet_stats_start_copy_compat(struct sk_buff *skb, int type,
>  				 int tc_stats_type, int xstats_type,
>  				 spinlock_t *lock, struct gnet_dump *d);
>  
> +int gnet_stats_copy_basic_cpu(struct gnet_dump *d,
> +			      struct gnet_stats_basic_cpu __percpu *b);
> +void __gnet_stats_copy_basic_cpu(struct gnet_stats_basic_packed *bstats,
> +				 struct gnet_stats_basic_cpu __percpu *b);
>  int gnet_stats_copy_basic(struct gnet_dump *d,
>  			  struct gnet_stats_basic_packed *b);
> +void __gnet_stats_copy_basic(struct gnet_stats_basic_packed *bstats,
> +			     struct gnet_stats_basic_packed *b);
>  int gnet_stats_copy_rate_est(struct gnet_dump *d,
>  			     const struct gnet_stats_basic_packed *b,
> +			     const struct gnet_stats_basic_cpu __percpu *cpu_b,
>  			     struct gnet_stats_rate_est64 *r);
>  int gnet_stats_copy_queue(struct gnet_dump *d, struct gnet_stats_queue *q);
>  int gnet_stats_copy_app(struct gnet_dump *d, void *st, int len);
> @@ -37,13 +49,17 @@ int gnet_stats_copy_app(struct gnet_dump *d, void *st, int len);
>  int gnet_stats_finish_copy(struct gnet_dump *d);
>  
>  int gen_new_estimator(struct gnet_stats_basic_packed *bstats,
> +		      struct gnet_stats_basic_cpu __percpu *cpu_bstats,
>  		      struct gnet_stats_rate_est64 *rate_est,
>  		      spinlock_t *stats_lock, struct nlattr *opt);
>  void gen_kill_estimator(struct gnet_stats_basic_packed *bstats,
> +			struct gnet_stats_basic_cpu __percpu *cpu_bstats,
>  			struct gnet_stats_rate_est64 *rate_est);
>  int gen_replace_estimator(struct gnet_stats_basic_packed *bstats,
> +			  struct gnet_stats_basic_cpu __percpu *cpu_bstats,
>  			  struct gnet_stats_rate_est64 *rate_est,
>  			  spinlock_t *stats_lock, struct nlattr *opt);
>  bool gen_estimator_active(const struct gnet_stats_basic_packed *bstats,
> +			  const struct gnet_stats_basic_cpu __percpu *cpu_bstat,
>  			  const struct gnet_stats_rate_est64 *rate_est);
>  #endif
> diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
> index 1e89b9a..54e318f 100644
> --- a/include/net/sch_generic.h
> +++ b/include/net/sch_generic.h
> @@ -6,6 +6,7 @@
>  #include <linux/rcupdate.h>
>  #include <linux/pkt_sched.h>
>  #include <linux/pkt_cls.h>
> +#include <linux/percpu.h>
>  #include <net/gen_stats.h>
>  #include <net/rtnetlink.h>
>  
> @@ -57,6 +58,9 @@ struct Qdisc {
>  				      * Its true for MQ/MQPRIO slaves, or non
>  				      * multiqueue device.
>  				      */
> +#define TCQ_F_LLQDISC		0x20 /* lockless qdiscs can run without using
> +				      * the qdisc lock to serialize skbs.
> +				      */
>  #define TCQ_F_WARN_NONWC	(1 << 16)
>  	u32			limit;
>  	const struct Qdisc_ops	*ops;
> @@ -83,7 +87,10 @@ struct Qdisc {
>  	 */
>  	unsigned long		state;
>  	struct sk_buff_head	q;
> -	struct gnet_stats_basic_packed bstats;
> +	union {
> +		struct gnet_stats_basic_packed bstats;
> +		struct gnet_stats_basic_cpu __percpu *cpu_bstats;
> +	} bstats_qdisc;

I believe this union will loose the _packed attribute, and __state will
no longer fill the 4 bytes hole we had after struct
gnet_stats_basic_packed bstats

So I think you should add a __packed attribute :

+       union {
+               struct gnet_stats_basic_packed bstats;
+               struct gnet_stats_basic_cpu __percpu *cpu_bstats;
+       } __packed bstats_qdisc;

btw, have you tried to use an anonymous union ?
	

>  	unsigned int		__state;
>  	struct gnet_stats_queue	qstats;
>  	struct rcu_head		rcu_head;
> @@ -486,7 +493,6 @@ static inline int qdisc_enqueue_root(struct sk_buff *skb, struct Qdisc *sch)
>  	return qdisc_enqueue(skb, sch) & NET_XMIT_MASK;
>  }
>  
> -
>  static inline void bstats_update(struct gnet_stats_basic_packed *bstats,
>  				 const struct sk_buff *skb)
>  {
> @@ -494,10 +500,38 @@ static inline void bstats_update(struct gnet_stats_basic_packed *bstats,
>  	bstats->packets += skb_is_gso(skb) ? skb_shinfo(skb)->gso_segs : 1;
>  }
>  
> +static inline void qdisc_bstats_update_cpu(struct Qdisc *sch,
> +					   const struct sk_buff *skb)
> +{
> +	struct gnet_stats_basic_cpu *bstats =
> +				this_cpu_ptr(sch->bstats_qdisc.cpu_bstats);
> +
> +	u64_stats_update_begin(&bstats->syncp);
> +	bstats_update(&bstats->bstats, skb);
> +	u64_stats_update_end(&bstats->syncp);
> +}
> +
>  static inline void qdisc_bstats_update(struct Qdisc *sch,
>  				       const struct sk_buff *skb)
>  {
> -	bstats_update(&sch->bstats, skb);
> +	bstats_update(&sch->bstats_qdisc.bstats, skb);
> +}
> +
> +static inline bool qdisc_is_lockless(struct Qdisc *q)

const ?

btw, not sure if the name is correct. You could still use percpu stats
even for a qdisc using a lock at some point...

> +{
> +	return q->flags & TCQ_F_LLQDISC;
> +}
> +
> +static inline int
> +qdisc_bstats_copy_qdisc(struct gnet_dump *d, struct Qdisc *q)

const struct Qdisc *q ?

> +{
> +	int err;
> +
> +	if (qdisc_is_lockless(q))
> +		err = gnet_stats_copy_basic_cpu(d, q->bstats_qdisc.cpu_bstats);
> +	else
> +		err = gnet_stats_copy_basic(d, &q->bstats_qdisc.bstats);
> +	return err;
>  }

I believe you should reuse a single function, gnet_stats_copy_basic()
and add an additional parameter (per_cpu pointer), that can be NULL.

>  
>  static inline int __qdisc_enqueue_tail(struct sk_buff *skb, struct Qdisc *sch,
> diff --git a/net/core/gen_estimator.c b/net/core/gen_estimator.c
> index 9d33dff..0d13319 100644
> --- a/net/core/gen_estimator.c
> +++ b/net/core/gen_estimator.c
> @@ -91,6 +91,8 @@ struct gen_estimator
>  	u32			avpps;
>  	struct rcu_head		e_rcu;
>  	struct rb_node		node;
> +	struct gnet_stats_basic_cpu __percpu *cpu_bstats;
> +	struct rcu_head		head;
>  };
>  
>  struct gen_estimator_head
> @@ -122,11 +124,21 @@ static void est_timer(unsigned long arg)
>  
>  		spin_lock(e->stats_lock);
>  		read_lock(&est_lock);
> -		if (e->bstats == NULL)
> +
> +		if (e->bstats == NULL && e->cpu_bstats == NULL)
>  			goto skip;

You should only test e->bstats here.

>  
> -		nbytes = e->bstats->bytes;
> -		npackets = e->bstats->packets;
> +		if (e->cpu_bstats) {
> +			struct gnet_stats_basic_packed b = {0};
> +
> +			__gnet_stats_copy_basic_cpu(&b, e->cpu_bstats);
> +			nbytes = b.bytes;
> +			npackets = b.packets;
> +		} else {
> +			nbytes = e->bstats->bytes;
> +			npackets = e->bstats->packets;
> +		}
> +
>  		brate = (nbytes - e->last_bytes)<<(7 - idx);
>  		e->last_bytes = nbytes;
>  		e->avbps += (brate >> e->ewma_log) - (e->avbps >> e->ewma_log);
> @@ -146,6 +158,11 @@ skip:
>  	rcu_read_unlock();
>  }
>  
> +static void *gen_get_bstats(struct gen_estimator *est)
> +{
> +	return est->cpu_bstats ? (void *)est->cpu_bstats : (void *)est->bstats;
> +}

Note there is no guarantee that percpu addresses and kmalloc() addresses
do not collide on some arches.

Thats a bit ugly, you could change your convention to always sort on
est->bstats

(No need for this helper)

> +
>  static void gen_add_node(struct gen_estimator *est)
>  {
>  	struct rb_node **p = &est_root.rb_node, *parent = NULL;
> @@ -156,7 +173,7 @@ static void gen_add_node(struct gen_estimator *est)
>  		parent = *p;
>  		e = rb_entry(parent, struct gen_estimator, node);
>  
> -		if (est->bstats > e->bstats)
> +		if (gen_get_bstats(est)  > gen_get_bstats(e))

Change not needed.

>  			p = &parent->rb_right;
>  		else
>  			p = &parent->rb_left;
> @@ -167,18 +184,20 @@ static void gen_add_node(struct gen_estimator *est)
>  
>  static
>  struct gen_estimator *gen_find_node(const struct gnet_stats_basic_packed *bstats,
> +				    const struct gnet_stats_basic_cpu __percpu *cpu_bstats,
>  				    const struct gnet_stats_rate_est64 *rate_est)
>  {
>  	struct rb_node *p = est_root.rb_node;
> +	void *b = bstats ? (void *)bstats : (void *)cpu_bstats;
>  

Same complexity not needed. You should not have to change this function
at all.

>  	while (p) {
>  		struct gen_estimator *e;
>  
>  		e = rb_entry(p, struct gen_estimator, node);
>  
> -		if (bstats > e->bstats)
> +		if (b > gen_get_bstats(e))
>  			p = p->rb_right;
> -		else if (bstats < e->bstats || rate_est != e->rate_est)
> +		else if (b < gen_get_bstats(e) || rate_est != e->rate_est)
>  			p = p->rb_left;
>  		else
>  			return e;
> @@ -203,6 +222,7 @@ struct gen_estimator *gen_find_node(const struct gnet_stats_basic_packed *bstats
>   *
>   */
>  int gen_new_estimator(struct gnet_stats_basic_packed *bstats,
> +		      struct gnet_stats_basic_cpu __percpu *cpu_bstats,
>  		      struct gnet_stats_rate_est64 *rate_est,
>  		      spinlock_t *stats_lock,
>  		      struct nlattr *opt)
> @@ -221,14 +241,24 @@ int gen_new_estimator(struct gnet_stats_basic_packed *bstats,
>  	if (est == NULL)
>  		return -ENOBUFS;
>  
> +	if (cpu_bstats) {
> +		struct gnet_stats_basic_packed b = {0};
> +
> +		est->cpu_bstats = cpu_bstats;
> +		__gnet_stats_copy_basic_cpu(&b, cpu_bstats);
> +		est->last_bytes = b.bytes;
> +		est->last_packets = b.packets;
> +	} else {
> +		est->bstats = bstats;

	est->bstats should be set in both cases, this is the key for searches.


> +		est->last_bytes = bstats->bytes;
> +		est->last_packets = bstats->packets;
> +	}
> +
>  	idx = parm->interval + 2;
> -	est->bstats = bstats;
>  	est->rate_est = rate_est;
>  	est->stats_lock = stats_lock;
>  	est->ewma_log = parm->ewma_log;
> -	est->last_bytes = bstats->bytes;
>  	est->avbps = rate_est->bps<<5;
> -	est->last_packets = bstats->packets;
>  	est->avpps = rate_est->pps<<10;
>  
>  	spin_lock_bh(&est_tree_lock);
> @@ -258,14 +288,14 @@ EXPORT_SYMBOL(gen_new_estimator);
>   * Note : Caller should respect an RCU grace period before freeing stats_lock
>   */
>  void gen_kill_estimator(struct gnet_stats_basic_packed *bstats,
> +			struct gnet_stats_basic_cpu __percpu *cpu_bstats,
>  			struct gnet_stats_rate_est64 *rate_est)
>  {
>  	struct gen_estimator *e;
>  
>  	spin_lock_bh(&est_tree_lock);
> -	while ((e = gen_find_node(bstats, rate_est))) {
> +	while ((e = gen_find_node(bstats, cpu_bstats, rate_est))) {
>  		rb_erase(&e->node, &est_root);
> -
>  		write_lock(&est_lock);
>  		e->bstats = NULL;
>  		write_unlock(&est_lock);
> @@ -290,11 +320,12 @@ EXPORT_SYMBOL(gen_kill_estimator);
>   * Returns 0 on success or a negative error code.
>   */
>  int gen_replace_estimator(struct gnet_stats_basic_packed *bstats,
> +			  struct gnet_stats_basic_cpu __percpu *cpu_bstats,
>  			  struct gnet_stats_rate_est64 *rate_est,
>  			  spinlock_t *stats_lock, struct nlattr *opt)
>  {
> -	gen_kill_estimator(bstats, rate_est);
> -	return gen_new_estimator(bstats, rate_est, stats_lock, opt);
> +	gen_kill_estimator(bstats, cpu_bstats, rate_est);
> +	return gen_new_estimator(bstats, cpu_bstats, rate_est, stats_lock, opt);
>  }
>  EXPORT_SYMBOL(gen_replace_estimator);
>  
> @@ -306,6 +337,7 @@ EXPORT_SYMBOL(gen_replace_estimator);
>   * Returns true if estimator is active, and false if not.
>   */
>  bool gen_estimator_active(const struct gnet_stats_basic_packed *bstats,
> +			  const struct gnet_stats_basic_cpu __percpu *cpu_bstats,
>  			  const struct gnet_stats_rate_est64 *rate_est)
>  {
>  	bool res;
> @@ -313,7 +345,7 @@ bool gen_estimator_active(const struct gnet_stats_basic_packed *bstats,
>  	ASSERT_RTNL();
>  
>  	spin_lock_bh(&est_tree_lock);
> -	res = gen_find_node(bstats, rate_est) != NULL;
> +	res = gen_find_node(bstats, cpu_bstats, rate_est) != NULL;
>  	spin_unlock_bh(&est_tree_lock);
>  
>  	return res;
> diff --git a/net/core/gen_stats.c b/net/core/gen_stats.c
> index 2ddbce4..e43b55f 100644
> --- a/net/core/gen_stats.c
> +++ b/net/core/gen_stats.c
> @@ -128,6 +128,50 @@ gnet_stats_copy_basic(struct gnet_dump *d, struct gnet_stats_basic_packed *b)
>  }
>  EXPORT_SYMBOL(gnet_stats_copy_basic);
>  
> +void
> +__gnet_stats_copy_basic(struct gnet_stats_basic_packed *bstats,
> +			struct gnet_stats_basic_packed *b)
> +{
> +	bstats->bytes = b->bytes;
> +	bstats->packets = b->packets;
> +}
> +EXPORT_SYMBOL(__gnet_stats_copy_basic);
> +
> +void
> +__gnet_stats_copy_basic_cpu(struct gnet_stats_basic_packed *bstats,
> +			    struct gnet_stats_basic_cpu __percpu *b)
> +{
> +	int i;
> +
> +	for_each_possible_cpu(i) {
> +		struct gnet_stats_basic_cpu *bcpu = per_cpu_ptr(b, i);
> +		unsigned int start;
> +		__u64 bytes;
> +		__u32 packets;
> +
> +		do {
> +			start = u64_stats_fetch_begin_irq(&bcpu->syncp);
> +			bytes = bcpu->bstats.bytes;
> +			packets = bcpu->bstats.packets;
> +		} while (u64_stats_fetch_retry_irq(&bcpu->syncp, start));
> +
> +		bstats->bytes += bcpu->bstats.bytes;
> +		bstats->packets += bcpu->bstats.packets;
> +	}
> +}
> +EXPORT_SYMBOL(__gnet_stats_copy_basic_cpu);
> +
> +int
> +gnet_stats_copy_basic_cpu(struct gnet_dump *d,
> +			  struct gnet_stats_basic_cpu __percpu *b)
> +{
> +	struct gnet_stats_basic_packed bstats = {0};
> +
> +	__gnet_stats_copy_basic_cpu(&bstats, b);
> +	return gnet_stats_copy_basic(d, &bstats);
> +}
> +EXPORT_SYMBOL(gnet_stats_copy_basic_cpu);
> +
>  /**
>   * gnet_stats_copy_rate_est - copy rate estimator statistics into statistics TLV
>   * @d: dumping handle
> @@ -143,12 +187,13 @@ EXPORT_SYMBOL(gnet_stats_copy_basic);
>  int
>  gnet_stats_copy_rate_est(struct gnet_dump *d,
>  			 const struct gnet_stats_basic_packed *b,
> +			 const struct gnet_stats_basic_cpu __percpu *cpu_b,
>  			 struct gnet_stats_rate_est64 *r)
>  {
>  	struct gnet_stats_rate_est est;
>  	int res;
>  
> -	if (b && !gen_estimator_active(b, r))
> +	if ((b || cpu_b) && !gen_estimator_active(b, cpu_b, r))
>  		return 0;
>  
>  	est.bps = min_t(u64, UINT_MAX, r->bps);
> diff --git a/net/netfilter/xt_RATEEST.c b/net/netfilter/xt_RATEEST.c
> index 370adf6..02e2532 100644
> --- a/net/netfilter/xt_RATEEST.c
> +++ b/net/netfilter/xt_RATEEST.c
> @@ -64,7 +64,7 @@ void xt_rateest_put(struct xt_rateest *est)
>  	mutex_lock(&xt_rateest_mutex);
>  	if (--est->refcnt == 0) {
>  		hlist_del(&est->list);
> -		gen_kill_estimator(&est->bstats, &est->rstats);
> +		gen_kill_estimator(&est->bstats, NULL, &est->rstats);
>  		/*
>  		 * gen_estimator est_timer() might access est->lock or bstats,
>  		 * wait a RCU grace period before freeing 'est'
> @@ -136,7 +136,7 @@ static int xt_rateest_tg_checkentry(const struct xt_tgchk_param *par)
>  	cfg.est.interval	= info->interval;
>  	cfg.est.ewma_log	= info->ewma_log;
>  
> -	ret = gen_new_estimator(&est->bstats, &est->rstats,
> +	ret = gen_new_estimator(&est->bstats, NULL, &est->rstats,
>  				&est->lock, &cfg.opt);
>  	if (ret < 0)
>  		goto err2;
> diff --git a/net/sched/act_api.c b/net/sched/act_api.c
> index ae32b5b..89504ea 100644
> --- a/net/sched/act_api.c
> +++ b/net/sched/act_api.c
> @@ -35,7 +35,7 @@ void tcf_hash_destroy(struct tc_action *a)
>  	spin_lock_bh(&hinfo->lock);
>  	hlist_del(&p->tcfc_head);
>  	spin_unlock_bh(&hinfo->lock);
> -	gen_kill_estimator(&p->tcfc_bstats,
> +	gen_kill_estimator(&p->tcfc_bstats, NULL,
>  			   &p->tcfc_rate_est);
>  	/*
>  	 * gen_estimator est_timer() might access p->tcfc_lock
> @@ -228,7 +228,7 @@ void tcf_hash_cleanup(struct tc_action *a, struct nlattr *est)
>  {
>  	struct tcf_common *pc = a->priv;
>  	if (est)
> -		gen_kill_estimator(&pc->tcfc_bstats,
> +		gen_kill_estimator(&pc->tcfc_bstats, NULL,
>  				   &pc->tcfc_rate_est);
>  	kfree_rcu(pc, tcfc_rcu);
>  }
> @@ -252,7 +252,8 @@ int tcf_hash_create(u32 index, struct nlattr *est, struct tc_action *a,
>  	p->tcfc_tm.install = jiffies;
>  	p->tcfc_tm.lastuse = jiffies;
>  	if (est) {
> -		int err = gen_new_estimator(&p->tcfc_bstats, &p->tcfc_rate_est,
> +		int err = gen_new_estimator(&p->tcfc_bstats, NULL,
> +					    &p->tcfc_rate_est,
>  					    &p->tcfc_lock, est);
>  		if (err) {
>  			kfree(p);
> @@ -620,7 +621,7 @@ int tcf_action_copy_stats(struct sk_buff *skb, struct tc_action *a,
>  		goto errout;
>  
>  	if (gnet_stats_copy_basic(&d, &p->tcfc_bstats) < 0 ||
> -	    gnet_stats_copy_rate_est(&d, &p->tcfc_bstats,
> +	    gnet_stats_copy_rate_est(&d, &p->tcfc_bstats, NULL,
>  				     &p->tcfc_rate_est) < 0 ||
>  	    gnet_stats_copy_queue(&d, &p->tcfc_qstats) < 0)
>  		goto errout;
> diff --git a/net/sched/act_police.c b/net/sched/act_police.c
> index f32bcb0..26f4bb3 100644
> --- a/net/sched/act_police.c
> +++ b/net/sched/act_police.c
> @@ -178,14 +178,14 @@ override:
>  
>  	spin_lock_bh(&police->tcf_lock);
>  	if (est) {
> -		err = gen_replace_estimator(&police->tcf_bstats,
> +		err = gen_replace_estimator(&police->tcf_bstats, NULL,
>  					    &police->tcf_rate_est,
>  					    &police->tcf_lock, est);
>  		if (err)
>  			goto failure_unlock;
>  	} else if (tb[TCA_POLICE_AVRATE] &&
>  		   (ret == ACT_P_CREATED ||
> -		    !gen_estimator_active(&police->tcf_bstats,
> +		    !gen_estimator_active(&police->tcf_bstats, NULL,
>  					  &police->tcf_rate_est))) {
>  		err = -EINVAL;
>  		goto failure_unlock;
> diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
> index ca62483..beb2064 100644
> --- a/net/sched/sch_api.c
> +++ b/net/sched/sch_api.c
> @@ -942,6 +942,13 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue,
>  	sch->handle = handle;
>  
>  	if (!ops->init || (err = ops->init(sch, tca[TCA_OPTIONS])) == 0) {
> +		if (qdisc_is_lockless(sch)) {
> +			sch->bstats_qdisc.cpu_bstats =
> +				alloc_percpu(struct gnet_stats_basic_cpu);
> +			if (!sch->bstats_qdisc.cpu_bstats)
> +				goto err_out4;
> +		}
> +
>  		if (tca[TCA_STAB]) {
>  			stab = qdisc_get_stab(tca[TCA_STAB]);
>  			if (IS_ERR(stab)) {
> @@ -964,8 +971,18 @@ qdisc_create(struct net_device *dev, struct netdev_queue *dev_queue,
>  			else
>  				root_lock = qdisc_lock(sch);
>  
> -			err = gen_new_estimator(&sch->bstats, &sch->rate_est,
> -						root_lock, tca[TCA_RATE]);
> +			if (qdisc_is_lockless(sch))
> +				err = gen_new_estimator(NULL,
> +							sch->bstats_qdisc.cpu_bstats,
> +							&sch->rate_est,
> +							root_lock,
> +							tca[TCA_RATE]);
> +			else
> +				err = gen_new_estimator(&sch->bstats_qdisc.bstats,
> +							NULL,
> +							&sch->rate_est,
> +							root_lock,
> +							tca[TCA_RATE]);


Note that you could have a simpler convention : and use	

err = gen_new_estimator(&sch->bstats_qdisc.bstats,
			sch->bstats_qdisc.cpu_bstats,
			root_lock,
			tca[TCA_RATE]);

This way, this patch would be way smaller.
		  


>  			if (err)
>  				goto err_out4;
>  		}
> @@ -1022,9 +1039,11 @@ static int qdisc_change(struct Qdisc *sch, struct nlattr **tca)
>  		   because change can't be undone. */
>  		if (sch->flags & TCQ_F_MQROOT)
>  			goto out;
> -		gen_replace_estimator(&sch->bstats, &sch->rate_est,
> -					    qdisc_root_sleeping_lock(sch),
> -					    tca[TCA_RATE]);
> +		gen_replace_estimator(&sch->bstats_qdisc.bstats,
> +				      sch->bstats_qdisc.cpu_bstats,
> +				      &sch->rate_est,
> +				      qdisc_root_sleeping_lock(sch),
> +				      tca[TCA_RATE]);
>  	}
>  out:
>  	return 0;
> @@ -1304,6 +1323,7 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid,
>  	unsigned char *b = skb_tail_pointer(skb);
>  	struct gnet_dump d;
>  	struct qdisc_size_table *stab;
> +	int err;
>  
>  	cond_resched();
>  	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags);
> @@ -1334,10 +1354,25 @@ static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid,
>  	if (q->ops->dump_stats && q->ops->dump_stats(q, &d) < 0)
>  		goto nla_put_failure;
>  
> -	if (gnet_stats_copy_basic(&d, &q->bstats) < 0 ||
> -	    gnet_stats_copy_rate_est(&d, &q->bstats, &q->rate_est) < 0 ||
> +	if (qdisc_is_lockless(q)) {
> +		err = gnet_stats_copy_basic_cpu(&d, q->bstats_qdisc.cpu_bstats);
> +		if (err < 0)
> +			goto nla_put_failure;
> +		err = gnet_stats_copy_rate_est(&d, NULL,
> +					       q->bstats_qdisc.cpu_bstats,
> +					       &q->rate_est);
> +	} else {
> +		err = gnet_stats_copy_basic(&d, &q->bstats_qdisc.bstats);
> +		if (err < 0)
> +			goto nla_put_failure;
> +		err = gnet_stats_copy_rate_est(&d,
> +					       &q->bstats_qdisc.bstats, NULL,
> +					       &q->rate_est);
> +	}

Same thing here....

You could simply extend existing functions with one cpu_bstats argument.

If it is null, then you copy the traditional bstats.


> +
> +	if (err < 0 ||
>  	    gnet_stats_copy_queue(&d, &q->qstats) < 0)
> -		goto nla_put_failure;
> +			goto nla_put_failure;
>  
>  	if (gnet_stats_finish_copy(&d) < 0)
>  		goto nla_put_failure;
> diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c
> index a3244a8..208074d 100644
> --- a/net/sched/sch_cbq.c
> +++ b/net/sched/sch_cbq.c
> @@ -1602,7 +1602,7 @@ cbq_dump_class_stats(struct Qdisc *sch, unsigned long arg,
>  		cl->xstats.undertime = cl->undertime - q->now;
>  
>  	if (gnet_stats_copy_basic(d, &cl->bstats) < 0 ||
> -	    gnet_stats_copy_rate_est(d, &cl->bstats, &cl->rate_est) < 0 ||
> +	    gnet_stats_copy_rate_est(d, &cl->bstats, NULL, &cl->rate_est) < 0 ||
>  	    gnet_stats_copy_queue(d, &cl->qstats) < 0)
>  		return -1;
>  
> @@ -1671,7 +1671,7 @@ static void cbq_destroy_class(struct Qdisc *sch, struct cbq_class *cl)
>  	tcf_destroy_chain(&cl->filter_list);
>  	qdisc_destroy(cl->q);
>  	qdisc_put_rtab(cl->R_tab);
> -	gen_kill_estimator(&cl->bstats, &cl->rate_est);
> +	gen_kill_estimator(&cl->bstats, NULL, &cl->rate_est);
>  	if (cl != &q->link)
>  		kfree(cl);
>  }
> @@ -1759,7 +1759,8 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t
>  		}
>  
>  		if (tca[TCA_RATE]) {
> -			err = gen_replace_estimator(&cl->bstats, &cl->rate_est,
> +			err = gen_replace_estimator(&cl->bstats, NULL,
> +						    &cl->rate_est,
>  						    qdisc_root_sleeping_lock(sch),
>  						    tca[TCA_RATE]);
>  			if (err) {
> @@ -1852,7 +1853,7 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t
>  		goto failure;
>  
>  	if (tca[TCA_RATE]) {
> -		err = gen_new_estimator(&cl->bstats, &cl->rate_est,
> +		err = gen_new_estimator(&cl->bstats, NULL, &cl->rate_est,
>  					qdisc_root_sleeping_lock(sch),
>  					tca[TCA_RATE]);
>  		if (err) {
> diff --git a/net/sched/sch_drr.c b/net/sched/sch_drr.c
> index d8b5ccf..88a65f6 100644
> --- a/net/sched/sch_drr.c
> +++ b/net/sched/sch_drr.c
> @@ -88,7 +88,8 @@ static int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
>  
>  	if (cl != NULL) {
>  		if (tca[TCA_RATE]) {
> -			err = gen_replace_estimator(&cl->bstats, &cl->rate_est,
> +			err = gen_replace_estimator(&cl->bstats, NULL,
> +						    &cl->rate_est,
>  						    qdisc_root_sleeping_lock(sch),
>  						    tca[TCA_RATE]);
>  			if (err)
> @@ -116,7 +117,7 @@ static int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
>  		cl->qdisc = &noop_qdisc;
>  
>  	if (tca[TCA_RATE]) {
> -		err = gen_replace_estimator(&cl->bstats, &cl->rate_est,
> +		err = gen_replace_estimator(&cl->bstats, NULL, &cl->rate_est,
>  					    qdisc_root_sleeping_lock(sch),
>  					    tca[TCA_RATE]);
>  		if (err) {
> @@ -138,7 +139,7 @@ static int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
>  
>  static void drr_destroy_class(struct Qdisc *sch, struct drr_class *cl)
>  {
> -	gen_kill_estimator(&cl->bstats, &cl->rate_est);
> +	gen_kill_estimator(&cl->bstats, NULL, &cl->rate_est);
>  	qdisc_destroy(cl->qdisc);
>  	kfree(cl);
>  }
> @@ -283,7 +284,7 @@ static int drr_dump_class_stats(struct Qdisc *sch, unsigned long arg,
>  	}
>  
>  	if (gnet_stats_copy_basic(d, &cl->bstats) < 0 ||
> -	    gnet_stats_copy_rate_est(d, &cl->bstats, &cl->rate_est) < 0 ||
> +	    gnet_stats_copy_rate_est(d, &cl->bstats, NULL, &cl->rate_est) < 0 ||
>  	    gnet_stats_copy_queue(d, &cl->qdisc->qstats) < 0)
>  		return -1;
>  
> diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
> index 346ef85..e3d203e 100644
> --- a/net/sched/sch_generic.c
> +++ b/net/sched/sch_generic.c
> @@ -632,6 +632,9 @@ static void qdisc_rcu_free(struct rcu_head *head)
>  {
>  	struct Qdisc *qdisc = container_of(head, struct Qdisc, rcu_head);
>  
> +	if (qdisc_is_lockless(qdisc))
> +		free_percpu(qdisc->bstats_qdisc.cpu_bstats);
> +
>  	kfree((char *) qdisc - qdisc->padded);
>  }
>  
> @@ -648,7 +651,13 @@ void qdisc_destroy(struct Qdisc *qdisc)
>  
>  	qdisc_put_stab(rtnl_dereference(qdisc->stab));
>  #endif
> -	gen_kill_estimator(&qdisc->bstats, &qdisc->rate_est);
> +	if (qdisc_is_lockless(qdisc))
> +		gen_kill_estimator(NULL, qdisc->bstats_qdisc.cpu_bstats,
> +				   &qdisc->rate_est);
> +	else
> +		gen_kill_estimator(&qdisc->bstats_qdisc.bstats, NULL,
> +				   &qdisc->rate_est);
> +
>  	if (ops->reset)
>  		ops->reset(qdisc);
>  	if (ops->destroy)
> diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c
> index 04b0de4..3b112d2 100644
> --- a/net/sched/sch_hfsc.c
> +++ b/net/sched/sch_hfsc.c
> @@ -1014,9 +1014,12 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
>  		cur_time = psched_get_time();
>  
>  		if (tca[TCA_RATE]) {
> -			err = gen_replace_estimator(&cl->bstats, &cl->rate_est,
> -					      qdisc_root_sleeping_lock(sch),
> -					      tca[TCA_RATE]);
> +			spinlock_t *lock = qdisc_root_sleeping_lock(sch);
> +
> +			err = gen_replace_estimator(&cl->bstats, NULL,
> +						    &cl->rate_est,
> +						    lock,
> +						    tca[TCA_RATE]);
>  			if (err)
>  				return err;
>  		}
> @@ -1063,7 +1066,7 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
>  		return -ENOBUFS;
>  
>  	if (tca[TCA_RATE]) {
> -		err = gen_new_estimator(&cl->bstats, &cl->rate_est,
> +		err = gen_new_estimator(&cl->bstats, NULL, &cl->rate_est,
>  					qdisc_root_sleeping_lock(sch),
>  					tca[TCA_RATE]);
>  		if (err) {
> @@ -1113,7 +1116,7 @@ hfsc_destroy_class(struct Qdisc *sch, struct hfsc_class *cl)
>  
>  	tcf_destroy_chain(&cl->filter_list);
>  	qdisc_destroy(cl->qdisc);
> -	gen_kill_estimator(&cl->bstats, &cl->rate_est);
> +	gen_kill_estimator(&cl->bstats, NULL, &cl->rate_est);
>  	if (cl != &q->root)
>  		kfree(cl);
>  }
> @@ -1375,7 +1378,7 @@ hfsc_dump_class_stats(struct Qdisc *sch, unsigned long arg,
>  	xstats.rtwork  = cl->cl_cumul;
>  
>  	if (gnet_stats_copy_basic(d, &cl->bstats) < 0 ||
> -	    gnet_stats_copy_rate_est(d, &cl->bstats, &cl->rate_est) < 0 ||
> +	    gnet_stats_copy_rate_est(d, &cl->bstats, NULL, &cl->rate_est) < 0 ||
>  	    gnet_stats_copy_queue(d, &cl->qstats) < 0)
>  		return -1;
>  
> diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c
> index 6d16b9b..8067a82 100644
> --- a/net/sched/sch_htb.c
> +++ b/net/sched/sch_htb.c
> @@ -1145,7 +1145,7 @@ htb_dump_class_stats(struct Qdisc *sch, unsigned long arg, struct gnet_dump *d)
>  	cl->xstats.ctokens = PSCHED_NS2TICKS(cl->ctokens);
>  
>  	if (gnet_stats_copy_basic(d, &cl->bstats) < 0 ||
> -	    gnet_stats_copy_rate_est(d, NULL, &cl->rate_est) < 0 ||
> +	    gnet_stats_copy_rate_est(d, NULL, NULL, &cl->rate_est) < 0 ||
>  	    gnet_stats_copy_queue(d, &cl->qstats) < 0)
>  		return -1;
>  
> @@ -1235,7 +1235,7 @@ static void htb_destroy_class(struct Qdisc *sch, struct htb_class *cl)
>  		WARN_ON(!cl->un.leaf.q);
>  		qdisc_destroy(cl->un.leaf.q);
>  	}
> -	gen_kill_estimator(&cl->bstats, &cl->rate_est);
> +	gen_kill_estimator(&cl->bstats, NULL, &cl->rate_est);
>  	tcf_destroy_chain(&cl->filter_list);
>  	kfree(cl);
>  }
> @@ -1402,7 +1402,8 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
>  			goto failure;
>  
>  		if (htb_rate_est || tca[TCA_RATE]) {
> -			err = gen_new_estimator(&cl->bstats, &cl->rate_est,
> +			err = gen_new_estimator(&cl->bstats, NULL,
> +						&cl->rate_est,
>  						qdisc_root_sleeping_lock(sch),
>  						tca[TCA_RATE] ? : &est.nla);
>  			if (err) {
> @@ -1464,8 +1465,11 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
>  			parent->children++;
>  	} else {
>  		if (tca[TCA_RATE]) {
> -			err = gen_replace_estimator(&cl->bstats, &cl->rate_est,
> -						    qdisc_root_sleeping_lock(sch),
> +			spinlock_t *lock = qdisc_root_sleeping_lock(sch);
> +
> +			err = gen_replace_estimator(&cl->bstats, NULL,
> +						    &cl->rate_est,
> +						    lock,
>  						    tca[TCA_RATE]);
>  			if (err)
>  				return err;
> diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c
> index b351125..25302be 100644
> --- a/net/sched/sch_ingress.c
> +++ b/net/sched/sch_ingress.c
> @@ -65,7 +65,7 @@ static int ingress_enqueue(struct sk_buff *skb, struct Qdisc *sch)
>  
>  	result = tc_classify(skb, fl, &res);
>  
> -	qdisc_bstats_update(sch, skb);
> +	qdisc_bstats_update_cpu(sch, skb);
>  	switch (result) {
>  	case TC_ACT_SHOT:
>  		result = TC_ACT_SHOT;
> diff --git a/net/sched/sch_mq.c b/net/sched/sch_mq.c
> index a8b2864..e96a41f 100644
> --- a/net/sched/sch_mq.c
> +++ b/net/sched/sch_mq.c
> @@ -98,20 +98,22 @@ static void mq_attach(struct Qdisc *sch)
>  
>  static int mq_dump(struct Qdisc *sch, struct sk_buff *skb)
>  {
> +	struct gnet_stats_basic_packed *bstats = &sch->bstats_qdisc.bstats;
>  	struct net_device *dev = qdisc_dev(sch);
>  	struct Qdisc *qdisc;
>  	unsigned int ntx;
>  
>  	sch->q.qlen = 0;
> -	memset(&sch->bstats, 0, sizeof(sch->bstats));
> +	memset(bstats, 0, sizeof(sch->bstats_qdisc));
>  	memset(&sch->qstats, 0, sizeof(sch->qstats));
>  
>  	for (ntx = 0; ntx < dev->num_tx_queues; ntx++) {
>  		qdisc = netdev_get_tx_queue(dev, ntx)->qdisc_sleeping;
>  		spin_lock_bh(qdisc_lock(qdisc));
>  		sch->q.qlen		+= qdisc->q.qlen;
> -		sch->bstats.bytes	+= qdisc->bstats.bytes;
> -		sch->bstats.packets	+= qdisc->bstats.packets;
> +
> +		bstats->bytes		+= qdisc->bstats_qdisc.bstats.bytes;
> +		bstats->packets		+= qdisc->bstats_qdisc.bstats.packets;
>  		sch->qstats.qlen	+= qdisc->qstats.qlen;
>  		sch->qstats.backlog	+= qdisc->qstats.backlog;
>  		sch->qstats.drops	+= qdisc->qstats.drops;
> @@ -201,7 +203,7 @@ static int mq_dump_class_stats(struct Qdisc *sch, unsigned long cl,
>  
>  	sch = dev_queue->qdisc_sleeping;
>  	sch->qstats.qlen = sch->q.qlen;
> -	if (gnet_stats_copy_basic(d, &sch->bstats) < 0 ||
> +	if (gnet_stats_copy_basic(d, &sch->bstats_qdisc.bstats) < 0 ||
>  	    gnet_stats_copy_queue(d, &sch->qstats) < 0)
>  		return -1;
>  	return 0;
> diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c
> index 37e7d25..6e3e4e9 100644
> --- a/net/sched/sch_mqprio.c
> +++ b/net/sched/sch_mqprio.c
> @@ -219,6 +219,7 @@ static int mqprio_graft(struct Qdisc *sch, unsigned long cl, struct Qdisc *new,
>  
>  static int mqprio_dump(struct Qdisc *sch, struct sk_buff *skb)
>  {
> +	struct gnet_stats_basic_packed *bstats = &sch->bstats_qdisc.bstats;
>  	struct net_device *dev = qdisc_dev(sch);
>  	struct mqprio_sched *priv = qdisc_priv(sch);
>  	unsigned char *b = skb_tail_pointer(skb);
> @@ -227,15 +228,15 @@ static int mqprio_dump(struct Qdisc *sch, struct sk_buff *skb)
>  	unsigned int i;
>  
>  	sch->q.qlen = 0;
> -	memset(&sch->bstats, 0, sizeof(sch->bstats));
> +	memset(bstats, 0, sizeof(sch->bstats_qdisc.bstats));
>  	memset(&sch->qstats, 0, sizeof(sch->qstats));
>  
>  	for (i = 0; i < dev->num_tx_queues; i++) {
>  		qdisc = rtnl_dereference(netdev_get_tx_queue(dev, i)->qdisc);
>  		spin_lock_bh(qdisc_lock(qdisc));
>  		sch->q.qlen		+= qdisc->q.qlen;
> -		sch->bstats.bytes	+= qdisc->bstats.bytes;
> -		sch->bstats.packets	+= qdisc->bstats.packets;
> +		bstats->bytes		+= qdisc->bstats_qdisc.bstats.bytes;
> +		bstats->packets		+= qdisc->bstats_qdisc.bstats.packets;
>  		sch->qstats.qlen	+= qdisc->qstats.qlen;
>  		sch->qstats.backlog	+= qdisc->qstats.backlog;
>  		sch->qstats.drops	+= qdisc->qstats.drops;
> @@ -344,8 +345,8 @@ static int mqprio_dump_class_stats(struct Qdisc *sch, unsigned long cl,
>  
>  			qdisc = rtnl_dereference(q->qdisc);
>  			spin_lock_bh(qdisc_lock(qdisc));
> -			bstats.bytes      += qdisc->bstats.bytes;
> -			bstats.packets    += qdisc->bstats.packets;
> +			bstats.bytes      += qdisc->bstats_qdisc.bstats.bytes;
> +			bstats.packets    += qdisc->bstats_qdisc.bstats.packets;
>  			qstats.qlen       += qdisc->qstats.qlen;
>  			qstats.backlog    += qdisc->qstats.backlog;
>  			qstats.drops      += qdisc->qstats.drops;
> @@ -363,7 +364,7 @@ static int mqprio_dump_class_stats(struct Qdisc *sch, unsigned long cl,
>  
>  		sch = dev_queue->qdisc_sleeping;
>  		sch->qstats.qlen = sch->q.qlen;
> -		if (gnet_stats_copy_basic(d, &sch->bstats) < 0 ||
> +		if (gnet_stats_copy_basic(d, &sch->bstats_qdisc.bstats) < 0 ||
>  		    gnet_stats_copy_queue(d, &sch->qstats) < 0)
>  			return -1;
>  	}
> diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c
> index c0466c1..d6430102 100644
> --- a/net/sched/sch_multiq.c
> +++ b/net/sched/sch_multiq.c
> @@ -361,7 +361,7 @@ static int multiq_dump_class_stats(struct Qdisc *sch, unsigned long cl,
>  
>  	cl_q = q->queues[cl - 1];
>  	cl_q->qstats.qlen = cl_q->q.qlen;
> -	if (gnet_stats_copy_basic(d, &cl_q->bstats) < 0 ||
> +	if (gnet_stats_copy_basic(d, &cl_q->bstats_qdisc.bstats) < 0 ||
>  	    gnet_stats_copy_queue(d, &cl_q->qstats) < 0)
>  		return -1;
>  
> diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c
> index 03ef99e..9069aba 100644
> --- a/net/sched/sch_prio.c
> +++ b/net/sched/sch_prio.c
> @@ -325,7 +325,7 @@ static int prio_dump_class_stats(struct Qdisc *sch, unsigned long cl,
>  
>  	cl_q = q->queues[cl - 1];
>  	cl_q->qstats.qlen = cl_q->q.qlen;
> -	if (gnet_stats_copy_basic(d, &cl_q->bstats) < 0 ||
> +	if (gnet_stats_copy_basic(d, &cl_q->bstats_qdisc.bstats) < 0 ||
>  	    gnet_stats_copy_queue(d, &cl_q->qstats) < 0)
>  		return -1;
>  
> diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c
> index 602ea01..52a602d 100644
> --- a/net/sched/sch_qfq.c
> +++ b/net/sched/sch_qfq.c
> @@ -459,7 +459,8 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
>  
>  	if (cl != NULL) { /* modify existing class */
>  		if (tca[TCA_RATE]) {
> -			err = gen_replace_estimator(&cl->bstats, &cl->rate_est,
> +			err = gen_replace_estimator(&cl->bstats, NULL,
> +						    &cl->rate_est,
>  						    qdisc_root_sleeping_lock(sch),
>  						    tca[TCA_RATE]);
>  			if (err)
> @@ -484,7 +485,8 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
>  		cl->qdisc = &noop_qdisc;
>  
>  	if (tca[TCA_RATE]) {
> -		err = gen_new_estimator(&cl->bstats, &cl->rate_est,
> +		err = gen_new_estimator(&cl->bstats, NULL,
> +					&cl->rate_est,
>  					qdisc_root_sleeping_lock(sch),
>  					tca[TCA_RATE]);
>  		if (err)
> @@ -505,7 +507,7 @@ set_change_agg:
>  		new_agg = kzalloc(sizeof(*new_agg), GFP_KERNEL);
>  		if (new_agg == NULL) {
>  			err = -ENOBUFS;
> -			gen_kill_estimator(&cl->bstats, &cl->rate_est);
> +			gen_kill_estimator(&cl->bstats, NULL, &cl->rate_est);
>  			goto destroy_class;
>  		}
>  		sch_tree_lock(sch);
> @@ -530,7 +532,7 @@ static void qfq_destroy_class(struct Qdisc *sch, struct qfq_class *cl)
>  	struct qfq_sched *q = qdisc_priv(sch);
>  
>  	qfq_rm_from_agg(q, cl);
> -	gen_kill_estimator(&cl->bstats, &cl->rate_est);
> +	gen_kill_estimator(&cl->bstats, NULL, &cl->rate_est);
>  	qdisc_destroy(cl->qdisc);
>  	kfree(cl);
>  }
> @@ -668,7 +670,7 @@ static int qfq_dump_class_stats(struct Qdisc *sch, unsigned long arg,
>  	xstats.lmax = cl->agg->lmax;
>  
>  	if (gnet_stats_copy_basic(d, &cl->bstats) < 0 ||
> -	    gnet_stats_copy_rate_est(d, &cl->bstats, &cl->rate_est) < 0 ||
> +	    gnet_stats_copy_rate_est(d, &cl->bstats, NULL, &cl->rate_est) < 0 ||
>  	    gnet_stats_copy_queue(d, &cl->qdisc->qstats) < 0)
>  		return -1;
>  
> 

^ permalink raw reply

* Re: [net-next PATCH v5 15/16] net: sched: make qstats per cpu
From: Eric Dumazet @ 2014-09-12 23:55 UTC (permalink / raw)
  To: John Fastabend; +Cc: xiyou.wangcong, davem, jhs, netdev, paulmck, brouer
In-Reply-To: <20140912163453.19588.6775.stgit@nitbit.x32>

On Fri, 2014-09-12 at 09:34 -0700, John Fastabend wrote:
> Now that we run qdisc without locked the qstats need to handle the
> case where multiple cpus are receiving or transmiting a skb. For now
> the only qdisc that supports running without locks is the ingress
> qdisc which increments the 32-bit drop counter.
> 
> When the stats are collected the summation of all CPUs is collected
> and returned in the dump tlv.
> 
> Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
> ---
>  include/net/codel.h       |    4 ++--
>  include/net/gen_stats.h   |    2 ++
>  include/net/sch_generic.h |   19 +++++++++++--------
>  net/core/gen_stats.c      |   30 +++++++++++++++++++++++++++++-
>  net/sched/sch_api.c       |   31 ++++++++++++++++++++++++++-----
>  net/sched/sch_atm.c       |    2 +-
>  net/sched/sch_cbq.c       |   10 +++++-----
>  net/sched/sch_choke.c     |   15 ++++++++-------
>  net/sched/sch_codel.c     |    2 +-
>  net/sched/sch_drr.c       |    8 ++++----
>  net/sched/sch_dsmark.c    |    2 +-
>  net/sched/sch_fifo.c      |    6 ++++--
>  net/sched/sch_fq.c        |    4 ++--
>  net/sched/sch_fq_codel.c  |    8 ++++----
>  net/sched/sch_generic.c   |    8 +++++---
>  net/sched/sch_gred.c      |   10 +++++-----
>  net/sched/sch_hfsc.c      |   15 ++++++++-------
>  net/sched/sch_hhf.c       |    8 ++++----
>  net/sched/sch_htb.c       |    6 +++---
>  net/sched/sch_ingress.c   |    4 +++-
>  net/sched/sch_mq.c        |   23 ++++++++++++++---------
>  net/sched/sch_mqprio.c    |   35 ++++++++++++++++++++++-------------
>  net/sched/sch_multiq.c    |    8 ++++----
>  net/sched/sch_netem.c     |   17 +++++++++--------
>  net/sched/sch_pie.c       |    8 ++++----
>  net/sched/sch_plug.c      |    2 +-
>  net/sched/sch_prio.c      |    8 ++++----
>  net/sched/sch_qfq.c       |    8 ++++----
>  net/sched/sch_red.c       |   13 +++++++------
>  net/sched/sch_sfb.c       |   13 +++++++------
>  net/sched/sch_sfq.c       |   19 ++++++++++---------
>  net/sched/sch_tbf.c       |   11 ++++++-----
>  32 files changed, 220 insertions(+), 139 deletions(-)
> 
> diff --git a/include/net/codel.h b/include/net/codel.h
> index aeee280..72d37a4 100644
> --- a/include/net/codel.h
> +++ b/include/net/codel.h
> @@ -228,13 +228,13 @@ static bool codel_should_drop(const struct sk_buff *skb,
>  	}
>  
>  	vars->ldelay = now - codel_get_enqueue_time(skb);
> -	sch->qstats.backlog -= qdisc_pkt_len(skb);
> +	sch->qstats_qdisc.qstats.backlog -= qdisc_pkt_len(skb);
>  
>  	if (unlikely(qdisc_pkt_len(skb) > stats->maxpacket))
>  		stats->maxpacket = qdisc_pkt_len(skb);
>  
>  	if (codel_time_before(vars->ldelay, params->target) ||
> -	    sch->qstats.backlog <= stats->maxpacket) {
> +	    sch->qstats_qdisc.qstats.backlog <= stats->maxpacket) {
>  		/* went below - stay below for at least interval */
>  		vars->first_above_time = 0;
>  		return false;
> diff --git a/include/net/gen_stats.h b/include/net/gen_stats.h
> index 4b7ca2b..d548dc9 100644
> --- a/include/net/gen_stats.h
> +++ b/include/net/gen_stats.h
> @@ -44,6 +44,8 @@ int gnet_stats_copy_rate_est(struct gnet_dump *d,
>  			     const struct gnet_stats_basic_cpu __percpu *cpu_b,
>  			     struct gnet_stats_rate_est64 *r);
>  int gnet_stats_copy_queue(struct gnet_dump *d, struct gnet_stats_queue *q);
> +int gnet_stats_copy_queue_cpu(struct gnet_dump *d,
> +			      struct gnet_stats_queue __percpu *q);
>  int gnet_stats_copy_app(struct gnet_dump *d, void *st, int len);
>  
>  int gnet_stats_finish_copy(struct gnet_dump *d);
> diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h
> index 54e318f..518be3b 100644
> --- a/include/net/sch_generic.h
> +++ b/include/net/sch_generic.h
> @@ -92,7 +92,10 @@ struct Qdisc {
>  		struct gnet_stats_basic_cpu __percpu *cpu_bstats;
>  	} bstats_qdisc;
>  	unsigned int		__state;
> -	struct gnet_stats_queue	qstats;
> +	union {
> +		struct gnet_stats_queue qstats;
> +		struct gnet_stats_queue __percpu *cpu_qstats;
> +	} qstats_qdisc;
>  	struct rcu_head		rcu_head;
>  	int			padded;
>  	atomic_t		refcnt;
> @@ -538,7 +541,7 @@ static inline int __qdisc_enqueue_tail(struct sk_buff *skb, struct Qdisc *sch,
>  				       struct sk_buff_head *list)
>  {
>  	__skb_queue_tail(list, skb);
> -	sch->qstats.backlog += qdisc_pkt_len(skb);
> +	sch->qstats_qdisc.qstats.backlog += qdisc_pkt_len(skb);

I am a bit lost :(

Probably a helper could make this cleaner.

>  
>  	return NET_XMIT_SUCCESS;
>  }
> @@ -554,7 +557,7 @@ static inline struct sk_buff *__qdisc_dequeue_head(struct Qdisc *sch,
>  	struct sk_buff *skb = __skb_dequeue(list);
>  
>  	if (likely(skb != NULL)) {
> -		sch->qstats.backlog -= qdisc_pkt_len(skb);
> +		sch->qstats_qdisc.qstats.backlog -= qdisc_pkt_len(skb);

because this kind of patch in the future could be easier : only change
the helper ;)


Please split this patch in 2 parts :

1) Add clean helpers/macros (no functional change)

2) Make the percpu changes 

^ permalink raw reply

* Re: [net-next PATCH v5 16/16] net: sched: drop ingress qdisc lock
From: Eric Dumazet @ 2014-09-12 23:56 UTC (permalink / raw)
  To: John Fastabend; +Cc: xiyou.wangcong, davem, jhs, netdev, paulmck, brouer
In-Reply-To: <20140912163519.19588.54966.stgit@nitbit.x32>

On Fri, 2014-09-12 at 09:35 -0700, John Fastabend wrote:
> After the previous patches to make the filters RCU safe and
> support per cpu counters we can drop the qdisc lock around
> the ingress qdisc hook.
> 
> This is possible because the ingress qdisc is a very basic
> qdisc and only updates stats and runs tc_classify. Its the
> simplest qdiscs we have.
> 
> In order for the per-cpu counters to get invoked the
> ingress qdisc must set the LLQDISC flag. We could use per-cpu
> counters on all counters but it is only necessary when the
> qdisc lock is not held.
> 
> Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
> ---

Acked-by: Eric Dumazet <edumazet@google.com>

^ permalink raw reply

* [PATCHv2] net/macb: Add hardware revision information during probe
From: Alexandre Belloni @ 2014-09-12 23:57 UTC (permalink / raw)
  To: David S. Miller
  Cc: linux-arm-kernel, Nicolas Ferre, linux-kernel, netdev, Bo Shen

From: Bo Shen <voice.shen@atmel.com>

Print the IP revision when probing.

Signed-off-by: Bo Shen <voice.shen@atmel.com>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
---
Changes in v2:
 - condense information on one line

 drivers/net/ethernet/cadence/macb.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/net/ethernet/cadence/macb.c b/drivers/net/ethernet/cadence/macb.c
index ca5d7798b265..d9b8e94b805f 100644
--- a/drivers/net/ethernet/cadence/macb.c
+++ b/drivers/net/ethernet/cadence/macb.c
@@ -2241,9 +2241,9 @@ static int __init macb_probe(struct platform_device *pdev)
 
 	netif_carrier_off(dev);
 
-	netdev_info(dev, "Cadence %s at 0x%08lx irq %d (%pM)\n",
-		    macb_is_gem(bp) ? "GEM" : "MACB", dev->base_addr,
-		    dev->irq, dev->dev_addr);
+	netdev_info(dev, "Cadence %s rev 0x%08x at 0x%08lx irq %d (%pM)\n",
+		    macb_is_gem(bp) ? "GEM" : "MACB", macb_readl(bp, MID),
+		    dev->base_addr, dev->irq, dev->dev_addr);
 
 	phydev = bp->phy_dev;
 	netdev_info(dev, "attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
-- 
1.9.1

^ permalink raw reply related

* Re: [net-next PATCH v5 13/16] net: sched: make tc_action safe to walk under RCU
From: John Fastabend @ 2014-09-13  0:05 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: xiyou.wangcong, davem, jhs, netdev, paulmck, brouer
In-Reply-To: <1410555114.7106.101.camel@edumazet-glaptop2.roam.corp.google.com>

On 09/12/2014 01:51 PM, Eric Dumazet wrote:
> On Fri, 2014-09-12 at 09:34 -0700, John Fastabend wrote:
>
>> Second there is a suspect usage of list_splice_init_rcu() in the
>> tcf_exts_change() routine. Notice how it is used twice in succession
>> and the second init works on the src tcf_exts. There is probably a
>> better way to accomplish that.

Not only is it suspect its broke as best as I can tell.

>>
>
>
>> +/* It is not safe to use src->actions after this due to _init_rcu usage
>> + * INIT_LIST_HEAD_RCU() is called on src->actions
>> + */
>>   void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst,
>>   		     struct tcf_exts *src)
>>   {
>>   #ifdef CONFIG_NET_CLS_ACT
>>   	LIST_HEAD(tmp);
>> -	tcf_tree_lock(tp);
>> -	list_splice_init(&dst->actions, &tmp);



>> -	list_splice(&src->actions, &dst->actions);
>> -	tcf_tree_unlock(tp);

Here we have three lists,

	 tmp  (which has just been initialized)

	  dst -> act1 -> act2 -> ... -> actn ->

	  src -> acta -> actb -> ... -> actn ->

The active list is dst from the tc_classify and we want to
switch to the src list


>> +	list_splice_init_rcu(&dst->actions, &tmp, synchronize_rcu);

Now we spliced in tmp and did an INIT_LIST_HEAD_RCU() on dst->actions
(oops) so the lists look like this

	tmp -> act1 -> act2 -> ... -> actn ->

	dst

	src  -> acta -> actb -> ... -> actn ->

The active list is still dst from the tc_classify caller so now we are
missing both the old and new action lists. This is broke.


>> +	list_splice_init_rcu(&src->actions,
>> +			     &dst->actions,
>> +			     synchronize_rcu);

But the final state is what we wanted,

	tmp -> act1 -> act2 -> ... -> actn ->

	dst -> acta -> actb -> ... -> actn ->

	src

>>   	tcf_action_destroy(&tmp, TCA_ACT_UNBIND);

Finally clean it all up,

	tmp

	dst -> acta -> actb -> ... -> actn ->

	src


>>   #endif
>>   }
>
> I am afraid I do not understand this part.

Because it doesn't work as far as I can tell. What I really want here
is to swap the head pointer with,


	struct list_head *tmp = dst->actions;

	rcu_assign_pointer(dst->actions, src->actions)
	synchronize_rcu()
	tcf_action_destroy(tmp);


This requires a bit more work. I'll work out a patch after a bit more
thought.

Thanks again!

.John



-- 
John Fastabend         Intel Corporation

^ permalink raw reply

* Re: [Patch net-next] ipv6: exit early in addrconf_notify() if IPv6 is disabled
From: Cong Wang @ 2014-09-13  0:10 UTC (permalink / raw)
  To: Hannes Frederic Sowa
  Cc: Linux Kernel Network Developers, Hideaki YOSHIFUJI,
	David S. Miller
In-Reply-To: <1410553099.2970.18.camel@localhost>

On Fri, Sep 12, 2014 at 1:18 PM, Hannes Frederic Sowa <hannes@redhat.com> wrote:
>
> disable_ipv6 is absolutely bad implemented. It should not be in the ipv6
> procfs namespace at all. An inet6_dev must be available to manage this
> knob! Thus we are automagically subscribed to all ipv6 LL multicast
> groups.


Exactly, this is chicken and egg problem and is why we have ipv6_idev_add()
in the NETDEV_REGISTER event.

^ permalink raw reply

* Re: [PATCH] net: DSA: Marvell mv88e6171 switch driver
From: Andrew Lunn @ 2014-09-13  0:12 UTC (permalink / raw)
  To: Florian Fainelli; +Cc: Andrew Lunn, davem, netdev, Claudio Leite
In-Reply-To: <541370DD.7090309@gmail.com>

On Fri, Sep 12, 2014 at 03:17:01PM -0700, Florian Fainelli wrote:
> Hi Andrew,
> 
> On 09/12/2014 02:58 PM, Andrew Lunn wrote:
> > This is the Marvell driver with some cleanups by Claudio Leite
> > and myself.
> 
> Looks good to me, this will slightly conflict with the patch for the
> tag_protocol field [1], other than that, good to see this driver
> submitted! Thanks!

Hi Florian

How do you want to handle this conflict?

      Andrew

^ permalink raw reply

* Re: [PATCH] net: DSA: Marvell mv88e6171 switch driver
From: Claudio Leite @ 2014-09-13  0:50 UTC (permalink / raw)
  To: netdev
In-Reply-To: <1410559124-10495-1-git-send-email-andrew@lunn.ch>

* Andrew Lunn (andrew@lunn.ch) wrote:
> This is the Marvell driver with some cleanups by Claudio Leite
> and myself.
> 
> Signed-off-by: Andrew Lunn <andrew@lunn.ch>
> Cc: Claudio Leite <leitec@staticky.com>
> ---

Signed-off-by: Claudio Leite <leitec@staticky.com>

^ permalink raw reply

* Re: [PATCH] net: DSA: Marvell mv88e6171 switch driver
From: Claudio Leite @ 2014-09-13  0:42 UTC (permalink / raw)
  To: Andrew Lunn; +Cc: davem, f.fainelli, netdev
In-Reply-To: <1410559124-10495-1-git-send-email-andrew@lunn.ch>

This is the Marvell driver with some cleanups by Claudio Leite
and myself.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Claudio Leite <leitec@staticky.com>
---
 drivers/net/dsa/Kconfig     |   9 +
 drivers/net/dsa/Makefile    |   3 +
 drivers/net/dsa/mv88e6171.c | 407 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/net/dsa/mv88e6xxx.c |   6 +
 drivers/net/dsa/mv88e6xxx.h |   1 +
 5 files changed, 426 insertions(+)
 create mode 100644 drivers/net/dsa/mv88e6171.c

diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index c6ee07c6a1b5..ea0697eaeff5 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -36,6 +36,15 @@ config NET_DSA_MV88E6123_61_65
 	  This enables support for the Marvell 88E6123/6161/6165
 	  ethernet switch chips.
 
+config NET_DSA_MV88E6171
+	tristate "Marvell 88E6171 ethernet switch chip support"
+	select NET_DSA
+	select NET_DSA_MV88E6XXX
+	select NET_DSA_TAG_EDSA
+	---help---
+	  This enables support for the Marvell 88E6171 ethernet switch
+	  chip.
+
 config NET_DSA_BCM_SF2
 	tristate "Broadcom Starfighter 2 Ethernet switch support"
 	select NET_DSA
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index dd3cd3b8157f..23a90de9830e 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -7,4 +7,7 @@ endif
 ifdef CONFIG_NET_DSA_MV88E6131
 mv88e6xxx_drv-y += mv88e6131.o
 endif
+ifdef CONFIG_NET_DSA_MV88E6171
+mv88e6xxx_drv-y += mv88e6171.o
+endif
 obj-$(CONFIG_NET_DSA_BCM_SF2)	+= bcm_sf2.o
diff --git a/drivers/net/dsa/mv88e6171.c b/drivers/net/dsa/mv88e6171.c
new file mode 100644
index 000000000000..8bae73bec1d7
--- /dev/null
+++ b/drivers/net/dsa/mv88e6171.c
@@ -0,0 +1,407 @@
+/* net/dsa/mv88e6171.c - Marvell 88e6171 switch chip support
+ * Copyright (c) 2008-2009 Marvell Semiconductor
+ * Copyright (c) 2014 Claudio Leite <leitec@staticky.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/delay.h>
+#include <linux/jiffies.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/phy.h>
+#include <net/dsa.h>
+#include "mv88e6xxx.h"
+
+static char *mv88e6171_probe(struct mii_bus *bus, int sw_addr)
+{
+	int ret;
+
+	ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03);
+	if (ret >= 0) {
+		if ((ret & 0xfff0) == 0x1710)
+			return "Marvell 88E6171";
+	}
+
+	return NULL;
+}
+
+static int mv88e6171_switch_reset(struct dsa_switch *ds)
+{
+	int i;
+	int ret;
+	unsigned long timeout;
+
+	/* Set all ports to the disabled state. */
+	for (i = 0; i < 8; i++) {
+		ret = REG_READ(REG_PORT(i), 0x04);
+		REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc);
+	}
+
+	/* Wait for transmit queues to drain. */
+	usleep_range(2000, 4000);
+
+	/* Reset the switch. */
+	REG_WRITE(REG_GLOBAL, 0x04, 0xc400);
+
+	/* Wait up to one second for reset to complete. */
+	timeout = jiffies + 1 * HZ;
+	while (time_before(jiffies, timeout)) {
+		ret = REG_READ(REG_GLOBAL, 0x00);
+		if ((ret & 0xc800) == 0xc800)
+			break;
+
+		usleep_range(1000, 2000);
+	}
+	if (time_after(jiffies, timeout))
+		return -ETIMEDOUT;
+
+	/* Enable ports not under DSA, e.g. WAN port */
+	for (i = 0; i < 8; i++) {
+		if (dsa_is_cpu_port(ds, i) || ds->phys_port_mask & (1 << i))
+			continue;
+
+		ret = REG_READ(REG_PORT(i), 0x04);
+		REG_WRITE(REG_PORT(i), 0x04, ret | 0x03);
+	}
+
+	return 0;
+}
+
+static int mv88e6171_setup_global(struct dsa_switch *ds)
+{
+	int ret;
+	int i;
+
+	/* Disable the PHY polling unit (since there won't be any
+	 * external PHYs to poll), don't discard packets with
+	 * excessive collisions, and mask all interrupt sources.
+	 */
+	REG_WRITE(REG_GLOBAL, 0x04, 0x0000);
+
+	/* Set the default address aging time to 5 minutes, and
+	 * enable address learn messages to be sent to all message
+	 * ports.
+	 */
+	REG_WRITE(REG_GLOBAL, 0x0a, 0x0148);
+
+	/* Configure the priority mapping registers. */
+	ret = mv88e6xxx_config_prio(ds);
+	if (ret < 0)
+		return ret;
+
+	/* Configure the upstream port, and configure the upstream
+	 * port as the port to which ingress and egress monitor frames
+	 * are to be sent.
+	 */
+	if (REG_READ(REG_PORT(0), 0x03) == 0x1710)
+		REG_WRITE(REG_GLOBAL, 0x1a, (dsa_upstream_port(ds) * 0x1111));
+	else
+		REG_WRITE(REG_GLOBAL, 0x1a, (dsa_upstream_port(ds) * 0x1110));
+
+	/* Disable remote management for now, and set the switch's
+	 * DSA device number.
+	 */
+	REG_WRITE(REG_GLOBAL, 0x1c, ds->index & 0x1f);
+
+	/* Send all frames with destination addresses matching
+	 * 01:80:c2:00:00:2x to the CPU port.
+	 */
+	REG_WRITE(REG_GLOBAL2, 0x02, 0xffff);
+
+	/* Send all frames with destination addresses matching
+	 * 01:80:c2:00:00:0x to the CPU port.
+	 */
+	REG_WRITE(REG_GLOBAL2, 0x03, 0xffff);
+
+	/* Disable the loopback filter, disable flow control
+	 * messages, disable flood broadcast override, disable
+	 * removing of provider tags, disable ATU age violation
+	 * interrupts, disable tag flow control, force flow
+	 * control priority to the highest, and send all special
+	 * multicast frames to the CPU at the highest priority.
+	 */
+	REG_WRITE(REG_GLOBAL2, 0x05, 0x00ff);
+
+	/* Program the DSA routing table. */
+	for (i = 0; i < 32; i++) {
+		int nexthop;
+
+		nexthop = 0x1f;
+		if (i != ds->index && i < ds->dst->pd->nr_chips)
+			nexthop = ds->pd->rtable[i] & 0x1f;
+
+		REG_WRITE(REG_GLOBAL2, 0x06, 0x8000 | (i << 8) | nexthop);
+	}
+
+	/* Clear all trunk masks. */
+	for (i = 0; i < 8; i++)
+		REG_WRITE(REG_GLOBAL2, 0x07, 0x8000 | (i << 12) | 0xff);
+
+	/* Clear all trunk mappings. */
+	for (i = 0; i < 16; i++)
+		REG_WRITE(REG_GLOBAL2, 0x08, 0x8000 | (i << 11));
+
+	/* Disable ingress rate limiting by resetting all ingress
+	 * rate limit registers to their initial state.
+	 */
+	for (i = 0; i < 6; i++)
+		REG_WRITE(REG_GLOBAL2, 0x09, 0x9000 | (i << 8));
+
+	/* Initialise cross-chip port VLAN table to reset defaults. */
+	REG_WRITE(REG_GLOBAL2, 0x0b, 0x9000);
+
+	/* Clear the priority override table. */
+	for (i = 0; i < 16; i++)
+		REG_WRITE(REG_GLOBAL2, 0x0f, 0x8000 | (i << 8));
+
+	/* @@@ initialise AVB (22/23) watchdog (27) sdet (29) registers */
+
+	return 0;
+}
+
+static int mv88e6171_setup_port(struct dsa_switch *ds, int p)
+{
+	int addr = REG_PORT(p);
+	u16 val;
+
+	/* MAC Forcing register: don't force link, speed, duplex
+	 * or flow control state to any particular values on physical
+	 * ports, but force the CPU port and all DSA ports to 1000 Mb/s
+	 * full duplex.
+	 */
+	val = REG_READ(addr, 0x01);
+	if (dsa_is_cpu_port(ds, p) || ds->dsa_port_mask & (1 << p))
+		REG_WRITE(addr, 0x01, val | 0x003e);
+	else
+		REG_WRITE(addr, 0x01, val | 0x0003);
+
+	/* Do not limit the period of time that this port can be
+	 * paused for by the remote end or the period of time that
+	 * this port can pause the remote end.
+	 */
+	REG_WRITE(addr, 0x02, 0x0000);
+
+	/* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
+	 * disable Header mode, enable IGMP/MLD snooping, disable VLAN
+	 * tunneling, determine priority by looking at 802.1p and IP
+	 * priority fields (IP prio has precedence), and set STP state
+	 * to Forwarding.
+	 *
+	 * If this is the CPU link, use DSA or EDSA tagging depending
+	 * on which tagging mode was configured.
+	 *
+	 * If this is a link to another switch, use DSA tagging mode.
+	 *
+	 * If this is the upstream port for this switch, enable
+	 * forwarding of unknown unicasts and multicasts.
+	 */
+	val = 0x0433;
+	if (dsa_is_cpu_port(ds, p)) {
+		if (ds->dst->tag_protocol == htons(ETH_P_EDSA))
+			val |= 0x3300;
+		else
+			val |= 0x0100;
+	}
+	if (ds->dsa_port_mask & (1 << p))
+		val |= 0x0100;
+	if (p == dsa_upstream_port(ds))
+		val |= 0x000c;
+	REG_WRITE(addr, 0x04, val);
+
+	/* Port Control 1: disable trunking.  Also, if this is the
+	 * CPU port, enable learn messages to be sent to this port.
+	 */
+	REG_WRITE(addr, 0x05, dsa_is_cpu_port(ds, p) ? 0x8000 : 0x0000);
+
+	/* Port based VLAN map: give each port its own address
+	 * database, allow the CPU port to talk to each of the 'real'
+	 * ports, and allow each of the 'real' ports to only talk to
+	 * the upstream port.
+	 */
+	val = (p & 0xf) << 12;
+	if (dsa_is_cpu_port(ds, p))
+		val |= ds->phys_port_mask;
+	else
+		val |= 1 << dsa_upstream_port(ds);
+	REG_WRITE(addr, 0x06, val);
+
+	/* Default VLAN ID and priority: don't set a default VLAN
+	 * ID, and set the default packet priority to zero.
+	 */
+	REG_WRITE(addr, 0x07, 0x0000);
+
+	/* Port Control 2: don't force a good FCS, set the maximum
+	 * frame size to 10240 bytes, don't let the switch add or
+	 * strip 802.1q tags, don't discard tagged or untagged frames
+	 * on this port, do a destination address lookup on all
+	 * received packets as usual, disable ARP mirroring and don't
+	 * send a copy of all transmitted/received frames on this port
+	 * to the CPU.
+	 */
+	REG_WRITE(addr, 0x08, 0x2080);
+
+	/* Egress rate control: disable egress rate control. */
+	REG_WRITE(addr, 0x09, 0x0001);
+
+	/* Egress rate control 2: disable egress rate control. */
+	REG_WRITE(addr, 0x0a, 0x0000);
+
+	/* Port Association Vector: when learning source addresses
+	 * of packets, add the address to the address database using
+	 * a port bitmap that has only the bit for this port set and
+	 * the other bits clear.
+	 */
+	REG_WRITE(addr, 0x0b, 1 << p);
+
+	/* Port ATU control: disable limiting the number of address
+	 * database entries that this port is allowed to use.
+	 */
+	REG_WRITE(addr, 0x0c, 0x0000);
+
+	/* Priority Override: disable DA, SA and VTU priority override. */
+	REG_WRITE(addr, 0x0d, 0x0000);
+
+	/* Port Ethertype: use the Ethertype DSA Ethertype value. */
+	REG_WRITE(addr, 0x0f, ETH_P_EDSA);
+
+	/* Tag Remap: use an identity 802.1p prio -> switch prio
+	 * mapping.
+	 */
+	REG_WRITE(addr, 0x18, 0x3210);
+
+	/* Tag Remap 2: use an identity 802.1p prio -> switch prio
+	 * mapping.
+	 */
+	REG_WRITE(addr, 0x19, 0x7654);
+
+	return 0;
+}
+
+static int mv88e6171_setup(struct dsa_switch *ds)
+{
+	struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
+	int i;
+	int ret;
+
+	mutex_init(&ps->smi_mutex);
+	mutex_init(&ps->stats_mutex);
+
+	ret = mv88e6171_switch_reset(ds);
+	if (ret < 0)
+		return ret;
+
+	/* @@@ initialise vtu and atu */
+
+	ret = mv88e6171_setup_global(ds);
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < 8; i++) {
+		if (!(dsa_is_cpu_port(ds, i) || ds->phys_port_mask & (1 << i)))
+			continue;
+
+		ret = mv88e6171_setup_port(ds, i);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int mv88e6171_port_to_phy_addr(int port)
+{
+	if (port >= 0 && port <= 4)
+		return port;
+	return -1;
+}
+
+static int
+mv88e6171_phy_read(struct dsa_switch *ds, int port, int regnum)
+{
+	int addr = mv88e6171_port_to_phy_addr(port);
+
+	return mv88e6xxx_phy_read(ds, addr, regnum);
+}
+
+static int
+mv88e6171_phy_write(struct dsa_switch *ds,
+		    int port, int regnum, u16 val)
+{
+	int addr = mv88e6171_port_to_phy_addr(port);
+
+	return mv88e6xxx_phy_write(ds, addr, regnum, val);
+}
+
+static struct mv88e6xxx_hw_stat mv88e6171_hw_stats[] = {
+	{ "in_good_octets", 8, 0x00, },
+	{ "in_bad_octets", 4, 0x02, },
+	{ "in_unicast", 4, 0x04, },
+	{ "in_broadcasts", 4, 0x06, },
+	{ "in_multicasts", 4, 0x07, },
+	{ "in_pause", 4, 0x16, },
+	{ "in_undersize", 4, 0x18, },
+	{ "in_fragments", 4, 0x19, },
+	{ "in_oversize", 4, 0x1a, },
+	{ "in_jabber", 4, 0x1b, },
+	{ "in_rx_error", 4, 0x1c, },
+	{ "in_fcs_error", 4, 0x1d, },
+	{ "out_octets", 8, 0x0e, },
+	{ "out_unicast", 4, 0x10, },
+	{ "out_broadcasts", 4, 0x13, },
+	{ "out_multicasts", 4, 0x12, },
+	{ "out_pause", 4, 0x15, },
+	{ "excessive", 4, 0x11, },
+	{ "collisions", 4, 0x1e, },
+	{ "deferred", 4, 0x05, },
+	{ "single", 4, 0x14, },
+	{ "multiple", 4, 0x17, },
+	{ "out_fcs_error", 4, 0x03, },
+	{ "late", 4, 0x1f, },
+	{ "hist_64bytes", 4, 0x08, },
+	{ "hist_65_127bytes", 4, 0x09, },
+	{ "hist_128_255bytes", 4, 0x0a, },
+	{ "hist_256_511bytes", 4, 0x0b, },
+	{ "hist_512_1023bytes", 4, 0x0c, },
+	{ "hist_1024_max_bytes", 4, 0x0d, },
+};
+
+static void
+mv88e6171_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
+{
+	mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6171_hw_stats),
+			      mv88e6171_hw_stats, port, data);
+}
+
+static void
+mv88e6171_get_ethtool_stats(struct dsa_switch *ds,
+			    int port, uint64_t *data)
+{
+	mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6171_hw_stats),
+				    mv88e6171_hw_stats, port, data);
+}
+
+static int mv88e6171_get_sset_count(struct dsa_switch *ds)
+{
+	return ARRAY_SIZE(mv88e6171_hw_stats);
+}
+
+struct dsa_switch_driver mv88e6171_switch_driver = {
+	.tag_protocol		= cpu_to_be16(ETH_P_EDSA),
+	.priv_size		= sizeof(struct mv88e6xxx_priv_state),
+	.probe			= mv88e6171_probe,
+	.setup			= mv88e6171_setup,
+	.set_addr		= mv88e6xxx_set_addr_indirect,
+	.phy_read		= mv88e6171_phy_read,
+	.phy_write		= mv88e6171_phy_write,
+	.poll_link		= mv88e6xxx_poll_link,
+	.get_strings		= mv88e6171_get_strings,
+	.get_ethtool_stats	= mv88e6171_get_ethtool_stats,
+	.get_sset_count		= mv88e6171_get_sset_count,
+};
+
+MODULE_ALIAS("platform:mv88e6171");
diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
index 9ce2146346b6..901d2a9704ef 100644
--- a/drivers/net/dsa/mv88e6xxx.c
+++ b/drivers/net/dsa/mv88e6xxx.c
@@ -501,12 +501,18 @@ static int __init mv88e6xxx_init(void)
 #if IS_ENABLED(CONFIG_NET_DSA_MV88E6123_61_65)
 	register_switch_driver(&mv88e6123_61_65_switch_driver);
 #endif
+#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171)
+	register_switch_driver(&mv88e6171_switch_driver);
+#endif
 	return 0;
 }
 module_init(mv88e6xxx_init);
 
 static void __exit mv88e6xxx_cleanup(void)
 {
+#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171)
+	unregister_switch_driver(&mv88e6171_switch_driver);
+#endif
 #if IS_ENABLED(CONFIG_NET_DSA_MV88E6123_61_65)
 	unregister_switch_driver(&mv88e6123_61_65_switch_driver);
 #endif
diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h
index 911ede58dd12..5e5145ad9525 100644
--- a/drivers/net/dsa/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx.h
@@ -70,6 +70,7 @@ void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
 
 extern struct dsa_switch_driver mv88e6131_switch_driver;
 extern struct dsa_switch_driver mv88e6123_61_65_switch_driver;
+extern struct dsa_switch_driver mv88e6171_switch_driver;
 
 #define REG_READ(addr, reg)						\
 	({								\
-- 
2.1.0

^ permalink raw reply related

* Re: [net-next PATCH v5 16/16] net: sched: drop ingress qdisc lock
From: Eric Dumazet @ 2014-09-13  1:33 UTC (permalink / raw)
  To: John Fastabend, Changli Gao
  Cc: xiyou.wangcong, davem, jhs, netdev, paulmck, brouer
In-Reply-To: <1410566188.7106.120.camel@edumazet-glaptop2.roam.corp.google.com>

On Fri, 2014-09-12 at 16:56 -0700, Eric Dumazet wrote:
> On Fri, 2014-09-12 at 09:35 -0700, John Fastabend wrote:
> > After the previous patches to make the filters RCU safe and
> > support per cpu counters we can drop the qdisc lock around
> > the ingress qdisc hook.
> > 
> > This is possible because the ingress qdisc is a very basic
> > qdisc and only updates stats and runs tc_classify. Its the
> > simplest qdiscs we have.
> > 
> > In order for the per-cpu counters to get invoked the
> > ingress qdisc must set the LLQDISC flag. We could use per-cpu
> > counters on all counters but it is only necessary when the
> > qdisc lock is not held.
> > 
> > Signed-off-by: John Fastabend <john.r.fastabend@intel.com>
> > ---
> 
> Acked-by: Eric Dumazet <edumazet@google.com>
> 
> 

BTW, it might be time to revive the multiqueue patch for IFB,
Changli Gao worked on this 5 years ago, but work was not completed.

^ permalink raw reply

* [PATCH] netdevice: Support DSA tagging when DSA is built as a module
From: Alexander Duyck @ 2014-09-13  1:59 UTC (permalink / raw)
  To: netdev; +Cc: f.fainelli, davem, kernel

This change corrects an error seen when DSA tagging is built as a module.
Without this change it is not possible to get XDSA tagged frames as the
test for tagging is stripped by the #ifdef check.

Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
---
 include/linux/netdevice.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index ba72f6b..c8f3806 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1789,7 +1789,7 @@ void dev_net_set(struct net_device *dev, struct net *net)
 
 static inline bool netdev_uses_dsa(struct net_device *dev)
 {
-#ifdef CONFIG_NET_DSA
+#if IS_ENABLED(CONFIG_NET_DSA)
 	if (dev->dsa_ptr != NULL)
 		return dsa_uses_tagged_protocol(dev->dsa_ptr);
 #endif

^ permalink raw reply related

* [net-next v6 0/4] Refactor vxlan and l2tp to use common UDP tunnel APIs
From: Andy Zhou @ 2014-09-13  2:21 UTC (permalink / raw)
  To: davem; +Cc: netdev, Andy Zhou

Andy Zhou (4):
  udp_tunnel: Seperate ipv6 functions into its own file.
  udp-tunnel: Expand UDP tunnel APIs
  vxlan: Refactor vxlan driver to make use of the common UDP tunnel
    functions.
  l2tp: Refactor l2tp core driver to make use of the common UDP tunnel
    functions

 drivers/net/vxlan.c       |  106 ++++++++--------------------------
 include/net/udp_tunnel.h  |   84 ++++++++++++++++++++++++++-
 net/ipv4/udp_tunnel.c     |  138 ++++++++++++++++++++++++---------------------
 net/ipv6/Makefile         |    1 +
 net/ipv6/ip6_udp_tunnel.c |  105 ++++++++++++++++++++++++++++++++++
 net/l2tp/l2tp_core.c      |   25 ++++----
 6 files changed, 295 insertions(+), 164 deletions(-)
 create mode 100644 net/ipv6/ip6_udp_tunnel.c

-- 
1.7.9.5

^ permalink raw reply

* [net-next v6 1/4] udp_tunnel: Seperate ipv6 functions into its own file.
From: Andy Zhou @ 2014-09-13  2:21 UTC (permalink / raw)
  To: davem; +Cc: netdev, Andy Zhou
In-Reply-To: <1410574920-16486-1-git-send-email-azhou@nicira.com>

Add ip6_udp_tunnel.c for ipv6 UDP tunnel functions to avoid ifdefs
in udp_tunnel.c

Signed-off-by: Andy Zhou <azhou@nicira.com>
---
 include/net/udp_tunnel.h  |   28 +++++++++-
 net/ipv4/udp_tunnel.c     |  136 ++++++++++++++++++++++++---------------------
 net/ipv6/Makefile         |    1 +
 net/ipv6/ip6_udp_tunnel.c |   63 +++++++++++++++++++++
 4 files changed, 162 insertions(+), 66 deletions(-)
 create mode 100644 net/ipv6/ip6_udp_tunnel.c

diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h
index ffd69cb..0b9e017 100644
--- a/include/net/udp_tunnel.h
+++ b/include/net/udp_tunnel.h
@@ -26,7 +26,31 @@ struct udp_port_cfg {
 				use_udp6_rx_checksums:1;
 };
 
-int udp_sock_create(struct net *net, struct udp_port_cfg *cfg,
-		    struct socket **sockp);
+int udp_sock_create4(struct net *net, struct udp_port_cfg *cfg,
+		     struct socket **sockp);
+
+#if IS_ENABLED(CONFIG_IPV6)
+int udp_sock_create6(struct net *net, struct udp_port_cfg *cfg,
+		     struct socket **sockp);
+#else
+static inline int udp_sock_create6(struct net *net, struct udp_port_cfg *cfg,
+				   struct socket **sockp)
+{
+	return 0;
+}
+#endif
+
+static inline int udp_sock_create(struct net *net,
+				  struct udp_port_cfg *cfg,
+				  struct socket **sockp)
+{
+	if (cfg->family == AF_INET)
+		return udp_sock_create4(net, cfg, sockp);
+
+	if (cfg->family == AF_INET6)
+		return udp_sock_create6(net, cfg, sockp);
+
+	return -EPFNOSUPPORT;
+}
 
 #endif
diff --git a/net/ipv4/udp_tunnel.c b/net/ipv4/udp_tunnel.c
index 61ec1a6..93ef133 100644
--- a/net/ipv4/udp_tunnel.c
+++ b/net/ipv4/udp_tunnel.c
@@ -8,83 +8,40 @@
 #include <net/udp_tunnel.h>
 #include <net/net_namespace.h>
 
-int udp_sock_create(struct net *net, struct udp_port_cfg *cfg,
-		    struct socket **sockp)
+int udp_sock_create4(struct net *net, struct udp_port_cfg *cfg,
+		     struct socket **sockp)
 {
 	int err = -EINVAL;
 	struct socket *sock = NULL;
+	struct sockaddr_in udp_addr;
 
-#if IS_ENABLED(CONFIG_IPV6)
-	if (cfg->family == AF_INET6) {
-		struct sockaddr_in6 udp6_addr;
+	err = sock_create_kern(AF_INET, SOCK_DGRAM, 0, &sock);
+	if (err < 0)
+		goto error;
 
-		err = sock_create_kern(AF_INET6, SOCK_DGRAM, 0, &sock);
-		if (err < 0)
-			goto error;
-
-		sk_change_net(sock->sk, net);
-
-		udp6_addr.sin6_family = AF_INET6;
-		memcpy(&udp6_addr.sin6_addr, &cfg->local_ip6,
-		       sizeof(udp6_addr.sin6_addr));
-		udp6_addr.sin6_port = cfg->local_udp_port;
-		err = kernel_bind(sock, (struct sockaddr *)&udp6_addr,
-				  sizeof(udp6_addr));
-		if (err < 0)
-			goto error;
-
-		if (cfg->peer_udp_port) {
-			udp6_addr.sin6_family = AF_INET6;
-			memcpy(&udp6_addr.sin6_addr, &cfg->peer_ip6,
-			       sizeof(udp6_addr.sin6_addr));
-			udp6_addr.sin6_port = cfg->peer_udp_port;
-			err = kernel_connect(sock,
-					     (struct sockaddr *)&udp6_addr,
-					     sizeof(udp6_addr), 0);
-		}
-		if (err < 0)
-			goto error;
+	sk_change_net(sock->sk, net);
 
-		udp_set_no_check6_tx(sock->sk, !cfg->use_udp6_tx_checksums);
-		udp_set_no_check6_rx(sock->sk, !cfg->use_udp6_rx_checksums);
-	} else
-#endif
-	if (cfg->family == AF_INET) {
-		struct sockaddr_in udp_addr;
-
-		err = sock_create_kern(AF_INET, SOCK_DGRAM, 0, &sock);
-		if (err < 0)
-			goto error;
-
-		sk_change_net(sock->sk, net);
+	udp_addr.sin_family = AF_INET;
+	udp_addr.sin_addr = cfg->local_ip;
+	udp_addr.sin_port = cfg->local_udp_port;
+	err = kernel_bind(sock, (struct sockaddr *)&udp_addr,
+			  sizeof(udp_addr));
+	if (err < 0)
+		goto error;
 
+	if (cfg->peer_udp_port) {
 		udp_addr.sin_family = AF_INET;
-		udp_addr.sin_addr = cfg->local_ip;
-		udp_addr.sin_port = cfg->local_udp_port;
-		err = kernel_bind(sock, (struct sockaddr *)&udp_addr,
-				  sizeof(udp_addr));
+		udp_addr.sin_addr = cfg->peer_ip;
+		udp_addr.sin_port = cfg->peer_udp_port;
+		err = kernel_connect(sock, (struct sockaddr *)&udp_addr,
+				     sizeof(udp_addr), 0);
 		if (err < 0)
 			goto error;
-
-		if (cfg->peer_udp_port) {
-			udp_addr.sin_family = AF_INET;
-			udp_addr.sin_addr = cfg->peer_ip;
-			udp_addr.sin_port = cfg->peer_udp_port;
-			err = kernel_connect(sock,
-					     (struct sockaddr *)&udp_addr,
-					     sizeof(udp_addr), 0);
-			if (err < 0)
-				goto error;
-		}
-
-		sock->sk->sk_no_check_tx = !cfg->use_udp_checksums;
-	} else {
-		return -EPFNOSUPPORT;
 	}
 
+	sock->sk->sk_no_check_tx = !cfg->use_udp_checksums;
 
 	*sockp = sock;
-
 	return 0;
 
 error:
@@ -95,6 +52,57 @@ error:
 	*sockp = NULL;
 	return err;
 }
-EXPORT_SYMBOL(udp_sock_create);
+EXPORT_SYMBOL(udp_sock_create4);
+
+void setup_udp_tunnel_sock(struct net *net, struct udp_tunnel_sock_cfg *cfg)
+{
+	struct socket *sock = cfg->sock;
+	struct sock *sk = sock->sk;
+
+	/* Disable multicast loopback */
+	inet_sk(sk)->mc_loop = 0;
+
+	/* Enable CHECKSUM_UNNECESSAY to CHECSUM_COMPLETE conversion */
+	udp_set_convert_csum(sk, true);
+
+	rcu_assign_sk_user_data(sk, cfg->sk_user_data);
+
+	udp_sk(sk)->encap_type = cfg->encap_type;
+	udp_sk(sk)->encap_rcv = cfg->encap_rcv;
+	udp_sk(sk)->encap_destroy = cfg->encap_destroy;
+
+	udp_tunnel_encap_enable(sock);
+}
+EXPORT_SYMBOL_GPL(setup_udp_tunnel_sock);
+
+int udp_tunnel_xmit_skb(struct socket *sock, struct rtable *rt,
+			struct sk_buff *skb, __be32 src, __be32 dst,
+			__u8 tos, __u8 ttl, __be16 df, __be16 src_port,
+			__be16 dst_port, bool xnet)
+{
+	struct udphdr *uh;
+
+	__skb_push(skb, sizeof(*uh));
+	skb_reset_transport_header(skb);
+	uh = udp_hdr(skb);
+
+	uh->dest = dst_port;
+	uh->source = src_port;
+	uh->len = htons(skb->len);
+
+	udp_set_csum(sock->sk->sk_no_check_tx, skb, src, dst, skb->len);
+
+	return iptunnel_xmit(sock->sk, rt, skb, src, dst, IPPROTO_UDP,
+			     tos, ttl, df, xnet);
+}
+EXPORT_SYMBOL_GPL(udp_tunnel_xmit_skb);
+
+void udp_tunnel_sock_release(struct socket *sock)
+{
+	rcu_assign_sk_user_data(sock->sk, NULL);
+	kernel_sock_shutdown(sock, SHUT_RDWR);
+	sk_release_kernel(sock->sk);
+}
+EXPORT_SYMBOL_GPL(udp_tunnel_sock_release);
 
 MODULE_LICENSE("GPL");
diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile
index 2fe6836..45f830e 100644
--- a/net/ipv6/Makefile
+++ b/net/ipv6/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION) += xfrm6_mode_ro.o
 obj-$(CONFIG_INET6_XFRM_MODE_BEET) += xfrm6_mode_beet.o
 obj-$(CONFIG_IPV6_MIP6) += mip6.o
 obj-$(CONFIG_NETFILTER)	+= netfilter/
+obj-$(CONFIG_NET_UDP_TUNNEL) += ip6_udp_tunnel.o
 
 obj-$(CONFIG_IPV6_VTI) += ip6_vti.o
 obj-$(CONFIG_IPV6_SIT) += sit.o
diff --git a/net/ipv6/ip6_udp_tunnel.c b/net/ipv6/ip6_udp_tunnel.c
new file mode 100644
index 0000000..bcfbb4b
--- /dev/null
+++ b/net/ipv6/ip6_udp_tunnel.c
@@ -0,0 +1,63 @@
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <linux/udp.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/in6.h>
+#include <net/udp.h>
+#include <net/udp_tunnel.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
+#include <net/ip6_tunnel.h>
+#include <net/ip6_checksum.h>
+
+int udp_sock_create6(struct net *net, struct udp_port_cfg *cfg,
+		     struct socket **sockp)
+{
+	struct sockaddr_in6 udp6_addr;
+	int err;
+	struct socket *sock = NULL;
+
+	err = sock_create_kern(AF_INET6, SOCK_DGRAM, 0, &sock);
+	if (err < 0)
+		goto error;
+
+	sk_change_net(sock->sk, net);
+
+	udp6_addr.sin6_family = AF_INET6;
+	memcpy(&udp6_addr.sin6_addr, &cfg->local_ip6,
+	       sizeof(udp6_addr.sin6_addr));
+	udp6_addr.sin6_port = cfg->local_udp_port;
+	err = kernel_bind(sock, (struct sockaddr *)&udp6_addr,
+			  sizeof(udp6_addr));
+	if (err < 0)
+		goto error;
+
+	if (cfg->peer_udp_port) {
+		udp6_addr.sin6_family = AF_INET6;
+		memcpy(&udp6_addr.sin6_addr, &cfg->peer_ip6,
+		       sizeof(udp6_addr.sin6_addr));
+		udp6_addr.sin6_port = cfg->peer_udp_port;
+		err = kernel_connect(sock,
+				     (struct sockaddr *)&udp6_addr,
+				     sizeof(udp6_addr), 0);
+	}
+	if (err < 0)
+		goto error;
+
+	udp_set_no_check6_tx(sock->sk, !cfg->use_udp6_tx_checksums);
+	udp_set_no_check6_rx(sock->sk, !cfg->use_udp6_rx_checksums);
+
+	*sockp = sock;
+	return 0;
+
+error:
+	if (sock) {
+		kernel_sock_shutdown(sock, SHUT_RDWR);
+		sk_release_kernel(sock->sk);
+	}
+	*sockp = NULL;
+	return err;
+}
+EXPORT_SYMBOL_GPL(udp_sock_create6);
-- 
1.7.9.5

^ permalink raw reply related

* [net-next v6 2/4] udp-tunnel: Expand UDP tunnel APIs
From: Andy Zhou @ 2014-09-13  2:21 UTC (permalink / raw)
  To: davem; +Cc: netdev, Andy Zhou
In-Reply-To: <1410574920-16486-1-git-send-email-azhou@nicira.com>

Added common udp tunnel socket creation, and packet transmission APIs
API that can be used by other UDP based tunneling protocol
implementation.

Signed-off-by: Andy Zhou <azhou@nicira.com>
---
 include/net/udp_tunnel.h  |   56 +++++++++++++++++++++++++++++++++++++++++++++
 net/ipv4/udp_tunnel.c     |    4 ++--
 net/ipv6/ip6_udp_tunnel.c |   42 ++++++++++++++++++++++++++++++++++
 3 files changed, 100 insertions(+), 2 deletions(-)

diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h
index 0b9e017..9c58c23 100644
--- a/include/net/udp_tunnel.h
+++ b/include/net/udp_tunnel.h
@@ -1,6 +1,14 @@
 #ifndef __NET_UDP_TUNNEL_H
 #define __NET_UDP_TUNNEL_H
 
+#include <net/ip_tunnels.h>
+#include <net/udp.h>
+
+#if IS_ENABLED(CONFIG_IPV6)
+#include <net/ipv6.h>
+#include <net/addrconf.h>
+#endif
+
 struct udp_port_cfg {
 	u8			family;
 
@@ -53,4 +61,52 @@ static inline int udp_sock_create(struct net *net,
 	return -EPFNOSUPPORT;
 }
 
+typedef int (*udp_tunnel_encap_rcv_t)(struct sock *sk, struct sk_buff *skb);
+typedef void (*udp_tunnel_encap_destroy_t)(struct sock *sk);
+
+struct udp_tunnel_sock_cfg {
+	struct socket *sock;	/* The socket UDP tunnel will attach to */
+	void *sk_user_data;     /* user data used by encap_rcv call back */
+	/* Used for setting up udp_sock fields, see udp.h for details */
+	__u8  encap_type;
+	udp_tunnel_encap_rcv_t encap_rcv;
+	udp_tunnel_encap_destroy_t encap_destroy;
+};
+
+void setup_udp_tunnel_sock(struct net *net,
+			   struct udp_tunnel_sock_cfg *sock_cfg);
+
+int udp_tunnel_xmit_skb(struct socket *sock, struct rtable *rt,
+			struct sk_buff *skb, __be32 src, __be32 dst,
+			__u8 tos, __u8 ttl, __be16 df, __be16 src_port,
+			__be16 dst_port, bool xnet);
+
+#if IS_ENABLED(CONFIG_IPV6)
+int udp_tunnel6_xmit_skb(struct socket *sock, struct dst_entry *dst,
+			 struct sk_buff *skb, struct net_device *dev,
+			 struct in6_addr *saddr, struct in6_addr *daddr,
+			 __u8 prio, __u8 ttl, __be16 src_port,
+			 __be16 dst_port);
+#endif
+
+void udp_tunnel_sock_release(struct socket *sock);
+
+static inline struct sk_buff *udp_tunnel_handle_offloads(struct sk_buff *skb,
+							 bool udp_csum)
+{
+	int type = udp_csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL;
+
+	return iptunnel_handle_offloads(skb, udp_csum, type);
+}
+
+static inline void udp_tunnel_encap_enable(struct socket *sock)
+{
+#if IS_ENABLED(CONFIG_IPV6)
+	if (sock->sk->sk_family == PF_INET6)
+		ipv6_stub->udpv6_encap_enable();
+	else
+#endif
+		udp_encap_enable();
+}
+
 #endif
diff --git a/net/ipv4/udp_tunnel.c b/net/ipv4/udp_tunnel.c
index 93ef133..db73ce8 100644
--- a/net/ipv4/udp_tunnel.c
+++ b/net/ipv4/udp_tunnel.c
@@ -11,7 +11,7 @@
 int udp_sock_create4(struct net *net, struct udp_port_cfg *cfg,
 		     struct socket **sockp)
 {
-	int err = -EINVAL;
+	int err;
 	struct socket *sock = NULL;
 	struct sockaddr_in udp_addr;
 
@@ -62,7 +62,7 @@ void setup_udp_tunnel_sock(struct net *net, struct udp_tunnel_sock_cfg *cfg)
 	/* Disable multicast loopback */
 	inet_sk(sk)->mc_loop = 0;
 
-	/* Enable CHECKSUM_UNNECESSAY to CHECSUM_COMPLETE conversion */
+	/* Enable CHECKSUM_UNNECESSARY to CHECKSUM_COMPLETE conversion */
 	udp_set_convert_csum(sk, true);
 
 	rcu_assign_sk_user_data(sk, cfg->sk_user_data);
diff --git a/net/ipv6/ip6_udp_tunnel.c b/net/ipv6/ip6_udp_tunnel.c
index bcfbb4b..cbc9907 100644
--- a/net/ipv6/ip6_udp_tunnel.c
+++ b/net/ipv6/ip6_udp_tunnel.c
@@ -61,3 +61,45 @@ error:
 	return err;
 }
 EXPORT_SYMBOL_GPL(udp_sock_create6);
+
+int udp_tunnel6_xmit_skb(struct socket *sock, struct dst_entry *dst,
+			 struct sk_buff *skb, struct net_device *dev,
+			 struct in6_addr *saddr, struct in6_addr *daddr,
+			 __u8 prio, __u8 ttl, __be16 src_port, __be16 dst_port)
+{
+	struct udphdr *uh;
+	struct ipv6hdr *ip6h;
+	struct sock *sk = sock->sk;
+
+	__skb_push(skb, sizeof(*uh));
+	skb_reset_transport_header(skb);
+	uh = udp_hdr(skb);
+
+	uh->dest = dst_port;
+	uh->source = src_port;
+
+	uh->len = htons(skb->len);
+	uh->check = 0;
+
+	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
+	IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED
+			    | IPSKB_REROUTED);
+	skb_dst_set(skb, dst);
+
+	udp6_set_csum(udp_get_no_check6_tx(sk), skb, &inet6_sk(sk)->saddr,
+		      &sk->sk_v6_daddr, skb->len);
+
+	__skb_push(skb, sizeof(*ip6h));
+	skb_reset_network_header(skb);
+	ip6h		  = ipv6_hdr(skb);
+	ip6_flow_hdr(ip6h, prio, htonl(0));
+	ip6h->payload_len = htons(skb->len);
+	ip6h->nexthdr     = IPPROTO_UDP;
+	ip6h->hop_limit   = ttl;
+	ip6h->daddr	  = *daddr;
+	ip6h->saddr	  = *saddr;
+
+	ip6tunnel_xmit(skb, dev);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(udp_tunnel6_xmit_skb);
-- 
1.7.9.5

^ permalink raw reply related

* [net-next v6 3/4] vxlan: Refactor vxlan driver to make use of the common UDP tunnel functions.
From: Andy Zhou @ 2014-09-13  2:21 UTC (permalink / raw)
  To: davem; +Cc: netdev, Andy Zhou
In-Reply-To: <1410574920-16486-1-git-send-email-azhou@nicira.com>

Signed-off-by: Andy Zhou <azhou@nicira.com>
---
 drivers/net/vxlan.c |  106 +++++++++++----------------------------------------
 1 file changed, 23 insertions(+), 83 deletions(-)

diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c
index 53c3ec1..bb56103 100644
--- a/drivers/net/vxlan.c
+++ b/drivers/net/vxlan.c
@@ -42,6 +42,7 @@
 #include <net/netns/generic.h>
 #include <net/vxlan.h>
 #include <net/protocol.h>
+#include <net/udp_tunnel.h>
 #if IS_ENABLED(CONFIG_IPV6)
 #include <net/ipv6.h>
 #include <net/addrconf.h>
@@ -1062,7 +1063,6 @@ void vxlan_sock_release(struct vxlan_sock *vs)
 
 	spin_lock(&vn->sock_lock);
 	hlist_del_rcu(&vs->hlist);
-	rcu_assign_sk_user_data(vs->sock->sk, NULL);
 	vxlan_notify_del_rx_port(vs);
 	spin_unlock(&vn->sock_lock);
 
@@ -1336,7 +1336,6 @@ out:
 }
 
 #if IS_ENABLED(CONFIG_IPV6)
-
 static struct sk_buff *vxlan_na_create(struct sk_buff *request,
 	struct neighbour *n, bool isrouter)
 {
@@ -1570,13 +1569,6 @@ static bool route_shortcircuit(struct net_device *dev, struct sk_buff *skb)
 	return false;
 }
 
-static inline struct sk_buff *vxlan_handle_offloads(struct sk_buff *skb,
-						    bool udp_csum)
-{
-	int type = udp_csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL;
-	return iptunnel_handle_offloads(skb, udp_csum, type);
-}
-
 #if IS_ENABLED(CONFIG_IPV6)
 static int vxlan6_xmit_skb(struct vxlan_sock *vs,
 			   struct dst_entry *dst, struct sk_buff *skb,
@@ -1585,13 +1577,12 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs,
 			   __be16 src_port, __be16 dst_port, __be32 vni,
 			   bool xnet)
 {
-	struct ipv6hdr *ip6h;
 	struct vxlanhdr *vxh;
-	struct udphdr *uh;
 	int min_headroom;
 	int err;
+	bool udp_sum = !udp_get_no_check6_tx(vs->sock->sk);
 
-	skb = vxlan_handle_offloads(skb, !udp_get_no_check6_tx(vs->sock->sk));
+	skb = udp_tunnel_handle_offloads(skb, udp_sum);
 	if (IS_ERR(skb))
 		return -EINVAL;
 
@@ -1619,38 +1610,8 @@ static int vxlan6_xmit_skb(struct vxlan_sock *vs,
 	vxh->vx_flags = htonl(VXLAN_FLAGS);
 	vxh->vx_vni = vni;
 
-	__skb_push(skb, sizeof(*uh));
-	skb_reset_transport_header(skb);
-	uh = udp_hdr(skb);
-
-	uh->dest = dst_port;
-	uh->source = src_port;
-
-	uh->len = htons(skb->len);
-
-	memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
-	IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
-			      IPSKB_REROUTED);
-	skb_dst_set(skb, dst);
-
-	udp6_set_csum(udp_get_no_check6_tx(vs->sock->sk), skb,
-		      saddr, daddr, skb->len);
-
-	__skb_push(skb, sizeof(*ip6h));
-	skb_reset_network_header(skb);
-	ip6h		  = ipv6_hdr(skb);
-	ip6h->version	  = 6;
-	ip6h->priority	  = prio;
-	ip6h->flow_lbl[0] = 0;
-	ip6h->flow_lbl[1] = 0;
-	ip6h->flow_lbl[2] = 0;
-	ip6h->payload_len = htons(skb->len);
-	ip6h->nexthdr     = IPPROTO_UDP;
-	ip6h->hop_limit   = ttl;
-	ip6h->daddr	  = *daddr;
-	ip6h->saddr	  = *saddr;
-
-	ip6tunnel_xmit(skb, dev);
+	udp_tunnel6_xmit_skb(vs->sock, dst, skb, dev, saddr, daddr, prio,
+			     ttl, src_port, dst_port);
 	return 0;
 }
 #endif
@@ -1661,11 +1622,11 @@ int vxlan_xmit_skb(struct vxlan_sock *vs,
 		   __be16 src_port, __be16 dst_port, __be32 vni, bool xnet)
 {
 	struct vxlanhdr *vxh;
-	struct udphdr *uh;
 	int min_headroom;
 	int err;
+	bool udp_sum = !vs->sock->sk->sk_no_check_tx;
 
-	skb = vxlan_handle_offloads(skb, !vs->sock->sk->sk_no_check_tx);
+	skb = udp_tunnel_handle_offloads(skb, udp_sum);
 	if (IS_ERR(skb))
 		return -EINVAL;
 
@@ -1691,20 +1652,8 @@ int vxlan_xmit_skb(struct vxlan_sock *vs,
 	vxh->vx_flags = htonl(VXLAN_FLAGS);
 	vxh->vx_vni = vni;
 
-	__skb_push(skb, sizeof(*uh));
-	skb_reset_transport_header(skb);
-	uh = udp_hdr(skb);
-
-	uh->dest = dst_port;
-	uh->source = src_port;
-
-	uh->len = htons(skb->len);
-
-	udp_set_csum(vs->sock->sk->sk_no_check_tx, skb,
-		     src, dst, skb->len);
-
-	return iptunnel_xmit(vs->sock->sk, rt, skb, src, dst, IPPROTO_UDP,
-			     tos, ttl, df, xnet);
+	return udp_tunnel_xmit_skb(vs->sock, rt, skb, src, dst, tos,
+				   ttl, df, src_port, dst_port, xnet);
 }
 EXPORT_SYMBOL_GPL(vxlan_xmit_skb);
 
@@ -1829,11 +1778,11 @@ static void vxlan_xmit_one(struct sk_buff *skb, struct net_device *dev,
 		tos = ip_tunnel_ecn_encap(tos, old_iph, skb);
 		ttl = ttl ? : ip4_dst_hoplimit(&rt->dst);
 
-		err = vxlan_xmit_skb(vxlan->vn_sock, rt, skb,
-				     fl4.saddr, dst->sin.sin_addr.s_addr,
-				     tos, ttl, df, src_port, dst_port,
-				     htonl(vni << 8),
-				     !net_eq(vxlan->net, dev_net(vxlan->dev)));
+		err = udp_tunnel_xmit_skb(vxlan->vn_sock->sock, rt, skb,
+					  fl4.saddr, dst->sin.sin_addr.s_addr,
+					  tos, ttl, df, src_port, dst_port,
+					  !net_eq(vxlan->net,
+						  dev_net(vxlan->dev)));
 
 		if (err < 0)
 			goto rt_tx_error;
@@ -2333,8 +2282,7 @@ static const struct ethtool_ops vxlan_ethtool_ops = {
 static void vxlan_del_work(struct work_struct *work)
 {
 	struct vxlan_sock *vs = container_of(work, struct vxlan_sock, del_work);
-
-	sk_release_kernel(vs->sock->sk);
+	udp_tunnel_sock_release(vs->sock);
 	kfree_rcu(vs, rcu);
 }
 
@@ -2367,11 +2315,6 @@ static struct socket *vxlan_create_sock(struct net *net, bool ipv6,
 	if (err < 0)
 		return ERR_PTR(err);
 
-	/* Disable multicast loopback */
-	inet_sk(sock->sk)->mc_loop = 0;
-
-	udp_set_convert_csum(sock->sk, true);
-
 	return sock;
 }
 
@@ -2383,9 +2326,9 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,
 	struct vxlan_net *vn = net_generic(net, vxlan_net_id);
 	struct vxlan_sock *vs;
 	struct socket *sock;
-	struct sock *sk;
 	unsigned int h;
 	bool ipv6 = !!(flags & VXLAN_F_IPV6);
+	struct udp_tunnel_sock_cfg tunnel_cfg;
 
 	vs = kzalloc(sizeof(*vs), GFP_KERNEL);
 	if (!vs)
@@ -2403,11 +2346,9 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,
 	}
 
 	vs->sock = sock;
-	sk = sock->sk;
 	atomic_set(&vs->refcnt, 1);
 	vs->rcv = rcv;
 	vs->data = data;
-	rcu_assign_sk_user_data(vs->sock->sk, vs);
 
 	/* Initialize the vxlan udp offloads structure */
 	vs->udp_offloads.port = port;
@@ -2420,14 +2361,13 @@ static struct vxlan_sock *vxlan_socket_create(struct net *net, __be16 port,
 	spin_unlock(&vn->sock_lock);
 
 	/* Mark socket as an encapsulation socket. */
-	udp_sk(sk)->encap_type = 1;
-	udp_sk(sk)->encap_rcv = vxlan_udp_encap_recv;
-#if IS_ENABLED(CONFIG_IPV6)
-	if (ipv6)
-		ipv6_stub->udpv6_encap_enable();
-	else
-#endif
-		udp_encap_enable();
+	tunnel_cfg.sock = sock;
+	tunnel_cfg.sk_user_data = vs;
+	tunnel_cfg.encap_type = 1;
+	tunnel_cfg.encap_rcv = vxlan_udp_encap_recv;
+	tunnel_cfg.encap_destroy = NULL;
+
+	setup_udp_tunnel_sock(net, &tunnel_cfg);
 
 	return vs;
 }
-- 
1.7.9.5

^ permalink raw reply related

* [net-next v6 4/4] l2tp: Refactor l2tp core driver to make use of the common UDP tunnel functions
From: Andy Zhou @ 2014-09-13  2:22 UTC (permalink / raw)
  To: davem; +Cc: netdev, Andy Zhou
In-Reply-To: <1410574920-16486-1-git-send-email-azhou@nicira.com>

Signed-off-by: Andy Zhou <azhou@nicira.com>
---
 net/l2tp/l2tp_core.c |   25 +++++++++++--------------
 1 file changed, 11 insertions(+), 14 deletions(-)

diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 2aa2b6c..c31b3d3 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -1392,8 +1392,6 @@ static int l2tp_tunnel_sock_create(struct net *net,
 		if (err < 0)
 			goto out;
 
-		udp_set_convert_csum(sock->sk, true);
-
 		break;
 
 	case L2TP_ENCAPTYPE_IP:
@@ -1584,19 +1582,18 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
 	/* Mark socket as an encapsulation socket. See net/ipv4/udp.c */
 	tunnel->encap = encap;
 	if (encap == L2TP_ENCAPTYPE_UDP) {
-		/* Mark socket as an encapsulation socket. See net/ipv4/udp.c */
-		udp_sk(sk)->encap_type = UDP_ENCAP_L2TPINUDP;
-		udp_sk(sk)->encap_rcv = l2tp_udp_encap_recv;
-		udp_sk(sk)->encap_destroy = l2tp_udp_encap_destroy;
-#if IS_ENABLED(CONFIG_IPV6)
-		if (sk->sk_family == PF_INET6 && !tunnel->v4mapped)
-			udpv6_encap_enable();
-		else
-#endif
-		udp_encap_enable();
-	}
+		struct udp_tunnel_sock_cfg udp_cfg;
+
+		udp_cfg.sock = sock;
+		udp_cfg.sk_user_data = tunnel;
+		udp_cfg.encap_type = UDP_ENCAP_L2TPINUDP;
+		udp_cfg.encap_rcv = l2tp_udp_encap_recv;
+		udp_cfg.encap_destroy = l2tp_udp_encap_destroy;
 
-	sk->sk_user_data = tunnel;
+		setup_udp_tunnel_sock(net, &udp_cfg);
+	} else {
+		sk->sk_user_data = tunnel;
+	}
 
 	/* Hook on the tunnel socket destructor so that we can cleanup
 	 * if the tunnel socket goes away.
-- 
1.7.9.5

^ permalink raw reply related

* Re: [net-next PATCH v5 00/16] net/sched rcu filters
From: David Miller @ 2014-09-13  2:36 UTC (permalink / raw)
  To: john.fastabend; +Cc: xiyou.wangcong, eric.dumazet, jhs, netdev, paulmck, brouer
In-Reply-To: <20140912162748.19588.39677.stgit@nitbit.x32>


John you're getting really close with this series.

Why don't you post everything up to the tc actions stuff,
which Eric has ACK'd already, and I can put that all into
the net-next tree right now.

Then you can concentrate on the bits that remain without
having to post the other stuff over and over again.

Thanks.

^ permalink raw reply

* Re: [PATCH 1/4] Update firmware for rtl8192ce
From: Kyle McMartin @ 2014-09-13  2:37 UTC (permalink / raw)
  To: Larry Finger
  Cc: linux-firmware-DgEjT+Ai2ygdnm+yROfE0A,
	linux-wireless-u79uwXL29TY76Z2rM5mHXA,
	netdev-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1409673648-20328-1-git-send-email-Larry.Finger-tQ5ms3gMjBLk1uMJSBkQmQ@public.gmane.org>

On Tue, Sep 02, 2014 at 11:00:45AM -0500, Larry Finger wrote:
> The latest driver, labelled rtlwifi_linux_mac80211_0019.0320.2014V628,
> has updated firmware for the RTL8192CE.
> 
> Signed-off-by: Larry Finger <Larry.Finger-tQ5ms3gMjBLk1uMJSBkQmQ@public.gmane.org>
> ---
>  WHENCE                    |   1 +
>  rtlwifi/rtl8192cfw.bin    | Bin 13540 -> 16192 bytes
>  rtlwifi/rtl8192cfwU_B.bin | Bin 14800 -> 16332 bytes
>  3 files changed, 1 insertion(+)
> 

i've applied all 4. thanks larry.

--kyle
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox