Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
authorDavid S. Miller <davem@davemloft.net>
Mon, 30 Oct 2017 05:10:01 +0000 (14:10 +0900)
committerDavid S. Miller <davem@davemloft.net>
Mon, 30 Oct 2017 12:09:24 +0000 (21:09 +0900)
Several conflicts here.

NFP driver bug fix adding nfp_netdev_is_nfp_repr() check to
nfp_fl_output() needed some adjustments because the code block is in
an else block now.

Parallel additions to net/pkt_cls.h and net/sch_generic.h

A bug fix in __tcp_retransmit_skb() conflicted with some of
the rbtree changes in net-next.

The tc action RCU callback fixes in 'net' had some overlap with some
of the recent tcf_block reworking.

Signed-off-by: David S. Miller <davem@davemloft.net>
47 files changed:
1  2 
MAINTAINERS
drivers/net/ethernet/intel/i40e/i40e_txrx.c
drivers/net/ethernet/intel/igb/igb_main.c
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
drivers/net/ethernet/marvell/mvpp2.c
drivers/net/ethernet/mellanox/mlx5/core/health.c
drivers/net/ethernet/netronome/nfp/flower/action.c
drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
drivers/net/macvtap.c
drivers/net/tun.c
include/linux/if_tap.h
include/net/fq_impl.h
include/net/inet_sock.h
include/net/pkt_cls.h
include/net/sch_generic.h
include/net/sctp/sm.h
include/net/tcp.h
include/uapi/linux/bpf.h
include/uapi/linux/sctp.h
kernel/bpf/sockmap.c
net/core/filter.c
net/dsa/dsa2.c
net/ipv4/inet_connection_sock.c
net/ipv4/ipip.c
net/ipv4/tcp_ipv4.c
net/ipv4/tcp_output.c
net/ipv6/ip6_gre.c
net/sched/cls_api.c
net/sched/cls_basic.c
net/sched/cls_bpf.c
net/sched/cls_flow.c
net/sched/cls_flower.c
net/sched/cls_fw.c
net/sched/cls_matchall.c
net/sched/cls_tcindex.c
net/sched/cls_u32.c
net/sched/sch_api.c
net/sctp/sm_sideeffect.c
net/sctp/socket.c
net/sctp/stream.c
net/sunrpc/xprt.c
net/wireless/sme.c
net/xfrm/xfrm_policy.c
scripts/mod/devicetable-offsets.c
scripts/mod/file2alias.c
tools/include/uapi/linux/bpf.h
tools/testing/selftests/tc-testing/tdc.py

diff --cc MAINTAINERS
Simple merge
Simple merge
index 0a5fc9f8545f68376649fd99d306c6eee5519677,8ea9320014ee78141f71f883452bb81926e208c6..de64cedf8b26448d1b003bc2159fb0a7566dbbdb
@@@ -104,326 -105,19 +104,328 @@@ nfp_fl_output(struct nfp_fl_output *out
        if (!out_dev)
                return -EOPNOTSUPP;
  
 -      /* Only offload egress ports are on the same device as the ingress
 -       * port.
 +      tmp_flags = last ? NFP_FL_OUT_FLAGS_LAST : 0;
 +
 +      if (tun_type) {
 +              /* Verify the egress netdev matches the tunnel type. */
 +              if (!nfp_fl_netdev_is_tunnel_type(out_dev, tun_type))
 +                      return -EOPNOTSUPP;
 +
 +              if (*tun_out_cnt)
 +                      return -EOPNOTSUPP;
 +              (*tun_out_cnt)++;
 +
 +              output->flags = cpu_to_be16(tmp_flags |
 +                                          NFP_FL_OUT_FLAGS_USE_TUN);
 +              output->port = cpu_to_be32(NFP_FL_PORT_TYPE_TUN | tun_type);
 +      } else {
 +              /* Set action output parameters. */
 +              output->flags = cpu_to_be16(tmp_flags);
 +
 +              /* Only offload if egress ports are on the same device as the
 +               * ingress port.
 +               */
 +              if (!switchdev_port_same_parent_id(in_dev, out_dev))
 +                      return -EOPNOTSUPP;
++              if (!nfp_netdev_is_nfp_repr(out_dev))
++                      return -EOPNOTSUPP;
 +
 +              output->port = cpu_to_be32(nfp_repr_get_port_id(out_dev));
 +              if (!output->port)
 +                      return -EOPNOTSUPP;
 +      }
 +      nfp_flow->meta.shortcut = output->port;
 +
 +      return 0;
 +}
 +
 +static bool nfp_fl_supported_tun_port(const struct tc_action *action)
 +{
 +      struct ip_tunnel_info *tun = tcf_tunnel_info(action);
 +
 +      return tun->key.tp_dst == htons(NFP_FL_VXLAN_PORT);
 +}
 +
 +static struct nfp_fl_pre_tunnel *nfp_fl_pre_tunnel(char *act_data, int act_len)
 +{
 +      size_t act_size = sizeof(struct nfp_fl_pre_tunnel);
 +      struct nfp_fl_pre_tunnel *pre_tun_act;
 +
 +      /* Pre_tunnel action must be first on action list.
 +       * If other actions already exist they need pushed forward.
         */
 -      if (!switchdev_port_same_parent_id(in_dev, out_dev))
 +      if (act_len)
 +              memmove(act_data + act_size, act_data, act_len);
 +
 +      pre_tun_act = (struct nfp_fl_pre_tunnel *)act_data;
 +
 +      memset(pre_tun_act, 0, act_size);
 +
 +      pre_tun_act->head.jump_id = NFP_FL_ACTION_OPCODE_PRE_TUNNEL;
 +      pre_tun_act->head.len_lw = act_size >> NFP_FL_LW_SIZ;
 +
 +      return pre_tun_act;
 +}
 +
 +static int
 +nfp_fl_set_vxlan(struct nfp_fl_set_vxlan *set_vxlan,
 +               const struct tc_action *action,
 +               struct nfp_fl_pre_tunnel *pre_tun)
 +{
 +      struct ip_tunnel_info *vxlan = tcf_tunnel_info(action);
 +      size_t act_size = sizeof(struct nfp_fl_set_vxlan);
 +      u32 tmp_set_vxlan_type_index = 0;
 +      /* Currently support one pre-tunnel so index is always 0. */
 +      int pretun_idx = 0;
 +
 +      if (vxlan->options_len) {
 +              /* Do not support options e.g. vxlan gpe. */
                return -EOPNOTSUPP;
 -      if (!nfp_netdev_is_nfp_repr(out_dev))
 +      }
 +
 +      set_vxlan->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV4_TUNNEL;
 +      set_vxlan->head.len_lw = act_size >> NFP_FL_LW_SIZ;
 +
 +      /* Set tunnel type and pre-tunnel index. */
 +      tmp_set_vxlan_type_index |=
 +              FIELD_PREP(NFP_FL_IPV4_TUNNEL_TYPE, NFP_FL_TUNNEL_VXLAN) |
 +              FIELD_PREP(NFP_FL_IPV4_PRE_TUN_INDEX, pretun_idx);
 +
 +      set_vxlan->tun_type_index = cpu_to_be32(tmp_set_vxlan_type_index);
 +
 +      set_vxlan->tun_id = vxlan->key.tun_id;
 +      set_vxlan->tun_flags = vxlan->key.tun_flags;
 +      set_vxlan->ipv4_ttl = vxlan->key.ttl;
 +      set_vxlan->ipv4_tos = vxlan->key.tos;
 +
 +      /* Complete pre_tunnel action. */
 +      pre_tun->ipv4_dst = vxlan->key.u.ipv4.dst;
 +
 +      return 0;
 +}
 +
 +static void nfp_fl_set_helper32(u32 value, u32 mask, u8 *p_exact, u8 *p_mask)
 +{
 +      u32 oldvalue = get_unaligned((u32 *)p_exact);
 +      u32 oldmask = get_unaligned((u32 *)p_mask);
 +
 +      value &= mask;
 +      value |= oldvalue & ~mask;
 +
 +      put_unaligned(oldmask | mask, (u32 *)p_mask);
 +      put_unaligned(value, (u32 *)p_exact);
 +}
 +
 +static int
 +nfp_fl_set_eth(const struct tc_action *action, int idx, u32 off,
 +             struct nfp_fl_set_eth *set_eth)
 +{
 +      u32 exact, mask;
 +
 +      if (off + 4 > ETH_ALEN * 2)
                return -EOPNOTSUPP;
  
 -      output->port = cpu_to_be32(nfp_repr_get_port_id(out_dev));
 -      if (!output->port)
 +      mask = ~tcf_pedit_mask(action, idx);
 +      exact = tcf_pedit_val(action, idx);
 +
 +      if (exact & ~mask)
                return -EOPNOTSUPP;
  
 -      nfp_flow->meta.shortcut = output->port;
 +      nfp_fl_set_helper32(exact, mask, &set_eth->eth_addr_val[off],
 +                          &set_eth->eth_addr_mask[off]);
 +
 +      set_eth->reserved = cpu_to_be16(0);
 +      set_eth->head.jump_id = NFP_FL_ACTION_OPCODE_SET_ETHERNET;
 +      set_eth->head.len_lw = sizeof(*set_eth) >> NFP_FL_LW_SIZ;
 +
 +      return 0;
 +}
 +
 +static int
 +nfp_fl_set_ip4(const struct tc_action *action, int idx, u32 off,
 +             struct nfp_fl_set_ip4_addrs *set_ip_addr)
 +{
 +      __be32 exact, mask;
 +
 +      /* We are expecting tcf_pedit to return a big endian value */
 +      mask = (__force __be32)~tcf_pedit_mask(action, idx);
 +      exact = (__force __be32)tcf_pedit_val(action, idx);
 +
 +      if (exact & ~mask)
 +              return -EOPNOTSUPP;
 +
 +      switch (off) {
 +      case offsetof(struct iphdr, daddr):
 +              set_ip_addr->ipv4_dst_mask = mask;
 +              set_ip_addr->ipv4_dst = exact;
 +              break;
 +      case offsetof(struct iphdr, saddr):
 +              set_ip_addr->ipv4_src_mask = mask;
 +              set_ip_addr->ipv4_src = exact;
 +              break;
 +      default:
 +              return -EOPNOTSUPP;
 +      }
 +
 +      set_ip_addr->reserved = cpu_to_be16(0);
 +      set_ip_addr->head.jump_id = NFP_FL_ACTION_OPCODE_SET_IPV4_ADDRS;
 +      set_ip_addr->head.len_lw = sizeof(*set_ip_addr) >> NFP_FL_LW_SIZ;
 +
 +      return 0;
 +}
 +
 +static void
 +nfp_fl_set_ip6_helper(int opcode_tag, int idx, __be32 exact, __be32 mask,
 +                    struct nfp_fl_set_ipv6_addr *ip6)
 +{
 +      ip6->ipv6[idx % 4].mask = mask;
 +      ip6->ipv6[idx % 4].exact = exact;
 +
 +      ip6->reserved = cpu_to_be16(0);
 +      ip6->head.jump_id = opcode_tag;
 +      ip6->head.len_lw = sizeof(*ip6) >> NFP_FL_LW_SIZ;
 +}
 +
 +static int
 +nfp_fl_set_ip6(const struct tc_action *action, int idx, u32 off,
 +             struct nfp_fl_set_ipv6_addr *ip_dst,
 +             struct nfp_fl_set_ipv6_addr *ip_src)
 +{
 +      __be32 exact, mask;
 +
 +      /* We are expecting tcf_pedit to return a big endian value */
 +      mask = (__force __be32)~tcf_pedit_mask(action, idx);
 +      exact = (__force __be32)tcf_pedit_val(action, idx);
 +
 +      if (exact & ~mask)
 +              return -EOPNOTSUPP;
 +
 +      if (off < offsetof(struct ipv6hdr, saddr))
 +              return -EOPNOTSUPP;
 +      else if (off < offsetof(struct ipv6hdr, daddr))
 +              nfp_fl_set_ip6_helper(NFP_FL_ACTION_OPCODE_SET_IPV6_SRC, idx,
 +                                    exact, mask, ip_src);
 +      else if (off < offsetof(struct ipv6hdr, daddr) +
 +                     sizeof(struct in6_addr))
 +              nfp_fl_set_ip6_helper(NFP_FL_ACTION_OPCODE_SET_IPV6_DST, idx,
 +                                    exact, mask, ip_dst);
 +      else
 +              return -EOPNOTSUPP;
 +
 +      return 0;
 +}
 +
 +static int
 +nfp_fl_set_tport(const struct tc_action *action, int idx, u32 off,
 +               struct nfp_fl_set_tport *set_tport, int opcode)
 +{
 +      u32 exact, mask;
 +
 +      if (off)
 +              return -EOPNOTSUPP;
 +
 +      mask = ~tcf_pedit_mask(action, idx);
 +      exact = tcf_pedit_val(action, idx);
 +
 +      if (exact & ~mask)
 +              return -EOPNOTSUPP;
 +
 +      nfp_fl_set_helper32(exact, mask, set_tport->tp_port_val,
 +                          set_tport->tp_port_mask);
 +
 +      set_tport->reserved = cpu_to_be16(0);
 +      set_tport->head.jump_id = opcode;
 +      set_tport->head.len_lw = sizeof(*set_tport) >> NFP_FL_LW_SIZ;
 +
 +      return 0;
 +}
 +
 +static int
 +nfp_fl_pedit(const struct tc_action *action, char *nfp_action, int *a_len)
 +{
 +      struct nfp_fl_set_ipv6_addr set_ip6_dst, set_ip6_src;
 +      struct nfp_fl_set_ip4_addrs set_ip_addr;
 +      struct nfp_fl_set_tport set_tport;
 +      struct nfp_fl_set_eth set_eth;
 +      enum pedit_header_type htype;
 +      int idx, nkeys, err;
 +      size_t act_size;
 +      u32 offset, cmd;
 +
 +      memset(&set_ip6_dst, 0, sizeof(set_ip6_dst));
 +      memset(&set_ip6_src, 0, sizeof(set_ip6_src));
 +      memset(&set_ip_addr, 0, sizeof(set_ip_addr));
 +      memset(&set_tport, 0, sizeof(set_tport));
 +      memset(&set_eth, 0, sizeof(set_eth));
 +      nkeys = tcf_pedit_nkeys(action);
 +
 +      for (idx = 0; idx < nkeys; idx++) {
 +              cmd = tcf_pedit_cmd(action, idx);
 +              htype = tcf_pedit_htype(action, idx);
 +              offset = tcf_pedit_offset(action, idx);
 +
 +              if (cmd != TCA_PEDIT_KEY_EX_CMD_SET)
 +                      return -EOPNOTSUPP;
 +
 +              switch (htype) {
 +              case TCA_PEDIT_KEY_EX_HDR_TYPE_ETH:
 +                      err = nfp_fl_set_eth(action, idx, offset, &set_eth);
 +                      break;
 +              case TCA_PEDIT_KEY_EX_HDR_TYPE_IP4:
 +                      err = nfp_fl_set_ip4(action, idx, offset, &set_ip_addr);
 +                      break;
 +              case TCA_PEDIT_KEY_EX_HDR_TYPE_IP6:
 +                      err = nfp_fl_set_ip6(action, idx, offset, &set_ip6_dst,
 +                                           &set_ip6_src);
 +                      break;
 +              case TCA_PEDIT_KEY_EX_HDR_TYPE_TCP:
 +                      err = nfp_fl_set_tport(action, idx, offset, &set_tport,
 +                                             NFP_FL_ACTION_OPCODE_SET_TCP);
 +                      break;
 +              case TCA_PEDIT_KEY_EX_HDR_TYPE_UDP:
 +                      err = nfp_fl_set_tport(action, idx, offset, &set_tport,
 +                                             NFP_FL_ACTION_OPCODE_SET_UDP);
 +                      break;
 +              default:
 +                      return -EOPNOTSUPP;
 +              }
 +              if (err)
 +                      return err;
 +      }
 +
 +      if (set_eth.head.len_lw) {
 +              act_size = sizeof(set_eth);
 +              memcpy(nfp_action, &set_eth, act_size);
 +              *a_len += act_size;
 +      } else if (set_ip_addr.head.len_lw) {
 +              act_size = sizeof(set_ip_addr);
 +              memcpy(nfp_action, &set_ip_addr, act_size);
 +              *a_len += act_size;
 +      } else if (set_ip6_dst.head.len_lw && set_ip6_src.head.len_lw) {
 +              /* TC compiles set src and dst IPv6 address as a single action,
 +               * the hardware requires this to be 2 separate actions.
 +               */
 +              act_size = sizeof(set_ip6_src);
 +              memcpy(nfp_action, &set_ip6_src, act_size);
 +              *a_len += act_size;
 +
 +              act_size = sizeof(set_ip6_dst);
 +              memcpy(&nfp_action[sizeof(set_ip6_src)], &set_ip6_dst,
 +                     act_size);
 +              *a_len += act_size;
 +      } else if (set_ip6_dst.head.len_lw) {
 +              act_size = sizeof(set_ip6_dst);
 +              memcpy(nfp_action, &set_ip6_dst, act_size);
 +              *a_len += act_size;
 +      } else if (set_ip6_src.head.len_lw) {
 +              act_size = sizeof(set_ip6_src);
 +              memcpy(nfp_action, &set_ip6_src, act_size);
 +              *a_len += act_size;
 +      } else if (set_tport.head.len_lw) {
 +              act_size = sizeof(set_tport);
 +              memcpy(nfp_action, &set_tport, act_size);
 +              *a_len += act_size;
 +      }
  
        return 0;
  }
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index 04caa246e747374fee4707646539e869c477e038,3009547f3c66347b38be87831ec17d7aa7a149ed..bf73e1675519278f9e2b37c165c7cc503770a7cf
@@@ -17,17 -18,7 +18,18 @@@ struct tcf_walker 
  int register_tcf_proto_ops(struct tcf_proto_ops *ops);
  int unregister_tcf_proto_ops(struct tcf_proto_ops *ops);
  
 +enum tcf_block_binder_type {
 +      TCF_BLOCK_BINDER_TYPE_UNSPEC,
 +      TCF_BLOCK_BINDER_TYPE_CLSACT_INGRESS,
 +      TCF_BLOCK_BINDER_TYPE_CLSACT_EGRESS,
 +};
 +
 +struct tcf_block_ext_info {
 +      enum tcf_block_binder_type binder_type;
 +};
 +
 +struct tcf_block_cb;
+ bool tcf_queue_work(struct work_struct *work);
  
  #ifdef CONFIG_NET_CLS
  struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index,
index 031dffd5836cfbaf9c60558dfb6dbc2b7ddffb22,0dec8a23be574cd54e4dab927f6df5c7879c33aa..07c179dab4782b2c848595af4d6e86bebc00de39
@@@ -270,9 -272,7 +271,10 @@@ struct tcf_chain 
  
  struct tcf_block {
        struct list_head chain_list;
 +      struct net *net;
 +      struct Qdisc *q;
 +      struct list_head cb_list;
+       struct work_struct work;
  };
  
  static inline void qdisc_cb_private_validate(const struct sk_buff *skb, int sz)
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index b79c44cc8145a1960b5e5d03c281a3e4ac82c44e,6ae94f825f72eb810b5252e10dd89cae66f8cbef..721c3088903390c0de85c8353a090324cf800a56
@@@ -4475,7 -4244,32 +4476,32 @@@ static u32 sock_ops_convert_ctx_access(
        return insn - insn_buf;
  }
  
 -const struct bpf_verifier_ops sk_filter_prog_ops = {
+ static u32 sk_skb_convert_ctx_access(enum bpf_access_type type,
+                                    const struct bpf_insn *si,
+                                    struct bpf_insn *insn_buf,
+                                    struct bpf_prog *prog, u32 *target_size)
+ {
+       struct bpf_insn *insn = insn_buf;
+       int off;
+       switch (si->off) {
+       case offsetof(struct __sk_buff, data_end):
+               off  = si->off;
+               off -= offsetof(struct __sk_buff, data_end);
+               off += offsetof(struct sk_buff, cb);
+               off += offsetof(struct tcp_skb_cb, bpf.data_end);
+               *insn++ = BPF_LDX_MEM(BPF_SIZEOF(void *), si->dst_reg,
+                                     si->src_reg, off);
+               break;
+       default:
+               return bpf_convert_ctx_access(type, si, insn_buf, prog,
+                                             target_size);
+       }
+       return insn - insn_buf;
+ }
 +const struct bpf_verifier_ops sk_filter_verifier_ops = {
        .get_func_proto         = sk_filter_func_proto,
        .is_valid_access        = sk_filter_is_valid_access,
        .convert_ctx_access     = bpf_convert_ctx_access,
@@@ -4559,13 -4324,10 +4585,13 @@@ const struct bpf_verifier_ops sock_ops_
        .convert_ctx_access     = sock_ops_convert_ctx_access,
  };
  
 -const struct bpf_verifier_ops sk_skb_prog_ops = {
 +const struct bpf_prog_ops sock_ops_prog_ops = {
 +};
 +
 +const struct bpf_verifier_ops sk_skb_verifier_ops = {
        .get_func_proto         = sk_skb_func_proto,
        .is_valid_access        = sk_skb_is_valid_access,
-       .convert_ctx_access     = bpf_convert_ctx_access,
+       .convert_ctx_access     = sk_skb_convert_ctx_access,
        .gen_prologue           = sk_skb_prologue,
  };
  
diff --cc net/dsa/dsa2.c
index de91c48b6806b6627ca83957fe47f12ba48b121c,045d8a1762793142de2619497da3034f0af55e0c..4d1ccf87c59c4ba448625e9d83d3605e98606698
@@@ -491,28 -505,26 +495,25 @@@ static int dsa_cpu_parse(struct dsa_por
                dev_put(ethernet_dev);
        }
  
-       if (!ethernet_dev)
-               return -EPROBE_DEFER;
        if (!dst->cpu_dp) {
                dst->cpu_dp = port;
 -              dst->cpu_dp->netdev = ethernet_dev;
 +              dst->cpu_dp->master = ethernet_dev;
        }
  
 -      /* Initialize cpu_port_mask now for drv->setup()
 -       * to have access to a correct value, just like what
 -       * net/dsa/dsa.c::dsa_switch_setup_one does.
 -       */
 -      ds->cpu_port_mask |= BIT(index);
 +      port->type = DSA_PORT_TYPE_CPU;
  
        tag_protocol = ds->ops->get_tag_protocol(ds);
 -      dst->tag_ops = dsa_resolve_tag_protocol(tag_protocol);
 -      if (IS_ERR(dst->tag_ops)) {
 +      tag_ops = dsa_resolve_tag_protocol(tag_protocol);
 +      if (IS_ERR(tag_ops)) {
                dev_warn(ds->dev, "No tagger for this switch\n");
 -              ds->cpu_port_mask &= ~BIT(index);
 -              return PTR_ERR(dst->tag_ops);
 +              return PTR_ERR(tag_ops);
        }
  
 -      dst->rcv = dst->tag_ops->rcv;
 +      dst->cpu_dp->tag_ops = tag_ops;
 +
 +      /* Make a few copies for faster access in master receive hot path */
 +      dst->cpu_dp->rcv = dst->cpu_dp->tag_ops->rcv;
 +      dst->cpu_dp->dst = dst;
  
        return 0;
  }
Simple merge
diff --cc net/ipv4/ipip.c
Simple merge
Simple merge
index aab6e7145013e5a77fa989c66e8b5f6937158bbf,ae60dd3faed0adc71731bc686f878afd4c628d32..a69a34f57330f46a6943191468fe4a7d41475a88
@@@ -2905,14 -2840,13 +2907,16 @@@ int __tcp_retransmit_skb(struct sock *s
                     skb_headroom(skb) >= 0xFFFF)) {
                struct sk_buff *nskb;
  
 -              nskb = __pskb_copy(skb, MAX_TCP_HEADER, GFP_ATOMIC);
 -              err = nskb ? tcp_transmit_skb(sk, nskb, 0, GFP_ATOMIC) :
 -                           -ENOBUFS;
 +              tcp_skb_tsorted_save(skb) {
 +                      nskb = __pskb_copy(skb, MAX_TCP_HEADER, GFP_ATOMIC);
 +                      err = nskb ? tcp_transmit_skb(sk, nskb, 0, GFP_ATOMIC) :
 +                                   -ENOBUFS;
 +              } tcp_skb_tsorted_restore(skb);
 +
-               if (!err)
+               if (!err) {
 -                      skb->skb_mstamp = tp->tcp_mstamp;
 +                      tcp_update_skb_after_send(tp, skb);
+                       tcp_rate_skb_sent(sk, skb);
+               }
        } else {
                err = tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC);
        }
Simple merge
index 0e96cdae9995e724077cad959da2333c0ea9d848,231181c602edbae39fa7aecc345a12a25b35a6bb..d9d54b367d232a41bb413c389051986ddd15e59d
@@@ -297,38 -273,32 +306,41 @@@ err_chain_create
        kfree(block);
        return err;
  }
 +EXPORT_SYMBOL(tcf_block_get_ext);
 +
 +int tcf_block_get(struct tcf_block **p_block,
 +                struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q)
 +{
 +      struct tcf_block_ext_info ei = {0, };
 +
 +      return tcf_block_get_ext(p_block, p_filter_chain, q, &ei);
 +}
  EXPORT_SYMBOL(tcf_block_get);
  
- void tcf_block_put_ext(struct tcf_block *block,
-                      struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q,
-                      struct tcf_block_ext_info *ei)
+ static void tcf_block_put_final(struct work_struct *work)
  {
+       struct tcf_block *block = container_of(work, struct tcf_block, work);
        struct tcf_chain *chain, *tmp;
  
-       if (!block)
-               return;
-       tcf_block_offload_unbind(block, q, ei);
-       /* XXX: Standalone actions are not allowed to jump to any chain, and
-        * bound actions should be all removed after flushing. However,
-        * filters are destroyed in RCU callbacks, we have to hold the chains
-        * first, otherwise we would always race with RCU callbacks on this list
-        * without proper locking.
-        */
+       /* At this point, all the chains should have refcnt == 1. */
+       rtnl_lock();
+       list_for_each_entry_safe(chain, tmp, &block->chain_list, list)
+               tcf_chain_put(chain);
+       rtnl_unlock();
+       kfree(block);
+ }
  
-       /* Wait for existing RCU callbacks to cool down. */
-       rcu_barrier();
+ /* XXX: Standalone actions are not allowed to jump to any chain, and bound
+  * actions should be all removed after flushing. However, filters are destroyed
+  * in RCU callbacks, we have to hold the chains first, otherwise we would
+  * always race with RCU callbacks on this list without proper locking.
+  */
+ static void tcf_block_put_deferred(struct work_struct *work)
+ {
+       struct tcf_block *block = container_of(work, struct tcf_block, work);
+       struct tcf_chain *chain;
  
+       rtnl_lock();
        /* Hold a refcnt for all chains, except 0, in case they are gone. */
        list_for_each_entry(chain, &block->chain_list, list)
                if (chain->index)
        list_for_each_entry(chain, &block->chain_list, list)
                tcf_chain_flush(chain);
  
-       /* Wait for RCU callbacks to release the reference count. */
+       INIT_WORK(&block->work, tcf_block_put_final);
+       /* Wait for RCU callbacks to release the reference count and make
+        * sure their works have been queued before this.
+        */
        rcu_barrier();
+       tcf_queue_work(&block->work);
+       rtnl_unlock();
+ }
  
-       /* At this point, all the chains should have refcnt == 1. */
-       list_for_each_entry_safe(chain, tmp, &block->chain_list, list)
-               tcf_chain_put(chain);
-       kfree(block);
 -void tcf_block_put(struct tcf_block *block)
++void tcf_block_put_ext(struct tcf_block *block,
++                     struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q,
++                     struct tcf_block_ext_info *ei)
+ {
+       if (!block)
+               return;
++      tcf_block_offload_unbind(block, q, ei);
++
+       INIT_WORK(&block->work, tcf_block_put_deferred);
+       /* Wait for existing RCU callbacks to cool down, make sure their works
+        * have been queued before this. We can not flush pending works here
+        * because we are holding the RTNL lock.
+        */
+       rcu_barrier();
+       tcf_queue_work(&block->work);
  }
 +EXPORT_SYMBOL(tcf_block_put_ext);
 +
 +void tcf_block_put(struct tcf_block *block)
 +{
 +      struct tcf_block_ext_info ei = {0, };
 +
 +      tcf_block_put_ext(block, NULL, block->q, &ei);
 +}
++
  EXPORT_SYMBOL(tcf_block_put);
  
 +struct tcf_block_cb {
 +      struct list_head list;
 +      tc_setup_cb_t *cb;
 +      void *cb_ident;
 +      void *cb_priv;
 +      unsigned int refcnt;
 +};
 +
 +void *tcf_block_cb_priv(struct tcf_block_cb *block_cb)
 +{
 +      return block_cb->cb_priv;
 +}
 +EXPORT_SYMBOL(tcf_block_cb_priv);
 +
 +struct tcf_block_cb *tcf_block_cb_lookup(struct tcf_block *block,
 +                                       tc_setup_cb_t *cb, void *cb_ident)
 +{     struct tcf_block_cb *block_cb;
 +
 +      list_for_each_entry(block_cb, &block->cb_list, list)
 +              if (block_cb->cb == cb && block_cb->cb_ident == cb_ident)
 +                      return block_cb;
 +      return NULL;
 +}
 +EXPORT_SYMBOL(tcf_block_cb_lookup);
 +
 +void tcf_block_cb_incref(struct tcf_block_cb *block_cb)
 +{
 +      block_cb->refcnt++;
 +}
 +EXPORT_SYMBOL(tcf_block_cb_incref);
 +
 +unsigned int tcf_block_cb_decref(struct tcf_block_cb *block_cb)
 +{
 +      return --block_cb->refcnt;
 +}
 +EXPORT_SYMBOL(tcf_block_cb_decref);
 +
 +struct tcf_block_cb *__tcf_block_cb_register(struct tcf_block *block,
 +                                           tc_setup_cb_t *cb, void *cb_ident,
 +                                           void *cb_priv)
 +{
 +      struct tcf_block_cb *block_cb;
 +
 +      block_cb = kzalloc(sizeof(*block_cb), GFP_KERNEL);
 +      if (!block_cb)
 +              return NULL;
 +      block_cb->cb = cb;
 +      block_cb->cb_ident = cb_ident;
 +      block_cb->cb_priv = cb_priv;
 +      list_add(&block_cb->list, &block->cb_list);
 +      return block_cb;
 +}
 +EXPORT_SYMBOL(__tcf_block_cb_register);
 +
 +int tcf_block_cb_register(struct tcf_block *block,
 +                        tc_setup_cb_t *cb, void *cb_ident,
 +                        void *cb_priv)
 +{
 +      struct tcf_block_cb *block_cb;
 +
 +      block_cb = __tcf_block_cb_register(block, cb, cb_ident, cb_priv);
 +      return block_cb ? 0 : -ENOMEM;
 +}
 +EXPORT_SYMBOL(tcf_block_cb_register);
 +
 +void __tcf_block_cb_unregister(struct tcf_block_cb *block_cb)
 +{
 +      list_del(&block_cb->list);
 +      kfree(block_cb);
 +}
 +EXPORT_SYMBOL(__tcf_block_cb_unregister);
 +
 +void tcf_block_cb_unregister(struct tcf_block *block,
 +                           tc_setup_cb_t *cb, void *cb_ident)
 +{
 +      struct tcf_block_cb *block_cb;
 +
 +      block_cb = tcf_block_cb_lookup(block, cb, cb_ident);
 +      if (!block_cb)
 +              return;
 +      __tcf_block_cb_unregister(block_cb);
 +}
 +EXPORT_SYMBOL(tcf_block_cb_unregister);
 +
 +static int tcf_block_cb_call(struct tcf_block *block, enum tc_setup_type type,
 +                           void *type_data, bool err_stop)
 +{
 +      struct tcf_block_cb *block_cb;
 +      int ok_count = 0;
 +      int err;
 +
 +      list_for_each_entry(block_cb, &block->cb_list, list) {
 +              err = block_cb->cb(type, type_data, block_cb->cb_priv);
 +              if (err) {
 +                      if (err_stop)
 +                              return err;
 +              } else {
 +                      ok_count++;
 +              }
 +      }
 +      return ok_count;
 +}
 +
  /* Main classifier routine: scans classifier chain attached
   * to this qdisc, (optionally) tests for protocol and asks
   * specific classifiers.
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index 70e78d74f6d3a755789cafd23c680346ff495fcf,c33f711b90198ab8b77bd314c289d09cbfe2d170..95dc997873e8e28f0aa4bd784ad5c219563a07ff
@@@ -46,24 -59,10 +59,24 @@@ static void mall_destroy_rcu(struct rcu
        struct cls_mall_head *head = container_of(rcu, struct cls_mall_head,
                                                  rcu);
  
-       tcf_exts_destroy(&head->exts);
-       kfree(head);
+       INIT_WORK(&head->work, mall_destroy_work);
+       tcf_queue_work(&head->work);
  }
  
 +static void mall_destroy_hw_filter(struct tcf_proto *tp,
 +                                 struct cls_mall_head *head,
 +                                 unsigned long cookie)
 +{
 +      struct tc_cls_matchall_offload cls_mall = {};
 +      struct tcf_block *block = tp->chain->block;
 +
 +      tc_cls_common_offload_init(&cls_mall.common, tp);
 +      cls_mall.command = TC_CLSMATCHALL_DESTROY;
 +      cls_mall.cookie = cookie;
 +
 +      tc_setup_cb_call(block, NULL, TC_SETUP_CLSMATCHALL, &cls_mall, false);
 +}
 +
  static int mall_replace_hw_filter(struct tcf_proto *tp,
                                  struct cls_mall_head *head,
                                  unsigned long cookie)
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge