summaryrefslogtreecommitdiffstats
path: root/drivers/nvme/host/pci.c
diff options
context:
space:
mode:
authorAlexander Duyck2018-04-24 23:47:27 +0200
committerBjorn Helgaas2018-04-24 23:47:27 +0200
commit74d986abc20bc2f0190a4e2683b9ae4d2de4117b (patch)
tree827d43c22275513b2867a9e2ee2b6efd76410cff /drivers/nvme/host/pci.c
parentnet: ena: Use pci_sriov_configure_simple() to enable VFs (diff)
downloadkernel-qcow2-linux-74d986abc20bc2f0190a4e2683b9ae4d2de4117b.tar.gz
kernel-qcow2-linux-74d986abc20bc2f0190a4e2683b9ae4d2de4117b.tar.xz
kernel-qcow2-linux-74d986abc20bc2f0190a4e2683b9ae4d2de4117b.zip
nvme-pci: Use pci_sriov_configure_simple() to enable VFs
Instead of implementing our own version of a SR-IOV configuration stub in the nvme driver, use the existing pci_sriov_configure_simple() function. Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Christoph Hellwig <hch@lst.de>
Diffstat (limited to 'drivers/nvme/host/pci.c')
-rw-r--r--drivers/nvme/host/pci.c20
1 files changed, 1 insertions, 19 deletions
diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
index fbc71fac6f1e..284cdd336271 100644
--- a/drivers/nvme/host/pci.c
+++ b/drivers/nvme/host/pci.c
@@ -2598,24 +2598,6 @@ static void nvme_remove(struct pci_dev *pdev)
nvme_put_ctrl(&dev->ctrl);
}
-static int nvme_pci_sriov_configure(struct pci_dev *pdev, int numvfs)
-{
- int ret = 0;
-
- if (numvfs == 0) {
- if (pci_vfs_assigned(pdev)) {
- dev_warn(&pdev->dev,
- "Cannot disable SR-IOV VFs while assigned\n");
- return -EPERM;
- }
- pci_disable_sriov(pdev);
- return 0;
- }
-
- ret = pci_enable_sriov(pdev, numvfs);
- return ret ? ret : numvfs;
-}
-
#ifdef CONFIG_PM_SLEEP
static int nvme_suspend(struct device *dev)
{
@@ -2734,7 +2716,7 @@ static struct pci_driver nvme_driver = {
.driver = {
.pm = &nvme_dev_pm_ops,
},
- .sriov_configure = nvme_pci_sriov_configure,
+ .sriov_configure = pci_sriov_configure_simple,
.err_handler = &nvme_err_handler,
};
onfig20
-rw-r--r--net/Makefile1
-rw-r--r--net/appletalk/aarp.c16
-rw-r--r--net/appletalk/ddp.c70
-rw-r--r--net/atm/br2684.c44
-rw-r--r--net/atm/clip.c17
-rw-r--r--net/atm/lec.c23
-rw-r--r--net/atm/mpc.c8
-rw-r--r--net/atm/proc.c9
-rw-r--r--net/bluetooth/Kconfig1
-rw-r--r--net/bluetooth/af_bluetooth.c4
-rw-r--r--net/bluetooth/bnep/core.c5
-rw-r--r--net/bluetooth/bnep/netdev.c9
-rw-r--r--net/bluetooth/hci_conn.c17
-rw-r--r--net/bluetooth/hci_core.c2
-rw-r--r--net/bluetooth/hci_event.c2
-rw-r--r--net/bluetooth/hidp/core.c66
-rw-r--r--net/bluetooth/hidp/hidp.h2
-rw-r--r--net/bluetooth/l2cap.c1357
-rw-r--r--net/bluetooth/rfcomm/core.c69
-rw-r--r--net/bluetooth/sco.c49
-rw-r--r--net/bridge/br_device.c4
-rw-r--r--net/bridge/br_forward.c3
-rw-r--r--net/bridge/br_if.c7
-rw-r--r--net/bridge/br_netfilter.c98
-rw-r--r--net/bridge/br_private.h6
-rw-r--r--net/bridge/br_stp.c2
-rw-r--r--net/bridge/br_sysfs_if.c17
-rw-r--r--net/bridge/netfilter/ebt_log.c29
-rw-r--r--net/bridge/netfilter/ebt_ulog.c2
-rw-r--r--net/bridge/netfilter/ebtable_broute.c2
-rw-r--r--net/bridge/netfilter/ebtable_filter.c8
-rw-r--r--net/bridge/netfilter/ebtable_nat.c6
-rw-r--r--net/bridge/netfilter/ebtables.c13
-rw-r--r--net/can/af_can.c20
-rw-r--r--net/can/bcm.c85
-rw-r--r--net/can/proc.c281
-rw-r--r--net/compat.c17
-rw-r--r--net/core/datagram.c3
-rw-r--r--net/core/dev.c656
-rw-r--r--net/core/drop_monitor.c14
-rw-r--r--net/core/ethtool.c32
-rw-r--r--net/core/neighbour.c95
-rw-r--r--net/core/net-sysfs.c2
-rw-r--r--net/core/net_namespace.c35
-rw-r--r--net/core/netpoll.c6
-rw-r--r--net/core/pktgen.c700
-rw-r--r--net/core/rtnetlink.c37
-rw-r--r--net/core/skbuff.c3
-rw-r--r--net/core/sock.c16
-rw-r--r--net/dcb/dcbnl.c130
-rw-r--r--net/dccp/ccids/ccid3.c4
-rw-r--r--net/dccp/feat.c7
-rw-r--r--net/dccp/ipv4.c2
-rw-r--r--net/dccp/ipv6.c8
-rw-r--r--net/dccp/proto.c4
-rw-r--r--net/decnet/dn_neigh.c6
-rw-r--r--net/decnet/dn_route.c2
-rw-r--r--net/dsa/dsa_priv.h6
-rw-r--r--net/dsa/tag_dsa.c2
-rw-r--r--net/dsa/tag_edsa.c2
-rw-r--r--net/dsa/tag_trailer.c2
-rw-r--r--net/econet/af_econet.c4
-rw-r--r--net/ieee802154/Makefile2
-rw-r--r--net/ieee802154/af_ieee802154.c8
-rw-r--r--net/ieee802154/dgram.c78
-rw-r--r--net/ieee802154/netlink.c172
-rw-r--r--net/ieee802154/nl_policy.c4
-rw-r--r--net/ieee802154/raw.c5
-rw-r--r--net/ieee802154/wpan-class.c159
-rw-r--r--net/ipv4/af_inet.c124
-rw-r--r--net/ipv4/arp.c8
-rw-r--r--net/ipv4/fib_trie.c101
-rw-r--r--net/ipv4/inet_timewait_sock.c2
-rw-r--r--net/ipv4/ip_gre.c8
-rw-r--r--net/ipv4/ip_output.c2
-rw-r--r--net/ipv4/ipip.c8
-rw-r--r--net/ipv4/ipmr.c4
-rw-r--r--net/ipv4/netfilter/arp_tables.c47
-rw-r--r--net/ipv4/netfilter/arptable_filter.c4
-rw-r--r--net/ipv4/netfilter/ip_tables.c51
-rw-r--r--net/ipv4/netfilter/iptable_filter.c10
-rw-r--r--net/ipv4/netfilter/iptable_mangle.c16
-rw-r--r--net/ipv4/netfilter/iptable_raw.c10
-rw-r--r--net/ipv4/netfilter/iptable_security.c12
-rw-r--r--net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c22
-rw-r--r--net/ipv4/netfilter/nf_nat_core.c8
-rw-r--r--net/ipv4/netfilter/nf_nat_rule.c6
-rw-r--r--net/ipv4/netfilter/nf_nat_standalone.c8
-rw-r--r--net/ipv4/protocol.c19
-rw-r--r--net/ipv4/raw.c9
-rw-r--r--net/ipv4/route.c22
-rw-r--r--net/ipv4/tcp.c16
-rw-r--r--net/ipv4/tcp_input.c5
-rw-r--r--net/ipv4/tcp_ipv4.c66
-rw-r--r--net/ipv4/tcp_minisocks.c25
-rw-r--r--net/ipv4/tcp_output.c63
-rw-r--r--net/ipv4/tcp_timer.c16
-rw-r--r--net/ipv4/udp.c156
-rw-r--r--net/ipv4/xfrm4_policy.c37
-rw-r--r--net/ipv6/addrconf.c12
-rw-r--r--net/ipv6/af_inet6.c20
-rw-r--r--net/ipv6/icmp.c17
-rw-r--r--net/ipv6/ip6_fib.c16
-rw-r--r--net/ipv6/ip6_input.c6
-rw-r--r--net/ipv6/ip6_output.c44
-rw-r--r--net/ipv6/ip6_tunnel.c6
-rw-r--r--net/ipv6/ip6mr.c11
-rw-r--r--net/ipv6/ipv6_sockglue.c5
-rw-r--r--net/ipv6/mcast.c1
-rw-r--r--net/ipv6/ndisc.c24
-rw-r--r--net/ipv6/netfilter/ip6_tables.c48
-rw-r--r--net/ipv6/netfilter/ip6t_eui64.c9
-rw-r--r--net/ipv6/netfilter/ip6table_filter.c10
-rw-r--r--net/ipv6/netfilter/ip6table_mangle.c16
-rw-r--r--net/ipv6/netfilter/ip6table_raw.c10
-rw-r--r--net/ipv6/netfilter/ip6table_security.c12
-rw-r--r--net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c18
-rw-r--r--net/ipv6/proc.c4
-rw-r--r--net/ipv6/protocol.c15
-rw-r--r--net/ipv6/raw.c9
-rw-r--r--net/ipv6/route.c34
-rw-r--r--net/ipv6/sit.c11
-rw-r--r--net/ipv6/sysctl_net_ipv6.c4
-rw-r--r--net/ipv6/tcp_ipv6.c21
-rw-r--r--net/ipv6/udp.c158
-rw-r--r--net/ipv6/xfrm6_policy.c38
-rw-r--r--net/irda/ircomm/ircomm_event.c4
-rw-r--r--net/irda/ircomm/ircomm_tty_attach.c4
-rw-r--r--net/irda/iriap.c4
-rw-r--r--net/irda/irlan/irlan_common.c4
-rw-r--r--net/irda/irlan/irlan_eth.c10
-rw-r--r--net/irda/irlap.c2
-rw-r--r--net/irda/irlap_event.c4
-rw-r--r--net/irda/irlmp_event.c6
-rw-r--r--net/irda/irnet/irnet_ppp.h2
-rw-r--r--net/irda/irnetlink.c2
-rw-r--r--net/irda/irproc.c14
-rw-r--r--net/key/af_key.c4
-rw-r--r--net/lapb/lapb_iface.c2
-rw-r--r--net/llc/llc_proc.c2
-rw-r--r--net/mac80211/Kconfig42
-rw-r--r--net/mac80211/Makefile4
-rw-r--r--net/mac80211/agg-tx.c3
-rw-r--r--net/mac80211/cfg.c234
-rw-r--r--net/mac80211/debugfs.c2
-rw-r--r--net/mac80211/debugfs_netdev.c52
-rw-r--r--net/mac80211/debugfs_sta.c98
-rw-r--r--net/mac80211/driver-ops.h119
-rw-r--r--net/mac80211/driver-trace.c9
-rw-r--r--net/mac80211/driver-trace.h672
-rw-r--r--net/mac80211/event.c23
-rw-r--r--net/mac80211/ibss.c23
-rw-r--r--net/mac80211/ieee80211_i.h260
-rw-r--r--net/mac80211/iface.c170
-rw-r--r--net/mac80211/main.c200
-rw-r--r--net/mac80211/mesh.c207
-rw-r--r--net/mac80211/mesh.h35
-rw-r--r--net/mac80211/mesh_hwmp.c47
-rw-r--r--net/mac80211/mesh_pathtbl.c177
-rw-r--r--net/mac80211/mesh_plink.c2
-rw-r--r--net/mac80211/mlme.c1988
-rw-r--r--net/mac80211/pm.c19
-rw-r--r--net/mac80211/rate.c31
-rw-r--r--net/mac80211/rc80211_minstrel.c23
-rw-r--r--net/mac80211/rc80211_minstrel.h1
-rw-r--r--net/mac80211/rc80211_minstrel_debugfs.c4
-rw-r--r--net/mac80211/rc80211_pid_algo.c28
-rw-r--r--net/mac80211/rc80211_pid_debugfs.c2
-rw-r--r--net/mac80211/rx.c259
-rw-r--r--net/mac80211/scan.c375
-rw-r--r--net/mac80211/sta_info.c2
-rw-r--r--net/mac80211/sta_info.h30
-rw-r--r--net/mac80211/tx.c580
-rw-r--r--net/mac80211/util.c130
-rw-r--r--net/mac80211/wep.c52
-rw-r--r--net/mac80211/wep.h7
-rw-r--r--net/mac80211/wext.c633
-rw-r--r--net/mac80211/wme.c6
-rw-r--r--net/mac80211/wme.h3
-rw-r--r--net/mac80211/wpa.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_app.c19
-rw-r--r--net/netfilter/ipvs/ip_vs_conn.c17
-rw-r--r--net/netfilter/ipvs/ip_vs_core.c56
-rw-r--r--net/netfilter/ipvs/ip_vs_ctl.c67
-rw-r--r--net/netfilter/ipvs/ip_vs_dh.c7
-rw-r--r--net/netfilter/ipvs/ip_vs_est.c4
-rw-r--r--net/netfilter/ipvs/ip_vs_ftp.c7
-rw-r--r--net/netfilter/ipvs/ip_vs_lblc.c9
-rw-r--r--net/netfilter/ipvs/ip_vs_lblcr.c17
-rw-r--r--net/netfilter/ipvs/ip_vs_lc.c5
-rw-r--r--net/netfilter/ipvs/ip_vs_nq.c5
-rw-r--r--net/netfilter/ipvs/ip_vs_proto.c12
-rw-r--r--net/netfilter/ipvs/ip_vs_proto_ah_esp.c7
-rw-r--r--net/netfilter/ipvs/ip_vs_proto_tcp.c7
-rw-r--r--net/netfilter/ipvs/ip_vs_proto_udp.c7
-rw-r--r--net/netfilter/ipvs/ip_vs_rr.c5
-rw-r--r--net/netfilter/ipvs/ip_vs_sched.c41
-rw-r--r--net/netfilter/ipvs/ip_vs_sed.c5
-rw-r--r--net/netfilter/ipvs/ip_vs_sh.c5
-rw-r--r--net/netfilter/ipvs/ip_vs_sync.c53
-rw-r--r--net/netfilter/ipvs/ip_vs_wlc.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_wrr.c14
-rw-r--r--net/netfilter/ipvs/ip_vs_xmit.c43
-rw-r--r--net/netfilter/nf_conntrack_core.c8
-rw-r--r--net/netfilter/nf_conntrack_netlink.c54
-rw-r--r--net/netfilter/nfnetlink.c2
-rw-r--r--net/netfilter/nfnetlink_log.c6
-rw-r--r--net/netfilter/nfnetlink_queue.c9
-rw-r--r--net/netfilter/x_tables.c7
-rw-r--r--net/netfilter/xt_CONNMARK.c134
-rw-r--r--net/netfilter/xt_DSCP.c46
-rw-r--r--net/netfilter/xt_MARK.c163
-rw-r--r--net/netfilter/xt_connmark.c101
-rw-r--r--net/netfilter/xt_conntrack.c155
-rw-r--r--net/netfilter/xt_dscp.c17
-rw-r--r--net/netfilter/xt_iprange.c45
-rw-r--r--net/netfilter/xt_mark.c86
-rw-r--r--net/netfilter/xt_osf.c6
-rw-r--r--net/netfilter/xt_owner.c130
-rw-r--r--net/netlabel/netlabel_kapi.c6
-rw-r--r--net/netlink/af_netlink.c74
-rw-r--r--net/netlink/genetlink.c188
-rw-r--r--net/netrom/nr_dev.c6
-rw-r--r--net/packet/af_packet.c246
-rw-r--r--net/phonet/datagram.c5
-rw-r--r--net/phonet/pep-gprs.c6
-rw-r--r--net/phonet/pep.c7
-rw-r--r--net/phonet/pn_dev.c34
-rw-r--r--net/phonet/pn_netlink.c2
-rw-r--r--net/phonet/socket.c99
-rw-r--r--net/rds/Kconfig26
-rw-r--r--net/rds/Makefile11
-rw-r--r--net/rds/af_rds.c8
-rw-r--r--net/rds/bind.c3
-rw-r--r--net/rds/cong.c1
-rw-r--r--net/rds/connection.c54
-rw-r--r--net/rds/ib.c7
-rw-r--r--net/rds/ib.h18
-rw-r--r--net/rds/ib_cm.c62
-rw-r--r--net/rds/ib_rdma.c12
-rw-r--r--net/rds/ib_recv.c53
-rw-r--r--net/rds/ib_stats.c2
-rw-r--r--net/rds/ib_sysctl.c12
-rw-r--r--net/rds/info.c3
-rw-r--r--net/rds/iw.c16
-rw-r--r--net/rds/iw.h1
-rw-r--r--net/rds/iw_rdma.c28
-rw-r--r--net/rds/iw_send.c2
-rw-r--r--net/rds/iw_stats.c2
-rw-r--r--net/rds/message.c6
-rw-r--r--net/rds/page.c1
-rw-r--r--net/rds/rdma_transport.c16
-rw-r--r--net/rds/rds.h9
-rw-r--r--net/rds/recv.c28
-rw-r--r--net/rds/send.c3
-rw-r--r--net/rds/stats.c6
-rw-r--r--net/rds/tcp.c320
-rw-r--r--net/rds/tcp.h93
-rw-r--r--net/rds/tcp_connect.c153
-rw-r--r--net/rds/tcp_listen.c199
-rw-r--r--net/rds/tcp_recv.c356
-rw-r--r--net/rds/tcp_send.c263
-rw-r--r--net/rds/tcp_stats.c74
-rw-r--r--net/rds/threads.c2
-rw-r--r--net/rds/transport.c31
-rw-r--r--net/rfkill/core.c14
-rw-r--r--net/rose/rose_dev.c4
-rw-r--r--net/rxrpc/ar-ack.c2
-rw-r--r--net/sched/Makefile2
-rw-r--r--net/sched/act_api.c2
-rw-r--r--net/sched/cls_api.c12
-rw-r--r--net/sched/sch_api.c139
-rw-r--r--net/sched/sch_cbq.c38
-rw-r--r--net/sched/sch_generic.c223
-rw-r--r--net/sched/sch_hfsc.c4
-rw-r--r--net/sched/sch_htb.c35
-rw-r--r--net/sched/sch_ingress.c14
-rw-r--r--net/sched/sch_mq.c235
-rw-r--r--net/sched/sch_multiq.c33
-rw-r--r--net/sched/sch_prio.c32
-rw-r--r--net/sched/sch_red.c21
-rw-r--r--net/sched/sch_sfq.c7
-rw-r--r--net/sched/sch_tbf.c22
-rw-r--r--net/sched/sch_teql.c10
-rw-r--r--net/sctp/associola.c93
-rw-r--r--net/sctp/bind_addr.c21
-rw-r--r--net/sctp/chunk.c62
-rw-r--r--net/sctp/debug.c14
-rw-r--r--net/sctp/output.c188
-rw-r--r--net/sctp/outqueue.c47
-rw-r--r--net/sctp/proc.c4
-rw-r--r--net/sctp/protocol.c11
-rw-r--r--net/sctp/sm_make_chunk.c25
-rw-r--r--net/sctp/sm_sideeffect.c56
-rw-r--r--net/sctp/sm_statefuns.c68
-rw-r--r--net/sctp/socket.c38
-rw-r--r--net/sctp/sysctl.c12
-rw-r--r--net/sctp/transport.c3
-rw-r--r--net/tipc/bearer.c2
-rw-r--r--net/tipc/netlink.c2
-rw-r--r--net/tipc/socket.c6
-rw-r--r--net/unix/af_unix.c5
-rw-r--r--net/wireless/Kconfig75
-rw-r--r--net/wireless/Makefile5
-rw-r--r--net/wireless/chan.c89
-rw-r--r--net/wireless/core.c499
-rw-r--r--net/wireless/core.h249
-rw-r--r--net/wireless/debugfs.c14
-rw-r--r--net/wireless/debugfs.h8
-rw-r--r--net/wireless/ibss.c294
-rw-r--r--net/wireless/mlme.c627
-rw-r--r--net/wireless/nl80211.c1724
-rw-r--r--net/wireless/nl80211.h71
-rw-r--r--net/wireless/reg.c169
-rw-r--r--net/wireless/reg.h15
-rw-r--r--net/wireless/scan.c159
-rw-r--r--net/wireless/sme.c933
-rw-r--r--net/wireless/util.c190
-rw-r--r--net/wireless/wext-compat.c880
-rw-r--r--net/wireless/wext-compat.h49
-rw-r--r--net/wireless/wext-sme.c404
-rw-r--r--net/wireless/wext.c257
-rw-r--r--net/xfrm/xfrm_proc.c2
328 files changed, 16882 insertions, 8111 deletions
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
index fe649081fbdc..8836575f9d79 100644
--- a/net/8021q/vlan.c
+++ b/net/8021q/vlan.c
@@ -225,12 +225,6 @@ int vlan_check_real_dev(struct net_device *real_dev, u16 vlan_id)
return -EOPNOTSUPP;
}
- /* The real device must be up and operating in order to
- * assosciate a VLAN device with it.
- */
- if (!(real_dev->flags & IFF_UP))
- return -ENETDOWN;
-
if (__find_vlan_dev(real_dev, vlan_id) != NULL)
return -EEXIST;
@@ -336,12 +330,13 @@ static int register_vlan_device(struct net_device *real_dev, u16 vlan_id)
snprintf(name, IFNAMSIZ, "vlan%.4i", vlan_id);
}
- new_dev = alloc_netdev(sizeof(struct vlan_dev_info), name,
- vlan_setup);
+ new_dev = alloc_netdev_mq(sizeof(struct vlan_dev_info), name,
+ vlan_setup, real_dev->num_tx_queues);
if (new_dev == NULL)
return -ENOBUFS;
+ new_dev->real_num_tx_queues = real_dev->real_num_tx_queues;
dev_net_set(new_dev, net);
/* need 4 bytes for extra VLAN header info,
* hope the underlying device can handle it.
@@ -397,6 +392,9 @@ static void vlan_transfer_features(struct net_device *dev,
vlandev->features &= ~dev->vlan_features;
vlandev->features |= dev->features & dev->vlan_features;
vlandev->gso_max_size = dev->gso_max_size;
+#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)
+ vlandev->fcoe_ddp_xid = dev->fcoe_ddp_xid;
+#endif
if (old_features != vlandev->features)
netdev_features_change(vlandev);
@@ -468,6 +466,19 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
}
break;
+ case NETDEV_CHANGEMTU:
+ for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
+ vlandev = vlan_group_get_device(grp, i);
+ if (!vlandev)
+ continue;
+
+ if (vlandev->mtu <= dev->mtu)
+ continue;
+
+ dev_set_mtu(vlandev, dev->mtu);
+ }
+ break;
+
case NETDEV_FEAT_CHANGE:
/* Propagate device features to underlying device */
for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index 96bad8f233e2..4198ec5c8abc 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -288,10 +288,14 @@ static int vlan_dev_hard_header(struct sk_buff *skb, struct net_device *dev,
return rc;
}
-static int vlan_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
{
- struct netdev_queue *txq = netdev_get_tx_queue(dev, 0);
+ int i = skb_get_queue_mapping(skb);
+ struct netdev_queue *txq = netdev_get_tx_queue(dev, i);
struct vlan_ethhdr *veth = (struct vlan_ethhdr *)(skb->data);
+ unsigned int len;
+ int ret;
/* Handle non-VLAN frames if they are sent to us, for example by DHCP.
*
@@ -317,29 +321,43 @@ static int vlan_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
vlan_dev_info(dev)->cnt_inc_headroom_on_tx++;
}
- txq->tx_packets++;
- txq->tx_bytes += skb->len;
skb->dev = vlan_dev_info(dev)->real_dev;
- dev_queue_xmit(skb);
+ len = skb->len;
+ ret = dev_queue_xmit(skb);
+
+ if (likely(ret == NET_XMIT_SUCCESS)) {
+ txq->tx_packets++;
+ txq->tx_bytes += len;
+ } else
+ txq->tx_dropped++;
+
return NETDEV_TX_OK;
}
-static int vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
+static netdev_tx_t vlan_dev_hwaccel_hard_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
{
- struct netdev_queue *txq = netdev_get_tx_queue(dev, 0);
+ int i = skb_get_queue_mapping(skb);
+ struct netdev_queue *txq = netdev_get_tx_queue(dev, i);
u16 vlan_tci;
+ unsigned int len;
+ int ret;
vlan_tci = vlan_dev_info(dev)->vlan_id;
vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb);
skb = __vlan_hwaccel_put_tag(skb, vlan_tci);
- txq->tx_packets++;
- txq->tx_bytes += skb->len;
-
skb->dev = vlan_dev_info(dev)->real_dev;
- dev_queue_xmit(skb);
+ len = skb->len;
+ ret = dev_queue_xmit(skb);
+
+ if (likely(ret == NET_XMIT_SUCCESS)) {
+ txq->tx_packets++;
+ txq->tx_bytes += len;
+ } else
+ txq->tx_dropped++;
+
return NETDEV_TX_OK;
}
@@ -561,6 +579,55 @@ static int vlan_dev_neigh_setup(struct net_device *dev, struct neigh_parms *pa)
return err;
}
+#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)
+static int vlan_dev_fcoe_ddp_setup(struct net_device *dev, u16 xid,
+ struct scatterlist *sgl, unsigned int sgc)
+{
+ struct net_device *real_dev = vlan_dev_info(dev)->real_dev;
+ const struct net_device_ops *ops = real_dev->netdev_ops;
+ int rc = 0;
+
+ if (ops->ndo_fcoe_ddp_setup)
+ rc = ops->ndo_fcoe_ddp_setup(real_dev, xid, sgl, sgc);
+
+ return rc;
+}
+
+static int vlan_dev_fcoe_ddp_done(struct net_device *dev, u16 xid)
+{
+ struct net_device *real_dev = vlan_dev_info(dev)->real_dev;
+ const struct net_device_ops *ops = real_dev->netdev_ops;
+ int len = 0;
+
+ if (ops->ndo_fcoe_ddp_done)
+ len = ops->ndo_fcoe_ddp_done(real_dev, xid);
+
+ return len;
+}
+
+static int vlan_dev_fcoe_enable(struct net_device *dev)
+{
+ struct net_device *real_dev = vlan_dev_info(dev)->real_dev;
+ const struct net_device_ops *ops = real_dev->netdev_ops;
+ int rc = -EINVAL;
+
+ if (ops->ndo_fcoe_enable)
+ rc = ops->ndo_fcoe_enable(real_dev);
+ return rc;
+}
+
+static int vlan_dev_fcoe_disable(struct net_device *dev)
+{
+ struct net_device *real_dev = vlan_dev_info(dev)->real_dev;
+ const struct net_device_ops *ops = real_dev->netdev_ops;
+ int rc = -EINVAL;
+
+ if (ops->ndo_fcoe_disable)
+ rc = ops->ndo_fcoe_disable(real_dev);
+ return rc;
+}
+#endif
+
static void vlan_dev_change_rx_flags(struct net_device *dev, int change)
{
struct net_device *real_dev = vlan_dev_info(dev)->real_dev;
@@ -635,6 +702,10 @@ static int vlan_dev_init(struct net_device *dev)
if (is_zero_ether_addr(dev->broadcast))
memcpy(dev->broadcast, real_dev->broadcast, dev->addr_len);
+#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)
+ dev->fcoe_ddp_xid = real_dev->fcoe_ddp_xid;
+#endif
+
if (real_dev->features & NETIF_F_HW_VLAN_TX) {
dev->header_ops = real_dev->header_ops;
dev->hard_header_len = real_dev->hard_header_len;
@@ -715,6 +786,12 @@ static const struct net_device_ops vlan_netdev_ops = {
.ndo_change_rx_flags = vlan_dev_change_rx_flags,
.ndo_do_ioctl = vlan_dev_ioctl,
.ndo_neigh_setup = vlan_dev_neigh_setup,
+#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)
+ .ndo_fcoe_ddp_setup = vlan_dev_fcoe_ddp_setup,
+ .ndo_fcoe_ddp_done = vlan_dev_fcoe_ddp_done,
+ .ndo_fcoe_enable = vlan_dev_fcoe_enable,
+ .ndo_fcoe_disable = vlan_dev_fcoe_disable,
+#endif
};
static const struct net_device_ops vlan_netdev_accel_ops = {
@@ -731,6 +808,12 @@ static const struct net_device_ops vlan_netdev_accel_ops = {
.ndo_change_rx_flags = vlan_dev_change_rx_flags,
.ndo_do_ioctl = vlan_dev_ioctl,
.ndo_neigh_setup = vlan_dev_neigh_setup,
+#if defined(CONFIG_FCOE) || defined(CONFIG_FCOE_MODULE)
+ .ndo_fcoe_ddp_setup = vlan_dev_fcoe_ddp_setup,
+ .ndo_fcoe_ddp_done = vlan_dev_fcoe_ddp_done,
+ .ndo_fcoe_enable = vlan_dev_fcoe_enable,
+ .ndo_fcoe_disable = vlan_dev_fcoe_disable,
+#endif
};
void vlan_setup(struct net_device *dev)
diff --git a/net/8021q/vlan_netlink.c b/net/8021q/vlan_netlink.c
index e9c91dcecc9b..343146e1bceb 100644
--- a/net/8021q/vlan_netlink.c
+++ b/net/8021q/vlan_netlink.c
@@ -100,6 +100,25 @@ static int vlan_changelink(struct net_device *dev,
return 0;
}
+static int vlan_get_tx_queues(struct net *net,
+ struct nlattr *tb[],
+ unsigned int *num_tx_queues,
+ unsigned int *real_num_tx_queues)
+{
+ struct net_device *real_dev;
+
+ if (!tb[IFLA_LINK])
+ return -EINVAL;
+
+ real_dev = __dev_get_by_index(net, nla_get_u32(tb[IFLA_LINK]));
+ if (!real_dev)
+ return -ENODEV;
+
+ *num_tx_queues = real_dev->num_tx_queues;
+ *real_num_tx_queues = real_dev->real_num_tx_queues;
+ return 0;
+}
+
static int vlan_newlink(struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[])
{
@@ -216,6 +235,7 @@ struct rtnl_link_ops vlan_link_ops __read_mostly = {
.maxtype = IFLA_VLAN_MAX,
.policy = vlan_policy,
.priv_size = sizeof(struct vlan_dev_info),
+ .get_tx_queues = vlan_get_tx_queues,
.setup = vlan_setup,
.validate = vlan_validate,
.newlink = vlan_newlink,
diff --git a/net/8021q/vlanproc.c b/net/8021q/vlanproc.c
index b55a091a33df..6262c335f3c2 100644
--- a/net/8021q/vlanproc.c
+++ b/net/8021q/vlanproc.c
@@ -107,7 +107,7 @@ static const struct file_operations vlandev_fops = {
*/
/* Strings */
-static const char *vlan_name_type_str[VLAN_NAME_TYPE_HIGHEST] = {
+static const char *const vlan_name_type_str[VLAN_NAME_TYPE_HIGHEST] = {
[VLAN_NAME_TYPE_RAW_PLUS_VID] = "VLAN_NAME_TYPE_RAW_PLUS_VID",
[VLAN_NAME_TYPE_PLUS_VID_NO_PAD] = "VLAN_NAME_TYPE_PLUS_VID_NO_PAD",
[VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD] = "VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD",
diff --git a/net/Kconfig b/net/Kconfig
index 7051b9710675..041c35edb763 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -23,6 +23,26 @@ menuconfig NET
if NET
+config WANT_COMPAT_NETLINK_MESSAGES
+ bool
+ help
+ This option can be selected by other options that need compat
+ netlink messages.
+
+config COMPAT_NETLINK_MESSAGES
+ def_bool y
+ depends on COMPAT
+ depends on WIRELESS_EXT || WANT_COMPAT_NETLINK_MESSAGES
+ help
+ This option makes it possible to send different netlink messages
+ to tasks depending on whether the task is a compat task or not. To
+ achieve this, you need to set skb_shinfo(skb)->frag_list to the
+ compat skb before sending the skb, the netlink code will sort out
+ which message to actually pass to the task.
+
+ Newly written code should NEVER need this option but do
+ compat-independent messages instead!
+
menu "Networking options"
source "net/packet/Kconfig"
diff --git a/net/Makefile b/net/Makefile
index ba324aefda73..1542e7268a7b 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -24,7 +24,6 @@ obj-y += ipv6/
endif
obj-$(CONFIG_PACKET) += packet/
obj-$(CONFIG_NET_KEY) += key/
-obj-$(CONFIG_NET_SCHED) += sched/
obj-$(CONFIG_BRIDGE) += bridge/
obj-$(CONFIG_NET_DSA) += dsa/
obj-$(CONFIG_IPX) += ipx/
diff --git a/net/appletalk/aarp.c b/net/appletalk/aarp.c
index 89f99d3beb60..9d4adfd22757 100644
--- a/net/appletalk/aarp.c
+++ b/net/appletalk/aarp.c
@@ -599,7 +599,7 @@ int aarp_send_ddp(struct net_device *dev, struct sk_buff *skb,
/* Non ELAP we cannot do. */
if (dev->type != ARPHRD_ETHER)
- return -1;
+ goto free_it;
skb->dev = dev;
skb->protocol = htons(ETH_P_ATALK);
@@ -634,7 +634,7 @@ int aarp_send_ddp(struct net_device *dev, struct sk_buff *skb,
if (!a) {
/* Whoops slipped... good job it's an unreliable protocol 8) */
write_unlock_bh(&aarp_lock);
- return -1;
+ goto free_it;
}
/* Set up the queue */
@@ -663,15 +663,21 @@ out_unlock:
write_unlock_bh(&aarp_lock);
/* Tell the ddp layer we have taken over for this frame. */
- return 0;
+ goto sent;
sendit:
if (skb->sk)
skb->priority = skb->sk->sk_priority;
- dev_queue_xmit(skb);
+ if (dev_queue_xmit(skb))
+ goto drop;
sent:
- return 1;
+ return NET_XMIT_SUCCESS;
+free_it:
+ kfree_skb(skb);
+drop:
+ return NET_XMIT_DROP;
}
+EXPORT_SYMBOL(aarp_send_ddp);
/*
* An entry in the aarp unresolved queue has become resolved. Send
diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c
index 875eda5dbad7..4a6ff2ba4d07 100644
--- a/net/appletalk/ddp.c
+++ b/net/appletalk/ddp.c
@@ -1270,8 +1270,10 @@ static int handle_ip_over_ddp(struct sk_buff *skb)
struct net_device_stats *stats;
/* This needs to be able to handle ipddp"N" devices */
- if (!dev)
- return -ENODEV;
+ if (!dev) {
+ kfree_skb(skb);
+ return NET_RX_DROP;
+ }
skb->protocol = htons(ETH_P_IP);
skb_pull(skb, 13);
@@ -1281,8 +1283,7 @@ static int handle_ip_over_ddp(struct sk_buff *skb)
stats = netdev_priv(dev);
stats->rx_packets++;
stats->rx_bytes += skb->len + 13;
- netif_rx(skb); /* Send the SKB up to a higher place. */
- return 0;
+ return netif_rx(skb); /* Send the SKB up to a higher place. */
}
#else
/* make it easy for gcc to optimize this test out, i.e. kill the code */
@@ -1290,9 +1291,8 @@ static int handle_ip_over_ddp(struct sk_buff *skb)
#define handle_ip_over_ddp(skb) 0
#endif
-static void atalk_route_packet(struct sk_buff *skb, struct net_device *dev,
- struct ddpehdr *ddp, __u16 len_hops,
- int origlen)
+static int atalk_route_packet(struct sk_buff *skb, struct net_device *dev,
+ struct ddpehdr *ddp, __u16 len_hops, int origlen)
{
struct atalk_route *rt;
struct atalk_addr ta;
@@ -1359,8 +1359,6 @@ static void atalk_route_packet(struct sk_buff *skb, struct net_device *dev,
/* 22 bytes - 12 ether, 2 len, 3 802.2 5 snap */
struct sk_buff *nskb = skb_realloc_headroom(skb, 32);
kfree_skb(skb);
- if (!nskb)
- goto out;
skb = nskb;
} else
skb = skb_unshare(skb, GFP_ATOMIC);
@@ -1369,12 +1367,16 @@ static void atalk_route_packet(struct sk_buff *skb, struct net_device *dev,
* If the buffer didn't vanish into the lack of space bitbucket we can
* send it.
*/
- if (skb && aarp_send_ddp(rt->dev, skb, &ta, NULL) == -1)
- goto free_it;
-out:
- return;
+ if (skb == NULL)
+ goto drop;
+
+ if (aarp_send_ddp(rt->dev, skb, &ta, NULL) == NET_XMIT_DROP)
+ return NET_RX_DROP;
+ return NET_XMIT_SUCCESS;
free_it:
kfree_skb(skb);
+drop:
+ return NET_RX_DROP;
}
/**
@@ -1400,7 +1402,7 @@ static int atalk_rcv(struct sk_buff *skb, struct net_device *dev,
__u16 len_hops;
if (!net_eq(dev_net(dev), &init_net))
- goto freeit;
+ goto drop;
/* Don't mangle buffer if shared */
if (!(skb = skb_share_check(skb, GFP_ATOMIC)))
@@ -1408,7 +1410,7 @@ static int atalk_rcv(struct sk_buff *skb, struct net_device *dev,
/* Size check and make sure header is contiguous */
if (!pskb_may_pull(skb, sizeof(*ddp)))
- goto freeit;
+ goto drop;
ddp = ddp_hdr(skb);
@@ -1426,7 +1428,7 @@ static int atalk_rcv(struct sk_buff *skb, struct net_device *dev,
if (skb->len < sizeof(*ddp) || skb->len < (len_hops & 1023)) {
pr_debug("AppleTalk: dropping corrupted frame (deh_len=%u, "
"skb->len=%u)\n", len_hops & 1023, skb->len);
- goto freeit;
+ goto drop;
}
/*
@@ -1436,7 +1438,7 @@ static int atalk_rcv(struct sk_buff *skb, struct net_device *dev,
if (ddp->deh_sum &&
atalk_checksum(skb, len_hops & 1023) != ddp->deh_sum)
/* Not a valid AppleTalk frame - dustbin time */
- goto freeit;
+ goto drop;
/* Check the packet is aimed at us */
if (!ddp->deh_dnet) /* Net 0 is 'this network' */
@@ -1448,8 +1450,7 @@ static int atalk_rcv(struct sk_buff *skb, struct net_device *dev,
/* Not ours, so we route the packet via the correct
* AppleTalk iface
*/
- atalk_route_packet(skb, dev, ddp, len_hops, origlen);
- goto out;
+ return atalk_route_packet(skb, dev, ddp, len_hops, origlen);
}
/* if IP over DDP is not selected this code will be optimized out */
@@ -1465,18 +1466,21 @@ static int atalk_rcv(struct sk_buff *skb, struct net_device *dev,
sock = atalk_search_socket(&tosat, atif);
if (!sock) /* But not one of our sockets */
- goto freeit;
+ goto drop;
/* Queue packet (standard) */
skb->sk = sock;
if (sock_queue_rcv_skb(sock, skb) < 0)
- goto freeit;
-out:
- return 0;
-freeit:
+ goto drop;
+
+ return NET_RX_SUCCESS;
+
+drop:
kfree_skb(skb);
- goto out;
+out:
+ return NET_RX_DROP;
+
}
/*
@@ -1652,10 +1656,10 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
if (skb2) {
loopback = 1;
SOCK_DEBUG(sk, "SK %p: send out(copy).\n", sk);
- if (aarp_send_ddp(dev, skb2,
- &usat->sat_addr, NULL) == -1)
- kfree_skb(skb2);
- /* else queued/sent above in the aarp queue */
+ /*
+ * If it fails it is queued/sent above in the aarp queue
+ */
+ aarp_send_ddp(dev, skb2, &usat->sat_addr, NULL);
}
}
@@ -1685,9 +1689,10 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
usat = &gsat;
}
- if (aarp_send_ddp(dev, skb, &usat->sat_addr, NULL) == -1)
- kfree_skb(skb);
- /* else queued/sent above in the aarp queue */
+ /*
+ * If it fails it is queued/sent above in the aarp queue
+ */
+ aarp_send_ddp(dev, skb, &usat->sat_addr, NULL);
}
SOCK_DEBUG(sk, "SK %p: Done write (%Zd).\n", sk, len);
@@ -1865,7 +1870,6 @@ static struct packet_type ppptalk_packet_type __read_mostly = {
static unsigned char ddp_snap_id[] = { 0x08, 0x00, 0x07, 0x80, 0x9B };
/* Export symbols for use by drivers when AppleTalk is a module */
-EXPORT_SYMBOL(aarp_send_ddp);
EXPORT_SYMBOL(atrtr_get_dev);
EXPORT_SYMBOL(atalk_find_dev_addr);
diff --git a/net/atm/br2684.c b/net/atm/br2684.c
index 2912665fc58c..26a646d4eb32 100644
--- a/net/atm/br2684.c
+++ b/net/atm/br2684.c
@@ -69,7 +69,7 @@ struct br2684_vcc {
struct net_device *device;
/* keep old push, pop functions for chaining */
void (*old_push) (struct atm_vcc * vcc, struct sk_buff * skb);
- /* void (*old_pop)(struct atm_vcc *vcc, struct sk_buff *skb); */
+ void (*old_pop)(struct atm_vcc *vcc, struct sk_buff *skb);
enum br2684_encaps encaps;
struct list_head brvccs;
#ifdef CONFIG_ATM_BR2684_IPFILTER
@@ -142,6 +142,22 @@ static struct net_device *br2684_find_dev(const struct br2684_if_spec *s)
return NULL;
}
+/* chained vcc->pop function. Check if we should wake the netif_queue */
+static void br2684_pop(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+ struct br2684_vcc *brvcc = BR2684_VCC(vcc);
+ struct net_device *net_dev = skb->dev;
+
+ pr_debug("br2684_pop(vcc %p ; net_dev %p )\n", vcc, net_dev);
+ brvcc->old_pop(vcc, skb);
+
+ if (!net_dev)
+ return;
+
+ if (atm_may_send(vcc, 0))
+ netif_wake_queue(net_dev);
+
+}
/*
* Send a packet out a particular vcc. Not to useful right now, but paves
* the way for multiple vcc's per itf. Returns true if we can send,
@@ -200,20 +216,19 @@ static int br2684_xmit_vcc(struct sk_buff *skb, struct net_device *dev,
ATM_SKB(skb)->vcc = atmvcc = brvcc->atmvcc;
pr_debug("atm_skb(%p)->vcc(%p)->dev(%p)\n", skb, atmvcc, atmvcc->dev);
- if (!atm_may_send(atmvcc, skb->truesize)) {
- /*
- * We free this here for now, because we cannot know in a higher
- * layer whether the skb pointer it supplied wasn't freed yet.
- * Now, it always is.
- */
- dev_kfree_skb(skb);
- return 0;
- }
atomic_add(skb->truesize, &sk_atm(atmvcc)->sk_wmem_alloc);
ATM_SKB(skb)->atm_options = atmvcc->atm_options;
dev->stats.tx_packets++;
dev->stats.tx_bytes += skb->len;
atmvcc->send(atmvcc, skb);
+
+ if (!atm_may_send(atmvcc, 0)) {
+ netif_stop_queue(brvcc->device);
+ /*check for race with br2684_pop*/
+ if (atm_may_send(atmvcc, 0))
+ netif_start_queue(brvcc->device);
+ }
+
return 1;
}
@@ -223,7 +238,8 @@ static inline struct br2684_vcc *pick_outgoing_vcc(const struct sk_buff *skb,
return list_empty(&brdev->brvccs) ? NULL : list_entry_brvcc(brdev->brvccs.next); /* 1 vcc/dev right now */
}
-static int br2684_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t br2684_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
{
struct br2684_dev *brdev = BRPRIV(dev);
struct br2684_vcc *brvcc;
@@ -238,7 +254,7 @@ static int br2684_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* netif_stop_queue(dev); */
dev_kfree_skb(skb);
read_unlock(&devs_lock);
- return 0;
+ return NETDEV_TX_OK;
}
if (!br2684_xmit_vcc(skb, dev, brvcc)) {
/*
@@ -252,7 +268,7 @@ static int br2684_start_xmit(struct sk_buff *skb, struct net_device *dev)
dev->stats.tx_fifo_errors++;
}
read_unlock(&devs_lock);
- return 0;
+ return NETDEV_TX_OK;
}
/*
@@ -503,8 +519,10 @@ static int br2684_regvcc(struct atm_vcc *atmvcc, void __user * arg)
atmvcc->user_back = brvcc;
brvcc->encaps = (enum br2684_encaps)be.encaps;
brvcc->old_push = atmvcc->push;
+ brvcc->old_pop = atmvcc->pop;
barrier();
atmvcc->push = br2684_push;
+ atmvcc->pop = br2684_pop;
__skb_queue_head_init(&queue);
rq = &sk_atm(atmvcc)->sk_receive_queue;
diff --git a/net/atm/clip.c b/net/atm/clip.c
index e65a3b1477f8..64629c354343 100644
--- a/net/atm/clip.c
+++ b/net/atm/clip.c
@@ -267,7 +267,7 @@ static void clip_neigh_error(struct neighbour *neigh, struct sk_buff *skb)
kfree_skb(skb);
}
-static struct neigh_ops clip_neigh_ops = {
+static const struct neigh_ops clip_neigh_ops = {
.family = AF_INET,
.solicit = clip_neigh_solicit,
.error_report = clip_neigh_error,
@@ -360,7 +360,8 @@ static int clip_encap(struct atm_vcc *vcc, int mode)
return 0;
}
-static int clip_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t clip_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
{
struct clip_priv *clip_priv = PRIV(dev);
struct atmarp_entry *entry;
@@ -373,7 +374,7 @@ static int clip_start_xmit(struct sk_buff *skb, struct net_device *dev)
printk(KERN_ERR "clip_start_xmit: skb_dst(skb) == NULL\n");
dev_kfree_skb(skb);
dev->stats.tx_dropped++;
- return 0;
+ return NETDEV_TX_OK;
}
if (!skb_dst(skb)->neighbour) {
#if 0
@@ -387,7 +388,7 @@ static int clip_start_xmit(struct sk_buff *skb, struct net_device *dev)
printk(KERN_ERR "clip_start_xmit: NO NEIGHBOUR !\n");
dev_kfree_skb(skb);
dev->stats.tx_dropped++;
- return 0;
+ return NETDEV_TX_OK;
}
entry = NEIGH2ENTRY(skb_dst(skb)->neighbour);
if (!entry->vccs) {
@@ -402,7 +403,7 @@ static int clip_start_xmit(struct sk_buff *skb, struct net_device *dev)
dev_kfree_skb(skb);
dev->stats.tx_dropped++;
}
- return 0;
+ return NETDEV_TX_OK;
}
pr_debug("neigh %p, vccs %p\n", entry, entry->vccs);
ATM_SKB(skb)->vcc = vcc = entry->vccs->vcc;
@@ -421,14 +422,14 @@ static int clip_start_xmit(struct sk_buff *skb, struct net_device *dev)
old = xchg(&entry->vccs->xoff, 1); /* assume XOFF ... */
if (old) {
printk(KERN_WARNING "clip_start_xmit: XOFF->XOFF transition\n");
- return 0;
+ return NETDEV_TX_OK;
}
dev->stats.tx_packets++;
dev->stats.tx_bytes += skb->len;
vcc->send(vcc, skb);
if (atm_may_send(vcc, 0)) {
entry->vccs->xoff = 0;
- return 0;
+ return NETDEV_TX_OK;
}
spin_lock_irqsave(&clip_priv->xoff_lock, flags);
netif_stop_queue(dev); /* XOFF -> throttle immediately */
@@ -440,7 +441,7 @@ static int clip_start_xmit(struct sk_buff *skb, struct net_device *dev)
of the brief netif_stop_queue. If this isn't true or if it
changes, use netif_wake_queue instead. */
spin_unlock_irqrestore(&clip_priv->xoff_lock, flags);
- return 0;
+ return NETDEV_TX_OK;
}
static int clip_mkip(struct atm_vcc *vcc, int timeout)
diff --git a/net/atm/lec.c b/net/atm/lec.c
index ff2e594dca9b..b2d644560323 100644
--- a/net/atm/lec.c
+++ b/net/atm/lec.c
@@ -59,7 +59,8 @@ static unsigned char bridge_ula_lec[] = { 0x01, 0x80, 0xc2, 0x00, 0x00 };
*/
static int lec_open(struct net_device *dev);
-static int lec_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static netdev_tx_t lec_start_xmit(struct sk_buff *skb,
+ struct net_device *dev);
static int lec_close(struct net_device *dev);
static void lec_init(struct net_device *dev);
static struct lec_arp_table *lec_arp_find(struct lec_priv *priv,
@@ -247,7 +248,8 @@ static void lec_tx_timeout(struct net_device *dev)
netif_wake_queue(dev);
}
-static int lec_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t lec_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
{
struct sk_buff *skb2;
struct lec_priv *priv = netdev_priv(dev);
@@ -289,7 +291,7 @@ static int lec_start_xmit(struct sk_buff *skb, struct net_device *dev)
skb2 = skb_realloc_headroom(skb, LEC_HEADER_LEN);
kfree_skb(skb);
if (skb2 == NULL)
- return 0;
+ return NETDEV_TX_OK;
skb = skb2;
}
skb_push(skb, 2);
@@ -307,7 +309,7 @@ static int lec_start_xmit(struct sk_buff *skb, struct net_device *dev)
skb2 = skb_realloc_headroom(skb, LEC_HEADER_LEN);
kfree_skb(skb);
if (skb2 == NULL)
- return 0;
+ return NETDEV_TX_OK;
skb = skb2;
}
#endif
@@ -345,7 +347,7 @@ static int lec_start_xmit(struct sk_buff *skb, struct net_device *dev)
dev_kfree_skb(skb);
if (skb2 == NULL) {
dev->stats.tx_dropped++;
- return 0;
+ return NETDEV_TX_OK;
}
skb = skb2;
}
@@ -416,7 +418,7 @@ out:
if (entry)
lec_arp_put(entry);
dev->trans_start = jiffies;
- return 0;
+ return NETDEV_TX_OK;
}
/* The inverse routine to net_open(). */
@@ -935,9 +937,9 @@ static int lecd_attach(struct atm_vcc *vcc, int arg)
}
#ifdef CONFIG_PROC_FS
-static char *lec_arp_get_status_string(unsigned char status)
+static const char *lec_arp_get_status_string(unsigned char status)
{
- static char *lec_arp_status_string[] = {
+ static const char *const lec_arp_status_string[] = {
"ESI_UNKNOWN ",
"ESI_ARP_PENDING ",
"ESI_VC_PENDING ",
@@ -1121,7 +1123,8 @@ static void *lec_seq_next(struct seq_file *seq, void *v, loff_t *pos)
static int lec_seq_show(struct seq_file *seq, void *v)
{
- static char lec_banner[] = "Itf MAC ATM destination"
+ static const char lec_banner[] =
+ "Itf MAC ATM destination"
" Status Flags "
"VPI/VCI Recv VPI/VCI\n";
@@ -1505,7 +1508,7 @@ lec_arp_remove(struct lec_priv *priv, struct lec_arp_table *to_remove)
}
#if DEBUG_ARP_TABLE
-static char *get_status_string(unsigned char st)
+static const char *get_status_string(unsigned char st)
{
switch (st) {
case ESI_UNKNOWN:
diff --git a/net/atm/mpc.c b/net/atm/mpc.c
index e5bf11453a18..38a6cb0863f0 100644
--- a/net/atm/mpc.c
+++ b/net/atm/mpc.c
@@ -73,7 +73,8 @@ static void mpoad_close(struct atm_vcc *vcc);
static int msg_from_mpoad(struct atm_vcc *vcc, struct sk_buff *skb);
static void mpc_push(struct atm_vcc *vcc, struct sk_buff *skb);
-static int mpc_send_packet(struct sk_buff *skb, struct net_device *dev);
+static netdev_tx_t mpc_send_packet(struct sk_buff *skb,
+ struct net_device *dev);
static int mpoa_event_listener(struct notifier_block *mpoa_notifier, unsigned long event, void *dev);
static void mpc_timer_refresh(void);
static void mpc_cache_check( unsigned long checking_time );
@@ -528,7 +529,8 @@ static int send_via_shortcut(struct sk_buff *skb, struct mpoa_client *mpc)
/*
* Probably needs some error checks and locking, not sure...
*/
-static int mpc_send_packet(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t mpc_send_packet(struct sk_buff *skb,
+ struct net_device *dev)
{
struct mpoa_client *mpc;
struct ethhdr *eth;
@@ -554,7 +556,7 @@ static int mpc_send_packet(struct sk_buff *skb, struct net_device *dev)
while (i < mpc->number_of_mps_macs) {
if (!compare_ether_addr(eth->h_dest, (mpc->mps_macs + i*ETH_ALEN)))
if ( send_via_shortcut(skb, mpc) == 0 ) /* try shortcut */
- return 0; /* success! */
+ return NETDEV_TX_OK; /* success! */
i++;
}
diff --git a/net/atm/proc.c b/net/atm/proc.c
index 38de5ff61ecd..ab8419a324b6 100644
--- a/net/atm/proc.c
+++ b/net/atm/proc.c
@@ -151,8 +151,9 @@ static void *vcc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
static void pvc_info(struct seq_file *seq, struct atm_vcc *vcc)
{
- static const char *class_name[] = { "off","UBR","CBR","VBR","ABR" };
- static const char *aal_name[] = {
+ static const char *const class_name[] =
+ {"off","UBR","CBR","VBR","ABR"};
+ static const char *const aal_name[] = {
"---", "1", "2", "3/4", /* 0- 3 */
"???", "5", "???", "???", /* 4- 7 */
"???", "???", "???", "???", /* 8-11 */
@@ -178,7 +179,7 @@ static void pvc_info(struct seq_file *seq, struct atm_vcc *vcc)
static const char *vcc_state(struct atm_vcc *vcc)
{
- static const char *map[] = { ATM_VS2TXT_MAP };
+ static const char *const map[] = { ATM_VS2TXT_MAP };
return map[ATM_VF2VS(vcc->flags)];
}
@@ -335,7 +336,7 @@ static const struct file_operations vcc_seq_fops = {
static int svc_seq_show(struct seq_file *seq, void *v)
{
- static char atm_svc_banner[] =
+ static const char atm_svc_banner[] =
"Itf VPI VCI State Remote\n";
if (v == SEQ_START_TOKEN)
diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig
index 59fdb1d2e8ed..ed371684c133 100644
--- a/net/bluetooth/Kconfig
+++ b/net/bluetooth/Kconfig
@@ -34,6 +34,7 @@ menuconfig BT
config BT_L2CAP
tristate "L2CAP protocol support"
depends on BT
+ select CRC16
help
L2CAP (Logical Link Control and Adaptation Protocol) provides
connection oriented and connection-less data transport. L2CAP
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index 0250e0600150..8cfb5a849841 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -49,7 +49,7 @@ static struct net_proto_family *bt_proto[BT_MAX_PROTO];
static DEFINE_RWLOCK(bt_proto_lock);
static struct lock_class_key bt_lock_key[BT_MAX_PROTO];
-static const char *bt_key_strings[BT_MAX_PROTO] = {
+static const char *const bt_key_strings[BT_MAX_PROTO] = {
"sk_lock-AF_BLUETOOTH-BTPROTO_L2CAP",
"sk_lock-AF_BLUETOOTH-BTPROTO_HCI",
"sk_lock-AF_BLUETOOTH-BTPROTO_SCO",
@@ -61,7 +61,7 @@ static const char *bt_key_strings[BT_MAX_PROTO] = {
};
static struct lock_class_key bt_slock_key[BT_MAX_PROTO];
-static const char *bt_slock_key_strings[BT_MAX_PROTO] = {
+static const char *const bt_slock_key_strings[BT_MAX_PROTO] = {
"slock-AF_BLUETOOTH-BTPROTO_L2CAP",
"slock-AF_BLUETOOTH-BTPROTO_HCI",
"slock-AF_BLUETOOTH-BTPROTO_SCO",
diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
index 52a6ce0d772b..cafe9f54d841 100644
--- a/net/bluetooth/bnep/core.c
+++ b/net/bluetooth/bnep/core.c
@@ -533,6 +533,10 @@ static struct device *bnep_get_device(struct bnep_session *session)
return conn ? &conn->dev : NULL;
}
+static struct device_type bnep_type = {
+ .name = "bluetooth",
+};
+
int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
{
struct net_device *dev;
@@ -586,6 +590,7 @@ int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
#endif
SET_NETDEV_DEV(dev, bnep_get_device(s));
+ SET_NETDEV_DEVTYPE(dev, &bnep_type);
err = register_netdev(dev);
if (err) {
diff --git a/net/bluetooth/bnep/netdev.c b/net/bluetooth/bnep/netdev.c
index d7a0e9722def..26fb831ef7e0 100644
--- a/net/bluetooth/bnep/netdev.c
+++ b/net/bluetooth/bnep/netdev.c
@@ -165,7 +165,8 @@ static inline int bnep_net_proto_filter(struct sk_buff *skb, struct bnep_session
}
#endif
-static int bnep_net_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t bnep_net_xmit(struct sk_buff *skb,
+ struct net_device *dev)
{
struct bnep_session *s = netdev_priv(dev);
struct sock *sk = s->sock->sk;
@@ -175,14 +176,14 @@ static int bnep_net_xmit(struct sk_buff *skb, struct net_device *dev)
#ifdef CONFIG_BT_BNEP_MC_FILTER
if (bnep_net_mc_filter(skb, s)) {
kfree_skb(skb);
- return 0;
+ return NETDEV_TX_OK;
}
#endif
#ifdef CONFIG_BT_BNEP_PROTO_FILTER
if (bnep_net_proto_filter(skb, s)) {
kfree_skb(skb);
- return 0;
+ return NETDEV_TX_OK;
}
#endif
@@ -203,7 +204,7 @@ static int bnep_net_xmit(struct sk_buff *skb, struct net_device *dev)
netif_stop_queue(dev);
}
- return 0;
+ return NETDEV_TX_OK;
}
static const struct net_device_ops bnep_netdev_ops = {
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index fa47d5d84f5c..a9750984f772 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -246,6 +246,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_CONN_ADD);
+ atomic_set(&conn->devref, 0);
+
hci_conn_init_sysfs(conn);
tasklet_enable(&hdev->tx_task);
@@ -288,7 +290,7 @@ int hci_conn_del(struct hci_conn *conn)
skb_queue_purge(&conn->data_q);
- hci_conn_del_sysfs(conn);
+ hci_conn_put_device(conn);
hci_dev_put(hdev);
@@ -583,6 +585,19 @@ void hci_conn_check_pending(struct hci_dev *hdev)
hci_dev_unlock(hdev);
}
+void hci_conn_hold_device(struct hci_conn *conn)
+{
+ atomic_inc(&conn->devref);
+}
+EXPORT_SYMBOL(hci_conn_hold_device);
+
+void hci_conn_put_device(struct hci_conn *conn)
+{
+ if (atomic_dec_and_test(&conn->devref))
+ hci_conn_del_sysfs(conn);
+}
+EXPORT_SYMBOL(hci_conn_put_device);
+
int hci_get_conn_list(void __user *arg)
{
struct hci_conn_list_req req, *cl;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 406ad07cdea1..e1da8f68759c 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -911,7 +911,7 @@ int hci_register_dev(struct hci_dev *hdev)
hdev->reassembly[i] = NULL;
init_waitqueue_head(&hdev->req_wait_q);
- init_MUTEX(&hdev->req_lock);
+ mutex_init(&hdev->req_lock);
inquiry_cache_init(hdev);
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 184ba0a88ec0..e99fe385fba2 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -887,6 +887,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
} else
conn->state = BT_CONNECTED;
+ hci_conn_hold_device(conn);
hci_conn_add_sysfs(conn);
if (test_bit(HCI_AUTH, &hdev->flags))
@@ -1693,6 +1694,7 @@ static inline void hci_sync_conn_complete_evt(struct hci_dev *hdev, struct sk_bu
conn->handle = __le16_to_cpu(ev->handle);
conn->state = BT_CONNECTED;
+ hci_conn_hold_device(conn);
hci_conn_add_sysfs(conn);
break;
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index b18676870d55..09bedeb5579c 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -40,6 +40,7 @@
#include <linux/input.h>
#include <linux/hid.h>
+#include <linux/hidraw.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
@@ -92,10 +93,14 @@ static void __hidp_link_session(struct hidp_session *session)
{
__module_get(THIS_MODULE);
list_add(&session->list, &hidp_session_list);
+
+ hci_conn_hold_device(session->conn);
}
static void __hidp_unlink_session(struct hidp_session *session)
{
+ hci_conn_put_device(session->conn);
+
list_del(&session->list);
module_put(THIS_MODULE);
}
@@ -374,6 +379,7 @@ static void hidp_process_hid_control(struct hidp_session *session,
/* Kill session thread */
atomic_inc(&session->terminate);
+ hidp_schedule(session);
}
}
@@ -573,7 +579,11 @@ static int hidp_session(void *arg)
if (session->hid) {
if (session->hid->claimed & HID_CLAIMED_INPUT)
hidinput_disconnect(session->hid);
+ if (session->hid->claimed & HID_CLAIMED_HIDRAW)
+ hidraw_disconnect(session->hid);
+
hid_destroy_device(session->hid);
+ session->hid = NULL;
}
/* Wakeup user-space polling for socket errors */
@@ -601,25 +611,27 @@ static struct device *hidp_get_device(struct hidp_session *session)
{
bdaddr_t *src = &bt_sk(session->ctrl_sock->sk)->src;
bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst;
+ struct device *device = NULL;
struct hci_dev *hdev;
- struct hci_conn *conn;
hdev = hci_get_route(dst, src);
if (!hdev)
return NULL;
- conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
+ session->conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
+ if (session->conn)
+ device = &session->conn->dev;
hci_dev_put(hdev);
- return conn ? &conn->dev : NULL;
+ return device;
}
static int hidp_setup_input(struct hidp_session *session,
struct hidp_connadd_req *req)
{
struct input_dev *input;
- int i;
+ int err, i;
input = input_allocate_device();
if (!input)
@@ -666,7 +678,13 @@ static int hidp_setup_input(struct hidp_session *session,
input->event = hidp_input_event;
- return input_register_device(input);
+ err = input_register_device(input);
+ if (err < 0) {
+ hci_conn_put_device(session->conn);
+ return err;
+ }
+
+ return 0;
}
static int hidp_open(struct hid_device *hid)
@@ -748,13 +766,11 @@ static int hidp_setup_hid(struct hidp_session *session,
{
struct hid_device *hid;
bdaddr_t src, dst;
- int ret;
+ int err;
hid = hid_allocate_device();
- if (IS_ERR(hid)) {
- ret = PTR_ERR(session->hid);
- goto err;
- }
+ if (IS_ERR(hid))
+ return PTR_ERR(session->hid);
session->hid = hid;
session->req = req;
@@ -776,16 +792,17 @@ static int hidp_setup_hid(struct hidp_session *session,
hid->dev.parent = hidp_get_device(session);
hid->ll_driver = &hidp_hid_driver;
- ret = hid_add_device(hid);
- if (ret)
- goto err_hid;
+ err = hid_add_device(hid);
+ if (err < 0)
+ goto failed;
return 0;
-err_hid:
+
+failed:
hid_destroy_device(hid);
session->hid = NULL;
-err:
- return ret;
+
+ return err;
}
int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, struct socket *intr_sock)
@@ -835,13 +852,13 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock,
if (req->rd_size > 0) {
err = hidp_setup_hid(session, req);
if (err && err != -ENODEV)
- goto err_skb;
+ goto purge;
}
if (!session->hid) {
err = hidp_setup_input(session, req);
if (err < 0)
- goto err_skb;
+ goto purge;
}
__hidp_link_session(session);
@@ -869,13 +886,20 @@ unlink:
__hidp_unlink_session(session);
- if (session->input)
+ if (session->input) {
input_unregister_device(session->input);
- if (session->hid)
+ session->input = NULL;
+ }
+
+ if (session->hid) {
hid_destroy_device(session->hid);
-err_skb:
+ session->hid = NULL;
+ }
+
+purge:
skb_queue_purge(&session->ctrl_transmit);
skb_queue_purge(&session->intr_transmit);
+
failed:
up_write(&hidp_session_sem);
diff --git a/net/bluetooth/hidp/hidp.h b/net/bluetooth/hidp/hidp.h
index e503c89057ad..faf3d74c3586 100644
--- a/net/bluetooth/hidp/hidp.h
+++ b/net/bluetooth/hidp/hidp.h
@@ -126,6 +126,8 @@ int hidp_get_conninfo(struct hidp_conninfo *ci);
struct hidp_session {
struct list_head list;
+ struct hci_conn *conn;
+
struct socket *ctrl_sock;
struct socket *intr_sock;
diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
index bd0a4c1bced0..b03012564647 100644
--- a/net/bluetooth/l2cap.c
+++ b/net/bluetooth/l2cap.c
@@ -41,6 +41,7 @@
#include <linux/list.h>
#include <linux/device.h>
#include <linux/uaccess.h>
+#include <linux/crc16.h>
#include <net/sock.h>
#include <asm/system.h>
@@ -50,7 +51,9 @@
#include <net/bluetooth/hci_core.h>
#include <net/bluetooth/l2cap.h>
-#define VERSION "2.13"
+#define VERSION "2.14"
+
+static int enable_ertm = 0;
static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN;
static u8 l2cap_fixed_chan[8] = { 0x02, };
@@ -331,6 +334,48 @@ static inline int l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16
return hci_send_acl(conn->hcon, skb, 0);
}
+static inline int l2cap_send_sframe(struct l2cap_pinfo *pi, u16 control)
+{
+ struct sk_buff *skb;
+ struct l2cap_hdr *lh;
+ struct l2cap_conn *conn = pi->conn;
+ int count, hlen = L2CAP_HDR_SIZE + 2;
+
+ if (pi->fcs == L2CAP_FCS_CRC16)
+ hlen += 2;
+
+ BT_DBG("pi %p, control 0x%2.2x", pi, control);
+
+ count = min_t(unsigned int, conn->mtu, hlen);
+ control |= L2CAP_CTRL_FRAME_TYPE;
+
+ skb = bt_skb_alloc(count, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
+ lh->len = cpu_to_le16(hlen - L2CAP_HDR_SIZE);
+ lh->cid = cpu_to_le16(pi->dcid);
+ put_unaligned_le16(control, skb_put(skb, 2));
+
+ if (pi->fcs == L2CAP_FCS_CRC16) {
+ u16 fcs = crc16(0, (u8 *)lh, count - 2);
+ put_unaligned_le16(fcs, skb_put(skb, 2));
+ }
+
+ return hci_send_acl(pi->conn->hcon, skb, 0);
+}
+
+static inline int l2cap_send_rr_or_rnr(struct l2cap_pinfo *pi, u16 control)
+{
+ if (pi->conn_state & L2CAP_CONN_LOCAL_BUSY)
+ control |= L2CAP_SUPER_RCV_NOT_READY;
+ else
+ control |= L2CAP_SUPER_RCV_READY;
+
+ return l2cap_send_sframe(pi, control);
+}
+
static void l2cap_do_start(struct sock *sk)
{
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
@@ -364,6 +409,16 @@ static void l2cap_do_start(struct sock *sk)
}
}
+static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct sock *sk)
+{
+ struct l2cap_disconn_req req;
+
+ req.dcid = cpu_to_le16(l2cap_pi(sk)->dcid);
+ req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
+ l2cap_send_cmd(conn, l2cap_get_ident(conn),
+ L2CAP_DISCONN_REQ, sizeof(req), &req);
+}
+
/* ---- L2CAP connections ---- */
static void l2cap_conn_start(struct l2cap_conn *conn)
{
@@ -648,15 +703,10 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
case BT_CONFIG:
if (sk->sk_type == SOCK_SEQPACKET) {
struct l2cap_conn *conn = l2cap_pi(sk)->conn;
- struct l2cap_disconn_req req;
sk->sk_state = BT_DISCONN;
l2cap_sock_set_timer(sk, sk->sk_sndtimeo);
-
- req.dcid = cpu_to_le16(l2cap_pi(sk)->dcid);
- req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
- l2cap_send_cmd(conn, l2cap_get_ident(conn),
- L2CAP_DISCONN_REQ, sizeof(req), &req);
+ l2cap_send_disconn_req(conn, sk);
} else
l2cap_chan_del(sk, reason);
break;
@@ -715,12 +765,16 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
pi->imtu = l2cap_pi(parent)->imtu;
pi->omtu = l2cap_pi(parent)->omtu;
+ pi->mode = l2cap_pi(parent)->mode;
+ pi->fcs = l2cap_pi(parent)->fcs;
pi->sec_level = l2cap_pi(parent)->sec_level;
pi->role_switch = l2cap_pi(parent)->role_switch;
pi->force_reliable = l2cap_pi(parent)->force_reliable;
} else {
pi->imtu = L2CAP_DEFAULT_MTU;
pi->omtu = 0;
+ pi->mode = L2CAP_MODE_BASIC;
+ pi->fcs = L2CAP_FCS_CRC16;
pi->sec_level = BT_SECURITY_LOW;
pi->role_switch = 0;
pi->force_reliable = 0;
@@ -956,6 +1010,19 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
goto done;
}
+ switch (l2cap_pi(sk)->mode) {
+ case L2CAP_MODE_BASIC:
+ break;
+ case L2CAP_MODE_ERTM:
+ case L2CAP_MODE_STREAMING:
+ if (enable_ertm)
+ break;
+ /* fall through */
+ default:
+ err = -ENOTSUPP;
+ goto done;
+ }
+
switch (sk->sk_state) {
case BT_CONNECT:
case BT_CONNECT2:
@@ -1007,6 +1074,19 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
goto done;
}
+ switch (l2cap_pi(sk)->mode) {
+ case L2CAP_MODE_BASIC:
+ break;
+ case L2CAP_MODE_ERTM:
+ case L2CAP_MODE_STREAMING:
+ if (enable_ertm)
+ break;
+ /* fall through */
+ default:
+ err = -ENOTSUPP;
+ goto done;
+ }
+
if (!l2cap_pi(sk)->psm) {
bdaddr_t *src = &bt_sk(sk)->src;
u16 psm;
@@ -1117,39 +1197,219 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l
return 0;
}
-static inline int l2cap_do_send(struct sock *sk, struct msghdr *msg, int len)
+static void l2cap_monitor_timeout(unsigned long arg)
{
- struct l2cap_conn *conn = l2cap_pi(sk)->conn;
- struct sk_buff *skb, **frag;
- int err, hlen, count, sent = 0;
- struct l2cap_hdr *lh;
+ struct sock *sk = (void *) arg;
+ u16 control;
- BT_DBG("sk %p len %d", sk, len);
+ bh_lock_sock(sk);
+ if (l2cap_pi(sk)->retry_count >= l2cap_pi(sk)->remote_max_tx) {
+ l2cap_send_disconn_req(l2cap_pi(sk)->conn, sk);
+ return;
+ }
- /* First fragment (with L2CAP header) */
- if (sk->sk_type == SOCK_DGRAM)
- hlen = L2CAP_HDR_SIZE + 2;
- else
- hlen = L2CAP_HDR_SIZE;
+ l2cap_pi(sk)->retry_count++;
+ __mod_monitor_timer();
- count = min_t(unsigned int, (conn->mtu - hlen), len);
+ control = L2CAP_CTRL_POLL;
+ l2cap_send_rr_or_rnr(l2cap_pi(sk), control);
+ bh_unlock_sock(sk);
+}
- skb = bt_skb_send_alloc(sk, hlen + count,
- msg->msg_flags & MSG_DONTWAIT, &err);
- if (!skb)
- return err;
+static void l2cap_retrans_timeout(unsigned long arg)
+{
+ struct sock *sk = (void *) arg;
+ u16 control;
- /* Create L2CAP header */
- lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
- lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid);
- lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
+ bh_lock_sock(sk);
+ l2cap_pi(sk)->retry_count = 1;
+ __mod_monitor_timer();
+
+ l2cap_pi(sk)->conn_state |= L2CAP_CONN_WAIT_F;
+
+ control = L2CAP_CTRL_POLL;
+ l2cap_send_rr_or_rnr(l2cap_pi(sk), control);
+ bh_unlock_sock(sk);
+}
+
+static void l2cap_drop_acked_frames(struct sock *sk)
+{
+ struct sk_buff *skb;
+
+ while ((skb = skb_peek(TX_QUEUE(sk)))) {
+ if (bt_cb(skb)->tx_seq == l2cap_pi(sk)->expected_ack_seq)
+ break;
+
+ skb = skb_dequeue(TX_QUEUE(sk));
+ kfree_skb(skb);
+
+ l2cap_pi(sk)->unacked_frames--;
+ }
+
+ if (!l2cap_pi(sk)->unacked_frames)
+ del_timer(&l2cap_pi(sk)->retrans_timer);
- if (sk->sk_type == SOCK_DGRAM)
- put_unaligned(l2cap_pi(sk)->psm, (__le16 *) skb_put(skb, 2));
+ return;
+}
+
+static inline int l2cap_do_send(struct sock *sk, struct sk_buff *skb)
+{
+ struct l2cap_pinfo *pi = l2cap_pi(sk);
+ int err;
+
+ BT_DBG("sk %p, skb %p len %d", sk, skb, skb->len);
+
+ err = hci_send_acl(pi->conn->hcon, skb, 0);
+ if (err < 0)
+ kfree_skb(skb);
+
+ return err;
+}
+
+static int l2cap_streaming_send(struct sock *sk)
+{
+ struct sk_buff *skb, *tx_skb;
+ struct l2cap_pinfo *pi = l2cap_pi(sk);
+ u16 control, fcs;
+ int err;
+
+ while ((skb = sk->sk_send_head)) {
+ tx_skb = skb_clone(skb, GFP_ATOMIC);
+
+ control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE);
+ control |= pi->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT;
+ put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE);
+
+ if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16) {
+ fcs = crc16(0, (u8 *)tx_skb->data, tx_skb->len - 2);
+ put_unaligned_le16(fcs, tx_skb->data + tx_skb->len - 2);
+ }
+
+ err = l2cap_do_send(sk, tx_skb);
+ if (err < 0) {
+ l2cap_send_disconn_req(pi->conn, sk);
+ return err;
+ }
+
+ pi->next_tx_seq = (pi->next_tx_seq + 1) % 64;
+
+ if (skb_queue_is_last(TX_QUEUE(sk), skb))
+ sk->sk_send_head = NULL;
+ else
+ sk->sk_send_head = skb_queue_next(TX_QUEUE(sk), skb);
+
+ skb = skb_dequeue(TX_QUEUE(sk));
+ kfree_skb(skb);
+ }
+ return 0;
+}
+
+static int l2cap_retransmit_frame(struct sock *sk, u8 tx_seq)
+{
+ struct l2cap_pinfo *pi = l2cap_pi(sk);
+ struct sk_buff *skb, *tx_skb;
+ u16 control, fcs;
+ int err;
+
+ skb = skb_peek(TX_QUEUE(sk));
+ do {
+ if (bt_cb(skb)->tx_seq != tx_seq) {
+ if (skb_queue_is_last(TX_QUEUE(sk), skb))
+ break;
+ skb = skb_queue_next(TX_QUEUE(sk), skb);
+ continue;
+ }
+
+ if (pi->remote_max_tx &&
+ bt_cb(skb)->retries == pi->remote_max_tx) {
+ l2cap_send_disconn_req(pi->conn, sk);
+ break;
+ }
+
+ tx_skb = skb_clone(skb, GFP_ATOMIC);
+ bt_cb(skb)->retries++;
+ control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE);
+ control |= (pi->req_seq << L2CAP_CTRL_REQSEQ_SHIFT)
+ | (tx_seq << L2CAP_CTRL_TXSEQ_SHIFT);
+ put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE);
+
+ if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16) {
+ fcs = crc16(0, (u8 *)tx_skb->data, tx_skb->len - 2);
+ put_unaligned_le16(fcs, tx_skb->data + tx_skb->len - 2);
+ }
+
+ err = l2cap_do_send(sk, tx_skb);
+ if (err < 0) {
+ l2cap_send_disconn_req(pi->conn, sk);
+ return err;
+ }
+ break;
+ } while(1);
+ return 0;
+}
+
+static int l2cap_ertm_send(struct sock *sk)
+{
+ struct sk_buff *skb, *tx_skb;
+ struct l2cap_pinfo *pi = l2cap_pi(sk);
+ u16 control, fcs;
+ int err;
+
+ if (pi->conn_state & L2CAP_CONN_WAIT_F)
+ return 0;
+
+ while ((skb = sk->sk_send_head) && (!l2cap_tx_window_full(sk))
+ && !(pi->conn_state & L2CAP_CONN_REMOTE_BUSY)) {
+ tx_skb = skb_clone(skb, GFP_ATOMIC);
+
+ if (pi->remote_max_tx &&
+ bt_cb(skb)->retries == pi->remote_max_tx) {
+ l2cap_send_disconn_req(pi->conn, sk);
+ break;
+ }
+
+ bt_cb(skb)->retries++;
+
+ control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE);
+ control |= (pi->req_seq << L2CAP_CTRL_REQSEQ_SHIFT)
+ | (pi->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT);
+ put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE);
+
+
+ if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16) {
+ fcs = crc16(0, (u8 *)skb->data, tx_skb->len - 2);
+ put_unaligned_le16(fcs, skb->data + tx_skb->len - 2);
+ }
+
+ err = l2cap_do_send(sk, tx_skb);
+ if (err < 0) {
+ l2cap_send_disconn_req(pi->conn, sk);
+ return err;
+ }
+ __mod_retrans_timer();
+
+ bt_cb(skb)->tx_seq = pi->next_tx_seq;
+ pi->next_tx_seq = (pi->next_tx_seq + 1) % 64;
+
+ pi->unacked_frames++;
+
+ if (skb_queue_is_last(TX_QUEUE(sk), skb))
+ sk->sk_send_head = NULL;
+ else
+ sk->sk_send_head = skb_queue_next(TX_QUEUE(sk), skb);
+ }
+
+ return 0;
+}
+
+static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, int len, int count, struct sk_buff *skb)
+{
+ struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+ struct sk_buff **frag;
+ int err, sent = 0;
if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) {
- err = -EFAULT;
- goto fail;
+ return -EFAULT;
}
sent += count;
@@ -1162,33 +1422,173 @@ static inline int l2cap_do_send(struct sock *sk, struct msghdr *msg, int len)
*frag = bt_skb_send_alloc(sk, count, msg->msg_flags & MSG_DONTWAIT, &err);
if (!*frag)
- goto fail;
-
- if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) {
- err = -EFAULT;
- goto fail;
- }
+ return -EFAULT;
+ if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count))
+ return -EFAULT;
sent += count;
len -= count;
frag = &(*frag)->next;
}
- err = hci_send_acl(conn->hcon, skb, 0);
- if (err < 0)
- goto fail;
return sent;
+}
-fail:
- kfree_skb(skb);
- return err;
+static struct sk_buff *l2cap_create_connless_pdu(struct sock *sk, struct msghdr *msg, size_t len)
+{
+ struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+ struct sk_buff *skb;
+ int err, count, hlen = L2CAP_HDR_SIZE + 2;
+ struct l2cap_hdr *lh;
+
+ BT_DBG("sk %p len %d", sk, (int)len);
+
+ count = min_t(unsigned int, (conn->mtu - hlen), len);
+ skb = bt_skb_send_alloc(sk, count + hlen,
+ msg->msg_flags & MSG_DONTWAIT, &err);
+ if (!skb)
+ return ERR_PTR(-ENOMEM);
+
+ /* Create L2CAP header */
+ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
+ lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid);
+ lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
+ put_unaligned_le16(l2cap_pi(sk)->psm, skb_put(skb, 2));
+
+ err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb);
+ if (unlikely(err < 0)) {
+ kfree_skb(skb);
+ return ERR_PTR(err);
+ }
+ return skb;
+}
+
+static struct sk_buff *l2cap_create_basic_pdu(struct sock *sk, struct msghdr *msg, size_t len)
+{
+ struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+ struct sk_buff *skb;
+ int err, count, hlen = L2CAP_HDR_SIZE;
+ struct l2cap_hdr *lh;
+
+ BT_DBG("sk %p len %d", sk, (int)len);
+
+ count = min_t(unsigned int, (conn->mtu - hlen), len);
+ skb = bt_skb_send_alloc(sk, count + hlen,
+ msg->msg_flags & MSG_DONTWAIT, &err);
+ if (!skb)
+ return ERR_PTR(-ENOMEM);
+
+ /* Create L2CAP header */
+ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
+ lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid);
+ lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
+
+ err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb);
+ if (unlikely(err < 0)) {
+ kfree_skb(skb);
+ return ERR_PTR(err);
+ }
+ return skb;
+}
+
+static struct sk_buff *l2cap_create_iframe_pdu(struct sock *sk, struct msghdr *msg, size_t len, u16 control, u16 sdulen)
+{
+ struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+ struct sk_buff *skb;
+ int err, count, hlen = L2CAP_HDR_SIZE + 2;
+ struct l2cap_hdr *lh;
+
+ BT_DBG("sk %p len %d", sk, (int)len);
+
+ if (sdulen)
+ hlen += 2;
+
+ if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16)
+ hlen += 2;
+
+ count = min_t(unsigned int, (conn->mtu - hlen), len);
+ skb = bt_skb_send_alloc(sk, count + hlen,
+ msg->msg_flags & MSG_DONTWAIT, &err);
+ if (!skb)
+ return ERR_PTR(-ENOMEM);
+
+ /* Create L2CAP header */
+ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
+ lh->cid = cpu_to_le16(l2cap_pi(sk)->dcid);
+ lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
+ put_unaligned_le16(control, skb_put(skb, 2));
+ if (sdulen)
+ put_unaligned_le16(sdulen, skb_put(skb, 2));
+
+ err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb);
+ if (unlikely(err < 0)) {
+ kfree_skb(skb);
+ return ERR_PTR(err);
+ }
+
+ if (l2cap_pi(sk)->fcs == L2CAP_FCS_CRC16)
+ put_unaligned_le16(0, skb_put(skb, 2));
+
+ bt_cb(skb)->retries = 0;
+ return skb;
+}
+
+static inline int l2cap_sar_segment_sdu(struct sock *sk, struct msghdr *msg, size_t len)
+{
+ struct l2cap_pinfo *pi = l2cap_pi(sk);
+ struct sk_buff *skb;
+ struct sk_buff_head sar_queue;
+ u16 control;
+ size_t size = 0;
+
+ __skb_queue_head_init(&sar_queue);
+ control = L2CAP_SDU_START;
+ skb = l2cap_create_iframe_pdu(sk, msg, pi->max_pdu_size, control, len);
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ __skb_queue_tail(&sar_queue, skb);
+ len -= pi->max_pdu_size;
+ size +=pi->max_pdu_size;
+ control = 0;
+
+ while (len > 0) {
+ size_t buflen;
+
+ if (len > pi->max_pdu_size) {
+ control |= L2CAP_SDU_CONTINUE;
+ buflen = pi->max_pdu_size;
+ } else {
+ control |= L2CAP_SDU_END;
+ buflen = len;
+ }
+
+ skb = l2cap_create_iframe_pdu(sk, msg, buflen, control, 0);
+ if (IS_ERR(skb)) {
+ skb_queue_purge(&sar_queue);
+ return PTR_ERR(skb);
+ }
+
+ __skb_queue_tail(&sar_queue, skb);
+ len -= buflen;
+ size += buflen;
+ control = 0;
+ }
+ skb_queue_splice_tail(&sar_queue, TX_QUEUE(sk));
+ if (sk->sk_send_head == NULL)
+ sk->sk_send_head = sar_queue.next;
+
+ return size;
}
static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len)
{
struct sock *sk = sock->sk;
- int err = 0;
+ struct l2cap_pinfo *pi = l2cap_pi(sk);
+ struct sk_buff *skb;
+ u16 control;
+ int err;
BT_DBG("sock %p, sk %p", sock, sk);
@@ -1200,16 +1600,73 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms
return -EOPNOTSUPP;
/* Check outgoing MTU */
- if (sk->sk_type != SOCK_RAW && len > l2cap_pi(sk)->omtu)
+ if (sk->sk_type == SOCK_SEQPACKET && pi->mode == L2CAP_MODE_BASIC
+ && len > pi->omtu)
return -EINVAL;
lock_sock(sk);
- if (sk->sk_state == BT_CONNECTED)
- err = l2cap_do_send(sk, msg, len);
- else
+ if (sk->sk_state != BT_CONNECTED) {
err = -ENOTCONN;
+ goto done;
+ }
+
+ /* Connectionless channel */
+ if (sk->sk_type == SOCK_DGRAM) {
+ skb = l2cap_create_connless_pdu(sk, msg, len);
+ err = l2cap_do_send(sk, skb);
+ goto done;
+ }
+
+ switch (pi->mode) {
+ case L2CAP_MODE_BASIC:
+ /* Create a basic PDU */
+ skb = l2cap_create_basic_pdu(sk, msg, len);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ goto done;
+ }
+
+ err = l2cap_do_send(sk, skb);
+ if (!err)
+ err = len;
+ break;
+
+ case L2CAP_MODE_ERTM:
+ case L2CAP_MODE_STREAMING:
+ /* Entire SDU fits into one PDU */
+ if (len <= pi->max_pdu_size) {
+ control = L2CAP_SDU_UNSEGMENTED;
+ skb = l2cap_create_iframe_pdu(sk, msg, len, control, 0);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ goto done;
+ }
+ __skb_queue_tail(TX_QUEUE(sk), skb);
+ if (sk->sk_send_head == NULL)
+ sk->sk_send_head = skb;
+ } else {
+ /* Segment SDU into multiples PDUs */
+ err = l2cap_sar_segment_sdu(sk, msg, len);
+ if (err < 0)
+ goto done;
+ }
+
+ if (pi->mode == L2CAP_MODE_STREAMING)
+ err = l2cap_streaming_send(sk);
+ else
+ err = l2cap_ertm_send(sk);
+
+ if (!err)
+ err = len;
+ break;
+
+ default:
+ BT_DBG("bad state %1.1x", pi->mode);
+ err = -EINVAL;
+ }
+done:
release_sock(sk);
return err;
}
@@ -1257,7 +1714,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
opts.imtu = l2cap_pi(sk)->imtu;
opts.omtu = l2cap_pi(sk)->omtu;
opts.flush_to = l2cap_pi(sk)->flush_to;
- opts.mode = L2CAP_MODE_BASIC;
+ opts.mode = l2cap_pi(sk)->mode;
+ opts.fcs = l2cap_pi(sk)->fcs;
len = min_t(unsigned int, sizeof(opts), optlen);
if (copy_from_user((char *) &opts, optval, len)) {
@@ -1265,8 +1723,10 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us
break;
}
- l2cap_pi(sk)->imtu = opts.imtu;
- l2cap_pi(sk)->omtu = opts.omtu;
+ l2cap_pi(sk)->imtu = opts.imtu;
+ l2cap_pi(sk)->omtu = opts.omtu;
+ l2cap_pi(sk)->mode = opts.mode;
+ l2cap_pi(sk)->fcs = opts.fcs;
break;
case L2CAP_LM:
@@ -1379,7 +1839,8 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us
opts.imtu = l2cap_pi(sk)->imtu;
opts.omtu = l2cap_pi(sk)->omtu;
opts.flush_to = l2cap_pi(sk)->flush_to;
- opts.mode = L2CAP_MODE_BASIC;
+ opts.mode = l2cap_pi(sk)->mode;
+ opts.fcs = l2cap_pi(sk)->fcs;
len = min_t(unsigned int, len, sizeof(opts));
if (copy_to_user(optval, (char *) &opts, len))
@@ -1708,16 +2169,108 @@ static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val)
*ptr += L2CAP_CONF_OPT_SIZE + len;
}
+static int l2cap_mode_supported(__u8 mode, __u32 feat_mask)
+{
+ u32 local_feat_mask = l2cap_feat_mask;
+ if (enable_ertm)
+ local_feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING;
+
+ switch (mode) {
+ case L2CAP_MODE_ERTM:
+ return L2CAP_FEAT_ERTM & feat_mask & local_feat_mask;
+ case L2CAP_MODE_STREAMING:
+ return L2CAP_FEAT_STREAMING & feat_mask & local_feat_mask;
+ default:
+ return 0x00;
+ }
+}
+
+static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask)
+{
+ switch (mode) {
+ case L2CAP_MODE_STREAMING:
+ case L2CAP_MODE_ERTM:
+ if (l2cap_mode_supported(mode, remote_feat_mask))
+ return mode;
+ /* fall through */
+ default:
+ return L2CAP_MODE_BASIC;
+ }
+}
+
static int l2cap_build_conf_req(struct sock *sk, void *data)
{
struct l2cap_pinfo *pi = l2cap_pi(sk);
struct l2cap_conf_req *req = data;
+ struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_ERTM };
void *ptr = req->data;
BT_DBG("sk %p", sk);
- if (pi->imtu != L2CAP_DEFAULT_MTU)
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu);
+ if (pi->num_conf_req || pi->num_conf_rsp)
+ goto done;
+
+ switch (pi->mode) {
+ case L2CAP_MODE_STREAMING:
+ case L2CAP_MODE_ERTM:
+ pi->conf_state |= L2CAP_CONF_STATE2_DEVICE;
+ if (!l2cap_mode_supported(pi->mode, pi->conn->feat_mask))
+ l2cap_send_disconn_req(pi->conn, sk);
+ break;
+ default:
+ pi->mode = l2cap_select_mode(rfc.mode, pi->conn->feat_mask);
+ break;
+ }
+
+done:
+ switch (pi->mode) {
+ case L2CAP_MODE_BASIC:
+ if (pi->imtu != L2CAP_DEFAULT_MTU)
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->imtu);
+ break;
+
+ case L2CAP_MODE_ERTM:
+ rfc.mode = L2CAP_MODE_ERTM;
+ rfc.txwin_size = L2CAP_DEFAULT_TX_WINDOW;
+ rfc.max_transmit = L2CAP_DEFAULT_MAX_TX;
+ rfc.retrans_timeout = 0;
+ rfc.monitor_timeout = 0;
+ rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE);
+
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
+ sizeof(rfc), (unsigned long) &rfc);
+
+ if (!(pi->conn->feat_mask & L2CAP_FEAT_FCS))
+ break;
+
+ if (pi->fcs == L2CAP_FCS_NONE ||
+ pi->conf_state & L2CAP_CONF_NO_FCS_RECV) {
+ pi->fcs = L2CAP_FCS_NONE;
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, pi->fcs);
+ }
+ break;
+
+ case L2CAP_MODE_STREAMING:
+ rfc.mode = L2CAP_MODE_STREAMING;
+ rfc.txwin_size = 0;
+ rfc.max_transmit = 0;
+ rfc.retrans_timeout = 0;
+ rfc.monitor_timeout = 0;
+ rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE);
+
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
+ sizeof(rfc), (unsigned long) &rfc);
+
+ if (!(pi->conn->feat_mask & L2CAP_FEAT_FCS))
+ break;
+
+ if (pi->fcs == L2CAP_FCS_NONE ||
+ pi->conf_state & L2CAP_CONF_NO_FCS_RECV) {
+ pi->fcs = L2CAP_FCS_NONE;
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, pi->fcs);
+ }
+ break;
+ }
/* FIXME: Need actual value of the flush timeout */
//if (flush_to != L2CAP_DEFAULT_FLUSH_TO)
@@ -1767,6 +2320,12 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data)
memcpy(&rfc, (void *) val, olen);
break;
+ case L2CAP_CONF_FCS:
+ if (val == L2CAP_FCS_NONE)
+ pi->conf_state |= L2CAP_CONF_NO_FCS_RECV;
+
+ break;
+
default:
if (hint)
break;
@@ -1777,30 +2336,83 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data)
}
}
+ if (pi->num_conf_rsp || pi->num_conf_req)
+ goto done;
+
+ switch (pi->mode) {
+ case L2CAP_MODE_STREAMING:
+ case L2CAP_MODE_ERTM:
+ pi->conf_state |= L2CAP_CONF_STATE2_DEVICE;
+ if (!l2cap_mode_supported(pi->mode, pi->conn->feat_mask))
+ return -ECONNREFUSED;
+ break;
+ default:
+ pi->mode = l2cap_select_mode(rfc.mode, pi->conn->feat_mask);
+ break;
+ }
+
+done:
+ if (pi->mode != rfc.mode) {
+ result = L2CAP_CONF_UNACCEPT;
+ rfc.mode = pi->mode;
+
+ if (pi->num_conf_rsp == 1)
+ return -ECONNREFUSED;
+
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
+ sizeof(rfc), (unsigned long) &rfc);
+ }
+
+
if (result == L2CAP_CONF_SUCCESS) {
/* Configure output options and let the other side know
* which ones we don't like. */
- if (rfc.mode == L2CAP_MODE_BASIC) {
- if (mtu < pi->omtu)
- result = L2CAP_CONF_UNACCEPT;
- else {
- pi->omtu = mtu;
- pi->conf_state |= L2CAP_CONF_OUTPUT_DONE;
- }
+ if (mtu < L2CAP_DEFAULT_MIN_MTU)
+ result = L2CAP_CONF_UNACCEPT;
+ else {
+ pi->omtu = mtu;
+ pi->conf_state |= L2CAP_CONF_MTU_DONE;
+ }
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu);
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu);
- } else {
+ switch (rfc.mode) {
+ case L2CAP_MODE_BASIC:
+ pi->fcs = L2CAP_FCS_NONE;
+ pi->conf_state |= L2CAP_CONF_MODE_DONE;
+ break;
+
+ case L2CAP_MODE_ERTM:
+ pi->remote_tx_win = rfc.txwin_size;
+ pi->remote_max_tx = rfc.max_transmit;
+ pi->max_pdu_size = rfc.max_pdu_size;
+
+ rfc.retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
+ rfc.monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
+
+ pi->conf_state |= L2CAP_CONF_MODE_DONE;
+ break;
+
+ case L2CAP_MODE_STREAMING:
+ pi->remote_tx_win = rfc.txwin_size;
+ pi->max_pdu_size = rfc.max_pdu_size;
+
+ pi->conf_state |= L2CAP_CONF_MODE_DONE;
+ break;
+
+ default:
result = L2CAP_CONF_UNACCEPT;
memset(&rfc, 0, sizeof(rfc));
- rfc.mode = L2CAP_MODE_BASIC;
-
- l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
- sizeof(rfc), (unsigned long) &rfc);
+ rfc.mode = pi->mode;
}
- }
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
+ sizeof(rfc), (unsigned long) &rfc);
+
+ if (result == L2CAP_CONF_SUCCESS)
+ pi->conf_state |= L2CAP_CONF_OUTPUT_DONE;
+ }
rsp->scid = cpu_to_le16(pi->dcid);
rsp->result = cpu_to_le16(result);
rsp->flags = cpu_to_le16(0x0000);
@@ -1808,6 +2420,73 @@ static int l2cap_parse_conf_req(struct sock *sk, void *data)
return ptr - data;
}
+static int l2cap_parse_conf_rsp(struct sock *sk, void *rsp, int len, void *data, u16 *result)
+{
+ struct l2cap_pinfo *pi = l2cap_pi(sk);
+ struct l2cap_conf_req *req = data;
+ void *ptr = req->data;
+ int type, olen;
+ unsigned long val;
+ struct l2cap_conf_rfc rfc;
+
+ BT_DBG("sk %p, rsp %p, len %d, req %p", sk, rsp, len, data);
+
+ while (len >= L2CAP_CONF_OPT_SIZE) {
+ len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val);
+
+ switch (type) {
+ case L2CAP_CONF_MTU:
+ if (val < L2CAP_DEFAULT_MIN_MTU) {
+ *result = L2CAP_CONF_UNACCEPT;
+ pi->omtu = L2CAP_DEFAULT_MIN_MTU;
+ } else
+ pi->omtu = val;
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, pi->omtu);
+ break;
+
+ case L2CAP_CONF_FLUSH_TO:
+ pi->flush_to = val;
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_FLUSH_TO,
+ 2, pi->flush_to);
+ break;
+
+ case L2CAP_CONF_RFC:
+ if (olen == sizeof(rfc))
+ memcpy(&rfc, (void *)val, olen);
+
+ if ((pi->conf_state & L2CAP_CONF_STATE2_DEVICE) &&
+ rfc.mode != pi->mode)
+ return -ECONNREFUSED;
+
+ pi->mode = rfc.mode;
+ pi->fcs = 0;
+
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC,
+ sizeof(rfc), (unsigned long) &rfc);
+ break;
+ }
+ }
+
+ if (*result == L2CAP_CONF_SUCCESS) {
+ switch (rfc.mode) {
+ case L2CAP_MODE_ERTM:
+ pi->remote_tx_win = rfc.txwin_size;
+ pi->retrans_timeout = rfc.retrans_timeout;
+ pi->monitor_timeout = rfc.monitor_timeout;
+ pi->max_pdu_size = le16_to_cpu(rfc.max_pdu_size);
+ break;
+ case L2CAP_MODE_STREAMING:
+ pi->max_pdu_size = le16_to_cpu(rfc.max_pdu_size);
+ break;
+ }
+ }
+
+ req->dcid = cpu_to_le16(pi->dcid);
+ req->flags = cpu_to_le16(0x0000);
+
+ return ptr - data;
+}
+
static int l2cap_build_conf_rsp(struct sock *sk, void *data, u16 result, u16 flags)
{
struct l2cap_conf_rsp *rsp = data;
@@ -1994,6 +2673,7 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
l2cap_build_conf_req(sk, req), req);
+ l2cap_pi(sk)->num_conf_req++;
break;
case L2CAP_CR_PEND:
@@ -2052,10 +2732,13 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
/* Complete config. */
len = l2cap_parse_conf_req(sk, rsp);
- if (len < 0)
+ if (len < 0) {
+ l2cap_send_disconn_req(conn, sk);
goto unlock;
+ }
l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp);
+ l2cap_pi(sk)->num_conf_rsp++;
/* Reset config buffer. */
l2cap_pi(sk)->conf_len = 0;
@@ -2064,7 +2747,22 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
goto unlock;
if (l2cap_pi(sk)->conf_state & L2CAP_CONF_INPUT_DONE) {
+ if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_NO_FCS_RECV)
+ || l2cap_pi(sk)->fcs != L2CAP_FCS_NONE)
+ l2cap_pi(sk)->fcs = L2CAP_FCS_CRC16;
+
sk->sk_state = BT_CONNECTED;
+ l2cap_pi(sk)->next_tx_seq = 0;
+ l2cap_pi(sk)->expected_ack_seq = 0;
+ l2cap_pi(sk)->unacked_frames = 0;
+
+ setup_timer(&l2cap_pi(sk)->retrans_timer,
+ l2cap_retrans_timeout, (unsigned long) sk);
+ setup_timer(&l2cap_pi(sk)->monitor_timer,
+ l2cap_monitor_timeout, (unsigned long) sk);
+
+ __skb_queue_head_init(TX_QUEUE(sk));
+ __skb_queue_head_init(SREJ_QUEUE(sk));
l2cap_chan_ready(sk);
goto unlock;
}
@@ -2073,6 +2771,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr
u8 buf[64];
l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
l2cap_build_conf_req(sk, buf), buf);
+ l2cap_pi(sk)->num_conf_req++;
}
unlock:
@@ -2102,29 +2801,32 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
break;
case L2CAP_CONF_UNACCEPT:
- if (++l2cap_pi(sk)->conf_retry < L2CAP_CONF_MAX_RETRIES) {
- char req[128];
- /* It does not make sense to adjust L2CAP parameters
- * that are currently defined in the spec. We simply
- * resend config request that we sent earlier. It is
- * stupid, but it helps qualification testing which
- * expects at least some response from us. */
- l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
- l2cap_build_conf_req(sk, req), req);
- goto done;
+ if (l2cap_pi(sk)->num_conf_rsp <= L2CAP_CONF_MAX_CONF_RSP) {
+ int len = cmd->len - sizeof(*rsp);
+ char req[64];
+
+ /* throw out any old stored conf requests */
+ result = L2CAP_CONF_SUCCESS;
+ len = l2cap_parse_conf_rsp(sk, rsp->data,
+ len, req, &result);
+ if (len < 0) {
+ l2cap_send_disconn_req(conn, sk);
+ goto done;
+ }
+
+ l2cap_send_cmd(conn, l2cap_get_ident(conn),
+ L2CAP_CONF_REQ, len, req);
+ l2cap_pi(sk)->num_conf_req++;
+ if (result != L2CAP_CONF_SUCCESS)
+ goto done;
+ break;
}
default:
sk->sk_state = BT_DISCONN;
sk->sk_err = ECONNRESET;
l2cap_sock_set_timer(sk, HZ * 5);
- {
- struct l2cap_disconn_req req;
- req.dcid = cpu_to_le16(l2cap_pi(sk)->dcid);
- req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
- l2cap_send_cmd(conn, l2cap_get_ident(conn),
- L2CAP_DISCONN_REQ, sizeof(req), &req);
- }
+ l2cap_send_disconn_req(conn, sk);
goto done;
}
@@ -2134,7 +2836,16 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr
l2cap_pi(sk)->conf_state |= L2CAP_CONF_INPUT_DONE;
if (l2cap_pi(sk)->conf_state & L2CAP_CONF_OUTPUT_DONE) {
+ if (!(l2cap_pi(sk)->conf_state & L2CAP_CONF_NO_FCS_RECV)
+ || l2cap_pi(sk)->fcs != L2CAP_FCS_NONE)
+ l2cap_pi(sk)->fcs = L2CAP_FCS_CRC16;
+
sk->sk_state = BT_CONNECTED;
+ l2cap_pi(sk)->expected_tx_seq = 0;
+ l2cap_pi(sk)->buffer_seq = 0;
+ l2cap_pi(sk)->num_to_ack = 0;
+ __skb_queue_head_init(TX_QUEUE(sk));
+ __skb_queue_head_init(SREJ_QUEUE(sk));
l2cap_chan_ready(sk);
}
@@ -2165,6 +2876,11 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn, struct l2cap_cmd
sk->sk_shutdown = SHUTDOWN_MASK;
+ skb_queue_purge(TX_QUEUE(sk));
+ skb_queue_purge(SREJ_QUEUE(sk));
+ del_timer(&l2cap_pi(sk)->retrans_timer);
+ del_timer(&l2cap_pi(sk)->monitor_timer);
+
l2cap_chan_del(sk, ECONNRESET);
bh_unlock_sock(sk);
@@ -2187,6 +2903,11 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn, struct l2cap_cmd
if (!sk)
return 0;
+ skb_queue_purge(TX_QUEUE(sk));
+ skb_queue_purge(SREJ_QUEUE(sk));
+ del_timer(&l2cap_pi(sk)->retrans_timer);
+ del_timer(&l2cap_pi(sk)->monitor_timer);
+
l2cap_chan_del(sk, 0);
bh_unlock_sock(sk);
@@ -2205,10 +2926,14 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm
if (type == L2CAP_IT_FEAT_MASK) {
u8 buf[8];
+ u32 feat_mask = l2cap_feat_mask;
struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf;
rsp->type = cpu_to_le16(L2CAP_IT_FEAT_MASK);
rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS);
- put_unaligned(cpu_to_le32(l2cap_feat_mask), (__le32 *) rsp->data);
+ if (enable_ertm)
+ feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING
+ | L2CAP_FEAT_FCS;
+ put_unaligned_le32(feat_mask, rsp->data);
l2cap_send_cmd(conn, cmd->ident,
L2CAP_INFO_RSP, sizeof(buf), buf);
} else if (type == L2CAP_IT_FIXED_CHAN) {
@@ -2359,9 +3084,374 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, struct sk_buff *sk
kfree_skb(skb);
}
+static int l2cap_check_fcs(struct l2cap_pinfo *pi, struct sk_buff *skb)
+{
+ u16 our_fcs, rcv_fcs;
+ int hdr_size = L2CAP_HDR_SIZE + 2;
+
+ if (pi->fcs == L2CAP_FCS_CRC16) {
+ skb_trim(skb, skb->len - 2);
+ rcv_fcs = get_unaligned_le16(skb->data + skb->len);
+ our_fcs = crc16(0, skb->data - hdr_size, skb->len + hdr_size);
+
+ if (our_fcs != rcv_fcs)
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void l2cap_add_to_srej_queue(struct sock *sk, struct sk_buff *skb, u8 tx_seq, u8 sar)
+{
+ struct sk_buff *next_skb;
+
+ bt_cb(skb)->tx_seq = tx_seq;
+ bt_cb(skb)->sar = sar;
+
+ next_skb = skb_peek(SREJ_QUEUE(sk));
+ if (!next_skb) {
+ __skb_queue_tail(SREJ_QUEUE(sk), skb);
+ return;
+ }
+
+ do {
+ if (bt_cb(next_skb)->tx_seq > tx_seq) {
+ __skb_queue_before(SREJ_QUEUE(sk), next_skb, skb);
+ return;
+ }
+
+ if (skb_queue_is_last(SREJ_QUEUE(sk), next_skb))
+ break;
+
+ } while((next_skb = skb_queue_next(SREJ_QUEUE(sk), next_skb)));
+
+ __skb_queue_tail(SREJ_QUEUE(sk), skb);
+}
+
+static int l2cap_sar_reassembly_sdu(struct sock *sk, struct sk_buff *skb, u16 control)
+{
+ struct l2cap_pinfo *pi = l2cap_pi(sk);
+ struct sk_buff *_skb;
+ int err = -EINVAL;
+
+ switch (control & L2CAP_CTRL_SAR) {
+ case L2CAP_SDU_UNSEGMENTED:
+ if (pi->conn_state & L2CAP_CONN_SAR_SDU) {
+ kfree_skb(pi->sdu);
+ break;
+ }
+
+ err = sock_queue_rcv_skb(sk, skb);
+ if (!err)
+ return 0;
+
+ break;
+
+ case L2CAP_SDU_START:
+ if (pi->conn_state & L2CAP_CONN_SAR_SDU) {
+ kfree_skb(pi->sdu);
+ break;
+ }
+
+ pi->sdu_len = get_unaligned_le16(skb->data);
+ skb_pull(skb, 2);
+
+ pi->sdu = bt_skb_alloc(pi->sdu_len, GFP_ATOMIC);
+ if (!pi->sdu) {
+ err = -ENOMEM;
+ break;
+ }
+
+ memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len);
+
+ pi->conn_state |= L2CAP_CONN_SAR_SDU;
+ pi->partial_sdu_len = skb->len;
+ err = 0;
+ break;
+
+ case L2CAP_SDU_CONTINUE:
+ if (!(pi->conn_state & L2CAP_CONN_SAR_SDU))
+ break;
+
+ memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len);
+
+ pi->partial_sdu_len += skb->len;
+ if (pi->partial_sdu_len > pi->sdu_len)
+ kfree_skb(pi->sdu);
+ else
+ err = 0;
+
+ break;
+
+ case L2CAP_SDU_END:
+ if (!(pi->conn_state & L2CAP_CONN_SAR_SDU))
+ break;
+
+ memcpy(skb_put(pi->sdu, skb->len), skb->data, skb->len);
+
+ pi->conn_state &= ~L2CAP_CONN_SAR_SDU;
+ pi->partial_sdu_len += skb->len;
+
+ if (pi->partial_sdu_len == pi->sdu_len) {
+ _skb = skb_clone(pi->sdu, GFP_ATOMIC);
+ err = sock_queue_rcv_skb(sk, _skb);
+ if (err < 0)
+ kfree_skb(_skb);
+ }
+ kfree_skb(pi->sdu);
+ err = 0;
+
+ break;
+ }
+
+ kfree_skb(skb);
+ return err;
+}
+
+static void l2cap_check_srej_gap(struct sock *sk, u8 tx_seq)
+{
+ struct sk_buff *skb;
+ u16 control = 0;
+
+ while((skb = skb_peek(SREJ_QUEUE(sk)))) {
+ if (bt_cb(skb)->tx_seq != tx_seq)
+ break;
+
+ skb = skb_dequeue(SREJ_QUEUE(sk));
+ control |= bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT;
+ l2cap_sar_reassembly_sdu(sk, skb, control);
+ l2cap_pi(sk)->buffer_seq_srej =
+ (l2cap_pi(sk)->buffer_seq_srej + 1) % 64;
+ tx_seq++;
+ }
+}
+
+static void l2cap_resend_srejframe(struct sock *sk, u8 tx_seq)
+{
+ struct l2cap_pinfo *pi = l2cap_pi(sk);
+ struct srej_list *l, *tmp;
+ u16 control;
+
+ list_for_each_entry_safe(l,tmp, SREJ_LIST(sk), list) {
+ if (l->tx_seq == tx_seq) {
+ list_del(&l->list);
+ kfree(l);
+ return;
+ }
+ control = L2CAP_SUPER_SELECT_REJECT;
+ control |= l->tx_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+ l2cap_send_sframe(pi, control);
+ list_del(&l->list);
+ list_add_tail(&l->list, SREJ_LIST(sk));
+ }
+}
+
+static void l2cap_send_srejframe(struct sock *sk, u8 tx_seq)
+{
+ struct l2cap_pinfo *pi = l2cap_pi(sk);
+ struct srej_list *new;
+ u16 control;
+
+ while (tx_seq != pi->expected_tx_seq) {
+ control = L2CAP_SUPER_SELECT_REJECT;
+ control |= pi->expected_tx_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+ if (pi->conn_state & L2CAP_CONN_SEND_PBIT) {
+ control |= L2CAP_CTRL_POLL;
+ pi->conn_state &= ~L2CAP_CONN_SEND_PBIT;
+ }
+ l2cap_send_sframe(pi, control);
+
+ new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC);
+ new->tx_seq = pi->expected_tx_seq++;
+ list_add_tail(&new->list, SREJ_LIST(sk));
+ }
+ pi->expected_tx_seq++;
+}
+
+static inline int l2cap_data_channel_iframe(struct sock *sk, u16 rx_control, struct sk_buff *skb)
+{
+ struct l2cap_pinfo *pi = l2cap_pi(sk);
+ u8 tx_seq = __get_txseq(rx_control);
+ u16 tx_control = 0;
+ u8 sar = rx_control >> L2CAP_CTRL_SAR_SHIFT;
+ int err = 0;
+
+ BT_DBG("sk %p rx_control 0x%4.4x len %d", sk, rx_control, skb->len);
+
+ if (tx_seq == pi->expected_tx_seq)
+ goto expected;
+
+ if (pi->conn_state & L2CAP_CONN_SREJ_SENT) {
+ struct srej_list *first;
+
+ first = list_first_entry(SREJ_LIST(sk),
+ struct srej_list, list);
+ if (tx_seq == first->tx_seq) {
+ l2cap_add_to_srej_queue(sk, skb, tx_seq, sar);
+ l2cap_check_srej_gap(sk, tx_seq);
+
+ list_del(&first->list);
+ kfree(first);
+
+ if (list_empty(SREJ_LIST(sk))) {
+ pi->buffer_seq = pi->buffer_seq_srej;
+ pi->conn_state &= ~L2CAP_CONN_SREJ_SENT;
+ }
+ } else {
+ struct srej_list *l;
+ l2cap_add_to_srej_queue(sk, skb, tx_seq, sar);
+
+ list_for_each_entry(l, SREJ_LIST(sk), list) {
+ if (l->tx_seq == tx_seq) {
+ l2cap_resend_srejframe(sk, tx_seq);
+ return 0;
+ }
+ }
+ l2cap_send_srejframe(sk, tx_seq);
+ }
+ } else {
+ pi->conn_state |= L2CAP_CONN_SREJ_SENT;
+
+ INIT_LIST_HEAD(SREJ_LIST(sk));
+ pi->buffer_seq_srej = pi->buffer_seq;
+
+ __skb_queue_head_init(SREJ_QUEUE(sk));
+ l2cap_add_to_srej_queue(sk, skb, tx_seq, sar);
+
+ pi->conn_state |= L2CAP_CONN_SEND_PBIT;
+
+ l2cap_send_srejframe(sk, tx_seq);
+ }
+ return 0;
+
+expected:
+ pi->expected_tx_seq = (pi->expected_tx_seq + 1) % 64;
+
+ if (pi->conn_state & L2CAP_CONN_SREJ_SENT) {
+ l2cap_add_to_srej_queue(sk, skb, tx_seq, sar);
+ return 0;
+ }
+
+ pi->buffer_seq = (pi->buffer_seq + 1) % 64;
+
+ err = l2cap_sar_reassembly_sdu(sk, skb, rx_control);
+ if (err < 0)
+ return err;
+
+ pi->num_to_ack = (pi->num_to_ack + 1) % L2CAP_DEFAULT_NUM_TO_ACK;
+ if (pi->num_to_ack == L2CAP_DEFAULT_NUM_TO_ACK - 1) {
+ tx_control |= L2CAP_SUPER_RCV_READY;
+ tx_control |= pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT;
+ l2cap_send_sframe(pi, tx_control);
+ }
+ return 0;
+}
+
+static inline int l2cap_data_channel_sframe(struct sock *sk, u16 rx_control, struct sk_buff *skb)
+{
+ struct l2cap_pinfo *pi = l2cap_pi(sk);
+ u8 tx_seq = __get_reqseq(rx_control);
+
+ BT_DBG("sk %p rx_control 0x%4.4x len %d", sk, rx_control, skb->len);
+
+ switch (rx_control & L2CAP_CTRL_SUPERVISE) {
+ case L2CAP_SUPER_RCV_READY:
+ if (rx_control & L2CAP_CTRL_POLL) {
+ u16 control = L2CAP_CTRL_FINAL;
+ control |= L2CAP_SUPER_RCV_READY |
+ (pi->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT);
+ l2cap_send_sframe(l2cap_pi(sk), control);
+ pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
+
+ } else if (rx_control & L2CAP_CTRL_FINAL) {
+ pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
+ pi->expected_ack_seq = tx_seq;
+ l2cap_drop_acked_frames(sk);
+
+ if (!(pi->conn_state & L2CAP_CONN_WAIT_F))
+ break;
+
+ pi->conn_state &= ~L2CAP_CONN_WAIT_F;
+ del_timer(&pi->monitor_timer);
+
+ if (pi->unacked_frames > 0)
+ __mod_retrans_timer();
+ } else {
+ pi->expected_ack_seq = tx_seq;
+ l2cap_drop_acked_frames(sk);
+
+ if ((pi->conn_state & L2CAP_CONN_REMOTE_BUSY)
+ && (pi->unacked_frames > 0))
+ __mod_retrans_timer();
+
+ l2cap_ertm_send(sk);
+ pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
+ }
+ break;
+
+ case L2CAP_SUPER_REJECT:
+ pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
+
+ pi->expected_ack_seq = __get_reqseq(rx_control);
+ l2cap_drop_acked_frames(sk);
+
+ sk->sk_send_head = TX_QUEUE(sk)->next;
+ pi->next_tx_seq = pi->expected_ack_seq;
+
+ l2cap_ertm_send(sk);
+
+ break;
+
+ case L2CAP_SUPER_SELECT_REJECT:
+ pi->conn_state &= ~L2CAP_CONN_REMOTE_BUSY;
+
+ if (rx_control & L2CAP_CTRL_POLL) {
+ l2cap_retransmit_frame(sk, tx_seq);
+ pi->expected_ack_seq = tx_seq;
+ l2cap_drop_acked_frames(sk);
+ l2cap_ertm_send(sk);
+ if (pi->conn_state & L2CAP_CONN_WAIT_F) {
+ pi->srej_save_reqseq = tx_seq;
+ pi->conn_state |= L2CAP_CONN_SREJ_ACT;
+ }
+ } else if (rx_control & L2CAP_CTRL_FINAL) {
+ if ((pi->conn_state & L2CAP_CONN_SREJ_ACT) &&
+ pi->srej_save_reqseq == tx_seq)
+ pi->srej_save_reqseq &= ~L2CAP_CONN_SREJ_ACT;
+ else
+ l2cap_retransmit_frame(sk, tx_seq);
+ }
+ else {
+ l2cap_retransmit_frame(sk, tx_seq);
+ if (pi->conn_state & L2CAP_CONN_WAIT_F) {
+ pi->srej_save_reqseq = tx_seq;
+ pi->conn_state |= L2CAP_CONN_SREJ_ACT;
+ }
+ }
+ break;
+
+ case L2CAP_SUPER_RCV_NOT_READY:
+ pi->conn_state |= L2CAP_CONN_REMOTE_BUSY;
+ pi->expected_ack_seq = tx_seq;
+ l2cap_drop_acked_frames(sk);
+
+ del_timer(&l2cap_pi(sk)->retrans_timer);
+ if (rx_control & L2CAP_CTRL_POLL) {
+ u16 control = L2CAP_CTRL_FINAL;
+ l2cap_send_rr_or_rnr(l2cap_pi(sk), control);
+ }
+ break;
+ }
+
+ return 0;
+}
+
static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk_buff *skb)
{
struct sock *sk;
+ struct l2cap_pinfo *pi;
+ u16 control, len;
+ u8 tx_seq;
+ int err;
sk = l2cap_get_chan_by_scid(&conn->chan_list, cid);
if (!sk) {
@@ -2369,22 +3459,91 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk
goto drop;
}
+ pi = l2cap_pi(sk);
+
BT_DBG("sk %p, len %d", sk, skb->len);
if (sk->sk_state != BT_CONNECTED)
goto drop;
- if (l2cap_pi(sk)->imtu < skb->len)
- goto drop;
+ switch (pi->mode) {
+ case L2CAP_MODE_BASIC:
+ /* If socket recv buffers overflows we drop data here
+ * which is *bad* because L2CAP has to be reliable.
+ * But we don't have any other choice. L2CAP doesn't
+ * provide flow control mechanism. */
- /* If socket recv buffers overflows we drop data here
- * which is *bad* because L2CAP has to be reliable.
- * But we don't have any other choice. L2CAP doesn't
- * provide flow control mechanism. */
+ if (pi->imtu < skb->len)
+ goto drop;
+
+ if (!sock_queue_rcv_skb(sk, skb))
+ goto done;
+ break;
+
+ case L2CAP_MODE_ERTM:
+ control = get_unaligned_le16(skb->data);
+ skb_pull(skb, 2);
+ len = skb->len;
+
+ if (__is_sar_start(control))
+ len -= 2;
+
+ if (pi->fcs == L2CAP_FCS_CRC16)
+ len -= 2;
+
+ /*
+ * We can just drop the corrupted I-frame here.
+ * Receiver will miss it and start proper recovery
+ * procedures and ask retransmission.
+ */
+ if (len > L2CAP_DEFAULT_MAX_PDU_SIZE)
+ goto drop;
+
+ if (l2cap_check_fcs(pi, skb))
+ goto drop;
+
+ if (__is_iframe(control))
+ err = l2cap_data_channel_iframe(sk, control, skb);
+ else
+ err = l2cap_data_channel_sframe(sk, control, skb);
+
+ if (!err)
+ goto done;
+ break;
+
+ case L2CAP_MODE_STREAMING:
+ control = get_unaligned_le16(skb->data);
+ skb_pull(skb, 2);
+ len = skb->len;
+
+ if (__is_sar_start(control))
+ len -= 2;
+
+ if (pi->fcs == L2CAP_FCS_CRC16)
+ len -= 2;
+
+ if (len > L2CAP_DEFAULT_MAX_PDU_SIZE || __is_sframe(control))
+ goto drop;
+
+ if (l2cap_check_fcs(pi, skb))
+ goto drop;
+
+ tx_seq = __get_txseq(control);
+
+ if (pi->expected_tx_seq == tx_seq)
+ pi->expected_tx_seq = (pi->expected_tx_seq + 1) % 64;
+ else
+ pi->expected_tx_seq = tx_seq + 1;
+
+ err = l2cap_sar_reassembly_sdu(sk, skb, control);
- if (!sock_queue_rcv_skb(sk, skb))
goto done;
+ default:
+ BT_DBG("sk %p: bad mode 0x%2.2x", sk, l2cap_pi(sk)->mode);
+ break;
+ }
+
drop:
kfree_skb(skb);
@@ -2433,6 +3592,11 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
cid = __le16_to_cpu(lh->cid);
len = __le16_to_cpu(lh->len);
+ if (len != skb->len) {
+ kfree_skb(skb);
+ return;
+ }
+
BT_DBG("len %d, cid 0x%4.4x", len, cid);
switch (cid) {
@@ -2441,7 +3605,7 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
break;
case L2CAP_CID_CONN_LESS:
- psm = get_unaligned((__le16 *) skb->data);
+ psm = get_unaligned_le16(skb->data);
skb_pull(skb, 2);
l2cap_conless_channel(conn, psm, skb);
break;
@@ -2828,6 +3992,9 @@ EXPORT_SYMBOL(l2cap_load);
module_init(l2cap_init);
module_exit(l2cap_exit);
+module_param(enable_ertm, bool, 0644);
+MODULE_PARM_DESC(enable_ertm, "Enable enhanced retransmission mode");
+
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
MODULE_DESCRIPTION("Bluetooth L2CAP ver " VERSION);
MODULE_VERSION(VERSION);
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index 94b3388c188b..25692bc0a342 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -244,6 +244,33 @@ static inline int rfcomm_check_security(struct rfcomm_dlc *d)
auth_type);
}
+static void rfcomm_session_timeout(unsigned long arg)
+{
+ struct rfcomm_session *s = (void *) arg;
+
+ BT_DBG("session %p state %ld", s, s->state);
+
+ set_bit(RFCOMM_TIMED_OUT, &s->flags);
+ rfcomm_session_put(s);
+ rfcomm_schedule(RFCOMM_SCHED_TIMEO);
+}
+
+static void rfcomm_session_set_timer(struct rfcomm_session *s, long timeout)
+{
+ BT_DBG("session %p state %ld timeout %ld", s, s->state, timeout);
+
+ if (!mod_timer(&s->timer, jiffies + timeout))
+ rfcomm_session_hold(s);
+}
+
+static void rfcomm_session_clear_timer(struct rfcomm_session *s)
+{
+ BT_DBG("session %p state %ld", s, s->state);
+
+ if (timer_pending(&s->timer) && del_timer(&s->timer))
+ rfcomm_session_put(s);
+}
+
/* ---- RFCOMM DLCs ---- */
static void rfcomm_dlc_timeout(unsigned long arg)
{
@@ -320,6 +347,7 @@ static void rfcomm_dlc_link(struct rfcomm_session *s, struct rfcomm_dlc *d)
rfcomm_session_hold(s);
+ rfcomm_session_clear_timer(s);
rfcomm_dlc_hold(d);
list_add(&d->list, &s->dlcs);
d->session = s;
@@ -335,6 +363,9 @@ static void rfcomm_dlc_unlink(struct rfcomm_dlc *d)
d->session = NULL;
rfcomm_dlc_put(d);
+ if (list_empty(&s->dlcs))
+ rfcomm_session_set_timer(s, RFCOMM_IDLE_TIMEOUT);
+
rfcomm_session_put(s);
}
@@ -567,6 +598,8 @@ static struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state)
BT_DBG("session %p sock %p", s, sock);
+ setup_timer(&s->timer, rfcomm_session_timeout, (unsigned long) s);
+
INIT_LIST_HEAD(&s->dlcs);
s->state = state;
s->sock = sock;
@@ -598,6 +631,7 @@ static void rfcomm_session_del(struct rfcomm_session *s)
if (state == BT_CONNECTED)
rfcomm_send_disc(s, 0);
+ rfcomm_session_clear_timer(s);
sock_release(s->sock);
kfree(s);
@@ -639,6 +673,7 @@ static void rfcomm_session_close(struct rfcomm_session *s, int err)
__rfcomm_dlc_close(d, err);
}
+ rfcomm_session_clear_timer(s);
rfcomm_session_put(s);
}
@@ -1879,6 +1914,12 @@ static inline void rfcomm_process_sessions(void)
struct rfcomm_session *s;
s = list_entry(p, struct rfcomm_session, list);
+ if (test_and_clear_bit(RFCOMM_TIMED_OUT, &s->flags)) {
+ s->state = BT_DISCONN;
+ rfcomm_send_disc(s, 0);
+ continue;
+ }
+
if (s->state == BT_LISTEN) {
rfcomm_accept_connection(s);
continue;
@@ -2080,7 +2121,7 @@ static CLASS_ATTR(rfcomm_dlc, S_IRUGO, rfcomm_dlc_sysfs_show, NULL);
/* ---- Initialization ---- */
static int __init rfcomm_init(void)
{
- int ret;
+ int err;
l2cap_load();
@@ -2088,33 +2129,35 @@ static int __init rfcomm_init(void)
rfcomm_thread = kthread_run(rfcomm_run, NULL, "krfcommd");
if (IS_ERR(rfcomm_thread)) {
- ret = PTR_ERR(rfcomm_thread);
- goto out_thread;
+ err = PTR_ERR(rfcomm_thread);
+ goto unregister;
}
if (class_create_file(bt_class, &class_attr_rfcomm_dlc) < 0)
BT_ERR("Failed to create RFCOMM info file");
- ret = rfcomm_init_ttys();
- if (ret)
- goto out_tty;
+ err = rfcomm_init_ttys();
+ if (err < 0)
+ goto stop;
- ret = rfcomm_init_sockets();
- if (ret)
- goto out_sock;
+ err = rfcomm_init_sockets();
+ if (err < 0)
+ goto cleanup;
BT_INFO("RFCOMM ver %s", VERSION);
return 0;
-out_sock:
+cleanup:
rfcomm_cleanup_ttys();
-out_tty:
+
+stop:
kthread_stop(rfcomm_thread);
-out_thread:
+
+unregister:
hci_unregister_cb(&rfcomm_cb);
- return ret;
+ return err;
}
static void __exit rfcomm_exit(void)
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index 51ae0c3e470a..13c27f17192c 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -359,20 +359,9 @@ static void sco_sock_kill(struct sock *sk)
sock_put(sk);
}
-/* Close socket.
- * Must be called on unlocked socket.
- */
-static void sco_sock_close(struct sock *sk)
+static void __sco_sock_close(struct sock *sk)
{
- struct sco_conn *conn;
-
- sco_sock_clear_timer(sk);
-
- lock_sock(sk);
-
- conn = sco_pi(sk)->conn;
-
- BT_DBG("sk %p state %d conn %p socket %p", sk, sk->sk_state, conn, sk->sk_socket);
+ BT_DBG("sk %p state %d socket %p", sk, sk->sk_state, sk->sk_socket);
switch (sk->sk_state) {
case BT_LISTEN:
@@ -390,9 +379,15 @@ static void sco_sock_close(struct sock *sk)
sock_set_flag(sk, SOCK_ZAPPED);
break;
}
+}
+/* Must be called on unlocked socket. */
+static void sco_sock_close(struct sock *sk)
+{
+ sco_sock_clear_timer(sk);
+ lock_sock(sk);
+ __sco_sock_close(sk);
release_sock(sk);
-
sco_sock_kill(sk);
}
@@ -748,6 +743,30 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
return err;
}
+static int sco_sock_shutdown(struct socket *sock, int how)
+{
+ struct sock *sk = sock->sk;
+ int err = 0;
+
+ BT_DBG("sock %p, sk %p", sock, sk);
+
+ if (!sk)
+ return 0;
+
+ lock_sock(sk);
+ if (!sk->sk_shutdown) {
+ sk->sk_shutdown = SHUTDOWN_MASK;
+ sco_sock_clear_timer(sk);
+ __sco_sock_close(sk);
+
+ if (sock_flag(sk, SOCK_LINGER) && sk->sk_lingertime)
+ err = bt_sock_wait_state(sk, BT_CLOSED,
+ sk->sk_lingertime);
+ }
+ release_sock(sk);
+ return err;
+}
+
static int sco_sock_release(struct socket *sock)
{
struct sock *sk = sock->sk;
@@ -969,7 +988,7 @@ static const struct proto_ops sco_sock_ops = {
.ioctl = bt_sock_ioctl,
.mmap = sock_no_mmap,
.socketpair = sock_no_socketpair,
- .shutdown = sock_no_shutdown,
+ .shutdown = sco_sock_shutdown,
.setsockopt = sco_sock_setsockopt,
.getsockopt = sco_sock_getsockopt
};
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 18538d7460d7..07a07770c8b6 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -20,7 +20,7 @@
#include "br_private.h"
/* net device transmit always called with no BH (preempt_disabled) */
-int br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
+netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct net_bridge *br = netdev_priv(dev);
const unsigned char *dest = skb->data;
@@ -39,7 +39,7 @@ int br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
else
br_flood_deliver(br, skb);
- return 0;
+ return NETDEV_TX_OK;
}
static int br_dev_open(struct net_device *dev)
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
index d2c27c808d3b..bc1704ac6cd9 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -22,7 +22,8 @@
static inline int should_deliver(const struct net_bridge_port *p,
const struct sk_buff *skb)
{
- return (skb->dev != p->dev && p->state == BR_STATE_FORWARDING);
+ return (((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) &&
+ p->state == BR_STATE_FORWARDING);
}
static inline unsigned packet_length(const struct sk_buff *skb)
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index eb404dc3ed6e..142ebac14176 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -256,6 +256,7 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br,
p->path_cost = port_cost(dev);
p->priority = 0x8000 >> BR_PORT_BITS;
p->port_no = index;
+ p->flags = 0;
br_init_port(p);
p->state = BR_STATE_DISABLED;
br_stp_port_timer_init(p);
@@ -263,6 +264,10 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br,
return p;
}
+static struct device_type br_type = {
+ .name = "bridge",
+};
+
int br_add_bridge(struct net *net, const char *name)
{
struct net_device *dev;
@@ -279,6 +284,8 @@ int br_add_bridge(struct net *net, const char *name)
goto out_free;
}
+ SET_NETDEV_DEVTYPE(dev, &br_type);
+
ret = register_netdevice(dev);
if (ret)
goto out_free;
diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c
index d22f611e4004..907a82e9023d 100644
--- a/net/bridge/br_netfilter.c
+++ b/net/bridge/br_netfilter.c
@@ -359,7 +359,7 @@ static int br_nf_pre_routing_finish(struct sk_buff *skb)
},
.proto = 0,
};
- struct in_device *in_dev = in_dev_get(dev);
+ struct in_device *in_dev = __in_dev_get_rcu(dev);
/* If err equals -EHOSTUNREACH the error is due to a
* martian destination or due to the fact that
@@ -905,46 +905,62 @@ static unsigned int ip_sabotage_in(unsigned int hook, struct sk_buff *skb,
* For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because
* ip_refrag() can return NF_STOLEN. */
static struct nf_hook_ops br_nf_ops[] __read_mostly = {
- { .hook = br_nf_pre_routing,
- .owner = THIS_MODULE,
- .pf = PF_BRIDGE,
- .hooknum = NF_BR_PRE_ROUTING,
- .priority = NF_BR_PRI_BRNF, },
- { .hook = br_nf_local_in,
- .owner = THIS_MODULE,
- .pf = PF_BRIDGE,
- .hooknum = NF_BR_LOCAL_IN,
- .priority = NF_BR_PRI_BRNF, },
- { .hook = br_nf_forward_ip,
- .owner = THIS_MODULE,
- .pf = PF_BRIDGE,
- .hooknum = NF_BR_FORWARD,
- .priority = NF_BR_PRI_BRNF - 1, },
- { .hook = br_nf_forward_arp,
- .owner = THIS_MODULE,
- .pf = PF_BRIDGE,
- .hooknum = NF_BR_FORWARD,
- .priority = NF_BR_PRI_BRNF, },
- { .hook = br_nf_local_out,
- .owner = THIS_MODULE,
- .pf = PF_BRIDGE,
- .hooknum = NF_BR_LOCAL_OUT,
- .priority = NF_BR_PRI_FIRST, },
- { .hook = br_nf_post_routing,
- .owner = THIS_MODULE,
- .pf = PF_BRIDGE,
- .hooknum = NF_BR_POST_ROUTING,
- .priority = NF_BR_PRI_LAST, },
- { .hook = ip_sabotage_in,
- .owner = THIS_MODULE,
- .pf = PF_INET,
- .hooknum = NF_INET_PRE_ROUTING,
- .priority = NF_IP_PRI_FIRST, },
- { .hook = ip_sabotage_in,
- .owner = THIS_MODULE,
- .pf = PF_INET6,
- .hooknum = NF_INET_PRE_ROUTING,
- .priority = NF_IP6_PRI_FIRST, },
+ {
+ .hook = br_nf_pre_routing,
+ .owner = THIS_MODULE,
+ .pf = PF_BRIDGE,
+ .hooknum = NF_BR_PRE_ROUTING,
+ .priority = NF_BR_PRI_BRNF,
+ },
+ {
+ .hook = br_nf_local_in,
+ .owner = THIS_MODULE,
+ .pf = PF_BRIDGE,
+ .hooknum = NF_BR_LOCAL_IN,
+ .priority = NF_BR_PRI_BRNF,
+ },
+ {
+ .hook = br_nf_forward_ip,
+ .owner = THIS_MODULE,
+ .pf = PF_BRIDGE,
+ .hooknum = NF_BR_FORWARD,
+ .priority = NF_BR_PRI_BRNF - 1,
+ },
+ {
+ .hook = br_nf_forward_arp,
+ .owner = THIS_MODULE,
+ .pf = PF_BRIDGE,
+ .hooknum = NF_BR_FORWARD,
+ .priority = NF_BR_PRI_BRNF,
+ },
+ {
+ .hook = br_nf_local_out,
+ .owner = THIS_MODULE,
+ .pf = PF_BRIDGE,
+ .hooknum = NF_BR_LOCAL_OUT,
+ .priority = NF_BR_PRI_FIRST,
+ },
+ {
+ .hook = br_nf_post_routing,
+ .owner = THIS_MODULE,
+ .pf = PF_BRIDGE,
+ .hooknum = NF_BR_POST_ROUTING,
+ .priority = NF_BR_PRI_LAST,
+ },
+ {
+ .hook = ip_sabotage_in,
+ .owner = THIS_MODULE,
+ .pf = PF_INET,
+ .hooknum = NF_INET_PRE_ROUTING,
+ .priority = NF_IP_PRI_FIRST,
+ },
+ {
+ .hook = ip_sabotage_in,
+ .owner = THIS_MODULE,
+ .pf = PF_INET6,
+ .hooknum = NF_INET_PRE_ROUTING,
+ .priority = NF_IP6_PRI_FIRST,
+ },
};
#ifdef CONFIG_SYSCTL
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index d5b5537272b4..2114e45682ea 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -81,6 +81,9 @@ struct net_bridge_port
struct timer_list message_age_timer;
struct kobject kobj;
struct rcu_head rcu;
+
+ unsigned long flags;
+#define BR_HAIRPIN_MODE 0x00000001
};
struct net_bridge
@@ -140,7 +143,8 @@ static inline int br_is_root_bridge(const struct net_bridge *br)
/* br_device.c */
extern void br_dev_setup(struct net_device *dev);
-extern int br_dev_xmit(struct sk_buff *skb, struct net_device *dev);
+extern netdev_tx_t br_dev_xmit(struct sk_buff *skb,
+ struct net_device *dev);
/* br_fdb.c */
extern int br_fdb_init(void);
diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c
index 0660515f3992..fd3f8d6c0998 100644
--- a/net/bridge/br_stp.c
+++ b/net/bridge/br_stp.c
@@ -21,7 +21,7 @@
*/
#define MESSAGE_AGE_INCR ((HZ < 256) ? 1 : (HZ/256))
-static const char *br_port_state_names[] = {
+static const char *const br_port_state_names[] = {
[BR_STATE_DISABLED] = "disabled",
[BR_STATE_LISTENING] = "listening",
[BR_STATE_LEARNING] = "learning",
diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
index 4a3cdf8f3813..820643a3ba9c 100644
--- a/net/bridge/br_sysfs_if.c
+++ b/net/bridge/br_sysfs_if.c
@@ -143,6 +143,22 @@ static ssize_t store_flush(struct net_bridge_port *p, unsigned long v)
}
static BRPORT_ATTR(flush, S_IWUSR, NULL, store_flush);
+static ssize_t show_hairpin_mode(struct net_bridge_port *p, char *buf)
+{
+ int hairpin_mode = (p->flags & BR_HAIRPIN_MODE) ? 1 : 0;
+ return sprintf(buf, "%d\n", hairpin_mode);
+}
+static ssize_t store_hairpin_mode(struct net_bridge_port *p, unsigned long v)
+{
+ if (v)
+ p->flags |= BR_HAIRPIN_MODE;
+ else
+ p->flags &= ~BR_HAIRPIN_MODE;
+ return 0;
+}
+static BRPORT_ATTR(hairpin_mode, S_IRUGO | S_IWUSR,
+ show_hairpin_mode, store_hairpin_mode);
+
static struct brport_attribute *brport_attrs[] = {
&brport_attr_path_cost,
&brport_attr_priority,
@@ -159,6 +175,7 @@ static struct brport_attribute *brport_attrs[] = {
&brport_attr_forward_delay_timer,
&brport_attr_hold_timer,
&brport_attr_flush,
+ &brport_attr_hairpin_mode,
NULL
};
diff --git a/net/bridge/netfilter/ebt_log.c b/net/bridge/netfilter/ebt_log.c
index a94f3cc377c0..e4ea3fdd1d41 100644
--- a/net/bridge/netfilter/ebt_log.c
+++ b/net/bridge/netfilter/ebt_log.c
@@ -50,14 +50,6 @@ struct arppayload
unsigned char ip_dst[4];
};
-static void print_MAC(const unsigned char *p)
-{
- int i;
-
- for (i = 0; i < ETH_ALEN; i++, p++)
- printk("%02x%c", *p, i == ETH_ALEN - 1 ? ' ':':');
-}
-
static void
print_ports(const struct sk_buff *skb, uint8_t protocol, int offset)
{
@@ -88,14 +80,11 @@ ebt_log_packet(u_int8_t pf, unsigned int hooknum,
unsigned int bitmask;
spin_lock_bh(&ebt_log_lock);
- printk("<%c>%s IN=%s OUT=%s MAC source = ", '0' + loginfo->u.log.level,
- prefix, in ? in->name : "", out ? out->name : "");
-
- print_MAC(eth_hdr(skb)->h_source);
- printk("MAC dest = ");
- print_MAC(eth_hdr(skb)->h_dest);
-
- printk("proto = 0x%04x", ntohs(eth_hdr(skb)->h_proto));
+ printk("<%c>%s IN=%s OUT=%s MAC source = %pM MAC dest = %pM proto = 0x%04x",
+ '0' + loginfo->u.log.level, prefix,
+ in ? in->name : "", out ? out->name : "",
+ eth_hdr(skb)->h_source, eth_hdr(skb)->h_dest,
+ ntohs(eth_hdr(skb)->h_proto));
if (loginfo->type == NF_LOG_TYPE_LOG)
bitmask = loginfo->u.log.logflags;
@@ -171,12 +160,8 @@ ebt_log_packet(u_int8_t pf, unsigned int hooknum,
printk(" INCOMPLETE ARP payload");
goto out;
}
- printk(" ARP MAC SRC=");
- print_MAC(ap->mac_src);
- printk(" ARP IP SRC=%pI4", ap->ip_src);
- printk(" ARP MAC DST=");
- print_MAC(ap->mac_dst);
- printk(" ARP IP DST=%pI4", ap->ip_dst);
+ printk(" ARP MAC SRC=%pM ARP IP SRC=%pI4 ARP MAC DST=%pM ARP IP DST=%pI4",
+ ap->mac_src, ap->ip_src, ap->mac_dst, ap->ip_dst);
}
}
out:
diff --git a/net/bridge/netfilter/ebt_ulog.c b/net/bridge/netfilter/ebt_ulog.c
index 133eeae45a4f..ce50688a6431 100644
--- a/net/bridge/netfilter/ebt_ulog.c
+++ b/net/bridge/netfilter/ebt_ulog.c
@@ -266,7 +266,7 @@ static bool ebt_ulog_tg_check(const struct xt_tgchk_param *par)
if (uloginfo->qthreshold > EBT_ULOG_MAX_QLEN)
uloginfo->qthreshold = EBT_ULOG_MAX_QLEN;
- return 0;
+ return true;
}
static struct xt_target ebt_ulog_tg_reg __read_mostly = {
diff --git a/net/bridge/netfilter/ebtable_broute.c b/net/bridge/netfilter/ebtable_broute.c
index c751111440f8..d32ab13e728c 100644
--- a/net/bridge/netfilter/ebtable_broute.c
+++ b/net/bridge/netfilter/ebtable_broute.c
@@ -41,7 +41,7 @@ static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
return 0;
}
-static struct ebt_table broute_table =
+static const struct ebt_table broute_table =
{
.name = "broute",
.table = &initial_table,
diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c
index a5eea72938a6..60b1a6ca7185 100644
--- a/net/bridge/netfilter/ebtable_filter.c
+++ b/net/bridge/netfilter/ebtable_filter.c
@@ -50,7 +50,7 @@ static int check(const struct ebt_table_info *info, unsigned int valid_hooks)
return 0;
}
-static struct ebt_table frame_filter =
+static const struct ebt_table frame_filter =
{
.name = "filter",
.table = &initial_table,
@@ -77,21 +77,21 @@ static struct nf_hook_ops ebt_ops_filter[] __read_mostly = {
{
.hook = ebt_in_hook,
.owner = THIS_MODULE,
- .pf = PF_BRIDGE,
+ .pf = NFPROTO_BRIDGE,
.hooknum = NF_BR_LOCAL_IN,
.priority = NF_BR_PRI_FILTER_BRIDGED,
},
{
.hook = ebt_in_hook,
.owner = THIS_MODULE,
- .pf = PF_BRIDGE,
+ .pf = NFPROTO_BRIDGE,
.hooknum = NF_BR_FORWARD,
.priority = NF_BR_PRI_FILTER_BRIDGED,
},
{
.hook = ebt_out_hook,
.owner = THIS_MODULE,
- .pf = PF_BRIDGE,
+ .pf = NFPROTO_BRIDGE,
.hooknum = NF_BR_LOCAL_OUT,
.priority = NF_BR_PRI_FILTER_OTHER,
},
diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c
index 6024c551f9a9..4a98804203b0 100644
--- a/net/bridge/netfilter/ebtable_nat.c
+++ b/net/bridge/netfilter/ebtable_nat.c
@@ -77,21 +77,21 @@ static struct nf_hook_ops ebt_ops_nat[] __read_mostly = {
{
.hook = ebt_nat_out,
.owner = THIS_MODULE,
- .pf = PF_BRIDGE,
+ .pf = NFPROTO_BRIDGE,
.hooknum = NF_BR_LOCAL_OUT,
.priority = NF_BR_PRI_NAT_DST_OTHER,
},
{
.hook = ebt_nat_out,
.owner = THIS_MODULE,
- .pf = PF_BRIDGE,
+ .pf = NFPROTO_BRIDGE,
.hooknum = NF_BR_POST_ROUTING,
.priority = NF_BR_PRI_NAT_SRC,
},
{
.hook = ebt_nat_in,
.owner = THIS_MODULE,
- .pf = PF_BRIDGE,
+ .pf = NFPROTO_BRIDGE,
.hooknum = NF_BR_PRE_ROUTING,
.priority = NF_BR_PRI_NAT_DST_BRIDGED,
},
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index 37928d5f2840..bd1c65425d4f 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -1103,23 +1103,24 @@ free_newinfo:
return ret;
}
-struct ebt_table *ebt_register_table(struct net *net, struct ebt_table *table)
+struct ebt_table *
+ebt_register_table(struct net *net, const struct ebt_table *input_table)
{
struct ebt_table_info *newinfo;
- struct ebt_table *t;
+ struct ebt_table *t, *table;
struct ebt_replace_kernel *repl;
int ret, i, countersize;
void *p;
- if (!table || !(repl = table->table) || !repl->entries ||
- repl->entries_size == 0 ||
- repl->counters || table->private) {
+ if (input_table == NULL || (repl = input_table->table) == NULL ||
+ repl->entries == 0 || repl->entries_size == 0 ||
+ repl->counters != NULL || input_table->private != NULL) {
BUGPRINT("Bad table data for ebt_register_table!!!\n");
return ERR_PTR(-EINVAL);
}
/* Don't add one table to multiple lists. */
- table = kmemdup(table, sizeof(struct ebt_table), GFP_KERNEL);
+ table = kmemdup(input_table, sizeof(struct ebt_table), GFP_KERNEL);
if (!table) {
ret = -ENOMEM;
goto out;
diff --git a/net/can/af_can.c b/net/can/af_can.c
index e733725b11d4..ef1c43a2ed56 100644
--- a/net/can/af_can.c
+++ b/net/can/af_can.c
@@ -651,12 +651,16 @@ static int can_rcv(struct sk_buff *skb, struct net_device *dev,
struct can_frame *cf = (struct can_frame *)skb->data;
int matches;
- if (dev->type != ARPHRD_CAN || !net_eq(dev_net(dev), &init_net)) {
- kfree_skb(skb);
- return 0;
- }
+ if (!net_eq(dev_net(dev), &init_net))
+ goto drop;
- BUG_ON(skb->len != sizeof(struct can_frame) || cf->can_dlc > 8);
+ if (WARN_ONCE(dev->type != ARPHRD_CAN ||
+ skb->len != sizeof(struct can_frame) ||
+ cf->can_dlc > 8,
+ "PF_CAN: dropped non conform skbuf: "
+ "dev type %d, len %d, can_dlc %d\n",
+ dev->type, skb->len, cf->can_dlc))
+ goto drop;
/* update statistics */
can_stats.rx_frames++;
@@ -682,7 +686,11 @@ static int can_rcv(struct sk_buff *skb, struct net_device *dev,
can_stats.matches_delta++;
}
- return 0;
+ return NET_RX_SUCCESS;
+
+drop:
+ kfree_skb(skb);
+ return NET_RX_DROP;
}
/*
diff --git a/net/can/bcm.c b/net/can/bcm.c
index 72720c710351..597da4f8f888 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -46,6 +46,7 @@
#include <linux/hrtimer.h>
#include <linux/list.h>
#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
#include <linux/uio.h>
#include <linux/net.h>
#include <linux/netdevice.h>
@@ -146,23 +147,18 @@ static char *bcm_proc_getifname(int ifindex)
return "???";
}
-static int bcm_read_proc(char *page, char **start, off_t off,
- int count, int *eof, void *data)
+static int bcm_proc_show(struct seq_file *m, void *v)
{
- int len = 0;
- struct sock *sk = (struct sock *)data;
+ struct sock *sk = (struct sock *)m->private;
struct bcm_sock *bo = bcm_sk(sk);
struct bcm_op *op;
- len += snprintf(page + len, PAGE_SIZE - len, ">>> socket %p",
- sk->sk_socket);
- len += snprintf(page + len, PAGE_SIZE - len, " / sk %p", sk);
- len += snprintf(page + len, PAGE_SIZE - len, " / bo %p", bo);
- len += snprintf(page + len, PAGE_SIZE - len, " / dropped %lu",
- bo->dropped_usr_msgs);
- len += snprintf(page + len, PAGE_SIZE - len, " / bound %s",
- bcm_proc_getifname(bo->ifindex));
- len += snprintf(page + len, PAGE_SIZE - len, " <<<\n");
+ seq_printf(m, ">>> socket %p", sk->sk_socket);
+ seq_printf(m, " / sk %p", sk);
+ seq_printf(m, " / bo %p", bo);
+ seq_printf(m, " / dropped %lu", bo->dropped_usr_msgs);
+ seq_printf(m, " / bound %s", bcm_proc_getifname(bo->ifindex));
+ seq_printf(m, " <<<\n");
list_for_each_entry(op, &bo->rx_ops, list) {
@@ -172,71 +168,62 @@ static int bcm_read_proc(char *page, char **start, off_t off,
if (!op->frames_abs)
continue;
- len += snprintf(page + len, PAGE_SIZE - len,
- "rx_op: %03X %-5s ",
+ seq_printf(m, "rx_op: %03X %-5s ",
op->can_id, bcm_proc_getifname(op->ifindex));
- len += snprintf(page + len, PAGE_SIZE - len, "[%d]%c ",
- op->nframes,
+ seq_printf(m, "[%d]%c ", op->nframes,
(op->flags & RX_CHECK_DLC)?'d':' ');
if (op->kt_ival1.tv64)
- len += snprintf(page + len, PAGE_SIZE - len,
- "timeo=%lld ",
+ seq_printf(m, "timeo=%lld ",
(long long)
ktime_to_us(op->kt_ival1));
if (op->kt_ival2.tv64)
- len += snprintf(page + len, PAGE_SIZE - len,
- "thr=%lld ",
+ seq_printf(m, "thr=%lld ",
(long long)
ktime_to_us(op->kt_ival2));
- len += snprintf(page + len, PAGE_SIZE - len,
- "# recv %ld (%ld) => reduction: ",
+ seq_printf(m, "# recv %ld (%ld) => reduction: ",
op->frames_filtered, op->frames_abs);
reduction = 100 - (op->frames_filtered * 100) / op->frames_abs;
- len += snprintf(page + len, PAGE_SIZE - len, "%s%ld%%\n",
+ seq_printf(m, "%s%ld%%\n",
(reduction == 100)?"near ":"", reduction);
-
- if (len > PAGE_SIZE - 200) {
- /* mark output cut off */
- len += snprintf(page + len, PAGE_SIZE - len, "(..)\n");
- break;
- }
}
list_for_each_entry(op, &bo->tx_ops, list) {
- len += snprintf(page + len, PAGE_SIZE - len,
- "tx_op: %03X %s [%d] ",
+ seq_printf(m, "tx_op: %03X %s [%d] ",
op->can_id, bcm_proc_getifname(op->ifindex),
op->nframes);
if (op->kt_ival1.tv64)
- len += snprintf(page + len, PAGE_SIZE - len, "t1=%lld ",
+ seq_printf(m, "t1=%lld ",
(long long) ktime_to_us(op->kt_ival1));
if (op->kt_ival2.tv64)
- len += snprintf(page + len, PAGE_SIZE - len, "t2=%lld ",
+ seq_printf(m, "t2=%lld ",
(long long) ktime_to_us(op->kt_ival2));
- len += snprintf(page + len, PAGE_SIZE - len, "# sent %ld\n",
- op->frames_abs);
-
- if (len > PAGE_SIZE - 100) {
- /* mark output cut off */
- len += snprintf(page + len, PAGE_SIZE - len, "(..)\n");
- break;
- }
+ seq_printf(m, "# sent %ld\n", op->frames_abs);
}
+ seq_putc(m, '\n');
+ return 0;
+}
- len += snprintf(page + len, PAGE_SIZE - len, "\n");
-
- *eof = 1;
- return len;
+static int bcm_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, bcm_proc_show, PDE(inode)->data);
}
+static const struct file_operations bcm_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = bcm_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
/*
* bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface
* of the given bcm tx op
@@ -1515,9 +1502,9 @@ static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len,
if (proc_dir) {
/* unique socket address as filename */
sprintf(bo->procname, "%p", sock);
- bo->bcm_proc_read = create_proc_read_entry(bo->procname, 0644,
- proc_dir,
- bcm_read_proc, sk);
+ bo->bcm_proc_read = proc_create_data(bo->procname, 0644,
+ proc_dir,
+ &bcm_proc_fops, sk);
}
return 0;
diff --git a/net/can/proc.c b/net/can/proc.c
index 1463653dbe34..9b9ad29be567 100644
--- a/net/can/proc.c
+++ b/net/can/proc.c
@@ -196,8 +196,8 @@ void can_stat_update(unsigned long data)
*
*/
-static int can_print_rcvlist(char *page, int len, struct hlist_head *rx_list,
- struct net_device *dev)
+static void can_print_rcvlist(struct seq_file *m, struct hlist_head *rx_list,
+ struct net_device *dev)
{
struct receiver *r;
struct hlist_node *n;
@@ -208,199 +208,188 @@ static int can_print_rcvlist(char *page, int len, struct hlist_head *rx_list,
" %-5s %08X %08x %08x %08x %8ld %s\n" :
" %-5s %03X %08x %08lx %08lx %8ld %s\n";
- len += snprintf(page + len, PAGE_SIZE - len, fmt,
- DNAME(dev), r->can_id, r->mask,
+ seq_printf(m, fmt, DNAME(dev), r->can_id, r->mask,
(unsigned long)r->func, (unsigned long)r->data,
r->matches, r->ident);
-
- /* does a typical line fit into the current buffer? */
-
- /* 100 Bytes before end of buffer */
- if (len > PAGE_SIZE - 100) {
- /* mark output cut off */
- len += snprintf(page + len, PAGE_SIZE - len,
- " (..)\n");
- break;
- }
}
rcu_read_unlock();
-
- return len;
}
-static int can_print_recv_banner(char *page, int len)
+static void can_print_recv_banner(struct seq_file *m)
{
/*
* can1. 00000000 00000000 00000000
* ....... 0 tp20
*/
- len += snprintf(page + len, PAGE_SIZE - len,
- " device can_id can_mask function"
+ seq_puts(m, " device can_id can_mask function"
" userdata matches ident\n");
-
- return len;
}
-static int can_proc_read_stats(char *page, char **start, off_t off,
- int count, int *eof, void *data)
+static int can_stats_proc_show(struct seq_file *m, void *v)
{
- int len = 0;
+ seq_putc(m, '\n');
+ seq_printf(m, " %8ld transmitted frames (TXF)\n", can_stats.tx_frames);
+ seq_printf(m, " %8ld received frames (RXF)\n", can_stats.rx_frames);
+ seq_printf(m, " %8ld matched frames (RXMF)\n", can_stats.matches);
- len += snprintf(page + len, PAGE_SIZE - len, "\n");
- len += snprintf(page + len, PAGE_SIZE - len,
- " %8ld transmitted frames (TXF)\n",
- can_stats.tx_frames);
- len += snprintf(page + len, PAGE_SIZE - len,
- " %8ld received frames (RXF)\n", can_stats.rx_frames);
- len += snprintf(page + len, PAGE_SIZE - len,
- " %8ld matched frames (RXMF)\n", can_stats.matches);
-
- len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ seq_putc(m, '\n');
if (can_stattimer.function == can_stat_update) {
- len += snprintf(page + len, PAGE_SIZE - len,
- " %8ld %% total match ratio (RXMR)\n",
+ seq_printf(m, " %8ld %% total match ratio (RXMR)\n",
can_stats.total_rx_match_ratio);
- len += snprintf(page + len, PAGE_SIZE - len,
- " %8ld frames/s total tx rate (TXR)\n",
+ seq_printf(m, " %8ld frames/s total tx rate (TXR)\n",
can_stats.total_tx_rate);
- len += snprintf(page + len, PAGE_SIZE - len,
- " %8ld frames/s total rx rate (RXR)\n",
+ seq_printf(m, " %8ld frames/s total rx rate (RXR)\n",
can_stats.total_rx_rate);
- len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ seq_putc(m, '\n');
- len += snprintf(page + len, PAGE_SIZE - len,
- " %8ld %% current match ratio (CRXMR)\n",
+ seq_printf(m, " %8ld %% current match ratio (CRXMR)\n",
can_stats.current_rx_match_ratio);
- len += snprintf(page + len, PAGE_SIZE - len,
- " %8ld frames/s current tx rate (CTXR)\n",
+ seq_printf(m, " %8ld frames/s current tx rate (CTXR)\n",
can_stats.current_tx_rate);
- len += snprintf(page + len, PAGE_SIZE - len,
- " %8ld frames/s current rx rate (CRXR)\n",
+ seq_printf(m, " %8ld frames/s current rx rate (CRXR)\n",
can_stats.current_rx_rate);
- len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ seq_putc(m, '\n');
- len += snprintf(page + len, PAGE_SIZE - len,
- " %8ld %% max match ratio (MRXMR)\n",
+ seq_printf(m, " %8ld %% max match ratio (MRXMR)\n",
can_stats.max_rx_match_ratio);
- len += snprintf(page + len, PAGE_SIZE - len,
- " %8ld frames/s max tx rate (MTXR)\n",
+ seq_printf(m, " %8ld frames/s max tx rate (MTXR)\n",
can_stats.max_tx_rate);
- len += snprintf(page + len, PAGE_SIZE - len,
- " %8ld frames/s max rx rate (MRXR)\n",
+ seq_printf(m, " %8ld frames/s max rx rate (MRXR)\n",
can_stats.max_rx_rate);
- len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ seq_putc(m, '\n');
}
- len += snprintf(page + len, PAGE_SIZE - len,
- " %8ld current receive list entries (CRCV)\n",
+ seq_printf(m, " %8ld current receive list entries (CRCV)\n",
can_pstats.rcv_entries);
- len += snprintf(page + len, PAGE_SIZE - len,
- " %8ld maximum receive list entries (MRCV)\n",
+ seq_printf(m, " %8ld maximum receive list entries (MRCV)\n",
can_pstats.rcv_entries_max);
if (can_pstats.stats_reset)
- len += snprintf(page + len, PAGE_SIZE - len,
- "\n %8ld statistic resets (STR)\n",
+ seq_printf(m, "\n %8ld statistic resets (STR)\n",
can_pstats.stats_reset);
if (can_pstats.user_reset)
- len += snprintf(page + len, PAGE_SIZE - len,
- " %8ld user statistic resets (USTR)\n",
+ seq_printf(m, " %8ld user statistic resets (USTR)\n",
can_pstats.user_reset);
- len += snprintf(page + len, PAGE_SIZE - len, "\n");
-
- *eof = 1;
- return len;
+ seq_putc(m, '\n');
+ return 0;
}
-static int can_proc_read_reset_stats(char *page, char **start, off_t off,
- int count, int *eof, void *data)
+static int can_stats_proc_open(struct inode *inode, struct file *file)
{
- int len = 0;
+ return single_open(file, can_stats_proc_show, NULL);
+}
+
+static const struct file_operations can_stats_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = can_stats_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+static int can_reset_stats_proc_show(struct seq_file *m, void *v)
+{
user_reset = 1;
if (can_stattimer.function == can_stat_update) {
- len += snprintf(page + len, PAGE_SIZE - len,
- "Scheduled statistic reset #%ld.\n",
+ seq_printf(m, "Scheduled statistic reset #%ld.\n",
can_pstats.stats_reset + 1);
} else {
if (can_stats.jiffies_init != jiffies)
can_init_stats();
- len += snprintf(page + len, PAGE_SIZE - len,
- "Performed statistic reset #%ld.\n",
+ seq_printf(m, "Performed statistic reset #%ld.\n",
can_pstats.stats_reset);
}
+ return 0;
+}
- *eof = 1;
- return len;
+static int can_reset_stats_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, can_reset_stats_proc_show, NULL);
}
-static int can_proc_read_version(char *page, char **start, off_t off,
- int count, int *eof, void *data)
+static const struct file_operations can_reset_stats_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = can_reset_stats_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int can_version_proc_show(struct seq_file *m, void *v)
{
- int len = 0;
+ seq_printf(m, "%s\n", CAN_VERSION_STRING);
+ return 0;
+}
- len += snprintf(page + len, PAGE_SIZE - len, "%s\n",
- CAN_VERSION_STRING);
- *eof = 1;
- return len;
+static int can_version_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, can_version_proc_show, NULL);
}
-static int can_proc_read_rcvlist(char *page, char **start, off_t off,
- int count, int *eof, void *data)
+static const struct file_operations can_version_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = can_version_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int can_rcvlist_proc_show(struct seq_file *m, void *v)
{
/* double cast to prevent GCC warning */
- int idx = (int)(long)data;
- int len = 0;
+ int idx = (int)(long)m->private;
struct dev_rcv_lists *d;
struct hlist_node *n;
- len += snprintf(page + len, PAGE_SIZE - len,
- "\nreceive list '%s':\n", rx_list_name[idx]);
+ seq_printf(m, "\nreceive list '%s':\n", rx_list_name[idx]);
rcu_read_lock();
hlist_for_each_entry_rcu(d, n, &can_rx_dev_list, list) {
if (!hlist_empty(&d->rx[idx])) {
- len = can_print_recv_banner(page, len);
- len = can_print_rcvlist(page, len, &d->rx[idx], d->dev);
+ can_print_recv_banner(m);
+ can_print_rcvlist(m, &d->rx[idx], d->dev);
} else
- len += snprintf(page + len, PAGE_SIZE - len,
- " (%s: no entry)\n", DNAME(d->dev));
-
- /* exit on end of buffer? */
- if (len > PAGE_SIZE - 100)
- break;
+ seq_printf(m, " (%s: no entry)\n", DNAME(d->dev));
}
rcu_read_unlock();
- len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ seq_putc(m, '\n');
+ return 0;
+}
- *eof = 1;
- return len;
+static int can_rcvlist_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, can_rcvlist_proc_show, PDE(inode)->data);
}
-static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off,
- int count, int *eof, void *data)
+static const struct file_operations can_rcvlist_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = can_rcvlist_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int can_rcvlist_sff_proc_show(struct seq_file *m, void *v)
{
- int len = 0;
struct dev_rcv_lists *d;
struct hlist_node *n;
/* RX_SFF */
- len += snprintf(page + len, PAGE_SIZE - len,
- "\nreceive list 'rx_sff':\n");
+ seq_puts(m, "\nreceive list 'rx_sff':\n");
rcu_read_lock();
hlist_for_each_entry_rcu(d, n, &can_rx_dev_list, list) {
@@ -413,46 +402,38 @@ static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off,
}
if (!all_empty) {
- len = can_print_recv_banner(page, len);
+ can_print_recv_banner(m);
for (i = 0; i < 0x800; i++) {
- if (!hlist_empty(&d->rx_sff[i]) &&
- len < PAGE_SIZE - 100)
- len = can_print_rcvlist(page, len,
- &d->rx_sff[i],
- d->dev);
+ if (!hlist_empty(&d->rx_sff[i]))
+ can_print_rcvlist(m, &d->rx_sff[i],
+ d->dev);
}
} else
- len += snprintf(page + len, PAGE_SIZE - len,
- " (%s: no entry)\n", DNAME(d->dev));
-
- /* exit on end of buffer? */
- if (len > PAGE_SIZE - 100)
- break;
+ seq_printf(m, " (%s: no entry)\n", DNAME(d->dev));
}
rcu_read_unlock();
- len += snprintf(page + len, PAGE_SIZE - len, "\n");
+ seq_putc(m, '\n');
+ return 0;
+}
- *eof = 1;
- return len;
+static int can_rcvlist_sff_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, can_rcvlist_sff_proc_show, NULL);
}
+static const struct file_operations can_rcvlist_sff_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = can_rcvlist_sff_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
/*
* proc utility functions
*/
-static struct proc_dir_entry *can_create_proc_readentry(const char *name,
- mode_t mode,
- read_proc_t *read_proc,
- void *data)
-{
- if (can_dir)
- return create_proc_read_entry(name, mode, can_dir, read_proc,
- data);
- else
- return NULL;
-}
-
static void can_remove_proc_readentry(const char *name)
{
if (can_dir)
@@ -474,24 +455,24 @@ void can_init_proc(void)
}
/* own procfs entries from the AF_CAN core */
- pde_version = can_create_proc_readentry(CAN_PROC_VERSION, 0644,
- can_proc_read_version, NULL);
- pde_stats = can_create_proc_readentry(CAN_PROC_STATS, 0644,
- can_proc_read_stats, NULL);
- pde_reset_stats = can_create_proc_readentry(CAN_PROC_RESET_STATS, 0644,
- can_proc_read_reset_stats, NULL);
- pde_rcvlist_err = can_create_proc_readentry(CAN_PROC_RCVLIST_ERR, 0644,
- can_proc_read_rcvlist, (void *)RX_ERR);
- pde_rcvlist_all = can_create_proc_readentry(CAN_PROC_RCVLIST_ALL, 0644,
- can_proc_read_rcvlist, (void *)RX_ALL);
- pde_rcvlist_fil = can_create_proc_readentry(CAN_PROC_RCVLIST_FIL, 0644,
- can_proc_read_rcvlist, (void *)RX_FIL);
- pde_rcvlist_inv = can_create_proc_readentry(CAN_PROC_RCVLIST_INV, 0644,
- can_proc_read_rcvlist, (void *)RX_INV);
- pde_rcvlist_eff = can_create_proc_readentry(CAN_PROC_RCVLIST_EFF, 0644,
- can_proc_read_rcvlist, (void *)RX_EFF);
- pde_rcvlist_sff = can_create_proc_readentry(CAN_PROC_RCVLIST_SFF, 0644,
- can_proc_read_rcvlist_sff, NULL);
+ pde_version = proc_create(CAN_PROC_VERSION, 0644, can_dir,
+ &can_version_proc_fops);
+ pde_stats = proc_create(CAN_PROC_STATS, 0644, can_dir,
+ &can_stats_proc_fops);
+ pde_reset_stats = proc_create(CAN_PROC_RESET_STATS, 0644, can_dir,
+ &can_reset_stats_proc_fops);
+ pde_rcvlist_err = proc_create_data(CAN_PROC_RCVLIST_ERR, 0644, can_dir,
+ &can_rcvlist_proc_fops, (void *)RX_ERR);
+ pde_rcvlist_all = proc_create_data(CAN_PROC_RCVLIST_ALL, 0644, can_dir,
+ &can_rcvlist_proc_fops, (void *)RX_ALL);
+ pde_rcvlist_fil = proc_create_data(CAN_PROC_RCVLIST_FIL, 0644, can_dir,
+ &can_rcvlist_proc_fops, (void *)RX_FIL);
+ pde_rcvlist_inv = proc_create_data(CAN_PROC_RCVLIST_INV, 0644, can_dir,
+ &can_rcvlist_proc_fops, (void *)RX_INV);
+ pde_rcvlist_eff = proc_create_data(CAN_PROC_RCVLIST_EFF, 0644, can_dir,
+ &can_rcvlist_proc_fops, (void *)RX_EFF);
+ pde_rcvlist_sff = proc_create(CAN_PROC_RCVLIST_SFF, 0644, can_dir,
+ &can_rcvlist_sff_proc_fops);
}
/*
diff --git a/net/compat.c b/net/compat.c
index 8d739053afe4..12728b17a226 100644
--- a/net/compat.c
+++ b/net/compat.c
@@ -743,6 +743,18 @@ asmlinkage long compat_sys_recvmsg(int fd, struct compat_msghdr __user *msg, uns
return sys_recvmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT);
}
+asmlinkage long compat_sys_recv(int fd, void __user *buf, size_t len, unsigned flags)
+{
+ return sys_recv(fd, buf, len, flags | MSG_CMSG_COMPAT);
+}
+
+asmlinkage long compat_sys_recvfrom(int fd, void __user *buf, size_t len,
+ unsigned flags, struct sockaddr __user *addr,
+ int __user *addrlen)
+{
+ return sys_recvfrom(fd, buf, len, flags | MSG_CMSG_COMPAT, addr, addrlen);
+}
+
asmlinkage long compat_sys_socketcall(int call, u32 __user *args)
{
int ret;
@@ -788,10 +800,11 @@ asmlinkage long compat_sys_socketcall(int call, u32 __user *args)
ret = sys_sendto(a0, compat_ptr(a1), a[2], a[3], compat_ptr(a[4]), a[5]);
break;
case SYS_RECV:
- ret = sys_recv(a0, compat_ptr(a1), a[2], a[3]);
+ ret = compat_sys_recv(a0, compat_ptr(a1), a[2], a[3]);
break;
case SYS_RECVFROM:
- ret = sys_recvfrom(a0, compat_ptr(a1), a[2], a[3], compat_ptr(a[4]), compat_ptr(a[5]));
+ ret = compat_sys_recvfrom(a0, compat_ptr(a1), a[2], a[3],
+ compat_ptr(a[4]), compat_ptr(a[5]));
break;
case SYS_SHUTDOWN:
ret = sys_shutdown(a0,a1);
diff --git a/net/core/datagram.c b/net/core/datagram.c
index b0fe69211eef..1c6cf3a1a4f6 100644
--- a/net/core/datagram.c
+++ b/net/core/datagram.c
@@ -55,6 +55,7 @@
#include <net/checksum.h>
#include <net/sock.h>
#include <net/tcp_states.h>
+#include <trace/events/skb.h>
/*
* Is a socket 'connection oriented' ?
@@ -284,6 +285,8 @@ int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset,
int i, copy = start - offset;
struct sk_buff *frag_iter;
+ trace_skb_copy_datagram_iovec(skb, len);
+
/* Copy header. */
if (copy > 0) {
if (copy > len)
diff --git a/net/core/dev.c b/net/core/dev.c
index 278d489aad3b..84945470ab38 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -191,7 +191,6 @@ static struct list_head ptype_all __read_mostly; /* Taps */
* semaphore held.
*/
DEFINE_RWLOCK(dev_base_lock);
-
EXPORT_SYMBOL(dev_base_lock);
#define NETDEV_HASHBITS 8
@@ -248,6 +247,7 @@ static RAW_NOTIFIER_HEAD(netdev_chain);
*/
DEFINE_PER_CPU(struct softnet_data, softnet_data);
+EXPORT_PER_CPU_SYMBOL(softnet_data);
#ifdef CONFIG_LOCKDEP
/*
@@ -269,10 +269,10 @@ static const unsigned short netdev_lock_type[] =
ARPHRD_IRDA, ARPHRD_FCPP, ARPHRD_FCAL, ARPHRD_FCPL,
ARPHRD_FCFABRIC, ARPHRD_IEEE802_TR, ARPHRD_IEEE80211,
ARPHRD_IEEE80211_PRISM, ARPHRD_IEEE80211_RADIOTAP, ARPHRD_PHONET,
- ARPHRD_PHONET_PIPE, ARPHRD_IEEE802154, ARPHRD_IEEE802154_PHY,
+ ARPHRD_PHONET_PIPE, ARPHRD_IEEE802154,
ARPHRD_VOID, ARPHRD_NONE};
-static const char *netdev_lock_name[] =
+static const char *const netdev_lock_name[] =
{"_xmit_NETROM", "_xmit_ETHER", "_xmit_EETHER", "_xmit_AX25",
"_xmit_PRONET", "_xmit_CHAOS", "_xmit_IEEE802", "_xmit_ARCNET",
"_xmit_APPLETLK", "_xmit_DLCI", "_xmit_ATM", "_xmit_METRICOM",
@@ -287,7 +287,7 @@ static const char *netdev_lock_name[] =
"_xmit_IRDA", "_xmit_FCPP", "_xmit_FCAL", "_xmit_FCPL",
"_xmit_FCFABRIC", "_xmit_IEEE802_TR", "_xmit_IEEE80211",
"_xmit_IEEE80211_PRISM", "_xmit_IEEE80211_RADIOTAP", "_xmit_PHONET",
- "_xmit_PHONET_PIPE", "_xmit_IEEE802154", "_xmit_IEEE802154_PHY",
+ "_xmit_PHONET_PIPE", "_xmit_IEEE802154",
"_xmit_VOID", "_xmit_NONE"};
static struct lock_class_key netdev_xmit_lock_key[ARRAY_SIZE(netdev_lock_type)];
@@ -381,6 +381,7 @@ void dev_add_pack(struct packet_type *pt)
}
spin_unlock_bh(&ptype_lock);
}
+EXPORT_SYMBOL(dev_add_pack);
/**
* __dev_remove_pack - remove packet handler
@@ -418,6 +419,8 @@ void __dev_remove_pack(struct packet_type *pt)
out:
spin_unlock_bh(&ptype_lock);
}
+EXPORT_SYMBOL(__dev_remove_pack);
+
/**
* dev_remove_pack - remove packet handler
* @pt: packet type declaration
@@ -436,6 +439,7 @@ void dev_remove_pack(struct packet_type *pt)
synchronize_net();
}
+EXPORT_SYMBOL(dev_remove_pack);
/******************************************************************************
@@ -499,6 +503,7 @@ int netdev_boot_setup_check(struct net_device *dev)
}
return 0;
}
+EXPORT_SYMBOL(netdev_boot_setup_check);
/**
@@ -591,6 +596,7 @@ struct net_device *__dev_get_by_name(struct net *net, const char *name)
}
return NULL;
}
+EXPORT_SYMBOL(__dev_get_by_name);
/**
* dev_get_by_name - find a device by its name
@@ -615,6 +621,7 @@ struct net_device *dev_get_by_name(struct net *net, const char *name)
read_unlock(&dev_base_lock);
return dev;
}
+EXPORT_SYMBOL(dev_get_by_name);
/**
* __dev_get_by_index - find a device by its ifindex
@@ -640,6 +647,7 @@ struct net_device *__dev_get_by_index(struct net *net, int ifindex)
}
return NULL;
}
+EXPORT_SYMBOL(__dev_get_by_index);
/**
@@ -664,6 +672,7 @@ struct net_device *dev_get_by_index(struct net *net, int ifindex)
read_unlock(&dev_base_lock);
return dev;
}
+EXPORT_SYMBOL(dev_get_by_index);
/**
* dev_getbyhwaddr - find a device by its hardware address
@@ -693,7 +702,6 @@ struct net_device *dev_getbyhwaddr(struct net *net, unsigned short type, char *h
return NULL;
}
-
EXPORT_SYMBOL(dev_getbyhwaddr);
struct net_device *__dev_getfirstbyhwtype(struct net *net, unsigned short type)
@@ -707,7 +715,6 @@ struct net_device *__dev_getfirstbyhwtype(struct net *net, unsigned short type)
return NULL;
}
-
EXPORT_SYMBOL(__dev_getfirstbyhwtype);
struct net_device *dev_getfirstbyhwtype(struct net *net, unsigned short type)
@@ -721,7 +728,6 @@ struct net_device *dev_getfirstbyhwtype(struct net *net, unsigned short type)
rtnl_unlock();
return dev;
}
-
EXPORT_SYMBOL(dev_getfirstbyhwtype);
/**
@@ -736,7 +742,8 @@ EXPORT_SYMBOL(dev_getfirstbyhwtype);
* dev_put to indicate they have finished with it.
*/
-struct net_device * dev_get_by_flags(struct net *net, unsigned short if_flags, unsigned short mask)
+struct net_device *dev_get_by_flags(struct net *net, unsigned short if_flags,
+ unsigned short mask)
{
struct net_device *dev, *ret;
@@ -752,6 +759,7 @@ struct net_device * dev_get_by_flags(struct net *net, unsigned short if_flags, u
read_unlock(&dev_base_lock);
return ret;
}
+EXPORT_SYMBOL(dev_get_by_flags);
/**
* dev_valid_name - check if name is okay for network device
@@ -777,6 +785,7 @@ int dev_valid_name(const char *name)
}
return 1;
}
+EXPORT_SYMBOL(dev_valid_name);
/**
* __dev_alloc_name - allocate a name for a device
@@ -870,6 +879,7 @@ int dev_alloc_name(struct net_device *dev, const char *name)
strlcpy(dev->name, buf, IFNAMSIZ);
return ret;
}
+EXPORT_SYMBOL(dev_alloc_name);
/**
@@ -906,8 +916,7 @@ int dev_change_name(struct net_device *dev, const char *newname)
err = dev_alloc_name(dev, newname);
if (err < 0)
return err;
- }
- else if (__dev_get_by_name(net, newname))
+ } else if (__dev_get_by_name(net, newname))
return -EEXIST;
else
strlcpy(dev->name, newname, IFNAMSIZ);
@@ -970,7 +979,7 @@ int dev_set_alias(struct net_device *dev, const char *alias, size_t len)
return 0;
}
- dev->ifalias = krealloc(dev->ifalias, len+1, GFP_KERNEL);
+ dev->ifalias = krealloc(dev->ifalias, len + 1, GFP_KERNEL);
if (!dev->ifalias)
return -ENOMEM;
@@ -1006,6 +1015,7 @@ void netdev_state_change(struct net_device *dev)
rtmsg_ifinfo(RTM_NEWLINK, dev, 0);
}
}
+EXPORT_SYMBOL(netdev_state_change);
void netdev_bonding_change(struct net_device *dev)
{
@@ -1034,6 +1044,7 @@ void dev_load(struct net *net, const char *name)
if (!dev && capable(CAP_NET_ADMIN))
request_module("%s", name);
}
+EXPORT_SYMBOL(dev_load);
/**
* dev_open - prepare an interface for use.
@@ -1118,6 +1129,7 @@ int dev_open(struct net_device *dev)
return ret;
}
+EXPORT_SYMBOL(dev_open);
/**
* dev_close - shutdown an interface.
@@ -1184,6 +1196,7 @@ int dev_close(struct net_device *dev)
return 0;
}
+EXPORT_SYMBOL(dev_close);
/**
@@ -1279,6 +1292,7 @@ rollback:
raw_notifier_chain_unregister(&netdev_chain, nb);
goto unlock;
}
+EXPORT_SYMBOL(register_netdevice_notifier);
/**
* unregister_netdevice_notifier - unregister a network notifier block
@@ -1299,6 +1313,7 @@ int unregister_netdevice_notifier(struct notifier_block *nb)
rtnl_unlock();
return err;
}
+EXPORT_SYMBOL(unregister_netdevice_notifier);
/**
* call_netdevice_notifiers - call all network notifier blocks
@@ -1321,11 +1336,13 @@ void net_enable_timestamp(void)
{
atomic_inc(&netstamp_needed);
}
+EXPORT_SYMBOL(net_enable_timestamp);
void net_disable_timestamp(void)
{
atomic_dec(&netstamp_needed);
}
+EXPORT_SYMBOL(net_disable_timestamp);
static inline void net_timestamp(struct sk_buff *skb)
{
@@ -1359,7 +1376,7 @@ static void dev_queue_xmit_nit(struct sk_buff *skb, struct net_device *dev)
if ((ptype->dev == dev || !ptype->dev) &&
(ptype->af_packet_priv == NULL ||
(struct sock *)ptype->af_packet_priv != skb->sk)) {
- struct sk_buff *skb2= skb_clone(skb, GFP_ATOMIC);
+ struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
if (!skb2)
break;
@@ -1527,6 +1544,7 @@ out_set_summed:
out:
return ret;
}
+EXPORT_SYMBOL(skb_checksum_help);
/**
* skb_gso_segment - Perform segmentation on skb.
@@ -1589,7 +1607,6 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb, int features)
return segs;
}
-
EXPORT_SYMBOL(skb_gso_segment);
/* Take action when hardware reception checksum errors are detected. */
@@ -1704,7 +1721,7 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
skb_dst_drop(skb);
rc = ops->ndo_start_xmit(skb, dev);
- if (rc == 0)
+ if (rc == NETDEV_TX_OK)
txq_trans_update(txq);
/*
* TODO: if skb_orphan() was called by
@@ -1730,7 +1747,7 @@ gso:
skb->next = nskb->next;
nskb->next = NULL;
rc = ops->ndo_start_xmit(nskb, dev);
- if (unlikely(rc)) {
+ if (unlikely(rc != NETDEV_TX_OK)) {
nskb->next = skb->next;
skb->next = nskb;
return rc;
@@ -1744,7 +1761,7 @@ gso:
out_kfree_skb:
kfree_skb(skb);
- return 0;
+ return NETDEV_TX_OK;
}
static u32 skb_tx_hashrnd;
@@ -1755,7 +1772,7 @@ u16 skb_tx_hash(const struct net_device *dev, const struct sk_buff *skb)
if (skb_rx_queue_recorded(skb)) {
hash = skb_get_rx_queue(skb);
- while (unlikely (hash >= dev->real_num_tx_queues))
+ while (unlikely(hash >= dev->real_num_tx_queues))
hash -= dev->real_num_tx_queues;
return hash;
}
@@ -1786,6 +1803,40 @@ static struct netdev_queue *dev_pick_tx(struct net_device *dev,
return netdev_get_tx_queue(dev, queue_index);
}
+static inline int __dev_xmit_skb(struct sk_buff *skb, struct Qdisc *q,
+ struct net_device *dev,
+ struct netdev_queue *txq)
+{
+ spinlock_t *root_lock = qdisc_lock(q);
+ int rc;
+
+ spin_lock(root_lock);
+ if (unlikely(test_bit(__QDISC_STATE_DEACTIVATED, &q->state))) {
+ kfree_skb(skb);
+ rc = NET_XMIT_DROP;
+ } else if ((q->flags & TCQ_F_CAN_BYPASS) && !qdisc_qlen(q) &&
+ !test_and_set_bit(__QDISC_STATE_RUNNING, &q->state)) {
+ /*
+ * This is a work-conserving queue; there are no old skbs
+ * waiting to be sent out; and the qdisc is not running -
+ * xmit the skb directly.
+ */
+ __qdisc_update_bstats(q, skb->len);
+ if (sch_direct_xmit(skb, q, dev, txq, root_lock))
+ __qdisc_run(q);
+ else
+ clear_bit(__QDISC_STATE_RUNNING, &q->state);
+
+ rc = NET_XMIT_SUCCESS;
+ } else {
+ rc = qdisc_enqueue_root(skb, q);
+ qdisc_run(q);
+ }
+ spin_unlock(root_lock);
+
+ return rc;
+}
+
/**
* dev_queue_xmit - transmit a buffer
* @skb: buffer to transmit
@@ -1856,22 +1907,10 @@ gso:
q = rcu_dereference(txq->qdisc);
#ifdef CONFIG_NET_CLS_ACT
- skb->tc_verd = SET_TC_AT(skb->tc_verd,AT_EGRESS);
+ skb->tc_verd = SET_TC_AT(skb->tc_verd, AT_EGRESS);
#endif
if (q->enqueue) {
- spinlock_t *root_lock = qdisc_lock(q);
-
- spin_lock(root_lock);
-
- if (unlikely(test_bit(__QDISC_STATE_DEACTIVATED, &q->state))) {
- kfree_skb(skb);
- rc = NET_XMIT_DROP;
- } else {
- rc = qdisc_enqueue_root(skb, q);
- qdisc_run(q);
- }
- spin_unlock(root_lock);
-
+ rc = __dev_xmit_skb(skb, q, dev, txq);
goto out;
}
@@ -1895,7 +1934,7 @@ gso:
HARD_TX_LOCK(dev, txq, cpu);
if (!netif_tx_queue_stopped(txq)) {
- rc = 0;
+ rc = NET_XMIT_SUCCESS;
if (!dev_hard_start_xmit(skb, dev, txq)) {
HARD_TX_UNLOCK(dev, txq);
goto out;
@@ -1924,6 +1963,7 @@ out:
rcu_read_unlock_bh();
return rc;
}
+EXPORT_SYMBOL(dev_queue_xmit);
/*=======================================================================
@@ -1990,6 +2030,7 @@ enqueue:
kfree_skb(skb);
return NET_RX_DROP;
}
+EXPORT_SYMBOL(netif_rx);
int netif_rx_ni(struct sk_buff *skb)
{
@@ -2003,7 +2044,6 @@ int netif_rx_ni(struct sk_buff *skb)
return err;
}
-
EXPORT_SYMBOL(netif_rx_ni);
static void net_tx_action(struct softirq_action *h)
@@ -2076,7 +2116,7 @@ static inline int deliver_skb(struct sk_buff *skb,
/* This hook is defined here for ATM LANE */
int (*br_fdb_test_addr_hook)(struct net_device *dev,
unsigned char *addr) __read_mostly;
-EXPORT_SYMBOL(br_fdb_test_addr_hook);
+EXPORT_SYMBOL_GPL(br_fdb_test_addr_hook);
#endif
/*
@@ -2085,7 +2125,7 @@ EXPORT_SYMBOL(br_fdb_test_addr_hook);
*/
struct sk_buff *(*br_handle_frame_hook)(struct net_bridge_port *p,
struct sk_buff *skb) __read_mostly;
-EXPORT_SYMBOL(br_handle_frame_hook);
+EXPORT_SYMBOL_GPL(br_handle_frame_hook);
static inline struct sk_buff *handle_bridge(struct sk_buff *skb,
struct packet_type **pt_prev, int *ret,
@@ -2336,6 +2376,7 @@ out:
rcu_read_unlock();
return ret;
}
+EXPORT_SYMBOL(netif_receive_skb);
/* Network device is going away, flush any packets still pending */
static void flush_backlog(void *arg)
@@ -2852,7 +2893,7 @@ softnet_break:
goto out;
}
-static gifconf_func_t * gifconf_list [NPROTO];
+static gifconf_func_t *gifconf_list[NPROTO];
/**
* register_gifconf - register a SIOCGIF handler
@@ -2863,13 +2904,14 @@ static gifconf_func_t * gifconf_list [NPROTO];
* that is passed must not be freed or reused until it has been replaced
* by another handler.
*/
-int register_gifconf(unsigned int family, gifconf_func_t * gifconf)
+int register_gifconf(unsigned int family, gifconf_func_t *gifconf)
{
if (family >= NPROTO)
return -EINVAL;
gifconf_list[family] = gifconf;
return 0;
}
+EXPORT_SYMBOL(register_gifconf);
/*
@@ -3080,7 +3122,7 @@ static int softnet_seq_show(struct seq_file *seq, void *v)
seq_printf(seq, "%08x %08x %08x %08x %08x %08x %08x %08x %08x\n",
s->total, s->dropped, s->time_squeeze, 0,
0, 0, 0, 0, /* was fastroute */
- s->cpu_collision );
+ s->cpu_collision);
return 0;
}
@@ -3316,6 +3358,7 @@ int netdev_set_master(struct net_device *slave, struct net_device *master)
rtmsg_ifinfo(RTM_NEWLINK, slave, IFF_SLAVE);
return 0;
}
+EXPORT_SYMBOL(netdev_set_master);
static void dev_change_rx_flags(struct net_device *dev, int flags)
{
@@ -3394,6 +3437,7 @@ int dev_set_promiscuity(struct net_device *dev, int inc)
dev_set_rx_mode(dev);
return err;
}
+EXPORT_SYMBOL(dev_set_promiscuity);
/**
* dev_set_allmulti - update allmulti count on a device
@@ -3437,6 +3481,7 @@ int dev_set_allmulti(struct net_device *dev, int inc)
}
return 0;
}
+EXPORT_SYMBOL(dev_set_allmulti);
/*
* Upload unicast and multicast address lists to device and
@@ -3927,6 +3972,7 @@ int __dev_addr_sync(struct dev_addr_list **to, int *to_count,
}
return err;
}
+EXPORT_SYMBOL_GPL(__dev_addr_sync);
void __dev_addr_unsync(struct dev_addr_list **to, int *to_count,
struct dev_addr_list **from, int *from_count)
@@ -3946,6 +3992,7 @@ void __dev_addr_unsync(struct dev_addr_list **to, int *to_count,
da = next;
}
}
+EXPORT_SYMBOL_GPL(__dev_addr_unsync);
/**
* dev_unicast_sync - Synchronize device's unicast list to another device
@@ -4064,6 +4111,7 @@ unsigned dev_get_flags(const struct net_device *dev)
return flags;
}
+EXPORT_SYMBOL(dev_get_flags);
/**
* dev_change_flags - change device settings
@@ -4114,12 +4162,13 @@ int dev_change_flags(struct net_device *dev, unsigned flags)
}
if (dev->flags & IFF_UP &&
- ((old_flags ^ dev->flags) &~ (IFF_UP | IFF_PROMISC | IFF_ALLMULTI |
+ ((old_flags ^ dev->flags) & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI |
IFF_VOLATILE)))
call_netdevice_notifiers(NETDEV_CHANGE, dev);
if ((flags ^ dev->gflags) & IFF_PROMISC) {
- int inc = (flags & IFF_PROMISC) ? +1 : -1;
+ int inc = (flags & IFF_PROMISC) ? 1 : -1;
+
dev->gflags ^= IFF_PROMISC;
dev_set_promiscuity(dev, inc);
}
@@ -4129,7 +4178,8 @@ int dev_change_flags(struct net_device *dev, unsigned flags)
IFF_ALLMULTI is requested not asking us and not reporting.
*/
if ((flags ^ dev->gflags) & IFF_ALLMULTI) {
- int inc = (flags & IFF_ALLMULTI) ? +1 : -1;
+ int inc = (flags & IFF_ALLMULTI) ? 1 : -1;
+
dev->gflags ^= IFF_ALLMULTI;
dev_set_allmulti(dev, inc);
}
@@ -4141,6 +4191,7 @@ int dev_change_flags(struct net_device *dev, unsigned flags)
return ret;
}
+EXPORT_SYMBOL(dev_change_flags);
/**
* dev_set_mtu - Change maximum transfer unit
@@ -4174,6 +4225,7 @@ int dev_set_mtu(struct net_device *dev, int new_mtu)
call_netdevice_notifiers(NETDEV_CHANGEMTU, dev);
return err;
}
+EXPORT_SYMBOL(dev_set_mtu);
/**
* dev_set_mac_address - Change Media Access Control Address
@@ -4198,6 +4250,7 @@ int dev_set_mac_address(struct net_device *dev, struct sockaddr *sa)
call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
return err;
}
+EXPORT_SYMBOL(dev_set_mac_address);
/*
* Perform the SIOCxIFxxx calls, inside read_lock(dev_base_lock)
@@ -4211,56 +4264,56 @@ static int dev_ifsioc_locked(struct net *net, struct ifreq *ifr, unsigned int cm
return -ENODEV;
switch (cmd) {
- case SIOCGIFFLAGS: /* Get interface flags */
- ifr->ifr_flags = (short) dev_get_flags(dev);
- return 0;
+ case SIOCGIFFLAGS: /* Get interface flags */
+ ifr->ifr_flags = (short) dev_get_flags(dev);
+ return 0;
- case SIOCGIFMETRIC: /* Get the metric on the interface
- (currently unused) */
- ifr->ifr_metric = 0;
- return 0;
+ case SIOCGIFMETRIC: /* Get the metric on the interface
+ (currently unused) */
+ ifr->ifr_metric = 0;
+ return 0;
- case SIOCGIFMTU: /* Get the MTU of a device */
- ifr->ifr_mtu = dev->mtu;
- return 0;
+ case SIOCGIFMTU: /* Get the MTU of a device */
+ ifr->ifr_mtu = dev->mtu;
+ return 0;
- case SIOCGIFHWADDR:
- if (!dev->addr_len)
- memset(ifr->ifr_hwaddr.sa_data, 0, sizeof ifr->ifr_hwaddr.sa_data);
- else
- memcpy(ifr->ifr_hwaddr.sa_data, dev->dev_addr,
- min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len));
- ifr->ifr_hwaddr.sa_family = dev->type;
- return 0;
+ case SIOCGIFHWADDR:
+ if (!dev->addr_len)
+ memset(ifr->ifr_hwaddr.sa_data, 0, sizeof ifr->ifr_hwaddr.sa_data);
+ else
+ memcpy(ifr->ifr_hwaddr.sa_data, dev->dev_addr,
+ min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len));
+ ifr->ifr_hwaddr.sa_family = dev->type;
+ return 0;
- case SIOCGIFSLAVE:
- err = -EINVAL;
- break;
+ case SIOCGIFSLAVE:
+ err = -EINVAL;
+ break;
- case SIOCGIFMAP:
- ifr->ifr_map.mem_start = dev->mem_start;
- ifr->ifr_map.mem_end = dev->mem_end;
- ifr->ifr_map.base_addr = dev->base_addr;
- ifr->ifr_map.irq = dev->irq;
- ifr->ifr_map.dma = dev->dma;
- ifr->ifr_map.port = dev->if_port;
- return 0;
+ case SIOCGIFMAP:
+ ifr->ifr_map.mem_start = dev->mem_start;
+ ifr->ifr_map.mem_end = dev->mem_end;
+ ifr->ifr_map.base_addr = dev->base_addr;
+ ifr->ifr_map.irq = dev->irq;
+ ifr->ifr_map.dma = dev->dma;
+ ifr->ifr_map.port = dev->if_port;
+ return 0;
- case SIOCGIFINDEX:
- ifr->ifr_ifindex = dev->ifindex;
- return 0;
+ case SIOCGIFINDEX:
+ ifr->ifr_ifindex = dev->ifindex;
+ return 0;
- case SIOCGIFTXQLEN:
- ifr->ifr_qlen = dev->tx_queue_len;
- return 0;
+ case SIOCGIFTXQLEN:
+ ifr->ifr_qlen = dev->tx_queue_len;
+ return 0;
- default:
- /* dev_ioctl() should ensure this case
- * is never reached
- */
- WARN_ON(1);
- err = -EINVAL;
- break;
+ default:
+ /* dev_ioctl() should ensure this case
+ * is never reached
+ */
+ WARN_ON(1);
+ err = -EINVAL;
+ break;
}
return err;
@@ -4281,92 +4334,91 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
ops = dev->netdev_ops;
switch (cmd) {
- case SIOCSIFFLAGS: /* Set interface flags */
- return dev_change_flags(dev, ifr->ifr_flags);
-
- case SIOCSIFMETRIC: /* Set the metric on the interface
- (currently unused) */
- return -EOPNOTSUPP;
+ case SIOCSIFFLAGS: /* Set interface flags */
+ return dev_change_flags(dev, ifr->ifr_flags);
- case SIOCSIFMTU: /* Set the MTU of a device */
- return dev_set_mtu(dev, ifr->ifr_mtu);
+ case SIOCSIFMETRIC: /* Set the metric on the interface
+ (currently unused) */
+ return -EOPNOTSUPP;
- case SIOCSIFHWADDR:
- return dev_set_mac_address(dev, &ifr->ifr_hwaddr);
+ case SIOCSIFMTU: /* Set the MTU of a device */
+ return dev_set_mtu(dev, ifr->ifr_mtu);
- case SIOCSIFHWBROADCAST:
- if (ifr->ifr_hwaddr.sa_family != dev->type)
- return -EINVAL;
- memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data,
- min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len));
- call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
- return 0;
+ case SIOCSIFHWADDR:
+ return dev_set_mac_address(dev, &ifr->ifr_hwaddr);
- case SIOCSIFMAP:
- if (ops->ndo_set_config) {
- if (!netif_device_present(dev))
- return -ENODEV;
- return ops->ndo_set_config(dev, &ifr->ifr_map);
- }
- return -EOPNOTSUPP;
-
- case SIOCADDMULTI:
- if ((!ops->ndo_set_multicast_list && !ops->ndo_set_rx_mode) ||
- ifr->ifr_hwaddr.sa_family != AF_UNSPEC)
- return -EINVAL;
- if (!netif_device_present(dev))
- return -ENODEV;
- return dev_mc_add(dev, ifr->ifr_hwaddr.sa_data,
- dev->addr_len, 1);
+ case SIOCSIFHWBROADCAST:
+ if (ifr->ifr_hwaddr.sa_family != dev->type)
+ return -EINVAL;
+ memcpy(dev->broadcast, ifr->ifr_hwaddr.sa_data,
+ min(sizeof ifr->ifr_hwaddr.sa_data, (size_t) dev->addr_len));
+ call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
+ return 0;
- case SIOCDELMULTI:
- if ((!ops->ndo_set_multicast_list && !ops->ndo_set_rx_mode) ||
- ifr->ifr_hwaddr.sa_family != AF_UNSPEC)
- return -EINVAL;
+ case SIOCSIFMAP:
+ if (ops->ndo_set_config) {
if (!netif_device_present(dev))
return -ENODEV;
- return dev_mc_delete(dev, ifr->ifr_hwaddr.sa_data,
- dev->addr_len, 1);
+ return ops->ndo_set_config(dev, &ifr->ifr_map);
+ }
+ return -EOPNOTSUPP;
- case SIOCSIFTXQLEN:
- if (ifr->ifr_qlen < 0)
- return -EINVAL;
- dev->tx_queue_len = ifr->ifr_qlen;
- return 0;
+ case SIOCADDMULTI:
+ if ((!ops->ndo_set_multicast_list && !ops->ndo_set_rx_mode) ||
+ ifr->ifr_hwaddr.sa_family != AF_UNSPEC)
+ return -EINVAL;
+ if (!netif_device_present(dev))
+ return -ENODEV;
+ return dev_mc_add(dev, ifr->ifr_hwaddr.sa_data,
+ dev->addr_len, 1);
+
+ case SIOCDELMULTI:
+ if ((!ops->ndo_set_multicast_list && !ops->ndo_set_rx_mode) ||
+ ifr->ifr_hwaddr.sa_family != AF_UNSPEC)
+ return -EINVAL;
+ if (!netif_device_present(dev))
+ return -ENODEV;
+ return dev_mc_delete(dev, ifr->ifr_hwaddr.sa_data,
+ dev->addr_len, 1);
- case SIOCSIFNAME:
- ifr->ifr_newname[IFNAMSIZ-1] = '\0';
- return dev_change_name(dev, ifr->ifr_newname);
+ case SIOCSIFTXQLEN:
+ if (ifr->ifr_qlen < 0)
+ return -EINVAL;
+ dev->tx_queue_len = ifr->ifr_qlen;
+ return 0;
- /*
- * Unknown or private ioctl
- */
+ case SIOCSIFNAME:
+ ifr->ifr_newname[IFNAMSIZ-1] = '\0';
+ return dev_change_name(dev, ifr->ifr_newname);
- default:
- if ((cmd >= SIOCDEVPRIVATE &&
- cmd <= SIOCDEVPRIVATE + 15) ||
- cmd == SIOCBONDENSLAVE ||
- cmd == SIOCBONDRELEASE ||
- cmd == SIOCBONDSETHWADDR ||
- cmd == SIOCBONDSLAVEINFOQUERY ||
- cmd == SIOCBONDINFOQUERY ||
- cmd == SIOCBONDCHANGEACTIVE ||
- cmd == SIOCGMIIPHY ||
- cmd == SIOCGMIIREG ||
- cmd == SIOCSMIIREG ||
- cmd == SIOCBRADDIF ||
- cmd == SIOCBRDELIF ||
- cmd == SIOCSHWTSTAMP ||
- cmd == SIOCWANDEV) {
- err = -EOPNOTSUPP;
- if (ops->ndo_do_ioctl) {
- if (netif_device_present(dev))
- err = ops->ndo_do_ioctl(dev, ifr, cmd);
- else
- err = -ENODEV;
- }
- } else
- err = -EINVAL;
+ /*
+ * Unknown or private ioctl
+ */
+ default:
+ if ((cmd >= SIOCDEVPRIVATE &&
+ cmd <= SIOCDEVPRIVATE + 15) ||
+ cmd == SIOCBONDENSLAVE ||
+ cmd == SIOCBONDRELEASE ||
+ cmd == SIOCBONDSETHWADDR ||
+ cmd == SIOCBONDSLAVEINFOQUERY ||
+ cmd == SIOCBONDINFOQUERY ||
+ cmd == SIOCBONDCHANGEACTIVE ||
+ cmd == SIOCGMIIPHY ||
+ cmd == SIOCGMIIREG ||
+ cmd == SIOCSMIIREG ||
+ cmd == SIOCBRADDIF ||
+ cmd == SIOCBRDELIF ||
+ cmd == SIOCSHWTSTAMP ||
+ cmd == SIOCWANDEV) {
+ err = -EOPNOTSUPP;
+ if (ops->ndo_do_ioctl) {
+ if (netif_device_present(dev))
+ err = ops->ndo_do_ioctl(dev, ifr, cmd);
+ else
+ err = -ENODEV;
+ }
+ } else
+ err = -EINVAL;
}
return err;
@@ -4423,135 +4475,135 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
*/
switch (cmd) {
- /*
- * These ioctl calls:
- * - can be done by all.
- * - atomic and do not require locking.
- * - return a value
- */
- case SIOCGIFFLAGS:
- case SIOCGIFMETRIC:
- case SIOCGIFMTU:
- case SIOCGIFHWADDR:
- case SIOCGIFSLAVE:
- case SIOCGIFMAP:
- case SIOCGIFINDEX:
- case SIOCGIFTXQLEN:
- dev_load(net, ifr.ifr_name);
- read_lock(&dev_base_lock);
- ret = dev_ifsioc_locked(net, &ifr, cmd);
- read_unlock(&dev_base_lock);
- if (!ret) {
- if (colon)
- *colon = ':';
- if (copy_to_user(arg, &ifr,
- sizeof(struct ifreq)))
- ret = -EFAULT;
- }
- return ret;
+ /*
+ * These ioctl calls:
+ * - can be done by all.
+ * - atomic and do not require locking.
+ * - return a value
+ */
+ case SIOCGIFFLAGS:
+ case SIOCGIFMETRIC:
+ case SIOCGIFMTU:
+ case SIOCGIFHWADDR:
+ case SIOCGIFSLAVE:
+ case SIOCGIFMAP:
+ case SIOCGIFINDEX:
+ case SIOCGIFTXQLEN:
+ dev_load(net, ifr.ifr_name);
+ read_lock(&dev_base_lock);
+ ret = dev_ifsioc_locked(net, &ifr, cmd);
+ read_unlock(&dev_base_lock);
+ if (!ret) {
+ if (colon)
+ *colon = ':';
+ if (copy_to_user(arg, &ifr,
+ sizeof(struct ifreq)))
+ ret = -EFAULT;
+ }
+ return ret;
- case SIOCETHTOOL:
- dev_load(net, ifr.ifr_name);
- rtnl_lock();
- ret = dev_ethtool(net, &ifr);
- rtnl_unlock();
- if (!ret) {
- if (colon)
- *colon = ':';
- if (copy_to_user(arg, &ifr,
- sizeof(struct ifreq)))
- ret = -EFAULT;
- }
- return ret;
+ case SIOCETHTOOL:
+ dev_load(net, ifr.ifr_name);
+ rtnl_lock();
+ ret = dev_ethtool(net, &ifr);
+ rtnl_unlock();
+ if (!ret) {
+ if (colon)
+ *colon = ':';
+ if (copy_to_user(arg, &ifr,
+ sizeof(struct ifreq)))
+ ret = -EFAULT;
+ }
+ return ret;
- /*
- * These ioctl calls:
- * - require superuser power.
- * - require strict serialization.
- * - return a value
- */
- case SIOCGMIIPHY:
- case SIOCGMIIREG:
- case SIOCSIFNAME:
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
- dev_load(net, ifr.ifr_name);
- rtnl_lock();
- ret = dev_ifsioc(net, &ifr, cmd);
- rtnl_unlock();
- if (!ret) {
- if (colon)
- *colon = ':';
- if (copy_to_user(arg, &ifr,
- sizeof(struct ifreq)))
- ret = -EFAULT;
- }
- return ret;
+ /*
+ * These ioctl calls:
+ * - require superuser power.
+ * - require strict serialization.
+ * - return a value
+ */
+ case SIOCGMIIPHY:
+ case SIOCGMIIREG:
+ case SIOCSIFNAME:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ dev_load(net, ifr.ifr_name);
+ rtnl_lock();
+ ret = dev_ifsioc(net, &ifr, cmd);
+ rtnl_unlock();
+ if (!ret) {
+ if (colon)
+ *colon = ':';
+ if (copy_to_user(arg, &ifr,
+ sizeof(struct ifreq)))
+ ret = -EFAULT;
+ }
+ return ret;
- /*
- * These ioctl calls:
- * - require superuser power.
- * - require strict serialization.
- * - do not return a value
- */
- case SIOCSIFFLAGS:
- case SIOCSIFMETRIC:
- case SIOCSIFMTU:
- case SIOCSIFMAP:
- case SIOCSIFHWADDR:
- case SIOCSIFSLAVE:
- case SIOCADDMULTI:
- case SIOCDELMULTI:
- case SIOCSIFHWBROADCAST:
- case SIOCSIFTXQLEN:
- case SIOCSMIIREG:
- case SIOCBONDENSLAVE:
- case SIOCBONDRELEASE:
- case SIOCBONDSETHWADDR:
- case SIOCBONDCHANGEACTIVE:
- case SIOCBRADDIF:
- case SIOCBRDELIF:
- case SIOCSHWTSTAMP:
- if (!capable(CAP_NET_ADMIN))
- return -EPERM;
- /* fall through */
- case SIOCBONDSLAVEINFOQUERY:
- case SIOCBONDINFOQUERY:
+ /*
+ * These ioctl calls:
+ * - require superuser power.
+ * - require strict serialization.
+ * - do not return a value
+ */
+ case SIOCSIFFLAGS:
+ case SIOCSIFMETRIC:
+ case SIOCSIFMTU:
+ case SIOCSIFMAP:
+ case SIOCSIFHWADDR:
+ case SIOCSIFSLAVE:
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ case SIOCSIFHWBROADCAST:
+ case SIOCSIFTXQLEN:
+ case SIOCSMIIREG:
+ case SIOCBONDENSLAVE:
+ case SIOCBONDRELEASE:
+ case SIOCBONDSETHWADDR:
+ case SIOCBONDCHANGEACTIVE:
+ case SIOCBRADDIF:
+ case SIOCBRDELIF:
+ case SIOCSHWTSTAMP:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+ /* fall through */
+ case SIOCBONDSLAVEINFOQUERY:
+ case SIOCBONDINFOQUERY:
+ dev_load(net, ifr.ifr_name);
+ rtnl_lock();
+ ret = dev_ifsioc(net, &ifr, cmd);
+ rtnl_unlock();
+ return ret;
+
+ case SIOCGIFMEM:
+ /* Get the per device memory space. We can add this but
+ * currently do not support it */
+ case SIOCSIFMEM:
+ /* Set the per device memory buffer space.
+ * Not applicable in our case */
+ case SIOCSIFLINK:
+ return -EINVAL;
+
+ /*
+ * Unknown or private ioctl.
+ */
+ default:
+ if (cmd == SIOCWANDEV ||
+ (cmd >= SIOCDEVPRIVATE &&
+ cmd <= SIOCDEVPRIVATE + 15)) {
dev_load(net, ifr.ifr_name);
rtnl_lock();
ret = dev_ifsioc(net, &ifr, cmd);
rtnl_unlock();
+ if (!ret && copy_to_user(arg, &ifr,
+ sizeof(struct ifreq)))
+ ret = -EFAULT;
return ret;
-
- case SIOCGIFMEM:
- /* Get the per device memory space. We can add this but
- * currently do not support it */
- case SIOCSIFMEM:
- /* Set the per device memory buffer space.
- * Not applicable in our case */
- case SIOCSIFLINK:
- return -EINVAL;
-
- /*
- * Unknown or private ioctl.
- */
- default:
- if (cmd == SIOCWANDEV ||
- (cmd >= SIOCDEVPRIVATE &&
- cmd <= SIOCDEVPRIVATE + 15)) {
- dev_load(net, ifr.ifr_name);
- rtnl_lock();
- ret = dev_ifsioc(net, &ifr, cmd);
- rtnl_unlock();
- if (!ret && copy_to_user(arg, &ifr,
- sizeof(struct ifreq)))
- ret = -EFAULT;
- return ret;
- }
- /* Take care of Wireless Extensions */
- if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST)
- return wext_handle_ioctl(net, &ifr, cmd, arg);
- return -EINVAL;
+ }
+ /* Take care of Wireless Extensions */
+ if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST)
+ return wext_handle_ioctl(net, &ifr, cmd, arg);
+ return -EINVAL;
}
}
@@ -4816,6 +4868,7 @@ err_uninit:
dev->netdev_ops->ndo_uninit(dev);
goto out;
}
+EXPORT_SYMBOL(register_netdevice);
/**
* init_dummy_netdev - init a dummy network device for NAPI
@@ -5168,6 +5221,7 @@ void free_netdev(struct net_device *dev)
/* will free via device release */
put_device(&dev->dev);
}
+EXPORT_SYMBOL(free_netdev);
/**
* synchronize_net - Synchronize with packet receive processing
@@ -5180,6 +5234,7 @@ void synchronize_net(void)
might_sleep();
synchronize_rcu();
}
+EXPORT_SYMBOL(synchronize_net);
/**
* unregister_netdevice - remove device from the kernel
@@ -5200,6 +5255,7 @@ void unregister_netdevice(struct net_device *dev)
/* Finish processing unregister after unlock */
net_set_todo(dev);
}
+EXPORT_SYMBOL(unregister_netdevice);
/**
* unregister_netdev - remove device from the kernel
@@ -5218,7 +5274,6 @@ void unregister_netdev(struct net_device *dev)
unregister_netdevice(dev);
rtnl_unlock();
}
-
EXPORT_SYMBOL(unregister_netdev);
/**
@@ -5347,6 +5402,7 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char
out:
return err;
}
+EXPORT_SYMBOL_GPL(dev_change_net_namespace);
static int dev_cpu_callback(struct notifier_block *nfb,
unsigned long action,
@@ -5407,7 +5463,7 @@ unsigned long netdev_increment_features(unsigned long all, unsigned long one,
unsigned long mask)
{
/* If device needs checksumming, downgrade to it. */
- if (all & NETIF_F_NO_CSUM && !(one & NETIF_F_NO_CSUM))
+ if (all & NETIF_F_NO_CSUM && !(one & NETIF_F_NO_CSUM))
all ^= NETIF_F_NO_CSUM | (one & NETIF_F_ALL_CSUM);
else if (mask & NETIF_F_ALL_CSUM) {
/* If one device supports v4/v6 checksumming, set for all. */
@@ -5633,41 +5689,3 @@ static int __init initialize_hashrnd(void)
late_initcall_sync(initialize_hashrnd);
-EXPORT_SYMBOL(__dev_get_by_index);
-EXPORT_SYMBOL(__dev_get_by_name);
-EXPORT_SYMBOL(__dev_remove_pack);
-EXPORT_SYMBOL(dev_valid_name);
-EXPORT_SYMBOL(dev_add_pack);
-EXPORT_SYMBOL(dev_alloc_name);
-EXPORT_SYMBOL(dev_close);
-EXPORT_SYMBOL(dev_get_by_flags);
-EXPORT_SYMBOL(dev_get_by_index);
-EXPORT_SYMBOL(dev_get_by_name);
-EXPORT_SYMBOL(dev_open);
-EXPORT_SYMBOL(dev_queue_xmit);
-EXPORT_SYMBOL(dev_remove_pack);
-EXPORT_SYMBOL(dev_set_allmulti);
-EXPORT_SYMBOL(dev_set_promiscuity);
-EXPORT_SYMBOL(dev_change_flags);
-EXPORT_SYMBOL(dev_set_mtu);
-EXPORT_SYMBOL(dev_set_mac_address);
-EXPORT_SYMBOL(free_netdev);
-EXPORT_SYMBOL(netdev_boot_setup_check);
-EXPORT_SYMBOL(netdev_set_master);
-EXPORT_SYMBOL(netdev_state_change);
-EXPORT_SYMBOL(netif_receive_skb);
-EXPORT_SYMBOL(netif_rx);
-EXPORT_SYMBOL(register_gifconf);
-EXPORT_SYMBOL(register_netdevice);
-EXPORT_SYMBOL(register_netdevice_notifier);
-EXPORT_SYMBOL(skb_checksum_help);
-EXPORT_SYMBOL(synchronize_net);
-EXPORT_SYMBOL(unregister_netdevice);
-EXPORT_SYMBOL(unregister_netdevice_notifier);
-EXPORT_SYMBOL(net_enable_timestamp);
-EXPORT_SYMBOL(net_disable_timestamp);
-EXPORT_SYMBOL(dev_get_flags);
-
-EXPORT_SYMBOL(dev_load);
-
-EXPORT_PER_CPU_SYMBOL(softnet_data);
diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c
index 9d66fa953ab7..0a113f26bc9f 100644
--- a/net/core/drop_monitor.c
+++ b/net/core/drop_monitor.c
@@ -52,6 +52,7 @@ struct per_cpu_dm_data {
struct dm_hw_stat_delta {
struct net_device *dev;
+ unsigned long last_rx;
struct list_head list;
struct rcu_head rcu;
unsigned long last_drop_val;
@@ -180,17 +181,25 @@ static void trace_napi_poll_hit(struct napi_struct *napi)
struct dm_hw_stat_delta *new_stat;
/*
- * Ratelimit our check time to dm_hw_check_delta jiffies
+ * Don't check napi structures with no associated device
*/
- if (!time_after(jiffies, napi->dev->last_rx + dm_hw_check_delta))
+ if (!napi->dev)
return;
rcu_read_lock();
list_for_each_entry_rcu(new_stat, &hw_stats_list, list) {
+ /*
+ * only add a note to our monitor buffer if:
+ * 1) this is the dev we received on
+ * 2) its after the last_rx delta
+ * 3) our rx_dropped count has gone up
+ */
if ((new_stat->dev == napi->dev) &&
+ (time_after(jiffies, new_stat->last_rx + dm_hw_check_delta)) &&
(napi->dev->stats.rx_dropped != new_stat->last_drop_val)) {
trace_drop_common(NULL, NULL);
new_stat->last_drop_val = napi->dev->stats.rx_dropped;
+ new_stat->last_rx = jiffies;
break;
}
}
@@ -286,6 +295,7 @@ static int dropmon_net_event(struct notifier_block *ev_block,
goto out;
new_stat->dev = dev;
+ new_stat->last_rx = jiffies;
INIT_RCU_HEAD(&new_stat->rcu);
spin_lock(&trace_state_lock);
list_add_rcu(&new_stat->list, &hw_stats_list);
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index d9d5160610d5..4c12ddb5f5ee 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -30,10 +30,17 @@ u32 ethtool_op_get_link(struct net_device *dev)
return netif_carrier_ok(dev) ? 1 : 0;
}
+u32 ethtool_op_get_rx_csum(struct net_device *dev)
+{
+ return (dev->features & NETIF_F_ALL_CSUM) != 0;
+}
+EXPORT_SYMBOL(ethtool_op_get_rx_csum);
+
u32 ethtool_op_get_tx_csum(struct net_device *dev)
{
return (dev->features & NETIF_F_ALL_CSUM) != 0;
}
+EXPORT_SYMBOL(ethtool_op_get_tx_csum);
int ethtool_op_set_tx_csum(struct net_device *dev, u32 data)
{
@@ -891,6 +898,19 @@ static int ethtool_set_value(struct net_device *dev, char __user *useraddr,
return actor(dev, edata.data);
}
+static int ethtool_flash_device(struct net_device *dev, char __user *useraddr)
+{
+ struct ethtool_flash efl;
+
+ if (copy_from_user(&efl, useraddr, sizeof(efl)))
+ return -EFAULT;
+
+ if (!dev->ethtool_ops->flash_device)
+ return -EOPNOTSUPP;
+
+ return dev->ethtool_ops->flash_device(dev, &efl);
+}
+
/* The main entry point in this file. Called from net/core/dev.c */
int dev_ethtool(struct net *net, struct ifreq *ifr)
@@ -1004,7 +1024,9 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
break;
case ETHTOOL_GRXCSUM:
rc = ethtool_get_value(dev, useraddr, ethcmd,
- dev->ethtool_ops->get_rx_csum);
+ (dev->ethtool_ops->get_rx_csum ?
+ dev->ethtool_ops->get_rx_csum :
+ ethtool_op_get_rx_csum));
break;
case ETHTOOL_SRXCSUM:
rc = ethtool_set_rx_csum(dev, useraddr);
@@ -1068,7 +1090,9 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
break;
case ETHTOOL_GFLAGS:
rc = ethtool_get_value(dev, useraddr, ethcmd,
- dev->ethtool_ops->get_flags);
+ (dev->ethtool_ops->get_flags ?
+ dev->ethtool_ops->get_flags :
+ ethtool_op_get_flags));
break;
case ETHTOOL_SFLAGS:
rc = ethtool_set_value(dev, useraddr,
@@ -1100,6 +1124,9 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
case ETHTOOL_SGRO:
rc = ethtool_set_gro(dev, useraddr);
break;
+ case ETHTOOL_FLASHDEV:
+ rc = ethtool_flash_device(dev, useraddr);
+ break;
default:
rc = -EOPNOTSUPP;
}
@@ -1116,7 +1143,6 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
EXPORT_SYMBOL(ethtool_op_get_link);
EXPORT_SYMBOL(ethtool_op_get_sg);
EXPORT_SYMBOL(ethtool_op_get_tso);
-EXPORT_SYMBOL(ethtool_op_get_tx_csum);
EXPORT_SYMBOL(ethtool_op_set_sg);
EXPORT_SYMBOL(ethtool_op_set_tso);
EXPORT_SYMBOL(ethtool_op_set_tx_csum);
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 163b4f5b0365..e587e6819698 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -692,75 +692,74 @@ static void neigh_connect(struct neighbour *neigh)
hh->hh_output = neigh->ops->hh_output;
}
-static void neigh_periodic_timer(unsigned long arg)
+static void neigh_periodic_work(struct work_struct *work)
{
- struct neigh_table *tbl = (struct neigh_table *)arg;
+ struct neigh_table *tbl = container_of(work, struct neigh_table, gc_work.work);
struct neighbour *n, **np;
- unsigned long expire, now = jiffies;
+ unsigned int i;
NEIGH_CACHE_STAT_INC(tbl, periodic_gc_runs);
- write_lock(&tbl->lock);
+ write_lock_bh(&tbl->lock);
/*
* periodically recompute ReachableTime from random function
*/
- if (time_after(now, tbl->last_rand + 300 * HZ)) {
+ if (time_after(jiffies, tbl->last_rand + 300 * HZ)) {
struct neigh_parms *p;
- tbl->last_rand = now;
+ tbl->last_rand = jiffies;
for (p = &tbl->parms; p; p = p->next)
p->reachable_time =
neigh_rand_reach_time(p->base_reachable_time);
}
- np = &tbl->hash_buckets[tbl->hash_chain_gc];
- tbl->hash_chain_gc = ((tbl->hash_chain_gc + 1) & tbl->hash_mask);
+ for (i = 0 ; i <= tbl->hash_mask; i++) {
+ np = &tbl->hash_buckets[i];
- while ((n = *np) != NULL) {
- unsigned int state;
+ while ((n = *np) != NULL) {
+ unsigned int state;
- write_lock(&n->lock);
+ write_lock(&n->lock);
- state = n->nud_state;
- if (state & (NUD_PERMANENT | NUD_IN_TIMER)) {
- write_unlock(&n->lock);
- goto next_elt;
- }
+ state = n->nud_state;
+ if (state & (NUD_PERMANENT | NUD_IN_TIMER)) {
+ write_unlock(&n->lock);
+ goto next_elt;
+ }
- if (time_before(n->used, n->confirmed))
- n->used = n->confirmed;
+ if (time_before(n->used, n->confirmed))
+ n->used = n->confirmed;
- if (atomic_read(&n->refcnt) == 1 &&
- (state == NUD_FAILED ||
- time_after(now, n->used + n->parms->gc_staletime))) {
- *np = n->next;
- n->dead = 1;
+ if (atomic_read(&n->refcnt) == 1 &&
+ (state == NUD_FAILED ||
+ time_after(jiffies, n->used + n->parms->gc_staletime))) {
+ *np = n->next;
+ n->dead = 1;
+ write_unlock(&n->lock);
+ neigh_cleanup_and_release(n);
+ continue;
+ }
write_unlock(&n->lock);
- neigh_cleanup_and_release(n);
- continue;
- }
- write_unlock(&n->lock);
next_elt:
- np = &n->next;
+ np = &n->next;
+ }
+ /*
+ * It's fine to release lock here, even if hash table
+ * grows while we are preempted.
+ */
+ write_unlock_bh(&tbl->lock);
+ cond_resched();
+ write_lock_bh(&tbl->lock);
}
-
/* Cycle through all hash buckets every base_reachable_time/2 ticks.
* ARP entry timeouts range from 1/2 base_reachable_time to 3/2
* base_reachable_time.
*/
- expire = tbl->parms.base_reachable_time >> 1;
- expire /= (tbl->hash_mask + 1);
- if (!expire)
- expire = 1;
-
- if (expire>HZ)
- mod_timer(&tbl->gc_timer, round_jiffies(now + expire));
- else
- mod_timer(&tbl->gc_timer, now + expire);
-
- write_unlock(&tbl->lock);
+ schedule_delayed_work(&tbl->gc_work,
+ tbl->parms.base_reachable_time >> 1);
+ write_unlock_bh(&tbl->lock);
}
static __inline__ int neigh_max_probes(struct neighbour *n)
@@ -1316,7 +1315,7 @@ void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,
}
EXPORT_SYMBOL(pneigh_enqueue);
-static inline struct neigh_parms *lookup_neigh_params(struct neigh_table *tbl,
+static inline struct neigh_parms *lookup_neigh_parms(struct neigh_table *tbl,
struct net *net, int ifindex)
{
struct neigh_parms *p;
@@ -1337,7 +1336,7 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev,
struct net *net = dev_net(dev);
const struct net_device_ops *ops = dev->netdev_ops;
- ref = lookup_neigh_params(tbl, net, 0);
+ ref = lookup_neigh_parms(tbl, net, 0);
if (!ref)
return NULL;
@@ -1442,10 +1441,8 @@ void neigh_table_init_no_netlink(struct neigh_table *tbl)
get_random_bytes(&tbl->hash_rnd, sizeof(tbl->hash_rnd));
rwlock_init(&tbl->lock);
- setup_timer(&tbl->gc_timer, neigh_periodic_timer, (unsigned long)tbl);
- tbl->gc_timer.expires = now + 1;
- add_timer(&tbl->gc_timer);
-
+ INIT_DELAYED_WORK_DEFERRABLE(&tbl->gc_work, neigh_periodic_work);
+ schedule_delayed_work(&tbl->gc_work, tbl->parms.reachable_time);
setup_timer(&tbl->proxy_timer, neigh_proxy_process, (unsigned long)tbl);
skb_queue_head_init_class(&tbl->proxy_queue,
&neigh_table_proxy_queue_class);
@@ -1482,7 +1479,8 @@ int neigh_table_clear(struct neigh_table *tbl)
struct neigh_table **tp;
/* It is not clean... Fix it to unload IPv6 module safely */
- del_timer_sync(&tbl->gc_timer);
+ cancel_delayed_work(&tbl->gc_work);
+ flush_scheduled_work();
del_timer_sync(&tbl->proxy_timer);
pneigh_queue_purge(&tbl->proxy_queue);
neigh_ifdown(tbl, NULL);
@@ -1752,7 +1750,6 @@ static int neightbl_fill_info(struct sk_buff *skb, struct neigh_table *tbl,
.ndtc_last_rand = jiffies_to_msecs(rand_delta),
.ndtc_hash_rnd = tbl->hash_rnd,
.ndtc_hash_mask = tbl->hash_mask,
- .ndtc_hash_chain_gc = tbl->hash_chain_gc,
.ndtc_proxy_qlen = tbl->proxy_queue.qlen,
};
@@ -1906,7 +1903,7 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
if (tbp[NDTPA_IFINDEX])
ifindex = nla_get_u32(tbp[NDTPA_IFINDEX]);
- p = lookup_neigh_params(tbl, net, ifindex);
+ p = lookup_neigh_parms(tbl, net, ifindex);
if (p == NULL) {
err = -ENOENT;
goto errout_tbl_lock;
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index 3994680c08b9..ad91e9e5f475 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -141,7 +141,7 @@ static ssize_t show_dormant(struct device *dev,
return -EINVAL;
}
-static const char *operstates[] = {
+static const char *const operstates[] = {
"unknown",
"notpresent", /* currently unused */
"down",
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index 197283072cc8..1c1af2756f38 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -6,6 +6,8 @@
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/idr.h>
+#include <linux/rculist.h>
+#include <linux/nsproxy.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>
@@ -127,7 +129,7 @@ static struct net *net_create(void)
rv = setup_net(net);
if (rv == 0) {
rtnl_lock();
- list_add_tail(&net->list, &net_namespace_list);
+ list_add_tail_rcu(&net->list, &net_namespace_list);
rtnl_unlock();
}
mutex_unlock(&net_mutex);
@@ -156,9 +158,16 @@ static void cleanup_net(struct work_struct *work)
/* Don't let anyone else find us. */
rtnl_lock();
- list_del(&net->list);
+ list_del_rcu(&net->list);
rtnl_unlock();
+ /*
+ * Another CPU might be rcu-iterating the list, wait for it.
+ * This needs to be before calling the exit() notifiers, so
+ * the rcu_barrier() below isn't sufficient alone.
+ */
+ synchronize_rcu();
+
/* Run all of the network namespace exit methods */
list_for_each_entry_reverse(ops, &pernet_list, list) {
if (ops->exit)
@@ -193,6 +202,26 @@ struct net *copy_net_ns(unsigned long flags, struct net *old_net)
}
#endif
+struct net *get_net_ns_by_pid(pid_t pid)
+{
+ struct task_struct *tsk;
+ struct net *net;
+
+ /* Lookup the network namespace */
+ net = ERR_PTR(-ESRCH);
+ rcu_read_lock();
+ tsk = find_task_by_vpid(pid);
+ if (tsk) {
+ struct nsproxy *nsproxy;
+ nsproxy = task_nsproxy(tsk);
+ if (nsproxy)
+ net = get_net(nsproxy->net_ns);
+ }
+ rcu_read_unlock();
+ return net;
+}
+EXPORT_SYMBOL_GPL(get_net_ns_by_pid);
+
static int __init net_ns_init(void)
{
struct net_generic *ng;
@@ -219,7 +248,7 @@ static int __init net_ns_init(void)
panic("Could not setup the initial network namespace");
rtnl_lock();
- list_add_tail(&init_net.list, &net_namespace_list);
+ list_add_tail_rcu(&init_net.list, &net_namespace_list);
rtnl_unlock();
mutex_unlock(&net_mutex);
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index 1b76eb11deb4..0b4d0d35ef40 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -9,6 +9,7 @@
* Copyright (C) 2002 Red Hat, Inc.
*/
+#include <linux/moduleparam.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/string.h>
@@ -50,6 +51,9 @@ static atomic_t trapped;
static void zap_completion_queue(void);
static void arp_reply(struct sk_buff *skb);
+static unsigned int carrier_timeout = 4;
+module_param(carrier_timeout, uint, 0644);
+
static void queue_process(struct work_struct *work)
{
struct netpoll_info *npinfo =
@@ -737,7 +741,7 @@ int netpoll_setup(struct netpoll *np)
}
atleast = jiffies + HZ/10;
- atmost = jiffies + 4*HZ;
+ atmost = jiffies + carrier_timeout * HZ;
while (!netif_carrier_ok(ndev)) {
if (time_after(jiffies, atmost)) {
printk(KERN_NOTICE
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index 19b8c20e98a4..0bcecbf06581 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -131,6 +131,7 @@
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/capability.h>
+#include <linux/hrtimer.h>
#include <linux/freezer.h>
#include <linux/delay.h>
#include <linux/timer.h>
@@ -162,14 +163,13 @@
#include <asm/byteorder.h>
#include <linux/rcupdate.h>
#include <linux/bitops.h>
-#include <asm/io.h>
+#include <linux/io.h>
+#include <linux/timex.h>
+#include <linux/uaccess.h>
#include <asm/dma.h>
-#include <asm/uaccess.h>
#include <asm/div64.h> /* do_div */
-#include <asm/timex.h>
-
-#define VERSION "pktgen v2.70: Packet Generator for packet performance testing.\n"
+#define VERSION "2.72"
#define IP_NAME_SZ 32
#define MAX_MPLS_LABELS 16 /* This is the max label stack depth */
#define MPLS_STACK_BOTTOM htonl(0x00000100)
@@ -206,7 +206,7 @@
#define PKTGEN_MAGIC 0xbe9be955
#define PG_PROC_DIR "pktgen"
#define PGCTRL "pgctrl"
-static struct proc_dir_entry *pg_proc_dir = NULL;
+static struct proc_dir_entry *pg_proc_dir;
#define MAX_CFLOWS 65536
@@ -231,9 +231,9 @@ struct pktgen_dev {
*/
struct proc_dir_entry *entry; /* proc file */
struct pktgen_thread *pg_thread;/* the owner */
- struct list_head list; /* Used for chaining in the thread's run-queue */
+ struct list_head list; /* chaining in the thread's run-queue */
- int running; /* if this changes to false, the test will stop */
+ int running; /* if false, the test will stop */
/* If min != max, then we will either do a linear iteration, or
* we will do a random selection from within the range.
@@ -246,33 +246,37 @@ struct pktgen_dev {
int max_pkt_size; /* = ETH_ZLEN; */
int pkt_overhead; /* overhead for MPLS, VLANs, IPSEC etc */
int nfrags;
- __u32 delay_us; /* Default delay */
- __u32 delay_ns;
+ u64 delay; /* nano-seconds */
+
__u64 count; /* Default No packets to send */
__u64 sofar; /* How many pkts we've sent so far */
__u64 tx_bytes; /* How many bytes we've transmitted */
- __u64 errors; /* Errors when trying to transmit, pkts will be re-sent */
+ __u64 errors; /* Errors when trying to transmit,
+ pkts will be re-sent */
/* runtime counters relating to clone_skb */
- __u64 next_tx_us; /* timestamp of when to tx next */
- __u32 next_tx_ns;
__u64 allocated_skbs;
__u32 clone_count;
int last_ok; /* Was last skb sent?
- * Or a failed transmit of some sort? This will keep
- * sequence numbers in order, for example.
+ * Or a failed transmit of some sort?
+ * This will keep sequence numbers in order
*/
- __u64 started_at; /* micro-seconds */
- __u64 stopped_at; /* micro-seconds */
- __u64 idle_acc; /* micro-seconds */
+ ktime_t next_tx;
+ ktime_t started_at;
+ ktime_t stopped_at;
+ u64 idle_acc; /* nano-seconds */
+
__u32 seq_num;
- int clone_skb; /* Use multiple SKBs during packet gen. If this number
- * is greater than 1, then that many copies of the same
- * packet will be sent before a new packet is allocated.
- * For instance, if you want to send 1024 identical packets
- * before creating a new packet, set clone_skb to 1024.
+ int clone_skb; /*
+ * Use multiple SKBs during packet gen.
+ * If this number is greater than 1, then
+ * that many copies of the same packet will be
+ * sent before a new packet is allocated.
+ * If you want to send 1024 identical packets
+ * before creating a new packet,
+ * set clone_skb to 1024.
*/
char dst_min[IP_NAME_SZ]; /* IP, ie 1.2.3.4 */
@@ -304,8 +308,10 @@ struct pktgen_dev {
__u16 udp_dst_max; /* exclusive, dest UDP port */
/* DSCP + ECN */
- __u8 tos; /* six most significant bits of (former) IPv4 TOS are for dscp codepoint */
- __u8 traffic_class; /* ditto for the (former) Traffic Class in IPv6 (see RFC 3260, sec. 4) */
+ __u8 tos; /* six MSB of (former) IPv4 TOS
+ are for dscp codepoint */
+ __u8 traffic_class; /* ditto for the (former) Traffic Class in IPv6
+ (see RFC 3260, sec. 4) */
/* MPLS */
unsigned nr_labels; /* Depth of stack, 0 = no MPLS */
@@ -346,15 +352,17 @@ struct pktgen_dev {
*/
__u16 pad; /* pad out the hh struct to an even 16 bytes */
- struct sk_buff *skb; /* skb we are to transmit next, mainly used for when we
+ struct sk_buff *skb; /* skb we are to transmit next, used for when we
* are transmitting the same one multiple times
*/
- struct net_device *odev; /* The out-going device. Note that the device should
- * have it's pg_info pointer pointing back to this
- * device. This will be set when the user specifies
- * the out-going device name (not when the inject is
- * started as it used to do.)
- */
+ struct net_device *odev; /* The out-going device.
+ * Note that the device should have it's
+ * pg_info pointer pointing back to this
+ * device.
+ * Set when the user specifies the out-going
+ * device name (not when the inject is
+ * started as it used to do.)
+ */
struct flow_state *flows;
unsigned cflows; /* Concurrent flows (config) */
unsigned lflow; /* Flow length (config) */
@@ -379,13 +387,14 @@ struct pktgen_hdr {
};
struct pktgen_thread {
- spinlock_t if_lock;
+ spinlock_t if_lock; /* for list of devices */
struct list_head if_list; /* All device here */
struct list_head th_list;
struct task_struct *tsk;
char result[512];
- /* Field for thread to receive "posted" events terminate, stop ifs etc. */
+ /* Field for thread to receive "posted" events terminate,
+ stop ifs etc. */
u32 control;
int cpu;
@@ -397,24 +406,22 @@ struct pktgen_thread {
#define REMOVE 1
#define FIND 0
-/** Convert to micro-seconds */
-static inline __u64 tv_to_us(const struct timeval *tv)
+static inline ktime_t ktime_now(void)
{
- __u64 us = tv->tv_usec;
- us += (__u64) tv->tv_sec * (__u64) 1000000;
- return us;
+ struct timespec ts;
+ ktime_get_ts(&ts);
+
+ return timespec_to_ktime(ts);
}
-static __u64 getCurUs(void)
+/* This works even if 32 bit because of careful byte order choice */
+static inline int ktime_lt(const ktime_t cmp1, const ktime_t cmp2)
{
- struct timeval tv;
- do_gettimeofday(&tv);
- return tv_to_us(&tv);
+ return cmp1.tv64 < cmp2.tv64;
}
-/* old include end */
-
-static char version[] __initdata = VERSION;
+static const char version[] =
+ "pktgen " VERSION ": Packet Generator for packet performance testing.\n";
static int pktgen_remove_device(struct pktgen_thread *t, struct pktgen_dev *i);
static int pktgen_add_device(struct pktgen_thread *t, const char *ifname);
@@ -424,7 +431,7 @@ static int pktgen_device_event(struct notifier_block *, unsigned long, void *);
static void pktgen_run_all_threads(void);
static void pktgen_reset_all_threads(void);
static void pktgen_stop_all_threads_ifs(void);
-static int pktgen_stop_device(struct pktgen_dev *pkt_dev);
+
static void pktgen_stop(struct pktgen_thread *t);
static void pktgen_clear_counters(struct pktgen_dev *pkt_dev);
@@ -432,10 +439,10 @@ static unsigned int scan_ip6(const char *s, char ip[16]);
static unsigned int fmt_ip6(char *s, const char ip[16]);
/* Module parameters, defaults. */
-static int pg_count_d = 1000; /* 1000 pkts by default */
-static int pg_delay_d;
-static int pg_clone_skb_d;
-static int debug;
+static int pg_count_d __read_mostly = 1000;
+static int pg_delay_d __read_mostly;
+static int pg_clone_skb_d __read_mostly;
+static int debug __read_mostly;
static DEFINE_MUTEX(pktgen_thread_lock);
static LIST_HEAD(pktgen_threads);
@@ -451,12 +458,12 @@ static struct notifier_block pktgen_notifier_block = {
static int pgctrl_show(struct seq_file *seq, void *v)
{
- seq_puts(seq, VERSION);
+ seq_puts(seq, version);
return 0;
}
-static ssize_t pgctrl_write(struct file *file, const char __user * buf,
- size_t count, loff_t * ppos)
+static ssize_t pgctrl_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
{
int err = 0;
char data[128];
@@ -509,10 +516,9 @@ static const struct file_operations pktgen_fops = {
static int pktgen_if_show(struct seq_file *seq, void *v)
{
- struct pktgen_dev *pkt_dev = seq->private;
- __u64 sa;
- __u64 stopped;
- __u64 now = getCurUs();
+ const struct pktgen_dev *pkt_dev = seq->private;
+ ktime_t stopped;
+ u64 idle;
seq_printf(seq,
"Params: count %llu min_pkt_size: %u max_pkt_size: %u\n",
@@ -520,9 +526,8 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
pkt_dev->max_pkt_size);
seq_printf(seq,
- " frags: %d delay: %u clone_skb: %d ifname: %s\n",
- pkt_dev->nfrags,
- 1000 * pkt_dev->delay_us + pkt_dev->delay_ns,
+ " frags: %d delay: %llu clone_skb: %d ifname: %s\n",
+ pkt_dev->nfrags, (unsigned long long) pkt_dev->delay,
pkt_dev->clone_skb, pkt_dev->odev->name);
seq_printf(seq, " flows: %u flowlen: %u\n", pkt_dev->cflows,
@@ -549,11 +554,14 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
" daddr: %s min_daddr: %s max_daddr: %s\n", b1,
b2, b3);
- } else
+ } else {
+ seq_printf(seq,
+ " dst_min: %s dst_max: %s\n",
+ pkt_dev->dst_min, pkt_dev->dst_max);
seq_printf(seq,
- " dst_min: %s dst_max: %s\n src_min: %s src_max: %s\n",
- pkt_dev->dst_min, pkt_dev->dst_max, pkt_dev->src_min,
- pkt_dev->src_max);
+ " src_min: %s src_max: %s\n",
+ pkt_dev->src_min, pkt_dev->src_max);
+ }
seq_puts(seq, " src_mac: ");
@@ -565,7 +573,8 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
seq_printf(seq, "%pM\n", pkt_dev->dst_mac);
seq_printf(seq,
- " udp_src_min: %d udp_src_max: %d udp_dst_min: %d udp_dst_max: %d\n",
+ " udp_src_min: %d udp_src_max: %d"
+ " udp_dst_min: %d udp_dst_max: %d\n",
pkt_dev->udp_src_min, pkt_dev->udp_src_max,
pkt_dev->udp_dst_min, pkt_dev->udp_dst_max);
@@ -581,23 +590,21 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
i == pkt_dev->nr_labels-1 ? "\n" : ", ");
}
- if (pkt_dev->vlan_id != 0xffff) {
+ if (pkt_dev->vlan_id != 0xffff)
seq_printf(seq, " vlan_id: %u vlan_p: %u vlan_cfi: %u\n",
- pkt_dev->vlan_id, pkt_dev->vlan_p, pkt_dev->vlan_cfi);
- }
+ pkt_dev->vlan_id, pkt_dev->vlan_p,
+ pkt_dev->vlan_cfi);
- if (pkt_dev->svlan_id != 0xffff) {
+ if (pkt_dev->svlan_id != 0xffff)
seq_printf(seq, " svlan_id: %u vlan_p: %u vlan_cfi: %u\n",
- pkt_dev->svlan_id, pkt_dev->svlan_p, pkt_dev->svlan_cfi);
- }
+ pkt_dev->svlan_id, pkt_dev->svlan_p,
+ pkt_dev->svlan_cfi);
- if (pkt_dev->tos) {
+ if (pkt_dev->tos)
seq_printf(seq, " tos: 0x%02x\n", pkt_dev->tos);
- }
- if (pkt_dev->traffic_class) {
+ if (pkt_dev->traffic_class)
seq_printf(seq, " traffic_class: 0x%02x\n", pkt_dev->traffic_class);
- }
seq_printf(seq, " Flags: ");
@@ -654,17 +661,21 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
seq_puts(seq, "\n");
- sa = pkt_dev->started_at;
- stopped = pkt_dev->stopped_at;
- if (pkt_dev->running)
- stopped = now; /* not really stopped, more like last-running-at */
+ /* not really stopped, more like last-running-at */
+ stopped = pkt_dev->running ? ktime_now() : pkt_dev->stopped_at;
+ idle = pkt_dev->idle_acc;
+ do_div(idle, NSEC_PER_USEC);
seq_printf(seq,
- "Current:\n pkts-sofar: %llu errors: %llu\n started: %lluus stopped: %lluus idle: %lluus\n",
+ "Current:\n pkts-sofar: %llu errors: %llu\n",
(unsigned long long)pkt_dev->sofar,
- (unsigned long long)pkt_dev->errors, (unsigned long long)sa,
- (unsigned long long)stopped,
- (unsigned long long)pkt_dev->idle_acc);
+ (unsigned long long)pkt_dev->errors);
+
+ seq_printf(seq,
+ " started: %lluus stopped: %lluus idle: %lluus\n",
+ (unsigned long long) ktime_to_us(pkt_dev->started_at),
+ (unsigned long long) ktime_to_us(stopped),
+ (unsigned long long) idle);
seq_printf(seq,
" seq_num: %d cur_dst_mac_offset: %d cur_src_mac_offset: %d\n",
@@ -696,7 +707,8 @@ static int pktgen_if_show(struct seq_file *seq, void *v)
}
-static int hex32_arg(const char __user *user_buffer, unsigned long maxlen, __u32 *num)
+static int hex32_arg(const char __user *user_buffer, unsigned long maxlen,
+ __u32 *num)
{
int i = 0;
*num = 0;
@@ -846,9 +858,9 @@ static ssize_t pktgen_if_write(struct file *file,
/* Read variable name */
len = strn_len(&user_buffer[i], sizeof(name) - 1);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
memset(name, 0, sizeof(name));
if (copy_from_user(name, &user_buffer[i], len))
return -EFAULT;
@@ -872,9 +884,9 @@ static ssize_t pktgen_if_write(struct file *file,
if (!strcmp(name, "min_pkt_size")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
if (value < 14 + 20 + 8)
value = 14 + 20 + 8;
@@ -889,9 +901,9 @@ static ssize_t pktgen_if_write(struct file *file,
if (!strcmp(name, "max_pkt_size")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
if (value < 14 + 20 + 8)
value = 14 + 20 + 8;
@@ -908,9 +920,9 @@ static ssize_t pktgen_if_write(struct file *file,
if (!strcmp(name, "pkt_size")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
if (value < 14 + 20 + 8)
value = 14 + 20 + 8;
@@ -925,9 +937,9 @@ static ssize_t pktgen_if_write(struct file *file,
if (!strcmp(name, "debug")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
debug = value;
sprintf(pg_result, "OK: debug=%u", debug);
@@ -936,9 +948,9 @@ static ssize_t pktgen_if_write(struct file *file,
if (!strcmp(name, "frags")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
pkt_dev->nfrags = value;
sprintf(pg_result, "OK: frags=%u", pkt_dev->nfrags);
@@ -946,26 +958,24 @@ static ssize_t pktgen_if_write(struct file *file,
}
if (!strcmp(name, "delay")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
- if (value == 0x7FFFFFFF) {
- pkt_dev->delay_us = 0x7FFFFFFF;
- pkt_dev->delay_ns = 0;
- } else {
- pkt_dev->delay_us = value / 1000;
- pkt_dev->delay_ns = value % 1000;
- }
- sprintf(pg_result, "OK: delay=%u",
- 1000 * pkt_dev->delay_us + pkt_dev->delay_ns);
+ if (value == 0x7FFFFFFF)
+ pkt_dev->delay = ULLONG_MAX;
+ else
+ pkt_dev->delay = (u64)value * NSEC_PER_USEC;
+
+ sprintf(pg_result, "OK: delay=%llu",
+ (unsigned long long) pkt_dev->delay);
return count;
}
if (!strcmp(name, "udp_src_min")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
if (value != pkt_dev->udp_src_min) {
pkt_dev->udp_src_min = value;
@@ -976,9 +986,9 @@ static ssize_t pktgen_if_write(struct file *file,
}
if (!strcmp(name, "udp_dst_min")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
if (value != pkt_dev->udp_dst_min) {
pkt_dev->udp_dst_min = value;
@@ -989,9 +999,9 @@ static ssize_t pktgen_if_write(struct file *file,
}
if (!strcmp(name, "udp_src_max")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
if (value != pkt_dev->udp_src_max) {
pkt_dev->udp_src_max = value;
@@ -1002,9 +1012,9 @@ static ssize_t pktgen_if_write(struct file *file,
}
if (!strcmp(name, "udp_dst_max")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
if (value != pkt_dev->udp_dst_max) {
pkt_dev->udp_dst_max = value;
@@ -1015,9 +1025,9 @@ static ssize_t pktgen_if_write(struct file *file,
}
if (!strcmp(name, "clone_skb")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
pkt_dev->clone_skb = value;
@@ -1026,9 +1036,9 @@ static ssize_t pktgen_if_write(struct file *file,
}
if (!strcmp(name, "count")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
pkt_dev->count = value;
sprintf(pg_result, "OK: count=%llu",
@@ -1037,9 +1047,9 @@ static ssize_t pktgen_if_write(struct file *file,
}
if (!strcmp(name, "src_mac_count")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
if (pkt_dev->src_mac_count != value) {
pkt_dev->src_mac_count = value;
@@ -1051,9 +1061,9 @@ static ssize_t pktgen_if_write(struct file *file,
}
if (!strcmp(name, "dst_mac_count")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
if (pkt_dev->dst_mac_count != value) {
pkt_dev->dst_mac_count = value;
@@ -1067,9 +1077,9 @@ static ssize_t pktgen_if_write(struct file *file,
char f[32];
memset(f, 0, 32);
len = strn_len(&user_buffer[i], sizeof(f) - 1);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
if (copy_from_user(f, &user_buffer[i], len))
return -EFAULT;
i += len;
@@ -1168,9 +1178,8 @@ static ssize_t pktgen_if_write(struct file *file,
}
if (!strcmp(name, "dst_min") || !strcmp(name, "dst")) {
len = strn_len(&user_buffer[i], sizeof(pkt_dev->dst_min) - 1);
- if (len < 0) {
+ if (len < 0)
return len;
- }
if (copy_from_user(buf, &user_buffer[i], len))
return -EFAULT;
@@ -1190,9 +1199,9 @@ static ssize_t pktgen_if_write(struct file *file,
}
if (!strcmp(name, "dst_max")) {
len = strn_len(&user_buffer[i], sizeof(pkt_dev->dst_max) - 1);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
if (copy_from_user(buf, &user_buffer[i], len))
return -EFAULT;
@@ -1303,9 +1312,9 @@ static ssize_t pktgen_if_write(struct file *file,
}
if (!strcmp(name, "src_min")) {
len = strn_len(&user_buffer[i], sizeof(pkt_dev->src_min) - 1);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
if (copy_from_user(buf, &user_buffer[i], len))
return -EFAULT;
buf[len] = 0;
@@ -1324,9 +1333,9 @@ static ssize_t pktgen_if_write(struct file *file,
}
if (!strcmp(name, "src_max")) {
len = strn_len(&user_buffer[i], sizeof(pkt_dev->src_max) - 1);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
if (copy_from_user(buf, &user_buffer[i], len))
return -EFAULT;
buf[len] = 0;
@@ -1350,9 +1359,9 @@ static ssize_t pktgen_if_write(struct file *file,
memcpy(old_dmac, pkt_dev->dst_mac, ETH_ALEN);
len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
memset(valstr, 0, sizeof(valstr));
if (copy_from_user(valstr, &user_buffer[i], len))
return -EFAULT;
@@ -1392,9 +1401,9 @@ static ssize_t pktgen_if_write(struct file *file,
memcpy(old_smac, pkt_dev->src_mac, ETH_ALEN);
len = strn_len(&user_buffer[i], sizeof(valstr) - 1);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
memset(valstr, 0, sizeof(valstr));
if (copy_from_user(valstr, &user_buffer[i], len))
return -EFAULT;
@@ -1435,9 +1444,9 @@ static ssize_t pktgen_if_write(struct file *file,
if (!strcmp(name, "flows")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
if (value > MAX_CFLOWS)
value = MAX_CFLOWS;
@@ -1449,9 +1458,9 @@ static ssize_t pktgen_if_write(struct file *file,
if (!strcmp(name, "flowlen")) {
len = num_arg(&user_buffer[i], 10, &value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
pkt_dev->lflow = value;
sprintf(pg_result, "OK: flowlen=%u", pkt_dev->lflow);
@@ -1460,9 +1469,9 @@ static ssize_t pktgen_if_write(struct file *file,
if (!strcmp(name, "queue_map_min")) {
len = num_arg(&user_buffer[i], 5, &value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
pkt_dev->queue_map_min = value;
sprintf(pg_result, "OK: queue_map_min=%u", pkt_dev->queue_map_min);
@@ -1471,9 +1480,9 @@ static ssize_t pktgen_if_write(struct file *file,
if (!strcmp(name, "queue_map_max")) {
len = num_arg(&user_buffer[i], 5, &value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
pkt_dev->queue_map_max = value;
sprintf(pg_result, "OK: queue_map_max=%u", pkt_dev->queue_map_max);
@@ -1505,9 +1514,9 @@ static ssize_t pktgen_if_write(struct file *file,
if (!strcmp(name, "vlan_id")) {
len = num_arg(&user_buffer[i], 4, &value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
if (value <= 4095) {
pkt_dev->vlan_id = value; /* turn on VLAN */
@@ -1532,9 +1541,9 @@ static ssize_t pktgen_if_write(struct file *file,
if (!strcmp(name, "vlan_p")) {
len = num_arg(&user_buffer[i], 1, &value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
if ((value <= 7) && (pkt_dev->vlan_id != 0xffff)) {
pkt_dev->vlan_p = value;
@@ -1547,9 +1556,9 @@ static ssize_t pktgen_if_write(struct file *file,
if (!strcmp(name, "vlan_cfi")) {
len = num_arg(&user_buffer[i], 1, &value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
if ((value <= 1) && (pkt_dev->vlan_id != 0xffff)) {
pkt_dev->vlan_cfi = value;
@@ -1562,9 +1571,9 @@ static ssize_t pktgen_if_write(struct file *file,
if (!strcmp(name, "svlan_id")) {
len = num_arg(&user_buffer[i], 4, &value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
if ((value <= 4095) && ((pkt_dev->vlan_id != 0xffff))) {
pkt_dev->svlan_id = value; /* turn on SVLAN */
@@ -1589,9 +1598,9 @@ static ssize_t pktgen_if_write(struct file *file,
if (!strcmp(name, "svlan_p")) {
len = num_arg(&user_buffer[i], 1, &value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
if ((value <= 7) && (pkt_dev->svlan_id != 0xffff)) {
pkt_dev->svlan_p = value;
@@ -1604,9 +1613,9 @@ static ssize_t pktgen_if_write(struct file *file,
if (!strcmp(name, "svlan_cfi")) {
len = num_arg(&user_buffer[i], 1, &value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
if ((value <= 1) && (pkt_dev->svlan_id != 0xffff)) {
pkt_dev->svlan_cfi = value;
@@ -1620,9 +1629,9 @@ static ssize_t pktgen_if_write(struct file *file,
if (!strcmp(name, "tos")) {
__u32 tmp_value = 0;
len = hex32_arg(&user_buffer[i], 2, &tmp_value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
if (len == 2) {
pkt_dev->tos = tmp_value;
@@ -1636,9 +1645,9 @@ static ssize_t pktgen_if_write(struct file *file,
if (!strcmp(name, "traffic_class")) {
__u32 tmp_value = 0;
len = hex32_arg(&user_buffer[i], 2, &tmp_value);
- if (len < 0) {
+ if (len < 0)
return len;
- }
+
i += len;
if (len == 2) {
pkt_dev->traffic_class = tmp_value;
@@ -1670,7 +1679,7 @@ static const struct file_operations pktgen_if_fops = {
static int pktgen_thread_show(struct seq_file *seq, void *v)
{
struct pktgen_thread *t = seq->private;
- struct pktgen_dev *pkt_dev;
+ const struct pktgen_dev *pkt_dev;
BUG_ON(!t);
@@ -1873,8 +1882,10 @@ static void pktgen_change_name(struct net_device *dev)
remove_proc_entry(pkt_dev->entry->name, pg_proc_dir);
- pkt_dev->entry = create_proc_entry(dev->name, 0600,
- pg_proc_dir);
+ pkt_dev->entry = proc_create_data(dev->name, 0600,
+ pg_proc_dir,
+ &pktgen_if_fops,
+ pkt_dev);
if (!pkt_dev->entry)
printk(KERN_ERR "pktgen: can't move proc "
" entry for '%s'\n", dev->name);
@@ -1908,13 +1919,14 @@ static int pktgen_device_event(struct notifier_block *unused,
return NOTIFY_DONE;
}
-static struct net_device *pktgen_dev_get_by_name(struct pktgen_dev *pkt_dev, const char *ifname)
+static struct net_device *pktgen_dev_get_by_name(struct pktgen_dev *pkt_dev,
+ const char *ifname)
{
char b[IFNAMSIZ+5];
int i = 0;
- for(i=0; ifname[i] != '@'; i++) {
- if(i == IFNAMSIZ)
+ for (i = 0; ifname[i] != '@'; i++) {
+ if (i == IFNAMSIZ)
break;
b[i] = ifname[i];
@@ -1981,7 +1993,7 @@ static void pktgen_setup_inject(struct pktgen_dev *pkt_dev)
printk(KERN_WARNING "pktgen: WARNING: Requested "
"queue_map_min (zero-based) (%d) exceeds valid range "
"[0 - %d] for (%d) queues on %s, resetting\n",
- pkt_dev->queue_map_min, (ntxq ?: 1)- 1, ntxq,
+ pkt_dev->queue_map_min, (ntxq ?: 1) - 1, ntxq,
pkt_dev->odev->name);
pkt_dev->queue_map_min = ntxq - 1;
}
@@ -1989,7 +2001,7 @@ static void pktgen_setup_inject(struct pktgen_dev *pkt_dev)
printk(KERN_WARNING "pktgen: WARNING: Requested "
"queue_map_max (zero-based) (%d) exceeds valid range "
"[0 - %d] for (%d) queues on %s, resetting\n",
- pkt_dev->queue_map_max, (ntxq ?: 1)- 1, ntxq,
+ pkt_dev->queue_map_max, (ntxq ?: 1) - 1, ntxq,
pkt_dev->odev->name);
pkt_dev->queue_map_max = ntxq - 1;
}
@@ -2030,7 +2042,8 @@ static void pktgen_setup_inject(struct pktgen_dev *pkt_dev)
*/
rcu_read_lock();
- if ((idev = __in6_dev_get(pkt_dev->odev)) != NULL) {
+ idev = __in6_dev_get(pkt_dev->odev);
+ if (idev) {
struct inet6_ifaddr *ifp;
read_lock_bh(&idev->lock);
@@ -2089,27 +2102,40 @@ static void pktgen_setup_inject(struct pktgen_dev *pkt_dev)
pkt_dev->nflows = 0;
}
-static void spin(struct pktgen_dev *pkt_dev, __u64 spin_until_us)
+
+static void spin(struct pktgen_dev *pkt_dev, ktime_t spin_until)
{
- __u64 start;
- __u64 now;
+ ktime_t start;
+ s32 remaining;
+ struct hrtimer_sleeper t;
+
+ hrtimer_init_on_stack(&t.timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+ hrtimer_set_expires(&t.timer, spin_until);
+
+ remaining = ktime_to_us(hrtimer_expires_remaining(&t.timer));
+ if (remaining <= 0)
+ return;
- start = now = getCurUs();
- while (now < spin_until_us) {
- /* TODO: optimize sleeping behavior */
- if (spin_until_us - now > jiffies_to_usecs(1) + 1)
- schedule_timeout_interruptible(1);
- else if (spin_until_us - now > 100) {
- if (!pkt_dev->running)
- return;
- if (need_resched())
+ start = ktime_now();
+ if (remaining < 100)
+ udelay(remaining); /* really small just spin */
+ else {
+ /* see do_nanosleep */
+ hrtimer_init_sleeper(&t, current);
+ do {
+ set_current_state(TASK_INTERRUPTIBLE);
+ hrtimer_start_expires(&t.timer, HRTIMER_MODE_ABS);
+ if (!hrtimer_active(&t.timer))
+ t.task = NULL;
+
+ if (likely(t.task))
schedule();
- }
- now = getCurUs();
+ hrtimer_cancel(&t.timer);
+ } while (t.task && pkt_dev->running && !signal_pending(current));
+ __set_current_state(TASK_RUNNING);
}
-
- pkt_dev->idle_acc += now - start;
+ pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_now(), start));
}
static inline void set_pkt_overhead(struct pktgen_dev *pkt_dev)
@@ -2120,13 +2146,9 @@ static inline void set_pkt_overhead(struct pktgen_dev *pkt_dev)
pkt_dev->pkt_overhead += SVLAN_TAG_SIZE(pkt_dev);
}
-static inline int f_seen(struct pktgen_dev *pkt_dev, int flow)
+static inline int f_seen(const struct pktgen_dev *pkt_dev, int flow)
{
-
- if (pkt_dev->flows[flow].flags & F_INIT)
- return 1;
- else
- return 0;
+ return !!(pkt_dev->flows[flow].flags & F_INIT);
}
static inline int f_pick(struct pktgen_dev *pkt_dev)
@@ -2174,7 +2196,7 @@ static void get_ipsec_sa(struct pktgen_dev *pkt_dev, int flow)
if (x) {
pkt_dev->flows[flow].x = x;
set_pkt_overhead(pkt_dev);
- pkt_dev->pkt_overhead+=x->props.header_len;
+ pkt_dev->pkt_overhead += x->props.header_len;
}
}
@@ -2313,18 +2335,18 @@ static void mod_cur_headers(struct pktgen_dev *pkt_dev)
if (!(pkt_dev->flags & F_IPV6)) {
- if ((imn = ntohl(pkt_dev->saddr_min)) < (imx =
- ntohl(pkt_dev->
- saddr_max))) {
+ imn = ntohl(pkt_dev->saddr_min);
+ imx = ntohl(pkt_dev->saddr_max);
+ if (imn < imx) {
__u32 t;
if (pkt_dev->flags & F_IPSRC_RND)
t = random32() % (imx - imn) + imn;
else {
t = ntohl(pkt_dev->cur_saddr);
t++;
- if (t > imx) {
+ if (t > imx)
t = imn;
- }
+
}
pkt_dev->cur_saddr = htonl(t);
}
@@ -2435,14 +2457,14 @@ static int pktgen_output_ipsec(struct sk_buff *skb, struct pktgen_dev *pkt_dev)
if (err)
goto error;
- x->curlft.bytes +=skb->len;
+ x->curlft.bytes += skb->len;
x->curlft.packets++;
error:
spin_unlock(&x->lock);
return err;
}
-static inline void free_SAs(struct pktgen_dev *pkt_dev)
+static void free_SAs(struct pktgen_dev *pkt_dev)
{
if (pkt_dev->cflows) {
/* let go of the SAs if we have them */
@@ -2457,7 +2479,7 @@ static inline void free_SAs(struct pktgen_dev *pkt_dev)
}
}
-static inline int process_ipsec(struct pktgen_dev *pkt_dev,
+static int process_ipsec(struct pktgen_dev *pkt_dev,
struct sk_buff *skb, __be16 protocol)
{
if (pkt_dev->flags & F_IPSEC_ON) {
@@ -2467,11 +2489,11 @@ static inline int process_ipsec(struct pktgen_dev *pkt_dev,
int ret;
__u8 *eth;
nhead = x->props.header_len - skb_headroom(skb);
- if (nhead >0) {
+ if (nhead > 0) {
ret = pskb_expand_head(skb, nhead, 0, GFP_ATOMIC);
if (ret < 0) {
printk(KERN_ERR "Error expanding "
- "ipsec packet %d\n",ret);
+ "ipsec packet %d\n", ret);
goto err;
}
}
@@ -2481,13 +2503,13 @@ static inline int process_ipsec(struct pktgen_dev *pkt_dev,
ret = pktgen_output_ipsec(skb, pkt_dev);
if (ret) {
printk(KERN_ERR "Error creating ipsec "
- "packet %d\n",ret);
+ "packet %d\n", ret);
goto err;
}
/* restore ll */
eth = (__u8 *) skb_push(skb, ETH_HLEN);
memcpy(eth, pkt_dev->hh, 12);
- *(u16 *) & eth[12] = protocol;
+ *(u16 *) &eth[12] = protocol;
}
}
return 1;
@@ -2500,9 +2522,9 @@ err:
static void mpls_push(__be32 *mpls, struct pktgen_dev *pkt_dev)
{
unsigned i;
- for (i = 0; i < pkt_dev->nr_labels; i++) {
+ for (i = 0; i < pkt_dev->nr_labels; i++)
*mpls++ = pkt_dev->labels[i] & ~MPLS_STACK_BOTTOM;
- }
+
mpls--;
*mpls |= MPLS_STACK_BOTTOM;
}
@@ -2543,8 +2565,9 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
mod_cur_headers(pkt_dev);
datalen = (odev->hard_header_len + 16) & ~0xf;
- skb = alloc_skb(pkt_dev->cur_pkt_size + 64 + datalen +
- pkt_dev->pkt_overhead, GFP_ATOMIC);
+ skb = __netdev_alloc_skb(odev,
+ pkt_dev->cur_pkt_size + 64
+ + datalen + pkt_dev->pkt_overhead, GFP_NOWAIT);
if (!skb) {
sprintf(pkt_dev->result, "No memory");
return NULL;
@@ -2668,8 +2691,9 @@ static struct sk_buff *fill_packet_ipv4(struct net_device *odev,
}
}
- /* Stamp the time, and sequence number, convert them to network byte order */
-
+ /* Stamp the time, and sequence number,
+ * convert them to network byte order
+ */
if (pgh) {
struct timeval timestamp;
@@ -2882,8 +2906,9 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
queue_map = pkt_dev->cur_queue_map;
mod_cur_headers(pkt_dev);
- skb = alloc_skb(pkt_dev->cur_pkt_size + 64 + 16 +
- pkt_dev->pkt_overhead, GFP_ATOMIC);
+ skb = __netdev_alloc_skb(odev,
+ pkt_dev->cur_pkt_size + 64
+ + 16 + pkt_dev->pkt_overhead, GFP_NOWAIT);
if (!skb) {
sprintf(pkt_dev->result, "No memory");
return NULL;
@@ -2922,7 +2947,7 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
udph = udp_hdr(skb);
memcpy(eth, pkt_dev->hh, 12);
- *(__be16 *) & eth[12] = protocol;
+ *(__be16 *) &eth[12] = protocol;
/* Eth + IPh + UDPh + mpls */
datalen = pkt_dev->cur_pkt_size - 14 -
@@ -3016,8 +3041,10 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
}
}
- /* Stamp the time, and sequence number, convert them to network byte order */
- /* should we update cloned packets too ? */
+ /* Stamp the time, and sequence number,
+ * convert them to network byte order
+ * should we update cloned packets too ?
+ */
if (pgh) {
struct timeval timestamp;
@@ -3033,8 +3060,8 @@ static struct sk_buff *fill_packet_ipv6(struct net_device *odev,
return skb;
}
-static inline struct sk_buff *fill_packet(struct net_device *odev,
- struct pktgen_dev *pkt_dev)
+static struct sk_buff *fill_packet(struct net_device *odev,
+ struct pktgen_dev *pkt_dev)
{
if (pkt_dev->flags & F_IPV6)
return fill_packet_ipv6(odev, pkt_dev);
@@ -3072,9 +3099,9 @@ static void pktgen_run(struct pktgen_thread *t)
pktgen_clear_counters(pkt_dev);
pkt_dev->running = 1; /* Cranke yeself! */
pkt_dev->skb = NULL;
- pkt_dev->started_at = getCurUs();
- pkt_dev->next_tx_us = getCurUs(); /* Transmit immediately */
- pkt_dev->next_tx_ns = 0;
+ pkt_dev->started_at =
+ pkt_dev->next_tx = ktime_now();
+
set_pkt_overhead(pkt_dev);
strcpy(pkt_dev->result, "Starting");
@@ -3101,17 +3128,14 @@ static void pktgen_stop_all_threads_ifs(void)
mutex_unlock(&pktgen_thread_lock);
}
-static int thread_is_running(struct pktgen_thread *t)
+static int thread_is_running(const struct pktgen_thread *t)
{
- struct pktgen_dev *pkt_dev;
- int res = 0;
+ const struct pktgen_dev *pkt_dev;
list_for_each_entry(pkt_dev, &t->if_list, list)
- if (pkt_dev->running) {
- res = 1;
- break;
- }
- return res;
+ if (pkt_dev->running)
+ return 1;
+ return 0;
}
static int pktgen_wait_thread_run(struct pktgen_thread *t)
@@ -3168,7 +3192,8 @@ static void pktgen_run_all_threads(void)
mutex_unlock(&pktgen_thread_lock);
- schedule_timeout_interruptible(msecs_to_jiffies(125)); /* Propagate thread->control */
+ /* Propagate thread->control */
+ schedule_timeout_interruptible(msecs_to_jiffies(125));
pktgen_wait_all_threads_run();
}
@@ -3186,35 +3211,29 @@ static void pktgen_reset_all_threads(void)
mutex_unlock(&pktgen_thread_lock);
- schedule_timeout_interruptible(msecs_to_jiffies(125)); /* Propagate thread->control */
+ /* Propagate thread->control */
+ schedule_timeout_interruptible(msecs_to_jiffies(125));
pktgen_wait_all_threads_run();
}
static void show_results(struct pktgen_dev *pkt_dev, int nr_frags)
{
- __u64 total_us, bps, mbps, pps, idle;
+ __u64 bps, mbps, pps;
char *p = pkt_dev->result;
-
- total_us = pkt_dev->stopped_at - pkt_dev->started_at;
-
- idle = pkt_dev->idle_acc;
-
- p += sprintf(p, "OK: %llu(c%llu+d%llu) usec, %llu (%dbyte,%dfrags)\n",
- (unsigned long long)total_us,
- (unsigned long long)(total_us - idle),
- (unsigned long long)idle,
+ ktime_t elapsed = ktime_sub(pkt_dev->stopped_at,
+ pkt_dev->started_at);
+ ktime_t idle = ns_to_ktime(pkt_dev->idle_acc);
+
+ p += sprintf(p, "OK: %llu(c%llu+d%llu) nsec, %llu (%dbyte,%dfrags)\n",
+ (unsigned long long)ktime_to_us(elapsed),
+ (unsigned long long)ktime_to_us(ktime_sub(elapsed, idle)),
+ (unsigned long long)ktime_to_us(idle),
(unsigned long long)pkt_dev->sofar,
pkt_dev->cur_pkt_size, nr_frags);
- pps = pkt_dev->sofar * USEC_PER_SEC;
-
- while ((total_us >> 32) != 0) {
- pps >>= 1;
- total_us >>= 1;
- }
-
- do_div(pps, total_us);
+ pps = div64_u64(pkt_dev->sofar * NSEC_PER_SEC,
+ ktime_to_ns(elapsed));
bps = pps * 8 * pkt_dev->cur_pkt_size;
@@ -3228,7 +3247,6 @@ static void show_results(struct pktgen_dev *pkt_dev, int nr_frags)
}
/* Set stopped-at timer, remove from running list, do counters & statistics */
-
static int pktgen_stop_device(struct pktgen_dev *pkt_dev)
{
int nr_frags = pkt_dev->skb ? skb_shinfo(pkt_dev->skb)->nr_frags : -1;
@@ -3239,7 +3257,9 @@ static int pktgen_stop_device(struct pktgen_dev *pkt_dev)
return -EINVAL;
}
- pkt_dev->stopped_at = getCurUs();
+ kfree_skb(pkt_dev->skb);
+ pkt_dev->skb = NULL;
+ pkt_dev->stopped_at = ktime_now();
pkt_dev->running = 0;
show_results(pkt_dev, nr_frags);
@@ -3258,7 +3278,7 @@ static struct pktgen_dev *next_to_run(struct pktgen_thread *t)
continue;
if (best == NULL)
best = pkt_dev;
- else if (pkt_dev->next_tx_us < best->next_tx_us)
+ else if (ktime_lt(pkt_dev->next_tx, best->next_tx))
best = pkt_dev;
}
if_unlock(t);
@@ -3275,9 +3295,6 @@ static void pktgen_stop(struct pktgen_thread *t)
list_for_each_entry(pkt_dev, &t->if_list, list) {
pktgen_stop_device(pkt_dev);
- kfree_skb(pkt_dev->skb);
-
- pkt_dev->skb = NULL;
}
if_unlock(t);
@@ -3348,30 +3365,37 @@ static void pktgen_rem_thread(struct pktgen_thread *t)
mutex_unlock(&pktgen_thread_lock);
}
-static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
+static void idle(struct pktgen_dev *pkt_dev)
+{
+ ktime_t idle_start = ktime_now();
+
+ if (need_resched())
+ schedule();
+ else
+ cpu_relax();
+
+ pkt_dev->idle_acc += ktime_to_ns(ktime_sub(ktime_now(), idle_start));
+}
+
+
+static void pktgen_xmit(struct pktgen_dev *pkt_dev)
{
struct net_device *odev = pkt_dev->odev;
- int (*xmit)(struct sk_buff *, struct net_device *)
+ netdev_tx_t (*xmit)(struct sk_buff *, struct net_device *)
= odev->netdev_ops->ndo_start_xmit;
struct netdev_queue *txq;
- __u64 idle_start = 0;
u16 queue_map;
int ret;
- if (pkt_dev->delay_us || pkt_dev->delay_ns) {
- u64 now;
-
- now = getCurUs();
- if (now < pkt_dev->next_tx_us)
- spin(pkt_dev, pkt_dev->next_tx_us);
+ if (pkt_dev->delay) {
+ spin(pkt_dev, pkt_dev->next_tx);
/* This is max DELAY, this has special meaning of
* "never transmit"
*/
- if (pkt_dev->delay_us == 0x7FFFFFFF) {
- pkt_dev->next_tx_us = getCurUs() + pkt_dev->delay_us;
- pkt_dev->next_tx_ns = pkt_dev->delay_ns;
- goto out;
+ if (pkt_dev->delay == ULLONG_MAX) {
+ pkt_dev->next_tx = ktime_add_ns(ktime_now(), ULONG_MAX);
+ return;
}
}
@@ -3383,47 +3407,32 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
}
txq = netdev_get_tx_queue(odev, queue_map);
- if (netif_tx_queue_stopped(txq) ||
- netif_tx_queue_frozen(txq) ||
- need_resched()) {
- idle_start = getCurUs();
-
- if (!netif_running(odev)) {
+ /* Did we saturate the queue already? */
+ if (netif_tx_queue_stopped(txq) || netif_tx_queue_frozen(txq)) {
+ /* If device is down, then all queues are permnantly frozen */
+ if (netif_running(odev))
+ idle(pkt_dev);
+ else
pktgen_stop_device(pkt_dev);
- kfree_skb(pkt_dev->skb);
- pkt_dev->skb = NULL;
- goto out;
- }
- if (need_resched())
- schedule();
-
- pkt_dev->idle_acc += getCurUs() - idle_start;
-
- if (netif_tx_queue_stopped(txq) ||
- netif_tx_queue_frozen(txq)) {
- pkt_dev->next_tx_us = getCurUs(); /* TODO */
- pkt_dev->next_tx_ns = 0;
- goto out; /* Try the next interface */
- }
+ return;
}
- if (pkt_dev->last_ok || !pkt_dev->skb) {
- if ((++pkt_dev->clone_count >= pkt_dev->clone_skb)
- || (!pkt_dev->skb)) {
- /* build a new pkt */
- kfree_skb(pkt_dev->skb);
+ if (!pkt_dev->skb || (pkt_dev->last_ok &&
+ ++pkt_dev->clone_count >= pkt_dev->clone_skb)) {
+ /* build a new pkt */
+ kfree_skb(pkt_dev->skb);
- pkt_dev->skb = fill_packet(odev, pkt_dev);
- if (pkt_dev->skb == NULL) {
- printk(KERN_ERR "pktgen: ERROR: couldn't "
- "allocate skb in fill_packet.\n");
- schedule();
- pkt_dev->clone_count--; /* back out increment, OOM */
- goto out;
- }
- pkt_dev->allocated_skbs++;
- pkt_dev->clone_count = 0; /* reset counter */
+ pkt_dev->skb = fill_packet(odev, pkt_dev);
+ if (pkt_dev->skb == NULL) {
+ printk(KERN_ERR "pktgen: ERROR: couldn't "
+ "allocate skb in fill_packet.\n");
+ schedule();
+ pkt_dev->clone_count--; /* back out increment, OOM */
+ return;
}
+
+ pkt_dev->allocated_skbs++;
+ pkt_dev->clone_count = 0; /* reset counter */
}
/* fill_packet() might have changed the queue */
@@ -3431,73 +3440,53 @@ static __inline__ void pktgen_xmit(struct pktgen_dev *pkt_dev)
txq = netdev_get_tx_queue(odev, queue_map);
__netif_tx_lock_bh(txq);
- if (!netif_tx_queue_stopped(txq) &&
- !netif_tx_queue_frozen(txq)) {
-
+ if (unlikely(netif_tx_queue_stopped(txq) || netif_tx_queue_frozen(txq)))
+ pkt_dev->last_ok = 0;
+ else {
atomic_inc(&(pkt_dev->skb->users));
- retry_now:
+
+ retry_now:
ret = (*xmit)(pkt_dev->skb, odev);
- if (likely(ret == NETDEV_TX_OK)) {
+ switch (ret) {
+ case NETDEV_TX_OK:
txq_trans_update(txq);
pkt_dev->last_ok = 1;
pkt_dev->sofar++;
pkt_dev->seq_num++;
pkt_dev->tx_bytes += pkt_dev->cur_pkt_size;
-
- } else if (ret == NETDEV_TX_LOCKED
- && (odev->features & NETIF_F_LLTX)) {
+ break;
+ case NETDEV_TX_LOCKED:
cpu_relax();
goto retry_now;
- } else { /* Retry it next time */
-
- atomic_dec(&(pkt_dev->skb->users));
-
- if (debug && net_ratelimit())
- printk(KERN_INFO "pktgen: Hard xmit error\n");
-
+ default: /* Drivers are not supposed to return other values! */
+ if (net_ratelimit())
+ pr_info("pktgen: %s xmit error: %d\n",
+ odev->name, ret);
pkt_dev->errors++;
+ /* fallthru */
+ case NETDEV_TX_BUSY:
+ /* Retry it next time */
+ atomic_dec(&(pkt_dev->skb->users));
pkt_dev->last_ok = 0;
}
- pkt_dev->next_tx_us = getCurUs();
- pkt_dev->next_tx_ns = 0;
-
- pkt_dev->next_tx_us += pkt_dev->delay_us;
- pkt_dev->next_tx_ns += pkt_dev->delay_ns;
-
- if (pkt_dev->next_tx_ns > 1000) {
- pkt_dev->next_tx_us++;
- pkt_dev->next_tx_ns -= 1000;
- }
+ if (pkt_dev->delay)
+ pkt_dev->next_tx = ktime_add_ns(ktime_now(),
+ pkt_dev->delay);
}
-
- else { /* Retry it next time */
- pkt_dev->last_ok = 0;
- pkt_dev->next_tx_us = getCurUs(); /* TODO */
- pkt_dev->next_tx_ns = 0;
- }
-
__netif_tx_unlock_bh(txq);
/* If pkt_dev->count is zero, then run forever */
if ((pkt_dev->count != 0) && (pkt_dev->sofar >= pkt_dev->count)) {
- if (atomic_read(&(pkt_dev->skb->users)) != 1) {
- idle_start = getCurUs();
- while (atomic_read(&(pkt_dev->skb->users)) != 1) {
- if (signal_pending(current)) {
- break;
- }
- schedule();
- }
- pkt_dev->idle_acc += getCurUs() - idle_start;
+ while (atomic_read(&(pkt_dev->skb->users)) != 1) {
+ if (signal_pending(current))
+ break;
+ idle(pkt_dev);
}
/* Done with this */
pktgen_stop_device(pkt_dev);
- kfree_skb(pkt_dev->skb);
- pkt_dev->skb = NULL;
}
-out:;
}
/*
@@ -3516,7 +3505,8 @@ static int pktgen_thread_worker(void *arg)
init_waitqueue_head(&t->queue);
complete(&t->start_done);
- pr_debug("pktgen: starting pktgen/%d: pid=%d\n", cpu, task_pid_nr(current));
+ pr_debug("pktgen: starting pktgen/%d: pid=%d\n",
+ cpu, task_pid_nr(current));
set_current_state(TASK_INTERRUPTIBLE);
@@ -3651,8 +3641,7 @@ static int pktgen_add_device(struct pktgen_thread *t, const char *ifname)
pkt_dev->max_pkt_size = ETH_ZLEN;
pkt_dev->nfrags = 0;
pkt_dev->clone_skb = pg_clone_skb_d;
- pkt_dev->delay_us = pg_delay_d / 1000;
- pkt_dev->delay_ns = pg_delay_d % 1000;
+ pkt_dev->delay = pg_delay_d;
pkt_dev->count = pg_count_d;
pkt_dev->sofar = 0;
pkt_dev->udp_src_min = 9; /* sink port */
@@ -3864,10 +3853,15 @@ static void __exit pg_cleanup(void)
module_init(pg_init);
module_exit(pg_cleanup);
-MODULE_AUTHOR("Robert Olsson <robert.olsson@its.uu.se");
+MODULE_AUTHOR("Robert Olsson <robert.olsson@its.uu.se>");
MODULE_DESCRIPTION("Packet Generator tool");
MODULE_LICENSE("GPL");
+MODULE_VERSION(VERSION);
module_param(pg_count_d, int, 0);
+MODULE_PARM_DESC(pg_count_d, "Default number of packets to inject");
module_param(pg_delay_d, int, 0);
+MODULE_PARM_DESC(pg_delay_d, "Default delay between packets (nanoseconds)");
module_param(pg_clone_skb_d, int, 0);
+MODULE_PARM_DESC(pg_clone_skb_d, "Default number of copies of the same packet");
module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "Enable debugging of pktgen module");
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index d78030f88bd0..eb42873f2a3a 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -35,7 +35,6 @@
#include <linux/security.h>
#include <linux/mutex.h>
#include <linux/if_addr.h>
-#include <linux/nsproxy.h>
#include <asm/uaccess.h>
#include <asm/system.h>
@@ -52,6 +51,7 @@
#include <net/pkt_sched.h>
#include <net/fib_rules.h>
#include <net/rtnetlink.h>
+#include <net/net_namespace.h>
struct rtnl_link
{
@@ -606,7 +606,6 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
int type, u32 pid, u32 seq, u32 change,
unsigned int flags)
{
- struct netdev_queue *txq;
struct ifinfomsg *ifm;
struct nlmsghdr *nlh;
const struct net_device_stats *stats;
@@ -637,9 +636,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
if (dev->master)
NLA_PUT_U32(skb, IFLA_MASTER, dev->master->ifindex);
- txq = netdev_get_tx_queue(dev, 0);
- if (txq->qdisc_sleeping)
- NLA_PUT_STRING(skb, IFLA_QDISC, txq->qdisc_sleeping->ops->id);
+ if (dev->qdisc)
+ NLA_PUT_STRING(skb, IFLA_QDISC, dev->qdisc->ops->id);
if (dev->ifalias)
NLA_PUT_STRING(skb, IFLA_IFALIAS, dev->ifalias);
@@ -725,25 +723,6 @@ static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = {
[IFLA_INFO_DATA] = { .type = NLA_NESTED },
};
-static struct net *get_net_ns_by_pid(pid_t pid)
-{
- struct task_struct *tsk;
- struct net *net;
-
- /* Lookup the network namespace */
- net = ERR_PTR(-ESRCH);
- rcu_read_lock();
- tsk = find_task_by_vpid(pid);
- if (tsk) {
- struct nsproxy *nsproxy;
- nsproxy = task_nsproxy(tsk);
- if (nsproxy)
- net = get_net(nsproxy->net_ns);
- }
- rcu_read_unlock();
- return net;
-}
-
static int validate_linkmsg(struct net_device *dev, struct nlattr *tb[])
{
if (dev) {
@@ -993,12 +972,20 @@ struct net_device *rtnl_create_link(struct net *net, char *ifname,
{
int err;
struct net_device *dev;
+ unsigned int num_queues = 1;
+ unsigned int real_num_queues = 1;
+ if (ops->get_tx_queues) {
+ err = ops->get_tx_queues(net, tb, &num_queues, &real_num_queues);
+ if (err)
+ goto err;
+ }
err = -ENOMEM;
- dev = alloc_netdev(ops->priv_size, ifname, ops->setup);
+ dev = alloc_netdev_mq(ops->priv_size, ifname, ops->setup, num_queues);
if (!dev)
goto err;
+ dev->real_num_tx_queues = real_num_queues;
if (strchr(dev->name, '%')) {
err = dev_alloc_name(dev, dev->name);
if (err < 0)
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 9e0597d189b0..80a96166df39 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -559,9 +559,6 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
#endif
#endif
new->vlan_tci = old->vlan_tci;
-#if defined(CONFIG_MAC80211) || defined(CONFIG_MAC80211_MODULE)
- new->do_not_encrypt = old->do_not_encrypt;
-#endif
skb_copy_secmark(new, old);
}
diff --git a/net/core/sock.c b/net/core/sock.c
index 76334228ed1c..30d5446512f9 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -142,7 +142,7 @@ static struct lock_class_key af_family_slock_keys[AF_MAX];
* strings build-time, so that runtime initialization of socket
* locks is fast):
*/
-static const char *af_family_key_strings[AF_MAX+1] = {
+static const char *const af_family_key_strings[AF_MAX+1] = {
"sk_lock-AF_UNSPEC", "sk_lock-AF_UNIX" , "sk_lock-AF_INET" ,
"sk_lock-AF_AX25" , "sk_lock-AF_IPX" , "sk_lock-AF_APPLETALK",
"sk_lock-AF_NETROM", "sk_lock-AF_BRIDGE" , "sk_lock-AF_ATMPVC" ,
@@ -158,7 +158,7 @@ static const char *af_family_key_strings[AF_MAX+1] = {
"sk_lock-AF_IEEE802154",
"sk_lock-AF_MAX"
};
-static const char *af_family_slock_key_strings[AF_MAX+1] = {
+static const char *const af_family_slock_key_strings[AF_MAX+1] = {
"slock-AF_UNSPEC", "slock-AF_UNIX" , "slock-AF_INET" ,
"slock-AF_AX25" , "slock-AF_IPX" , "slock-AF_APPLETALK",
"slock-AF_NETROM", "slock-AF_BRIDGE" , "slock-AF_ATMPVC" ,
@@ -174,7 +174,7 @@ static const char *af_family_slock_key_strings[AF_MAX+1] = {
"slock-AF_IEEE802154",
"slock-AF_MAX"
};
-static const char *af_family_clock_key_strings[AF_MAX+1] = {
+static const char *const af_family_clock_key_strings[AF_MAX+1] = {
"clock-AF_UNSPEC", "clock-AF_UNIX" , "clock-AF_INET" ,
"clock-AF_AX25" , "clock-AF_IPX" , "clock-AF_APPLETALK",
"clock-AF_NETROM", "clock-AF_BRIDGE" , "clock-AF_ATMPVC" ,
@@ -482,6 +482,8 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
sk->sk_reuse = valbool;
break;
case SO_TYPE:
+ case SO_PROTOCOL:
+ case SO_DOMAIN:
case SO_ERROR:
ret = -ENOPROTOOPT;
break;
@@ -764,6 +766,14 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
v.val = sk->sk_type;
break;
+ case SO_PROTOCOL:
+ v.val = sk->sk_protocol;
+ break;
+
+ case SO_DOMAIN:
+ v.val = sk->sk_family;
+ break;
+
case SO_ERROR:
v.val = -sock_error(sk);
if (v.val == 0)
diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c
index 8379496de82b..e0879bfb7dd5 100644
--- a/net/dcb/dcbnl.c
+++ b/net/dcb/dcbnl.c
@@ -64,6 +64,7 @@ static struct nla_policy dcbnl_rtnl_policy[DCB_ATTR_MAX + 1] = {
[DCB_ATTR_CAP] = {.type = NLA_NESTED},
[DCB_ATTR_PFC_STATE] = {.type = NLA_U8},
[DCB_ATTR_BCN] = {.type = NLA_NESTED},
+ [DCB_ATTR_APP] = {.type = NLA_NESTED},
};
/* DCB priority flow control to User Priority nested attributes */
@@ -158,6 +159,13 @@ static struct nla_policy dcbnl_bcn_nest[DCB_BCN_ATTR_MAX + 1] = {
[DCB_BCN_ATTR_ALL] = {.type = NLA_FLAG},
};
+/* DCB APP nested attributes. */
+static struct nla_policy dcbnl_app_nest[DCB_APP_ATTR_MAX + 1] = {
+ [DCB_APP_ATTR_IDTYPE] = {.type = NLA_U8},
+ [DCB_APP_ATTR_ID] = {.type = NLA_U16},
+ [DCB_APP_ATTR_PRIORITY] = {.type = NLA_U8},
+};
+
/* standard netlink reply call */
static int dcbnl_reply(u8 value, u8 event, u8 cmd, u8 attr, u32 pid,
u32 seq, u16 flags)
@@ -536,6 +544,120 @@ static int dcbnl_setpfcstate(struct net_device *netdev, struct nlattr **tb,
return ret;
}
+static int dcbnl_getapp(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags)
+{
+ struct sk_buff *dcbnl_skb;
+ struct nlmsghdr *nlh;
+ struct dcbmsg *dcb;
+ struct nlattr *app_nest;
+ struct nlattr *app_tb[DCB_APP_ATTR_MAX + 1];
+ u16 id;
+ u8 up, idtype;
+ int ret = -EINVAL;
+
+ if (!tb[DCB_ATTR_APP] || !netdev->dcbnl_ops->getapp)
+ goto out;
+
+ ret = nla_parse_nested(app_tb, DCB_APP_ATTR_MAX, tb[DCB_ATTR_APP],
+ dcbnl_app_nest);
+ if (ret)
+ goto out;
+
+ ret = -EINVAL;
+ /* all must be non-null */
+ if ((!app_tb[DCB_APP_ATTR_IDTYPE]) ||
+ (!app_tb[DCB_APP_ATTR_ID]))
+ goto out;
+
+ /* either by eth type or by socket number */
+ idtype = nla_get_u8(app_tb[DCB_APP_ATTR_IDTYPE]);
+ if ((idtype != DCB_APP_IDTYPE_ETHTYPE) &&
+ (idtype != DCB_APP_IDTYPE_PORTNUM))
+ goto out;
+
+ id = nla_get_u16(app_tb[DCB_APP_ATTR_ID]);
+ up = netdev->dcbnl_ops->getapp(netdev, idtype, id);
+
+ /* send this back */
+ dcbnl_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!dcbnl_skb)
+ goto out;
+
+ nlh = NLMSG_NEW(dcbnl_skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags);
+ dcb = NLMSG_DATA(nlh);
+ dcb->dcb_family = AF_UNSPEC;
+ dcb->cmd = DCB_CMD_GAPP;
+
+ app_nest = nla_nest_start(dcbnl_skb, DCB_ATTR_APP);
+ ret = nla_put_u8(dcbnl_skb, DCB_APP_ATTR_IDTYPE, idtype);
+ if (ret)
+ goto out_cancel;
+
+ ret = nla_put_u16(dcbnl_skb, DCB_APP_ATTR_ID, id);
+ if (ret)
+ goto out_cancel;
+
+ ret = nla_put_u8(dcbnl_skb, DCB_APP_ATTR_PRIORITY, up);
+ if (ret)
+ goto out_cancel;
+
+ nla_nest_end(dcbnl_skb, app_nest);
+ nlmsg_end(dcbnl_skb, nlh);
+
+ ret = rtnl_unicast(dcbnl_skb, &init_net, pid);
+ if (ret)
+ goto nlmsg_failure;
+
+ goto out;
+
+out_cancel:
+ nla_nest_cancel(dcbnl_skb, app_nest);
+nlmsg_failure:
+ kfree_skb(dcbnl_skb);
+out:
+ return ret;
+}
+
+static int dcbnl_setapp(struct net_device *netdev, struct nlattr **tb,
+ u32 pid, u32 seq, u16 flags)
+{
+ int ret = -EINVAL;
+ u16 id;
+ u8 up, idtype;
+ struct nlattr *app_tb[DCB_APP_ATTR_MAX + 1];
+
+ if (!tb[DCB_ATTR_APP] || !netdev->dcbnl_ops->setapp)
+ goto out;
+
+ ret = nla_parse_nested(app_tb, DCB_APP_ATTR_MAX, tb[DCB_ATTR_APP],
+ dcbnl_app_nest);
+ if (ret)
+ goto out;
+
+ ret = -EINVAL;
+ /* all must be non-null */
+ if ((!app_tb[DCB_APP_ATTR_IDTYPE]) ||
+ (!app_tb[DCB_APP_ATTR_ID]) ||
+ (!app_tb[DCB_APP_ATTR_PRIORITY]))
+ goto out;
+
+ /* either by eth type or by socket number */
+ idtype = nla_get_u8(app_tb[DCB_APP_ATTR_IDTYPE]);
+ if ((idtype != DCB_APP_IDTYPE_ETHTYPE) &&
+ (idtype != DCB_APP_IDTYPE_PORTNUM))
+ goto out;
+
+ id = nla_get_u16(app_tb[DCB_APP_ATTR_ID]);
+ up = nla_get_u8(app_tb[DCB_APP_ATTR_PRIORITY]);
+
+ ret = dcbnl_reply(netdev->dcbnl_ops->setapp(netdev, idtype, id, up),
+ RTM_SETDCB, DCB_CMD_SAPP, DCB_ATTR_APP,
+ pid, seq, flags);
+out:
+ return ret;
+}
+
static int __dcbnl_pg_getcfg(struct net_device *netdev, struct nlattr **tb,
u32 pid, u32 seq, u16 flags, int dir)
{
@@ -1093,6 +1215,14 @@ static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
ret = dcbnl_bcn_setcfg(netdev, tb, pid, nlh->nlmsg_seq,
nlh->nlmsg_flags);
goto out;
+ case DCB_CMD_GAPP:
+ ret = dcbnl_getapp(netdev, tb, pid, nlh->nlmsg_seq,
+ nlh->nlmsg_flags);
+ goto out;
+ case DCB_CMD_SAPP:
+ ret = dcbnl_setapp(netdev, tb, pid, nlh->nlmsg_seq,
+ nlh->nlmsg_flags);
+ goto out;
default:
goto errout;
}
diff --git a/net/dccp/ccids/ccid3.c b/net/dccp/ccids/ccid3.c
index a27b7f4c19c5..f596ce149c3c 100644
--- a/net/dccp/ccids/ccid3.c
+++ b/net/dccp/ccids/ccid3.c
@@ -52,7 +52,7 @@ static int ccid3_debug;
#ifdef CONFIG_IP_DCCP_CCID3_DEBUG
static const char *ccid3_tx_state_name(enum ccid3_hc_tx_states state)
{
- static char *ccid3_state_names[] = {
+ static const char *const ccid3_state_names[] = {
[TFRC_SSTATE_NO_SENT] = "NO_SENT",
[TFRC_SSTATE_NO_FBACK] = "NO_FBACK",
[TFRC_SSTATE_FBACK] = "FBACK",
@@ -646,7 +646,7 @@ enum ccid3_fback_type {
#ifdef CONFIG_IP_DCCP_CCID3_DEBUG
static const char *ccid3_rx_state_name(enum ccid3_hc_rx_states state)
{
- static char *ccid3_rx_state_names[] = {
+ static const char *const ccid3_rx_state_names[] = {
[TFRC_RSTATE_NO_DATA] = "NO_DATA",
[TFRC_RSTATE_DATA] = "DATA",
[TFRC_RSTATE_TERM] = "TERM",
diff --git a/net/dccp/feat.c b/net/dccp/feat.c
index b04160a2eea5..972b8dc918d6 100644
--- a/net/dccp/feat.c
+++ b/net/dccp/feat.c
@@ -213,7 +213,7 @@ static int dccp_feat_default_value(u8 feat_num)
*/
static const char *dccp_feat_fname(const u8 feat)
{
- static const char *feature_names[] = {
+ static const char *const feature_names[] = {
[DCCPF_RESERVED] = "Reserved",
[DCCPF_CCID] = "CCID",
[DCCPF_SHORT_SEQNOS] = "Allow Short Seqnos",
@@ -236,8 +236,9 @@ static const char *dccp_feat_fname(const u8 feat)
return feature_names[feat];
}
-static const char *dccp_feat_sname[] = { "DEFAULT", "INITIALISING", "CHANGING",
- "UNSTABLE", "STABLE" };
+static const char *const dccp_feat_sname[] = {
+ "DEFAULT", "INITIALISING", "CHANGING", "UNSTABLE", "STABLE",
+};
#ifdef CONFIG_IP_DCCP_DEBUG
static const char *dccp_feat_oname(const u8 opt)
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index a0a36c9e6cce..d01c00de1ad0 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -880,7 +880,7 @@ discard_and_relse:
goto discard_it;
}
-static struct inet_connection_sock_af_ops dccp_ipv4_af_ops = {
+static const struct inet_connection_sock_af_ops dccp_ipv4_af_ops = {
.queue_xmit = ip_queue_xmit,
.send_check = dccp_v4_send_check,
.rebuild_header = inet_sk_rebuild_header,
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index 3e70faab2989..64f011cc4491 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -35,8 +35,8 @@
/* The per-net dccp.v6_ctl_sk is used for sending RSTs and ACKs */
-static struct inet_connection_sock_af_ops dccp_ipv6_mapped;
-static struct inet_connection_sock_af_ops dccp_ipv6_af_ops;
+static const struct inet_connection_sock_af_ops dccp_ipv6_mapped;
+static const struct inet_connection_sock_af_ops dccp_ipv6_af_ops;
static void dccp_v6_hash(struct sock *sk)
{
@@ -1055,7 +1055,7 @@ failure:
return err;
}
-static struct inet_connection_sock_af_ops dccp_ipv6_af_ops = {
+static const struct inet_connection_sock_af_ops dccp_ipv6_af_ops = {
.queue_xmit = inet6_csk_xmit,
.send_check = dccp_v6_send_check,
.rebuild_header = inet6_sk_rebuild_header,
@@ -1076,7 +1076,7 @@ static struct inet_connection_sock_af_ops dccp_ipv6_af_ops = {
/*
* DCCP over IPv4 via INET6 API
*/
-static struct inet_connection_sock_af_ops dccp_ipv6_mapped = {
+static const struct inet_connection_sock_af_ops dccp_ipv6_mapped = {
.queue_xmit = ip_queue_xmit,
.send_check = dccp_v4_send_check,
.rebuild_header = inet_sk_rebuild_header,
diff --git a/net/dccp/proto.c b/net/dccp/proto.c
index 1bca9205104e..923db06c7e55 100644
--- a/net/dccp/proto.c
+++ b/net/dccp/proto.c
@@ -124,7 +124,7 @@ EXPORT_SYMBOL_GPL(dccp_done);
const char *dccp_packet_name(const int type)
{
- static const char *dccp_packet_names[] = {
+ static const char *const dccp_packet_names[] = {
[DCCP_PKT_REQUEST] = "REQUEST",
[DCCP_PKT_RESPONSE] = "RESPONSE",
[DCCP_PKT_DATA] = "DATA",
@@ -147,7 +147,7 @@ EXPORT_SYMBOL_GPL(dccp_packet_name);
const char *dccp_state_name(const int state)
{
- static char *dccp_state_names[] = {
+ static const char *const dccp_state_names[] = {
[DCCP_OPEN] = "OPEN",
[DCCP_REQUESTING] = "REQUESTING",
[DCCP_PARTOPEN] = "PARTOPEN",
diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c
index 923786bd6d01..794b5bf95af1 100644
--- a/net/decnet/dn_neigh.c
+++ b/net/decnet/dn_neigh.c
@@ -59,7 +59,7 @@ static int dn_phase3_output(struct sk_buff *);
/*
* For talking to broadcast devices: Ethernet & PPP
*/
-static struct neigh_ops dn_long_ops = {
+static const struct neigh_ops dn_long_ops = {
.family = AF_DECnet,
.error_report = dn_long_error_report,
.output = dn_long_output,
@@ -71,7 +71,7 @@ static struct neigh_ops dn_long_ops = {
/*
* For talking to pointopoint and multidrop devices: DDCMP and X.25
*/
-static struct neigh_ops dn_short_ops = {
+static const struct neigh_ops dn_short_ops = {
.family = AF_DECnet,
.error_report = dn_short_error_report,
.output = dn_short_output,
@@ -83,7 +83,7 @@ static struct neigh_ops dn_short_ops = {
/*
* For talking to DECnet phase III nodes
*/
-static struct neigh_ops dn_phase3_ops = {
+static const struct neigh_ops dn_phase3_ops = {
.family = AF_DECnet,
.error_report = dn_short_error_report, /* Can use short version here */
.output = dn_phase3_output,
diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c
index 1d6ca8a98dc6..9383d3e5a1ab 100644
--- a/net/decnet/dn_route.c
+++ b/net/decnet/dn_route.c
@@ -774,7 +774,7 @@ static int dn_rt_bug(struct sk_buff *skb)
kfree_skb(skb);
- return NET_RX_BAD;
+ return NET_RX_DROP;
}
static int dn_rt_set_next_hop(struct dn_route *rt, struct dn_fib_res *res)
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index 41055f33d28a..4b0ea0540442 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -169,13 +169,13 @@ struct net_device *dsa_slave_create(struct dsa_switch *ds,
int port, char *name);
/* tag_dsa.c */
-int dsa_xmit(struct sk_buff *skb, struct net_device *dev);
+netdev_tx_t dsa_xmit(struct sk_buff *skb, struct net_device *dev);
/* tag_edsa.c */
-int edsa_xmit(struct sk_buff *skb, struct net_device *dev);
+netdev_tx_t edsa_xmit(struct sk_buff *skb, struct net_device *dev);
/* tag_trailer.c */
-int trailer_xmit(struct sk_buff *skb, struct net_device *dev);
+netdev_tx_t trailer_xmit(struct sk_buff *skb, struct net_device *dev);
#endif
diff --git a/net/dsa/tag_dsa.c b/net/dsa/tag_dsa.c
index 8fa25bafe6ca..cdf2d28a0297 100644
--- a/net/dsa/tag_dsa.c
+++ b/net/dsa/tag_dsa.c
@@ -15,7 +15,7 @@
#define DSA_HLEN 4
-int dsa_xmit(struct sk_buff *skb, struct net_device *dev)
+netdev_tx_t dsa_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct dsa_slave_priv *p = netdev_priv(dev);
u8 *dsa_header;
diff --git a/net/dsa/tag_edsa.c b/net/dsa/tag_edsa.c
index 815607bd286f..8f53948cff4f 100644
--- a/net/dsa/tag_edsa.c
+++ b/net/dsa/tag_edsa.c
@@ -16,7 +16,7 @@
#define DSA_HLEN 4
#define EDSA_HLEN 8
-int edsa_xmit(struct sk_buff *skb, struct net_device *dev)
+netdev_tx_t edsa_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct dsa_slave_priv *p = netdev_priv(dev);
u8 *edsa_header;
diff --git a/net/dsa/tag_trailer.c b/net/dsa/tag_trailer.c
index 1c3e30c38b86..a85c829853c0 100644
--- a/net/dsa/tag_trailer.c
+++ b/net/dsa/tag_trailer.c
@@ -13,7 +13,7 @@
#include <linux/netdevice.h>
#include "dsa_priv.h"
-int trailer_xmit(struct sk_buff *skb, struct net_device *dev)
+netdev_tx_t trailer_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct dsa_slave_priv *p = netdev_priv(dev);
struct sk_buff *nskb;
diff --git a/net/econet/af_econet.c b/net/econet/af_econet.c
index f0bbc57926cd..0e0254fd767d 100644
--- a/net/econet/af_econet.c
+++ b/net/econet/af_econet.c
@@ -1073,7 +1073,7 @@ static int econet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet
skb->protocol = htons(ETH_P_IP);
skb_pull(skb, sizeof(struct ec_framehdr));
netif_rx(skb);
- return 0;
+ return NET_RX_SUCCESS;
}
sk = ec_listening_socket(hdr->port, hdr->src_stn, hdr->src_net);
@@ -1084,7 +1084,7 @@ static int econet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet
hdr->port))
goto drop;
- return 0;
+ return NET_RX_SUCCESS;
drop:
kfree_skb(skb);
diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile
index f99338a26100..4068a9f5113e 100644
--- a/net/ieee802154/Makefile
+++ b/net/ieee802154/Makefile
@@ -1,4 +1,4 @@
-obj-$(CONFIG_IEEE802154) += nl802154.o af_802154.o
+obj-$(CONFIG_IEEE802154) += nl802154.o af_802154.o wpan-class.o
nl802154-y := netlink.o nl_policy.o
af_802154-y := af_ieee802154.o raw.o dgram.o
diff --git a/net/ieee802154/af_ieee802154.c b/net/ieee802154/af_ieee802154.c
index af661805b9fa..cd949d5e451b 100644
--- a/net/ieee802154/af_ieee802154.c
+++ b/net/ieee802154/af_ieee802154.c
@@ -34,8 +34,8 @@
#include <net/tcp_states.h>
#include <net/route.h>
-#include <net/ieee802154/af_ieee802154.h>
-#include <net/ieee802154/netdevice.h>
+#include <net/af_ieee802154.h>
+#include <net/ieee802154_netdev.h>
#include "af802154.h"
@@ -147,9 +147,7 @@ static int ieee802154_dev_ioctl(struct sock *sk, struct ifreq __user *arg,
dev_load(sock_net(sk), ifr.ifr_name);
dev = dev_get_by_name(sock_net(sk), ifr.ifr_name);
- if ((dev->type == ARPHRD_IEEE802154 ||
- dev->type == ARPHRD_IEEE802154_PHY) &&
- dev->netdev_ops->ndo_do_ioctl)
+ if (dev->type == ARPHRD_IEEE802154 && dev->netdev_ops->ndo_do_ioctl)
ret = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, cmd);
if (!ret && copy_to_user(arg, &ifr, sizeof(struct ifreq)))
diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c
index ba8b214dda8f..77ae6852b93d 100644
--- a/net/ieee802154/dgram.c
+++ b/net/ieee802154/dgram.c
@@ -26,9 +26,9 @@
#include <linux/if_arp.h>
#include <linux/list.h>
#include <net/sock.h>
-#include <net/ieee802154/af_ieee802154.h>
-#include <net/ieee802154/mac_def.h>
-#include <net/ieee802154/netdevice.h>
+#include <net/af_ieee802154.h>
+#include <net/ieee802154.h>
+#include <net/ieee802154_netdev.h>
#include <asm/ioctls.h>
@@ -40,9 +40,11 @@ static DEFINE_RWLOCK(dgram_lock);
struct dgram_sock {
struct sock sk;
- int bound;
struct ieee802154_addr src_addr;
struct ieee802154_addr dst_addr;
+
+ unsigned bound:1;
+ unsigned want_ack:1;
};
static inline struct dgram_sock *dgram_sk(const struct sock *sk)
@@ -50,7 +52,6 @@ static inline struct dgram_sock *dgram_sk(const struct sock *sk)
return container_of(sk, struct dgram_sock, sk);
}
-
static void dgram_hash(struct sock *sk)
{
write_lock_bh(&dgram_lock);
@@ -73,6 +74,7 @@ static int dgram_init(struct sock *sk)
ro->dst_addr.addr_type = IEEE802154_ADDR_LONG;
ro->dst_addr.pan_id = 0xffff;
+ ro->want_ack = 1;
memset(&ro->dst_addr.hwaddr, 0xff, sizeof(ro->dst_addr.hwaddr));
return 0;
}
@@ -86,18 +88,18 @@ static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len)
{
struct sockaddr_ieee802154 *addr = (struct sockaddr_ieee802154 *)uaddr;
struct dgram_sock *ro = dgram_sk(sk);
- int err = 0;
+ int err = -EINVAL;
struct net_device *dev;
+ lock_sock(sk);
+
ro->bound = 0;
if (len < sizeof(*addr))
- return -EINVAL;
+ goto out;
if (addr->family != AF_IEEE802154)
- return -EINVAL;
-
- lock_sock(sk);
+ goto out;
dev = ieee802154_get_dev(sock_net(sk), &addr->addr);
if (!dev) {
@@ -113,6 +115,7 @@ static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len)
memcpy(&ro->src_addr, &addr->addr, sizeof(struct ieee802154_addr));
ro->bound = 1;
+ err = 0;
out_put:
dev_put(dev);
out:
@@ -235,7 +238,10 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk,
skb_reset_network_header(skb);
- mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA | MAC_CB_FLAG_ACKREQ;
+ mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA;
+ if (ro->want_ack)
+ mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ;
+
mac_cb(skb)->seq = ieee802154_mlme_ops(dev)->get_dsn(dev);
err = dev_hard_header(skb, dev, ETH_P_IEEE802154, &ro->dst_addr,
ro->bound ? &ro->src_addr : NULL, size);
@@ -380,13 +386,59 @@ int ieee802154_dgram_deliver(struct net_device *dev, struct sk_buff *skb)
static int dgram_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
{
- return -EOPNOTSUPP;
+ struct dgram_sock *ro = dgram_sk(sk);
+
+ int val, len;
+
+ if (level != SOL_IEEE802154)
+ return -EOPNOTSUPP;
+
+ if (get_user(len, optlen))
+ return -EFAULT;
+
+ len = min_t(unsigned int, len, sizeof(int));
+
+ switch (optname) {
+ case WPAN_WANTACK:
+ val = ro->want_ack;
+ break;
+ default:
+ return -ENOPROTOOPT;
+ }
+
+ if (put_user(len, optlen))
+ return -EFAULT;
+ if (copy_to_user(optval, &val, len))
+ return -EFAULT;
+ return 0;
}
static int dgram_setsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user optlen)
{
- return -EOPNOTSUPP;
+ struct dgram_sock *ro = dgram_sk(sk);
+ int val;
+ int err = 0;
+
+ if (optlen < sizeof(int))
+ return -EINVAL;
+
+ if (get_user(val, (int __user *)optval))
+ return -EFAULT;
+
+ lock_sock(sk);
+
+ switch (optname) {
+ case WPAN_WANTACK:
+ ro->want_ack = !!val;
+ break;
+ default:
+ err = -ENOPROTOOPT;
+ break;
+ }
+
+ release_sock(sk);
+ return err;
}
struct proto ieee802154_dgram_prot = {
diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c
index 27eda9fdf3c2..2106ecbf0308 100644
--- a/net/ieee802154/netlink.c
+++ b/net/ieee802154/netlink.c
@@ -19,6 +19,7 @@
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+ * Maxim Osipov <maxim.osipov@siemens.com>
*/
#include <linux/kernel.h>
@@ -26,10 +27,12 @@
#include <linux/netdevice.h>
#include <net/netlink.h>
#include <net/genetlink.h>
+#include <net/sock.h>
#include <linux/nl802154.h>
-#include <net/ieee802154/af_ieee802154.h>
-#include <net/ieee802154/nl802154.h>
-#include <net/ieee802154/netdevice.h>
+#include <net/af_ieee802154.h>
+#include <net/nl802154.h>
+#include <net/ieee802154.h>
+#include <net/ieee802154_netdev.h>
static unsigned int ieee802154_seq_num;
@@ -73,7 +76,7 @@ static int ieee802154_nl_finish(struct sk_buff *msg)
/* XXX: nlh is right at the start of msg */
void *hdr = genlmsg_data(NLMSG_DATA(msg->data));
- if (!genlmsg_end(msg, hdr))
+ if (genlmsg_end(msg, hdr) < 0)
goto out;
return genlmsg_multicast(msg, 0, ieee802154_coord_mcgrp.id,
@@ -229,7 +232,7 @@ nla_put_failure:
EXPORT_SYMBOL(ieee802154_nl_beacon_indic);
int ieee802154_nl_scan_confirm(struct net_device *dev,
- u8 status, u8 scan_type, u32 unscanned,
+ u8 status, u8 scan_type, u32 unscanned, u8 page,
u8 *edl/* , struct list_head *pan_desc_list */)
{
struct sk_buff *msg;
@@ -248,6 +251,7 @@ int ieee802154_nl_scan_confirm(struct net_device *dev,
NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status);
NLA_PUT_U8(msg, IEEE802154_ATTR_SCAN_TYPE, scan_type);
NLA_PUT_U32(msg, IEEE802154_ATTR_CHANNELS, unscanned);
+ NLA_PUT_U8(msg, IEEE802154_ATTR_PAGE, page);
if (edl)
NLA_PUT(msg, IEEE802154_ATTR_ED_LIST, 27, edl);
@@ -260,6 +264,60 @@ nla_put_failure:
}
EXPORT_SYMBOL(ieee802154_nl_scan_confirm);
+int ieee802154_nl_start_confirm(struct net_device *dev, u8 status)
+{
+ struct sk_buff *msg;
+
+ pr_debug("%s\n", __func__);
+
+ msg = ieee802154_nl_create(0, IEEE802154_START_CONF);
+ if (!msg)
+ return -ENOBUFS;
+
+ NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name);
+ NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex);
+ NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
+ dev->dev_addr);
+
+ NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status);
+
+ return ieee802154_nl_finish(msg);
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -ENOBUFS;
+}
+EXPORT_SYMBOL(ieee802154_nl_start_confirm);
+
+static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 pid,
+ u32 seq, int flags, struct net_device *dev)
+{
+ void *hdr;
+
+ pr_debug("%s\n", __func__);
+
+ hdr = genlmsg_put(msg, 0, seq, &ieee802154_coordinator_family, flags,
+ IEEE802154_LIST_IFACE);
+ if (!hdr)
+ goto out;
+
+ NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name);
+ NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex);
+
+ NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
+ dev->dev_addr);
+ NLA_PUT_U16(msg, IEEE802154_ATTR_SHORT_ADDR,
+ ieee802154_mlme_ops(dev)->get_short_addr(dev));
+ NLA_PUT_U16(msg, IEEE802154_ATTR_PAN_ID,
+ ieee802154_mlme_ops(dev)->get_pan_id(dev));
+ return genlmsg_end(msg, hdr);
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+out:
+ return -EMSGSIZE;
+}
+
/* Requests from userspace */
static struct net_device *ieee802154_nl_get_dev(struct genl_info *info)
{
@@ -272,7 +330,7 @@ static struct net_device *ieee802154_nl_get_dev(struct genl_info *info)
dev = dev_get_by_name(&init_net, name);
} else if (info->attrs[IEEE802154_ATTR_DEV_INDEX])
dev = dev_get_by_index(&init_net,
- nla_get_u32(info->attrs[IEEE802154_ATTR_DEV_INDEX]));
+ nla_get_u32(info->attrs[IEEE802154_ATTR_DEV_INDEX]));
else
return NULL;
@@ -292,6 +350,7 @@ static int ieee802154_associate_req(struct sk_buff *skb,
{
struct net_device *dev;
struct ieee802154_addr addr;
+ u8 page;
int ret = -EINVAL;
if (!info->attrs[IEEE802154_ATTR_CHANNEL] ||
@@ -317,8 +376,14 @@ static int ieee802154_associate_req(struct sk_buff *skb,
}
addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
+ if (info->attrs[IEEE802154_ATTR_PAGE])
+ page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
+ else
+ page = 0;
+
ret = ieee802154_mlme_ops(dev)->assoc_req(dev, &addr,
nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]),
+ page,
nla_get_u8(info->attrs[IEEE802154_ATTR_CAPABILITY]));
dev_put(dev);
@@ -401,6 +466,7 @@ static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
struct ieee802154_addr addr;
u8 channel, bcn_ord, sf_ord;
+ u8 page;
int pan_coord, blx, coord_realign;
int ret;
@@ -431,7 +497,19 @@ static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
blx = nla_get_u8(info->attrs[IEEE802154_ATTR_BAT_EXT]);
coord_realign = nla_get_u8(info->attrs[IEEE802154_ATTR_COORD_REALIGN]);
- ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel,
+ if (info->attrs[IEEE802154_ATTR_PAGE])
+ page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
+ else
+ page = 0;
+
+
+ if (addr.short_addr == IEEE802154_ADDR_BROADCAST) {
+ ieee802154_nl_start_confirm(dev, IEEE802154_NO_SHORT_ADDRESS);
+ dev_put(dev);
+ return -EINVAL;
+ }
+
+ ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page,
bcn_ord, sf_ord, pan_coord, blx, coord_realign);
dev_put(dev);
@@ -445,6 +523,7 @@ static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info)
u8 type;
u32 channels;
u8 duration;
+ u8 page;
if (!info->attrs[IEEE802154_ATTR_SCAN_TYPE] ||
!info->attrs[IEEE802154_ATTR_CHANNELS] ||
@@ -459,13 +538,80 @@ static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info)
channels = nla_get_u32(info->attrs[IEEE802154_ATTR_CHANNELS]);
duration = nla_get_u8(info->attrs[IEEE802154_ATTR_DURATION]);
- ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels,
+ if (info->attrs[IEEE802154_ATTR_PAGE])
+ page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
+ else
+ page = 0;
+
+
+ ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels, page,
duration);
dev_put(dev);
return ret;
}
+static int ieee802154_list_iface(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ /* Request for interface name, index, type, IEEE address,
+ PAN Id, short address */
+ struct sk_buff *msg;
+ struct net_device *dev = NULL;
+ int rc = -ENOBUFS;
+
+ pr_debug("%s\n", __func__);
+
+ dev = ieee802154_nl_get_dev(info);
+ if (!dev)
+ return -ENODEV;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!msg)
+ goto out_dev;
+
+ rc = ieee802154_nl_fill_iface(msg, info->snd_pid, info->snd_seq,
+ 0, dev);
+ if (rc < 0)
+ goto out_free;
+
+ dev_put(dev);
+
+ return genlmsg_unicast(&init_net, msg, info->snd_pid);
+out_free:
+ nlmsg_free(msg);
+out_dev:
+ dev_put(dev);
+ return rc;
+
+}
+
+static int ieee802154_dump_iface(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ struct net *net = sock_net(skb->sk);
+ struct net_device *dev;
+ int idx;
+ int s_idx = cb->args[0];
+
+ pr_debug("%s\n", __func__);
+
+ idx = 0;
+ for_each_netdev(net, dev) {
+ if (idx < s_idx || (dev->type != ARPHRD_IEEE802154))
+ goto cont;
+
+ if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq, NLM_F_MULTI, dev) < 0)
+ break;
+cont:
+ idx++;
+ }
+ cb->args[0] = idx;
+
+ return skb->len;
+}
+
#define IEEE802154_OP(_cmd, _func) \
{ \
.cmd = _cmd, \
@@ -475,12 +621,22 @@ static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info)
.flags = GENL_ADMIN_PERM, \
}
+#define IEEE802154_DUMP(_cmd, _func, _dump) \
+ { \
+ .cmd = _cmd, \
+ .policy = ieee802154_policy, \
+ .doit = _func, \
+ .dumpit = _dump, \
+ }
+
static struct genl_ops ieee802154_coordinator_ops[] = {
IEEE802154_OP(IEEE802154_ASSOCIATE_REQ, ieee802154_associate_req),
IEEE802154_OP(IEEE802154_ASSOCIATE_RESP, ieee802154_associate_resp),
IEEE802154_OP(IEEE802154_DISASSOCIATE_REQ, ieee802154_disassociate_req),
IEEE802154_OP(IEEE802154_SCAN_REQ, ieee802154_scan_req),
IEEE802154_OP(IEEE802154_START_REQ, ieee802154_start_req),
+ IEEE802154_DUMP(IEEE802154_LIST_IFACE, ieee802154_list_iface,
+ ieee802154_dump_iface),
};
static int __init ieee802154_nl_init(void)
diff --git a/net/ieee802154/nl_policy.c b/net/ieee802154/nl_policy.c
index c7d71d1adcac..2363ebee02e7 100644
--- a/net/ieee802154/nl_policy.c
+++ b/net/ieee802154/nl_policy.c
@@ -24,7 +24,7 @@
#define NLA_HW_ADDR NLA_U64
-struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = {
+const struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = {
[IEEE802154_ATTR_DEV_NAME] = { .type = NLA_STRING, },
[IEEE802154_ATTR_DEV_INDEX] = { .type = NLA_U32, },
@@ -33,6 +33,7 @@ struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = {
[IEEE802154_ATTR_HW_ADDR] = { .type = NLA_HW_ADDR, },
[IEEE802154_ATTR_PAN_ID] = { .type = NLA_U16, },
[IEEE802154_ATTR_CHANNEL] = { .type = NLA_U8, },
+ [IEEE802154_ATTR_PAGE] = { .type = NLA_U8, },
[IEEE802154_ATTR_COORD_SHORT_ADDR] = { .type = NLA_U16, },
[IEEE802154_ATTR_COORD_HW_ADDR] = { .type = NLA_HW_ADDR, },
[IEEE802154_ATTR_COORD_PAN_ID] = { .type = NLA_U16, },
@@ -50,3 +51,4 @@ struct nla_policy ieee802154_policy[IEEE802154_ATTR_MAX + 1] = {
[IEEE802154_ATTR_DURATION] = { .type = NLA_U8, },
[IEEE802154_ATTR_ED_LIST] = { .len = 27 },
};
+
diff --git a/net/ieee802154/raw.c b/net/ieee802154/raw.c
index 9315977c4c61..4681501aae93 100644
--- a/net/ieee802154/raw.c
+++ b/net/ieee802154/raw.c
@@ -26,7 +26,7 @@
#include <linux/if_arp.h>
#include <linux/list.h>
#include <net/sock.h>
-#include <net/ieee802154/af_ieee802154.h>
+#include <net/af_ieee802154.h>
#include "af802154.h"
@@ -74,8 +74,7 @@ static int raw_bind(struct sock *sk, struct sockaddr *uaddr, int len)
goto out;
}
- if (dev->type != ARPHRD_IEEE802154_PHY &&
- dev->type != ARPHRD_IEEE802154) {
+ if (dev->type != ARPHRD_IEEE802154) {
err = -ENODEV;
goto out_put;
}
diff --git a/net/ieee802154/wpan-class.c b/net/ieee802154/wpan-class.c
new file mode 100644
index 000000000000..f306604da67a
--- /dev/null
+++ b/net/ieee802154/wpan-class.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2007, 2008, 2009 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+
+#include <net/wpan-phy.h>
+
+#define MASTER_SHOW_COMPLEX(name, format_string, args...) \
+static ssize_t name ## _show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev); \
+ int ret; \
+ \
+ mutex_lock(&phy->pib_lock); \
+ ret = sprintf(buf, format_string "\n", args); \
+ mutex_unlock(&phy->pib_lock); \
+ return ret; \
+}
+
+#define MASTER_SHOW(field, format_string) \
+ MASTER_SHOW_COMPLEX(field, format_string, phy->field)
+
+MASTER_SHOW(current_channel, "%d");
+MASTER_SHOW(current_page, "%d");
+MASTER_SHOW(channels_supported, "%#x");
+MASTER_SHOW_COMPLEX(transmit_power, "%d +- %d dB",
+ ((signed char) (phy->transmit_power << 2)) >> 2,
+ (phy->transmit_power >> 6) ? (phy->transmit_power >> 6) * 3 : 1 );
+MASTER_SHOW(cca_mode, "%d");
+
+static struct device_attribute pmib_attrs[] = {
+ __ATTR_RO(current_channel),
+ __ATTR_RO(current_page),
+ __ATTR_RO(channels_supported),
+ __ATTR_RO(transmit_power),
+ __ATTR_RO(cca_mode),
+ {},
+};
+
+static void wpan_phy_release(struct device *d)
+{
+ struct wpan_phy *phy = container_of(d, struct wpan_phy, dev);
+ kfree(phy);
+}
+
+static struct class wpan_phy_class = {
+ .name = "ieee802154",
+ .dev_release = wpan_phy_release,
+ .dev_attrs = pmib_attrs,
+};
+
+static DEFINE_MUTEX(wpan_phy_mutex);
+static int wpan_phy_idx;
+
+static int wpan_phy_match(struct device *dev, void *data)
+{
+ return !strcmp(dev_name(dev), (const char *)data);
+}
+
+struct wpan_phy *wpan_phy_find(const char *str)
+{
+ struct device *dev;
+
+ if (WARN_ON(!str))
+ return NULL;
+
+ dev = class_find_device(&wpan_phy_class, NULL,
+ (void *)str, wpan_phy_match);
+ if (!dev)
+ return NULL;
+
+ return container_of(dev, struct wpan_phy, dev);
+}
+EXPORT_SYMBOL(wpan_phy_find);
+
+static int wpan_phy_idx_valid(int idx)
+{
+ return idx >= 0;
+}
+
+struct wpan_phy *wpan_phy_alloc(size_t priv_size)
+{
+ struct wpan_phy *phy = kzalloc(sizeof(*phy) + priv_size,
+ GFP_KERNEL);
+
+ mutex_lock(&wpan_phy_mutex);
+ phy->idx = wpan_phy_idx++;
+ if (unlikely(!wpan_phy_idx_valid(phy->idx))) {
+ wpan_phy_idx--;
+ mutex_unlock(&wpan_phy_mutex);
+ kfree(phy);
+ return NULL;
+ }
+ mutex_unlock(&wpan_phy_mutex);
+
+ mutex_init(&phy->pib_lock);
+
+ device_initialize(&phy->dev);
+ dev_set_name(&phy->dev, "wpan-phy%d", phy->idx);
+
+ phy->dev.class = &wpan_phy_class;
+
+ return phy;
+}
+EXPORT_SYMBOL(wpan_phy_alloc);
+
+int wpan_phy_register(struct device *parent, struct wpan_phy *phy)
+{
+ phy->dev.parent = parent;
+
+ return device_add(&phy->dev);
+}
+EXPORT_SYMBOL(wpan_phy_register);
+
+void wpan_phy_unregister(struct wpan_phy *phy)
+{
+ device_del(&phy->dev);
+}
+EXPORT_SYMBOL(wpan_phy_unregister);
+
+void wpan_phy_free(struct wpan_phy *phy)
+{
+ put_device(&phy->dev);
+}
+EXPORT_SYMBOL(wpan_phy_free);
+
+static int __init wpan_phy_class_init(void)
+{
+ return class_register(&wpan_phy_class);
+}
+subsys_initcall(wpan_phy_class_init);
+
+static void __exit wpan_phy_class_exit(void)
+{
+ class_unregister(&wpan_phy_class);
+}
+module_exit(wpan_phy_class_exit);
+
+MODULE_DESCRIPTION("IEEE 802.15.4 device class");
+MODULE_LICENSE("GPL v2");
+
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 566ea6c4321d..6c30a73f03f5 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -124,7 +124,6 @@ static struct list_head inetsw[SOCK_MAX];
static DEFINE_SPINLOCK(inetsw_lock);
struct ipv4_config ipv4_config;
-
EXPORT_SYMBOL(ipv4_config);
/* New destruction routine */
@@ -139,12 +138,12 @@ void inet_sock_destruct(struct sock *sk)
sk_mem_reclaim(sk);
if (sk->sk_type == SOCK_STREAM && sk->sk_state != TCP_CLOSE) {
- printk("Attempt to release TCP socket in state %d %p\n",
+ pr_err("Attempt to release TCP socket in state %d %p\n",
sk->sk_state, sk);
return;
}
if (!sock_flag(sk, SOCK_DEAD)) {
- printk("Attempt to release alive inet socket %p\n", sk);
+ pr_err("Attempt to release alive inet socket %p\n", sk);
return;
}
@@ -157,6 +156,7 @@ void inet_sock_destruct(struct sock *sk)
dst_release(sk->sk_dst_cache);
sk_refcnt_debug_dec(sk);
}
+EXPORT_SYMBOL(inet_sock_destruct);
/*
* The routines beyond this point handle the behaviour of an AF_INET
@@ -219,6 +219,7 @@ out:
release_sock(sk);
return err;
}
+EXPORT_SYMBOL(inet_listen);
u32 inet_ehash_secret __read_mostly;
EXPORT_SYMBOL(inet_ehash_secret);
@@ -435,9 +436,11 @@ int inet_release(struct socket *sock)
}
return 0;
}
+EXPORT_SYMBOL(inet_release);
/* It is off by default, see below. */
int sysctl_ip_nonlocal_bind __read_mostly;
+EXPORT_SYMBOL(sysctl_ip_nonlocal_bind);
int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
@@ -519,6 +522,7 @@ out_release_sock:
out:
return err;
}
+EXPORT_SYMBOL(inet_bind);
int inet_dgram_connect(struct socket *sock, struct sockaddr * uaddr,
int addr_len, int flags)
@@ -532,6 +536,7 @@ int inet_dgram_connect(struct socket *sock, struct sockaddr * uaddr,
return -EAGAIN;
return sk->sk_prot->connect(sk, (struct sockaddr *)uaddr, addr_len);
}
+EXPORT_SYMBOL(inet_dgram_connect);
static long inet_wait_for_connect(struct sock *sk, long timeo)
{
@@ -641,6 +646,7 @@ sock_error:
sock->state = SS_DISCONNECTING;
goto out;
}
+EXPORT_SYMBOL(inet_stream_connect);
/*
* Accept a pending connection. The TCP layer now gives BSD semantics.
@@ -668,6 +674,7 @@ int inet_accept(struct socket *sock, struct socket *newsock, int flags)
do_err:
return err;
}
+EXPORT_SYMBOL(inet_accept);
/*
@@ -699,6 +706,7 @@ int inet_getname(struct socket *sock, struct sockaddr *uaddr,
*uaddr_len = sizeof(*sin);
return 0;
}
+EXPORT_SYMBOL(inet_getname);
int inet_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
size_t size)
@@ -711,9 +719,11 @@ int inet_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
return sk->sk_prot->sendmsg(iocb, sk, msg, size);
}
+EXPORT_SYMBOL(inet_sendmsg);
-static ssize_t inet_sendpage(struct socket *sock, struct page *page, int offset, size_t size, int flags)
+static ssize_t inet_sendpage(struct socket *sock, struct page *page, int offset,
+ size_t size, int flags)
{
struct sock *sk = sock->sk;
@@ -780,6 +790,7 @@ int inet_shutdown(struct socket *sock, int how)
release_sock(sk);
return err;
}
+EXPORT_SYMBOL(inet_shutdown);
/*
* ioctl() calls you can issue on an INET socket. Most of these are
@@ -798,44 +809,45 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
struct net *net = sock_net(sk);
switch (cmd) {
- case SIOCGSTAMP:
- err = sock_get_timestamp(sk, (struct timeval __user *)arg);
- break;
- case SIOCGSTAMPNS:
- err = sock_get_timestampns(sk, (struct timespec __user *)arg);
- break;
- case SIOCADDRT:
- case SIOCDELRT:
- case SIOCRTMSG:
- err = ip_rt_ioctl(net, cmd, (void __user *)arg);
- break;
- case SIOCDARP:
- case SIOCGARP:
- case SIOCSARP:
- err = arp_ioctl(net, cmd, (void __user *)arg);
- break;
- case SIOCGIFADDR:
- case SIOCSIFADDR:
- case SIOCGIFBRDADDR:
- case SIOCSIFBRDADDR:
- case SIOCGIFNETMASK:
- case SIOCSIFNETMASK:
- case SIOCGIFDSTADDR:
- case SIOCSIFDSTADDR:
- case SIOCSIFPFLAGS:
- case SIOCGIFPFLAGS:
- case SIOCSIFFLAGS:
- err = devinet_ioctl(net, cmd, (void __user *)arg);
- break;
- default:
- if (sk->sk_prot->ioctl)
- err = sk->sk_prot->ioctl(sk, cmd, arg);
- else
- err = -ENOIOCTLCMD;
- break;
+ case SIOCGSTAMP:
+ err = sock_get_timestamp(sk, (struct timeval __user *)arg);
+ break;
+ case SIOCGSTAMPNS:
+ err = sock_get_timestampns(sk, (struct timespec __user *)arg);
+ break;
+ case SIOCADDRT:
+ case SIOCDELRT:
+ case SIOCRTMSG:
+ err = ip_rt_ioctl(net, cmd, (void __user *)arg);
+ break;
+ case SIOCDARP:
+ case SIOCGARP:
+ case SIOCSARP:
+ err = arp_ioctl(net, cmd, (void __user *)arg);
+ break;
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+ case SIOCGIFBRDADDR:
+ case SIOCSIFBRDADDR:
+ case SIOCGIFNETMASK:
+ case SIOCSIFNETMASK:
+ case SIOCGIFDSTADDR:
+ case SIOCSIFDSTADDR:
+ case SIOCSIFPFLAGS:
+ case SIOCGIFPFLAGS:
+ case SIOCSIFFLAGS:
+ err = devinet_ioctl(net, cmd, (void __user *)arg);
+ break;
+ default:
+ if (sk->sk_prot->ioctl)
+ err = sk->sk_prot->ioctl(sk, cmd, arg);
+ else
+ err = -ENOIOCTLCMD;
+ break;
}
return err;
}
+EXPORT_SYMBOL(inet_ioctl);
const struct proto_ops inet_stream_ops = {
.family = PF_INET,
@@ -862,6 +874,7 @@ const struct proto_ops inet_stream_ops = {
.compat_getsockopt = compat_sock_common_getsockopt,
#endif
};
+EXPORT_SYMBOL(inet_stream_ops);
const struct proto_ops inet_dgram_ops = {
.family = PF_INET,
@@ -887,6 +900,7 @@ const struct proto_ops inet_dgram_ops = {
.compat_getsockopt = compat_sock_common_getsockopt,
#endif
};
+EXPORT_SYMBOL(inet_dgram_ops);
/*
* For SOCK_RAW sockets; should be the same as inet_dgram_ops but without
@@ -1016,6 +1030,7 @@ out_illegal:
p->type);
goto out;
}
+EXPORT_SYMBOL(inet_register_protosw);
void inet_unregister_protosw(struct inet_protosw *p)
{
@@ -1031,6 +1046,7 @@ void inet_unregister_protosw(struct inet_protosw *p)
synchronize_net();
}
}
+EXPORT_SYMBOL(inet_unregister_protosw);
/*
* Shall we try to damage output packets if routing dev changes?
@@ -1141,7 +1157,6 @@ int inet_sk_rebuild_header(struct sock *sk)
return err;
}
-
EXPORT_SYMBOL(inet_sk_rebuild_header);
static int inet_gso_send_check(struct sk_buff *skb)
@@ -1187,6 +1202,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features)
int proto;
int ihl;
int id;
+ unsigned int offset = 0;
if (!(features & NETIF_F_V4_CSUM))
features &= ~NETIF_F_SG;
@@ -1229,7 +1245,14 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features)
skb = segs;
do {
iph = ip_hdr(skb);
- iph->id = htons(id++);
+ if (proto == IPPROTO_UDP) {
+ iph->id = htons(id);
+ iph->frag_off = htons(offset >> 3);
+ if (skb->next != NULL)
+ iph->frag_off |= htons(IP_MF);
+ offset += (skb->len - skb->mac_len - iph->ihl * 4);
+ } else
+ iph->id = htons(id++);
iph->tot_len = htons(skb->len - skb->mac_len);
iph->check = 0;
iph->check = ip_fast_csum(skb_network_header(skb), iph->ihl);
@@ -1361,7 +1384,6 @@ int inet_ctl_sock_create(struct sock **sk, unsigned short family,
}
return rc;
}
-
EXPORT_SYMBOL_GPL(inet_ctl_sock_create);
unsigned long snmp_fold_field(void *mib[], int offt)
@@ -1425,6 +1447,8 @@ static struct net_protocol tcp_protocol = {
static struct net_protocol udp_protocol = {
.handler = udp_rcv,
.err_handler = udp_err,
+ .gso_send_check = udp4_ufo_send_check,
+ .gso_segment = udp4_ufo_fragment,
.no_policy = 1,
.netns_ok = 1,
};
@@ -1666,19 +1690,3 @@ static int __init ipv4_proc_init(void)
MODULE_ALIAS_NETPROTO(PF_INET);
-EXPORT_SYMBOL(inet_accept);
-EXPORT_SYMBOL(inet_bind);
-EXPORT_SYMBOL(inet_dgram_connect);
-EXPORT_SYMBOL(inet_dgram_ops);
-EXPORT_SYMBOL(inet_getname);
-EXPORT_SYMBOL(inet_ioctl);
-EXPORT_SYMBOL(inet_listen);
-EXPORT_SYMBOL(inet_register_protosw);
-EXPORT_SYMBOL(inet_release);
-EXPORT_SYMBOL(inet_sendmsg);
-EXPORT_SYMBOL(inet_shutdown);
-EXPORT_SYMBOL(inet_sock_destruct);
-EXPORT_SYMBOL(inet_stream_connect);
-EXPORT_SYMBOL(inet_stream_ops);
-EXPORT_SYMBOL(inet_unregister_protosw);
-EXPORT_SYMBOL(sysctl_ip_nonlocal_bind);
diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
index 090e9991ac2a..4e80f336c0cf 100644
--- a/net/ipv4/arp.c
+++ b/net/ipv4/arp.c
@@ -130,7 +130,7 @@ static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb);
static void arp_error_report(struct neighbour *neigh, struct sk_buff *skb);
static void parp_redo(struct sk_buff *skb);
-static struct neigh_ops arp_generic_ops = {
+static const struct neigh_ops arp_generic_ops = {
.family = AF_INET,
.solicit = arp_solicit,
.error_report = arp_error_report,
@@ -140,7 +140,7 @@ static struct neigh_ops arp_generic_ops = {
.queue_xmit = dev_queue_xmit,
};
-static struct neigh_ops arp_hh_ops = {
+static const struct neigh_ops arp_hh_ops = {
.family = AF_INET,
.solicit = arp_solicit,
.error_report = arp_error_report,
@@ -150,7 +150,7 @@ static struct neigh_ops arp_hh_ops = {
.queue_xmit = dev_queue_xmit,
};
-static struct neigh_ops arp_direct_ops = {
+static const struct neigh_ops arp_direct_ops = {
.family = AF_INET,
.output = dev_queue_xmit,
.connected_output = dev_queue_xmit,
@@ -158,7 +158,7 @@ static struct neigh_ops arp_direct_ops = {
.queue_xmit = dev_queue_xmit,
};
-struct neigh_ops arp_broken_ops = {
+const struct neigh_ops arp_broken_ops = {
.family = AF_INET,
.solicit = arp_solicit,
.error_report = arp_error_report,
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index 63c2fa7b68c4..291bdf50a21f 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -48,7 +48,7 @@
* Patrick McHardy <kaber@trash.net>
*/
-#define VERSION "0.408"
+#define VERSION "0.409"
#include <asm/uaccess.h>
#include <asm/system.h>
@@ -164,6 +164,14 @@ static struct tnode *inflate(struct trie *t, struct tnode *tn);
static struct tnode *halve(struct trie *t, struct tnode *tn);
/* tnodes to free after resize(); protected by RTNL */
static struct tnode *tnode_free_head;
+static size_t tnode_free_size;
+
+/*
+ * synchronize_rcu after call_rcu for that many pages; it should be especially
+ * useful before resizing the root node with PREEMPT_NONE configs; the value was
+ * obtained experimentally, aiming to avoid visible slowdown.
+ */
+static const int sync_pages = 128;
static struct kmem_cache *fn_alias_kmem __read_mostly;
static struct kmem_cache *trie_leaf_kmem __read_mostly;
@@ -317,8 +325,7 @@ static inline void check_tnode(const struct tnode *tn)
static const int halve_threshold = 25;
static const int inflate_threshold = 50;
static const int halve_threshold_root = 15;
-static const int inflate_threshold_root = 25;
-
+static const int inflate_threshold_root = 30;
static void __alias_free_mem(struct rcu_head *head)
{
@@ -393,6 +400,8 @@ static void tnode_free_safe(struct tnode *tn)
BUG_ON(IS_LEAF(tn));
tn->tnode_free = tnode_free_head;
tnode_free_head = tn;
+ tnode_free_size += sizeof(struct tnode) +
+ (sizeof(struct node *) << tn->bits);
}
static void tnode_free_flush(void)
@@ -404,6 +413,11 @@ static void tnode_free_flush(void)
tn->tnode_free = NULL;
tnode_free(tn);
}
+
+ if (tnode_free_size >= PAGE_SIZE * sync_pages) {
+ tnode_free_size = 0;
+ synchronize_rcu();
+ }
}
static struct leaf *leaf_new(void)
@@ -499,14 +513,14 @@ static void tnode_put_child_reorg(struct tnode *tn, int i, struct node *n,
rcu_assign_pointer(tn->child[i], n);
}
+#define MAX_WORK 10
static struct node *resize(struct trie *t, struct tnode *tn)
{
int i;
- int err = 0;
struct tnode *old_tn;
int inflate_threshold_use;
int halve_threshold_use;
- int max_resize;
+ int max_work;
if (!tn)
return NULL;
@@ -521,18 +535,7 @@ static struct node *resize(struct trie *t, struct tnode *tn)
}
/* One child */
if (tn->empty_children == tnode_child_length(tn) - 1)
- for (i = 0; i < tnode_child_length(tn); i++) {
- struct node *n;
-
- n = tn->child[i];
- if (!n)
- continue;
-
- /* compress one level */
- node_set_parent(n, NULL);
- tnode_free_safe(tn);
- return n;
- }
+ goto one_child;
/*
* Double as long as the resulting node has a number of
* nonempty nodes that are above the threshold.
@@ -601,14 +604,17 @@ static struct node *resize(struct trie *t, struct tnode *tn)
/* Keep root node larger */
- if (!tn->parent)
+ if (!node_parent((struct node*) tn)) {
inflate_threshold_use = inflate_threshold_root;
- else
+ halve_threshold_use = halve_threshold_root;
+ }
+ else {
inflate_threshold_use = inflate_threshold;
+ halve_threshold_use = halve_threshold;
+ }
- err = 0;
- max_resize = 10;
- while ((tn->full_children > 0 && max_resize-- &&
+ max_work = MAX_WORK;
+ while ((tn->full_children > 0 && max_work-- &&
50 * (tn->full_children + tnode_child_length(tn)
- tn->empty_children)
>= inflate_threshold_use * tnode_child_length(tn))) {
@@ -625,35 +631,19 @@ static struct node *resize(struct trie *t, struct tnode *tn)
}
}
- if (max_resize < 0) {
- if (!tn->parent)
- pr_warning("Fix inflate_threshold_root."
- " Now=%d size=%d bits\n",
- inflate_threshold_root, tn->bits);
- else
- pr_warning("Fix inflate_threshold."
- " Now=%d size=%d bits\n",
- inflate_threshold, tn->bits);
- }
-
check_tnode(tn);
+ /* Return if at least one inflate is run */
+ if( max_work != MAX_WORK)
+ return (struct node *) tn;
+
/*
* Halve as long as the number of empty children in this
* node is above threshold.
*/
-
- /* Keep root node larger */
-
- if (!tn->parent)
- halve_threshold_use = halve_threshold_root;
- else
- halve_threshold_use = halve_threshold;
-
- err = 0;
- max_resize = 10;
- while (tn->bits > 1 && max_resize-- &&
+ max_work = MAX_WORK;
+ while (tn->bits > 1 && max_work-- &&
100 * (tnode_child_length(tn) - tn->empty_children) <
halve_threshold_use * tnode_child_length(tn)) {
@@ -668,19 +658,10 @@ static struct node *resize(struct trie *t, struct tnode *tn)
}
}
- if (max_resize < 0) {
- if (!tn->parent)
- pr_warning("Fix halve_threshold_root."
- " Now=%d size=%d bits\n",
- halve_threshold_root, tn->bits);
- else
- pr_warning("Fix halve_threshold."
- " Now=%d size=%d bits\n",
- halve_threshold, tn->bits);
- }
/* Only one child remains */
- if (tn->empty_children == tnode_child_length(tn) - 1)
+ if (tn->empty_children == tnode_child_length(tn) - 1) {
+one_child:
for (i = 0; i < tnode_child_length(tn); i++) {
struct node *n;
@@ -694,7 +675,7 @@ static struct node *resize(struct trie *t, struct tnode *tn)
tnode_free_safe(tn);
return n;
}
-
+ }
return (struct node *) tn;
}
@@ -1435,7 +1416,7 @@ static int fn_trie_lookup(struct fib_table *tb, const struct flowi *flp,
cindex = tkey_extract_bits(mask_pfx(key, current_prefix_length),
pos, bits);
- n = tnode_get_child(pn, cindex);
+ n = tnode_get_child_rcu(pn, cindex);
if (n == NULL) {
#ifdef CONFIG_IP_FIB_TRIE_STATS
@@ -1570,7 +1551,7 @@ backtrace:
if (chopped_off <= pn->bits) {
cindex &= ~(1 << (chopped_off-1));
} else {
- struct tnode *parent = node_parent((struct node *) pn);
+ struct tnode *parent = node_parent_rcu((struct node *) pn);
if (!parent)
goto failed;
@@ -1783,7 +1764,7 @@ static struct leaf *trie_firstleaf(struct trie *t)
static struct leaf *trie_nextleaf(struct leaf *l)
{
struct node *c = (struct node *) l;
- struct tnode *p = node_parent(c);
+ struct tnode *p = node_parent_rcu(c);
if (!p)
return NULL; /* trie with just one leaf */
@@ -2391,7 +2372,7 @@ static inline const char *rtn_scope(char *buf, size_t len, enum rt_scope_t s)
}
}
-static const char *rtn_type_names[__RTN_MAX] = {
+static const char *const rtn_type_names[__RTN_MAX] = {
[RTN_UNSPEC] = "UNSPEC",
[RTN_UNICAST] = "UNICAST",
[RTN_LOCAL] = "LOCAL",
diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c
index 61283f928825..13f0781f35cd 100644
--- a/net/ipv4/inet_timewait_sock.c
+++ b/net/ipv4/inet_timewait_sock.c
@@ -218,8 +218,8 @@ void inet_twdr_hangman(unsigned long data)
/* We purged the entire slot, anything left? */
if (twdr->tw_count)
need_timer = 1;
+ twdr->slot = ((twdr->slot + 1) & (INET_TWDR_TWKILL_SLOTS - 1));
}
- twdr->slot = ((twdr->slot + 1) & (INET_TWDR_TWKILL_SLOTS - 1));
if (need_timer)
mod_timer(&twdr->tw_timer, jiffies + twdr->period);
out:
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 82c11dd10a62..533afaadefd4 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -662,7 +662,7 @@ drop_nolock:
return(0);
}
-static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
struct net_device_stats *stats = &tunnel->dev->stats;
@@ -821,7 +821,7 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
stats->tx_dropped++;
dev_kfree_skb(skb);
tunnel->recursion--;
- return 0;
+ return NETDEV_TX_OK;
}
if (skb->sk)
skb_set_owner_w(new_skb, skb->sk);
@@ -889,7 +889,7 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
IPTUNNEL_XMIT();
tunnel->recursion--;
- return 0;
+ return NETDEV_TX_OK;
tx_error_icmp:
dst_link_failure(skb);
@@ -898,7 +898,7 @@ tx_error:
stats->tx_errors++;
dev_kfree_skb(skb);
tunnel->recursion--;
- return 0;
+ return NETDEV_TX_OK;
}
static int ipgre_tunnel_bind_dev(struct net_device *dev)
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 7ffcd96fe591..9fe5d7b81580 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -1304,7 +1304,7 @@ int ip_push_pending_frames(struct sock *sk)
err = ip_local_out(skb);
if (err) {
if (err > 0)
- err = inet->recverr ? net_xmit_errno(err) : 0;
+ err = net_xmit_errno(err);
if (err)
goto error;
}
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
index 93e2b787da20..62548cb0923c 100644
--- a/net/ipv4/ipip.c
+++ b/net/ipv4/ipip.c
@@ -387,7 +387,7 @@ static int ipip_rcv(struct sk_buff *skb)
* and that skb is filled properly by that function.
*/
-static int ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
struct net_device_stats *stats = &tunnel->dev->stats;
@@ -486,7 +486,7 @@ static int ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
stats->tx_dropped++;
dev_kfree_skb(skb);
tunnel->recursion--;
- return 0;
+ return NETDEV_TX_OK;
}
if (skb->sk)
skb_set_owner_w(new_skb, skb->sk);
@@ -524,7 +524,7 @@ static int ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
IPTUNNEL_XMIT();
tunnel->recursion--;
- return 0;
+ return NETDEV_TX_OK;
tx_error_icmp:
dst_link_failure(skb);
@@ -532,7 +532,7 @@ tx_error:
stats->tx_errors++;
dev_kfree_skb(skb);
tunnel->recursion--;
- return 0;
+ return NETDEV_TX_OK;
}
static void ipip_tunnel_bind_dev(struct net_device *dev)
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 9a8da5ed92b7..65d421cf5bc7 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -201,7 +201,7 @@ failure:
#ifdef CONFIG_IP_PIMSM
-static int reg_vif_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t reg_vif_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct net *net = dev_net(dev);
@@ -212,7 +212,7 @@ static int reg_vif_xmit(struct sk_buff *skb, struct net_device *dev)
IGMPMSG_WHOLEPKT);
read_unlock(&mrt_lock);
kfree_skb(skb);
- return 0;
+ return NETDEV_TX_OK;
}
static const struct net_device_ops reg_vif_netdev_ops = {
diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c
index 7505dff4ffdf..27774c99d888 100644
--- a/net/ipv4/netfilter/arp_tables.c
+++ b/net/ipv4/netfilter/arp_tables.c
@@ -8,7 +8,7 @@
* Copyright (C) 2002 David S. Miller (davem@redhat.com)
*
*/
-
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
@@ -341,15 +341,11 @@ unsigned int arpt_do_table(struct sk_buff *skb,
}
/* All zeroes == unconditional rule. */
-static inline int unconditional(const struct arpt_arp *arp)
+static inline bool unconditional(const struct arpt_arp *arp)
{
- unsigned int i;
+ static const struct arpt_arp uncond;
- for (i = 0; i < sizeof(*arp)/sizeof(__u32); i++)
- if (((__u32 *)arp)[i])
- return 0;
-
- return 1;
+ return memcmp(arp, &uncond, sizeof(uncond)) == 0;
}
/* Figures out from what hook each rule can be called: returns 0 if
@@ -537,12 +533,28 @@ out:
return ret;
}
+static bool check_underflow(struct arpt_entry *e)
+{
+ const struct arpt_entry_target *t;
+ unsigned int verdict;
+
+ if (!unconditional(&e->arp))
+ return false;
+ t = arpt_get_target(e);
+ if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
+ return false;
+ verdict = ((struct arpt_standard_target *)t)->verdict;
+ verdict = -verdict - 1;
+ return verdict == NF_DROP || verdict == NF_ACCEPT;
+}
+
static inline int check_entry_size_and_hooks(struct arpt_entry *e,
struct xt_table_info *newinfo,
unsigned char *base,
unsigned char *limit,
const unsigned int *hook_entries,
const unsigned int *underflows,
+ unsigned int valid_hooks,
unsigned int *i)
{
unsigned int h;
@@ -562,15 +574,21 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e,
/* Check hooks & underflows */
for (h = 0; h < NF_ARP_NUMHOOKS; h++) {
+ if (!(valid_hooks & (1 << h)))
+ continue;
if ((unsigned char *)e - base == hook_entries[h])
newinfo->hook_entry[h] = hook_entries[h];
- if ((unsigned char *)e - base == underflows[h])
+ if ((unsigned char *)e - base == underflows[h]) {
+ if (!check_underflow(e)) {
+ pr_err("Underflows must be unconditional and "
+ "use the STANDARD target with "
+ "ACCEPT/DROP\n");
+ return -EINVAL;
+ }
newinfo->underflow[h] = underflows[h];
+ }
}
- /* FIXME: underflows must be unconditional, standard verdicts
- < 0 (not ARPT_RETURN). --RR */
-
/* Clear counters and comefrom */
e->counters = ((struct xt_counters) { 0, 0 });
e->comefrom = 0;
@@ -630,7 +648,7 @@ static int translate_table(const char *name,
newinfo,
entry0,
entry0 + size,
- hook_entries, underflows, &i);
+ hook_entries, underflows, valid_hooks, &i);
duprintf("translate_table: ARPT_ENTRY_ITERATE gives %d\n", ret);
if (ret != 0)
return ret;
@@ -1760,7 +1778,8 @@ static int do_arpt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len
return ret;
}
-struct xt_table *arpt_register_table(struct net *net, struct xt_table *table,
+struct xt_table *arpt_register_table(struct net *net,
+ const struct xt_table *table,
const struct arpt_replace *repl)
{
int ret;
diff --git a/net/ipv4/netfilter/arptable_filter.c b/net/ipv4/netfilter/arptable_filter.c
index 6ecfdae7c589..97337601827a 100644
--- a/net/ipv4/netfilter/arptable_filter.c
+++ b/net/ipv4/netfilter/arptable_filter.c
@@ -15,7 +15,7 @@ MODULE_DESCRIPTION("arptables filter table");
#define FILTER_VALID_HOOKS ((1 << NF_ARP_IN) | (1 << NF_ARP_OUT) | \
(1 << NF_ARP_FORWARD))
-static struct
+static const struct
{
struct arpt_replace repl;
struct arpt_standard entries[3];
@@ -45,7 +45,7 @@ static struct
.term = ARPT_ERROR_INIT,
};
-static struct xt_table packet_filter = {
+static const struct xt_table packet_filter = {
.name = "filter",
.valid_hooks = FILTER_VALID_HOOKS,
.me = THIS_MODULE,
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c
index fdefae6b5dfc..cde755d5eeab 100644
--- a/net/ipv4/netfilter/ip_tables.c
+++ b/net/ipv4/netfilter/ip_tables.c
@@ -8,6 +8,7 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/cache.h>
#include <linux/capability.h>
#include <linux/skbuff.h>
@@ -190,16 +191,11 @@ get_entry(void *base, unsigned int offset)
/* All zeroes == unconditional rule. */
/* Mildly perf critical (only if packet tracing is on) */
-static inline int
-unconditional(const struct ipt_ip *ip)
+static inline bool unconditional(const struct ipt_ip *ip)
{
- unsigned int i;
-
- for (i = 0; i < sizeof(*ip)/sizeof(__u32); i++)
- if (((__u32 *)ip)[i])
- return 0;
+ static const struct ipt_ip uncond;
- return 1;
+ return memcmp(ip, &uncond, sizeof(uncond)) == 0;
#undef FWINV
}
@@ -315,7 +311,6 @@ ipt_do_table(struct sk_buff *skb,
static const char nulldevname[IFNAMSIZ] __attribute__((aligned(sizeof(long))));
const struct iphdr *ip;
- u_int16_t datalen;
bool hotdrop = false;
/* Initializing verdict to NF_DROP keeps gcc happy. */
unsigned int verdict = NF_DROP;
@@ -328,7 +323,6 @@ ipt_do_table(struct sk_buff *skb,
/* Initialization */
ip = ip_hdr(skb);
- datalen = skb->len - ip->ihl * 4;
indev = in ? in->name : nulldevname;
outdev = out ? out->name : nulldevname;
/* We handle fragments by dealing with the first fragment as
@@ -427,8 +421,6 @@ ipt_do_table(struct sk_buff *skb,
#endif
/* Target might have changed stuff. */
ip = ip_hdr(skb);
- datalen = skb->len - ip->ihl * 4;
-
if (verdict == IPT_CONTINUE)
e = ipt_next_entry(e);
else
@@ -716,6 +708,21 @@ find_check_entry(struct ipt_entry *e, const char *name, unsigned int size,
return ret;
}
+static bool check_underflow(struct ipt_entry *e)
+{
+ const struct ipt_entry_target *t;
+ unsigned int verdict;
+
+ if (!unconditional(&e->ip))
+ return false;
+ t = ipt_get_target(e);
+ if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
+ return false;
+ verdict = ((struct ipt_standard_target *)t)->verdict;
+ verdict = -verdict - 1;
+ return verdict == NF_DROP || verdict == NF_ACCEPT;
+}
+
static int
check_entry_size_and_hooks(struct ipt_entry *e,
struct xt_table_info *newinfo,
@@ -723,6 +730,7 @@ check_entry_size_and_hooks(struct ipt_entry *e,
unsigned char *limit,
const unsigned int *hook_entries,
const unsigned int *underflows,
+ unsigned int valid_hooks,
unsigned int *i)
{
unsigned int h;
@@ -742,15 +750,21 @@ check_entry_size_and_hooks(struct ipt_entry *e,
/* Check hooks & underflows */
for (h = 0; h < NF_INET_NUMHOOKS; h++) {
+ if (!(valid_hooks & (1 << h)))
+ continue;
if ((unsigned char *)e - base == hook_entries[h])
newinfo->hook_entry[h] = hook_entries[h];
- if ((unsigned char *)e - base == underflows[h])
+ if ((unsigned char *)e - base == underflows[h]) {
+ if (!check_underflow(e)) {
+ pr_err("Underflows must be unconditional and "
+ "use the STANDARD target with "
+ "ACCEPT/DROP\n");
+ return -EINVAL;
+ }
newinfo->underflow[h] = underflows[h];
+ }
}
- /* FIXME: underflows must be unconditional, standard verdicts
- < 0 (not IPT_RETURN). --RR */
-
/* Clear counters and comefrom */
e->counters = ((struct xt_counters) { 0, 0 });
e->comefrom = 0;
@@ -813,7 +827,7 @@ translate_table(const char *name,
newinfo,
entry0,
entry0 + size,
- hook_entries, underflows, &i);
+ hook_entries, underflows, valid_hooks, &i);
if (ret != 0)
return ret;
@@ -2051,7 +2065,8 @@ do_ipt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
return ret;
}
-struct xt_table *ipt_register_table(struct net *net, struct xt_table *table,
+struct xt_table *ipt_register_table(struct net *net,
+ const struct xt_table *table,
const struct ipt_replace *repl)
{
int ret;
diff --git a/net/ipv4/netfilter/iptable_filter.c b/net/ipv4/netfilter/iptable_filter.c
index c30a969724f8..df566cbd68e5 100644
--- a/net/ipv4/netfilter/iptable_filter.c
+++ b/net/ipv4/netfilter/iptable_filter.c
@@ -53,11 +53,11 @@ static struct
.term = IPT_ERROR_INIT, /* ERROR */
};
-static struct xt_table packet_filter = {
+static const struct xt_table packet_filter = {
.name = "filter",
.valid_hooks = FILTER_VALID_HOOKS,
.me = THIS_MODULE,
- .af = AF_INET,
+ .af = NFPROTO_IPV4,
};
/* The work comes in here from netfilter.c. */
@@ -102,21 +102,21 @@ static struct nf_hook_ops ipt_ops[] __read_mostly = {
{
.hook = ipt_local_in_hook,
.owner = THIS_MODULE,
- .pf = PF_INET,
+ .pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP_PRI_FILTER,
},
{
.hook = ipt_hook,
.owner = THIS_MODULE,
- .pf = PF_INET,
+ .pf = NFPROTO_IPV4,
.hooknum = NF_INET_FORWARD,
.priority = NF_IP_PRI_FILTER,
},
{
.hook = ipt_local_out_hook,
.owner = THIS_MODULE,
- .pf = PF_INET,
+ .pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_FILTER,
},
diff --git a/net/ipv4/netfilter/iptable_mangle.c b/net/ipv4/netfilter/iptable_mangle.c
index 4087614d9519..036047f9b0f2 100644
--- a/net/ipv4/netfilter/iptable_mangle.c
+++ b/net/ipv4/netfilter/iptable_mangle.c
@@ -28,7 +28,7 @@ MODULE_DESCRIPTION("iptables mangle table");
(1 << NF_INET_POST_ROUTING))
/* Ouch - five different hooks? Maybe this should be a config option..... -- BC */
-static struct
+static const struct
{
struct ipt_replace repl;
struct ipt_standard entries[5];
@@ -64,11 +64,11 @@ static struct
.term = IPT_ERROR_INIT, /* ERROR */
};
-static struct xt_table packet_mangler = {
+static const struct xt_table packet_mangler = {
.name = "mangle",
.valid_hooks = MANGLE_VALID_HOOKS,
.me = THIS_MODULE,
- .af = AF_INET,
+ .af = NFPROTO_IPV4,
};
/* The work comes in here from netfilter.c. */
@@ -162,35 +162,35 @@ static struct nf_hook_ops ipt_ops[] __read_mostly = {
{
.hook = ipt_pre_routing_hook,
.owner = THIS_MODULE,
- .pf = PF_INET,
+ .pf = NFPROTO_IPV4,
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP_PRI_MANGLE,
},
{
.hook = ipt_local_in_hook,
.owner = THIS_MODULE,
- .pf = PF_INET,
+ .pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP_PRI_MANGLE,
},
{
.hook = ipt_forward_hook,
.owner = THIS_MODULE,
- .pf = PF_INET,
+ .pf = NFPROTO_IPV4,
.hooknum = NF_INET_FORWARD,
.priority = NF_IP_PRI_MANGLE,
},
{
.hook = ipt_local_hook,
.owner = THIS_MODULE,
- .pf = PF_INET,
+ .pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_MANGLE,
},
{
.hook = ipt_post_routing_hook,
.owner = THIS_MODULE,
- .pf = PF_INET,
+ .pf = NFPROTO_IPV4,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_MANGLE,
},
diff --git a/net/ipv4/netfilter/iptable_raw.c b/net/ipv4/netfilter/iptable_raw.c
index e5356da1fb54..993edc23be09 100644
--- a/net/ipv4/netfilter/iptable_raw.c
+++ b/net/ipv4/netfilter/iptable_raw.c
@@ -9,7 +9,7 @@
#define RAW_VALID_HOOKS ((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_OUT))
-static struct
+static const struct
{
struct ipt_replace repl;
struct ipt_standard entries[2];
@@ -36,11 +36,11 @@ static struct
.term = IPT_ERROR_INIT, /* ERROR */
};
-static struct xt_table packet_raw = {
+static const struct xt_table packet_raw = {
.name = "raw",
.valid_hooks = RAW_VALID_HOOKS,
.me = THIS_MODULE,
- .af = AF_INET,
+ .af = NFPROTO_IPV4,
};
/* The work comes in here from netfilter.c. */
@@ -74,14 +74,14 @@ ipt_local_hook(unsigned int hook,
static struct nf_hook_ops ipt_ops[] __read_mostly = {
{
.hook = ipt_hook,
- .pf = PF_INET,
+ .pf = NFPROTO_IPV4,
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP_PRI_RAW,
.owner = THIS_MODULE,
},
{
.hook = ipt_local_hook,
- .pf = PF_INET,
+ .pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_RAW,
.owner = THIS_MODULE,
diff --git a/net/ipv4/netfilter/iptable_security.c b/net/ipv4/netfilter/iptable_security.c
index 29ab630f240a..99eb76c65d25 100644
--- a/net/ipv4/netfilter/iptable_security.c
+++ b/net/ipv4/netfilter/iptable_security.c
@@ -27,7 +27,7 @@ MODULE_DESCRIPTION("iptables security table, for MAC rules");
(1 << NF_INET_FORWARD) | \
(1 << NF_INET_LOCAL_OUT)
-static struct
+static const struct
{
struct ipt_replace repl;
struct ipt_standard entries[3];
@@ -57,11 +57,11 @@ static struct
.term = IPT_ERROR_INIT, /* ERROR */
};
-static struct xt_table security_table = {
+static const struct xt_table security_table = {
.name = "security",
.valid_hooks = SECURITY_VALID_HOOKS,
.me = THIS_MODULE,
- .af = AF_INET,
+ .af = NFPROTO_IPV4,
};
static unsigned int
@@ -105,21 +105,21 @@ static struct nf_hook_ops ipt_ops[] __read_mostly = {
{
.hook = ipt_local_in_hook,
.owner = THIS_MODULE,
- .pf = PF_INET,
+ .pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP_PRI_SECURITY,
},
{
.hook = ipt_forward_hook,
.owner = THIS_MODULE,
- .pf = PF_INET,
+ .pf = NFPROTO_IPV4,
.hooknum = NF_INET_FORWARD,
.priority = NF_IP_PRI_SECURITY,
},
{
.hook = ipt_local_out_hook,
.owner = THIS_MODULE,
- .pf = PF_INET,
+ .pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_SECURITY,
},
diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
index 7d2ead7228ac..aa95bb82ee6c 100644
--- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
+++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
@@ -26,6 +26,7 @@
#include <net/netfilter/ipv4/nf_conntrack_ipv4.h>
#include <net/netfilter/nf_nat_helper.h>
#include <net/netfilter/ipv4/nf_defrag_ipv4.h>
+#include <net/netfilter/nf_log.h>
int (*nf_nat_seq_adjust_hook)(struct sk_buff *skb,
struct nf_conn *ct,
@@ -113,8 +114,11 @@ static unsigned int ipv4_confirm(unsigned int hooknum,
ret = helper->help(skb, skb_network_offset(skb) + ip_hdrlen(skb),
ct, ctinfo);
- if (ret != NF_ACCEPT)
+ if (ret != NF_ACCEPT) {
+ nf_log_packet(NFPROTO_IPV4, hooknum, skb, in, out, NULL,
+ "nf_ct_%s: dropping packet", helper->name);
return ret;
+ }
if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status)) {
typeof(nf_nat_seq_adjust_hook) seq_adjust;
@@ -158,28 +162,28 @@ static struct nf_hook_ops ipv4_conntrack_ops[] __read_mostly = {
{
.hook = ipv4_conntrack_in,
.owner = THIS_MODULE,
- .pf = PF_INET,
+ .pf = NFPROTO_IPV4,
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP_PRI_CONNTRACK,
},
{
.hook = ipv4_conntrack_local,
.owner = THIS_MODULE,
- .pf = PF_INET,
+ .pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_CONNTRACK,
},
{
.hook = ipv4_confirm,
.owner = THIS_MODULE,
- .pf = PF_INET,
+ .pf = NFPROTO_IPV4,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_CONNTRACK_CONFIRM,
},
{
.hook = ipv4_confirm,
.owner = THIS_MODULE,
- .pf = PF_INET,
+ .pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP_PRI_CONNTRACK_CONFIRM,
},
@@ -256,11 +260,11 @@ getorigdst(struct sock *sk, int optval, void __user *user, int *len)
tuple.dst.u3.ip = inet->daddr;
tuple.dst.u.tcp.port = inet->dport;
tuple.src.l3num = PF_INET;
- tuple.dst.protonum = IPPROTO_TCP;
+ tuple.dst.protonum = sk->sk_protocol;
- /* We only do TCP at the moment: is there a better way? */
- if (strcmp(sk->sk_prot->name, "TCP")) {
- pr_debug("SO_ORIGINAL_DST: Not a TCP socket\n");
+ /* We only do TCP and SCTP at the moment: is there a better way? */
+ if (sk->sk_protocol != IPPROTO_TCP && sk->sk_protocol != IPPROTO_SCTP) {
+ pr_debug("SO_ORIGINAL_DST: Not a TCP/SCTP socket\n");
return -ENOPROTOOPT;
}
diff --git a/net/ipv4/netfilter/nf_nat_core.c b/net/ipv4/netfilter/nf_nat_core.c
index 3229e0a81ba6..68afc6ecd343 100644
--- a/net/ipv4/netfilter/nf_nat_core.c
+++ b/net/ipv4/netfilter/nf_nat_core.c
@@ -212,7 +212,7 @@ find_best_ips_proto(struct nf_conntrack_tuple *tuple,
maxip = ntohl(range->max_ip);
j = jhash_2words((__force u32)tuple->src.u3.ip,
range->flags & IP_NAT_RANGE_PERSISTENT ?
- (__force u32)tuple->dst.u3.ip : 0, 0);
+ 0 : (__force u32)tuple->dst.u3.ip, 0);
j = ((u64)j * (maxip - minip + 1)) >> 32;
*var_ipp = htonl(minip + j);
}
@@ -620,7 +620,7 @@ static const struct nla_policy nat_nla_policy[CTA_NAT_MAX+1] = {
};
static int
-nfnetlink_parse_nat(struct nlattr *nat,
+nfnetlink_parse_nat(const struct nlattr *nat,
const struct nf_conn *ct, struct nf_nat_range *range)
{
struct nlattr *tb[CTA_NAT_MAX+1];
@@ -656,7 +656,7 @@ nfnetlink_parse_nat(struct nlattr *nat,
static int
nfnetlink_parse_nat_setup(struct nf_conn *ct,
enum nf_nat_manip_type manip,
- struct nlattr *attr)
+ const struct nlattr *attr)
{
struct nf_nat_range range;
@@ -671,7 +671,7 @@ nfnetlink_parse_nat_setup(struct nf_conn *ct,
static int
nfnetlink_parse_nat_setup(struct nf_conn *ct,
enum nf_nat_manip_type manip,
- struct nlattr *attr)
+ const struct nlattr *attr)
{
return -EOPNOTSUPP;
}
diff --git a/net/ipv4/netfilter/nf_nat_rule.c b/net/ipv4/netfilter/nf_nat_rule.c
index 6348a793936e..9e81e0dfb4ec 100644
--- a/net/ipv4/netfilter/nf_nat_rule.c
+++ b/net/ipv4/netfilter/nf_nat_rule.c
@@ -28,7 +28,7 @@
(1 << NF_INET_POST_ROUTING) | \
(1 << NF_INET_LOCAL_OUT))
-static struct
+static const struct
{
struct ipt_replace repl;
struct ipt_standard entries[3];
@@ -58,11 +58,11 @@ static struct
.term = IPT_ERROR_INIT, /* ERROR */
};
-static struct xt_table nat_table = {
+static const struct xt_table nat_table = {
.name = "nat",
.valid_hooks = NAT_VALID_HOOKS,
.me = THIS_MODULE,
- .af = AF_INET,
+ .af = NFPROTO_IPV4,
};
/* Source NAT */
diff --git a/net/ipv4/netfilter/nf_nat_standalone.c b/net/ipv4/netfilter/nf_nat_standalone.c
index 5567bd0d0750..5f41d017ddd8 100644
--- a/net/ipv4/netfilter/nf_nat_standalone.c
+++ b/net/ipv4/netfilter/nf_nat_standalone.c
@@ -251,7 +251,7 @@ static struct nf_hook_ops nf_nat_ops[] __read_mostly = {
{
.hook = nf_nat_in,
.owner = THIS_MODULE,
- .pf = PF_INET,
+ .pf = NFPROTO_IPV4,
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP_PRI_NAT_DST,
},
@@ -259,7 +259,7 @@ static struct nf_hook_ops nf_nat_ops[] __read_mostly = {
{
.hook = nf_nat_out,
.owner = THIS_MODULE,
- .pf = PF_INET,
+ .pf = NFPROTO_IPV4,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_NAT_SRC,
},
@@ -267,7 +267,7 @@ static struct nf_hook_ops nf_nat_ops[] __read_mostly = {
{
.hook = nf_nat_local_fn,
.owner = THIS_MODULE,
- .pf = PF_INET,
+ .pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP_PRI_NAT_DST,
},
@@ -275,7 +275,7 @@ static struct nf_hook_ops nf_nat_ops[] __read_mostly = {
{
.hook = nf_nat_fn,
.owner = THIS_MODULE,
- .pf = PF_INET,
+ .pf = NFPROTO_IPV4,
.hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP_PRI_NAT_SRC,
},
diff --git a/net/ipv4/protocol.c b/net/ipv4/protocol.c
index ea50da0649fd..a2e5fc0a15e1 100644
--- a/net/ipv4/protocol.c
+++ b/net/ipv4/protocol.c
@@ -22,26 +22,11 @@
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
-
-#include <asm/uaccess.h>
-#include <asm/system.h>
+#include <linux/cache.h>
#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/inet.h>
#include <linux/netdevice.h>
-#include <linux/timer.h>
-#include <net/ip.h>
+#include <linux/spinlock.h>
#include <net/protocol.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <net/icmp.h>
-#include <net/udp.h>
-#include <net/ipip.h>
-#include <linux/igmp.h>
struct net_protocol *inet_protos[MAX_INET_PROTOS] ____cacheline_aligned_in_smp;
static DEFINE_SPINLOCK(inet_proto_lock);
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 2979f14bb188..ebb1e5848bc6 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -375,7 +375,7 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length,
err = NF_HOOK(PF_INET, NF_INET_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
dst_output);
if (err > 0)
- err = inet->recverr ? net_xmit_errno(err) : 0;
+ err = net_xmit_errno(err);
if (err)
goto error;
out:
@@ -386,6 +386,8 @@ error_fault:
kfree_skb(skb);
error:
IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS);
+ if (err == -ENOBUFS && !inet->recverr)
+ err = 0;
return err;
}
@@ -576,8 +578,11 @@ back_from_confirm:
&ipc, &rt, msg->msg_flags);
if (err)
ip_flush_pending_frames(sk);
- else if (!(msg->msg_flags & MSG_MORE))
+ else if (!(msg->msg_flags & MSG_MORE)) {
err = ip_push_pending_frames(sk);
+ if (err == -ENOBUFS && !inet->recverr)
+ err = 0;
+ }
release_sock(sk);
}
done:
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 278f46f5011b..91867d3e6328 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1514,13 +1514,17 @@ static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst)
void ip_rt_send_redirect(struct sk_buff *skb)
{
struct rtable *rt = skb_rtable(skb);
- struct in_device *in_dev = in_dev_get(rt->u.dst.dev);
+ struct in_device *in_dev;
+ int log_martians;
- if (!in_dev)
+ rcu_read_lock();
+ in_dev = __in_dev_get_rcu(rt->u.dst.dev);
+ if (!in_dev || !IN_DEV_TX_REDIRECTS(in_dev)) {
+ rcu_read_unlock();
return;
-
- if (!IN_DEV_TX_REDIRECTS(in_dev))
- goto out;
+ }
+ log_martians = IN_DEV_LOG_MARTIANS(in_dev);
+ rcu_read_unlock();
/* No redirected packets during ip_rt_redirect_silence;
* reset the algorithm.
@@ -1533,7 +1537,7 @@ void ip_rt_send_redirect(struct sk_buff *skb)
*/
if (rt->u.dst.rate_tokens >= ip_rt_redirect_number) {
rt->u.dst.rate_last = jiffies;
- goto out;
+ return;
}
/* Check for load limit; set rate_last to the latest sent
@@ -1547,7 +1551,7 @@ void ip_rt_send_redirect(struct sk_buff *skb)
rt->u.dst.rate_last = jiffies;
++rt->u.dst.rate_tokens;
#ifdef CONFIG_IP_ROUTE_VERBOSE
- if (IN_DEV_LOG_MARTIANS(in_dev) &&
+ if (log_martians &&
rt->u.dst.rate_tokens == ip_rt_redirect_number &&
net_ratelimit())
printk(KERN_WARNING "host %pI4/if%d ignores redirects for %pI4 to %pI4.\n",
@@ -1555,8 +1559,6 @@ void ip_rt_send_redirect(struct sk_buff *skb)
&rt->rt_dst, &rt->rt_gateway);
#endif
}
-out:
- in_dev_put(in_dev);
}
static int ip_error(struct sk_buff *skb)
@@ -3442,7 +3444,7 @@ int __init ip_rt_init(void)
printk(KERN_ERR "Unable to create route proc files\n");
#ifdef CONFIG_XFRM
xfrm_init();
- xfrm4_init();
+ xfrm4_init(ip_rt_max_size);
#endif
rtnl_register(PF_INET, RTM_GETROUTE, inet_rtm_getroute, NULL);
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 91145244ea63..edeea060db44 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -1839,7 +1839,7 @@ void tcp_close(struct sock *sk, long timeout)
/* Unread data was tossed, zap the connection. */
NET_INC_STATS_USER(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE);
tcp_set_state(sk, TCP_CLOSE);
- tcp_send_active_reset(sk, GFP_KERNEL);
+ tcp_send_active_reset(sk, sk->sk_allocation);
} else if (sock_flag(sk, SOCK_LINGER) && !sk->sk_lingertime) {
/* Check zero linger _after_ checking for unread data. */
sk->sk_prot->disconnect(sk, 0);
@@ -2336,13 +2336,13 @@ static int do_tcp_getsockopt(struct sock *sk, int level,
val = !!(tp->nonagle&TCP_NAGLE_CORK);
break;
case TCP_KEEPIDLE:
- val = (tp->keepalive_time ? : sysctl_tcp_keepalive_time) / HZ;
+ val = keepalive_time_when(tp) / HZ;
break;
case TCP_KEEPINTVL:
- val = (tp->keepalive_intvl ? : sysctl_tcp_keepalive_intvl) / HZ;
+ val = keepalive_intvl_when(tp) / HZ;
break;
case TCP_KEEPCNT:
- val = tp->keepalive_probes ? : sysctl_tcp_keepalive_probes;
+ val = keepalive_probes(tp);
break;
case TCP_SYNCNT:
val = icsk->icsk_syn_retries ? : sysctl_tcp_syn_retries;
@@ -2658,7 +2658,7 @@ void tcp_free_md5sig_pool(void)
EXPORT_SYMBOL(tcp_free_md5sig_pool);
-static struct tcp_md5sig_pool **__tcp_alloc_md5sig_pool(void)
+static struct tcp_md5sig_pool **__tcp_alloc_md5sig_pool(struct sock *sk)
{
int cpu;
struct tcp_md5sig_pool **pool;
@@ -2671,7 +2671,7 @@ static struct tcp_md5sig_pool **__tcp_alloc_md5sig_pool(void)
struct tcp_md5sig_pool *p;
struct crypto_hash *hash;
- p = kzalloc(sizeof(*p), GFP_KERNEL);
+ p = kzalloc(sizeof(*p), sk->sk_allocation);
if (!p)
goto out_free;
*per_cpu_ptr(pool, cpu) = p;
@@ -2688,7 +2688,7 @@ out_free:
return NULL;
}
-struct tcp_md5sig_pool **tcp_alloc_md5sig_pool(void)
+struct tcp_md5sig_pool **tcp_alloc_md5sig_pool(struct sock *sk)
{
struct tcp_md5sig_pool **pool;
int alloc = 0;
@@ -2709,7 +2709,7 @@ retry:
if (alloc) {
/* we cannot hold spinlock here because this may sleep. */
- struct tcp_md5sig_pool **p = __tcp_alloc_md5sig_pool();
+ struct tcp_md5sig_pool **p = __tcp_alloc_md5sig_pool(sk);
spin_lock_bh(&tcp_md5sig_pool_lock);
if (!p) {
tcp_md5sig_users--;
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 2bdb0da237e6..af6d6fa00db1 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -685,7 +685,7 @@ static inline void tcp_set_rto(struct sock *sk)
* is invisible. Actually, Linux-2.4 also generates erratic
* ACKs in some circumstances.
*/
- inet_csk(sk)->icsk_rto = (tp->srtt >> 3) + tp->rttvar;
+ inet_csk(sk)->icsk_rto = __tcp_set_rto(tp);
/* 2. Fixups made earlier cannot be right.
* If we do not estimate RTO correctly without them,
@@ -696,8 +696,7 @@ static inline void tcp_set_rto(struct sock *sk)
/* NOTE: clamping at TCP_RTO_MIN is not required, current algo
* guarantees that rto is higher.
*/
- if (inet_csk(sk)->icsk_rto > TCP_RTO_MAX)
- inet_csk(sk)->icsk_rto = TCP_RTO_MAX;
+ tcp_bound_rto(sk);
}
/* Save metrics learned by this TCP session.
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 6d88219c5e22..0543561da999 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -328,26 +328,29 @@ static void do_pmtu_discovery(struct sock *sk, struct iphdr *iph, u32 mtu)
*
*/
-void tcp_v4_err(struct sk_buff *skb, u32 info)
+void tcp_v4_err(struct sk_buff *icmp_skb, u32 info)
{
- struct iphdr *iph = (struct iphdr *)skb->data;
- struct tcphdr *th = (struct tcphdr *)(skb->data + (iph->ihl << 2));
+ struct iphdr *iph = (struct iphdr *)icmp_skb->data;
+ struct tcphdr *th = (struct tcphdr *)(icmp_skb->data + (iph->ihl << 2));
+ struct inet_connection_sock *icsk;
struct tcp_sock *tp;
struct inet_sock *inet;
- const int type = icmp_hdr(skb)->type;
- const int code = icmp_hdr(skb)->code;
+ const int type = icmp_hdr(icmp_skb)->type;
+ const int code = icmp_hdr(icmp_skb)->code;
struct sock *sk;
+ struct sk_buff *skb;
__u32 seq;
+ __u32 remaining;
int err;
- struct net *net = dev_net(skb->dev);
+ struct net *net = dev_net(icmp_skb->dev);
- if (skb->len < (iph->ihl << 2) + 8) {
+ if (icmp_skb->len < (iph->ihl << 2) + 8) {
ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS);
return;
}
sk = inet_lookup(net, &tcp_hashinfo, iph->daddr, th->dest,
- iph->saddr, th->source, inet_iif(skb));
+ iph->saddr, th->source, inet_iif(icmp_skb));
if (!sk) {
ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS);
return;
@@ -367,6 +370,7 @@ void tcp_v4_err(struct sk_buff *skb, u32 info)
if (sk->sk_state == TCP_CLOSE)
goto out;
+ icsk = inet_csk(sk);
tp = tcp_sk(sk);
seq = ntohl(th->seq);
if (sk->sk_state != TCP_LISTEN &&
@@ -393,6 +397,39 @@ void tcp_v4_err(struct sk_buff *skb, u32 info)
}
err = icmp_err_convert[code].errno;
+ /* check if icmp_skb allows revert of backoff
+ * (see draft-zimmermann-tcp-lcd) */
+ if (code != ICMP_NET_UNREACH && code != ICMP_HOST_UNREACH)
+ break;
+ if (seq != tp->snd_una || !icsk->icsk_retransmits ||
+ !icsk->icsk_backoff)
+ break;
+
+ icsk->icsk_backoff--;
+ inet_csk(sk)->icsk_rto = __tcp_set_rto(tp) <<
+ icsk->icsk_backoff;
+ tcp_bound_rto(sk);
+
+ skb = tcp_write_queue_head(sk);
+ BUG_ON(!skb);
+
+ remaining = icsk->icsk_rto - min(icsk->icsk_rto,
+ tcp_time_stamp - TCP_SKB_CB(skb)->when);
+
+ if (remaining) {
+ inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
+ remaining, TCP_RTO_MAX);
+ } else if (sock_owned_by_user(sk)) {
+ /* RTO revert clocked out retransmission,
+ * but socket is locked. Will defer. */
+ inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
+ HZ/20, TCP_RTO_MAX);
+ } else {
+ /* RTO revert clocked out retransmission.
+ * Will retransmit now */
+ tcp_retransmit_timer(sk);
+ }
+
break;
case ICMP_TIME_EXCEEDED:
err = EHOSTUNREACH;
@@ -849,7 +886,7 @@ int tcp_v4_md5_do_add(struct sock *sk, __be32 addr,
}
sk->sk_route_caps &= ~NETIF_F_GSO_MASK;
}
- if (tcp_alloc_md5sig_pool() == NULL) {
+ if (tcp_alloc_md5sig_pool(sk) == NULL) {
kfree(newkey);
return -ENOMEM;
}
@@ -970,8 +1007,9 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval,
if (!tcp_sk(sk)->md5sig_info) {
struct tcp_sock *tp = tcp_sk(sk);
- struct tcp_md5sig_info *p = kzalloc(sizeof(*p), GFP_KERNEL);
+ struct tcp_md5sig_info *p;
+ p = kzalloc(sizeof(*p), sk->sk_allocation);
if (!p)
return -EINVAL;
@@ -979,7 +1017,7 @@ static int tcp_v4_parse_md5_keys(struct sock *sk, char __user *optval,
sk->sk_route_caps &= ~NETIF_F_GSO_MASK;
}
- newkey = kmemdup(cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL);
+ newkey = kmemdup(cmd.tcpm_key, cmd.tcpm_keylen, sk->sk_allocation);
if (!newkey)
return -ENOMEM;
return tcp_v4_md5_do_add(sk, sin->sin_addr.s_addr,
@@ -1158,7 +1196,7 @@ struct request_sock_ops tcp_request_sock_ops __read_mostly = {
};
#ifdef CONFIG_TCP_MD5SIG
-static struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = {
+static const struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = {
.md5_lookup = tcp_v4_reqsk_md5_lookup,
.calc_md5_hash = tcp_v4_md5_hash_skb,
};
@@ -1717,7 +1755,7 @@ int tcp_v4_tw_remember_stamp(struct inet_timewait_sock *tw)
return 0;
}
-struct inet_connection_sock_af_ops ipv4_specific = {
+const struct inet_connection_sock_af_ops ipv4_specific = {
.queue_xmit = ip_queue_xmit,
.send_check = tcp_v4_send_check,
.rebuild_header = inet_sk_rebuild_header,
@@ -1737,7 +1775,7 @@ struct inet_connection_sock_af_ops ipv4_specific = {
};
#ifdef CONFIG_TCP_MD5SIG
-static struct tcp_sock_af_ops tcp_sock_ipv4_specific = {
+static const struct tcp_sock_af_ops tcp_sock_ipv4_specific = {
.md5_lookup = tcp_v4_md5_lookup,
.calc_md5_hash = tcp_v4_md5_hash_skb,
.md5_add = tcp_v4_md5_add_func,
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index f8d67ccc64f3..e48c37d74d77 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -322,7 +322,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo)
if (key != NULL) {
memcpy(&tcptw->tw_md5_key, key->key, key->keylen);
tcptw->tw_md5_keylen = key->keylen;
- if (tcp_alloc_md5sig_pool() == NULL)
+ if (tcp_alloc_md5sig_pool(sk) == NULL)
BUG();
}
} while (0);
@@ -657,29 +657,6 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL);
if (child == NULL)
goto listen_overflow;
-#ifdef CONFIG_TCP_MD5SIG
- else {
- /* Copy over the MD5 key from the original socket */
- struct tcp_md5sig_key *key;
- struct tcp_sock *tp = tcp_sk(sk);
- key = tp->af_specific->md5_lookup(sk, child);
- if (key != NULL) {
- /*
- * We're using one, so create a matching key on the
- * newsk structure. If we fail to get memory then we
- * end up not copying the key across. Shucks.
- */
- char *newkey = kmemdup(key->key, key->keylen,
- GFP_ATOMIC);
- if (newkey) {
- if (!tcp_alloc_md5sig_pool())
- BUG();
- tp->af_specific->md5_add(child, child, newkey,
- key->keylen);
- }
- }
- }
-#endif
inet_csk_reqsk_queue_unlink(sk, req, prev);
inet_csk_reqsk_queue_removed(sk, req);
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index bd62712848fa..5200aab0ca97 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -59,6 +59,7 @@ int sysctl_tcp_base_mss __read_mostly = 512;
/* By default, RFC2861 behavior. */
int sysctl_tcp_slow_start_after_idle __read_mostly = 1;
+/* Account for new data that has been sent to the network. */
static void tcp_event_new_data_sent(struct sock *sk, struct sk_buff *skb)
{
struct tcp_sock *tp = tcp_sk(sk);
@@ -142,6 +143,7 @@ static void tcp_cwnd_restart(struct sock *sk, struct dst_entry *dst)
tp->snd_cwnd_used = 0;
}
+/* Congestion state accounting after a packet has been sent. */
static void tcp_event_data_sent(struct tcp_sock *tp,
struct sk_buff *skb, struct sock *sk)
{
@@ -161,6 +163,7 @@ static void tcp_event_data_sent(struct tcp_sock *tp,
icsk->icsk_ack.pingpong = 1;
}
+/* Account for an ACK we sent. */
static inline void tcp_event_ack_sent(struct sock *sk, unsigned int pkts)
{
tcp_dec_quickack_mode(sk, pkts);
@@ -276,6 +279,7 @@ static u16 tcp_select_window(struct sock *sk)
return new_win;
}
+/* Packet ECN state for a SYN-ACK */
static inline void TCP_ECN_send_synack(struct tcp_sock *tp, struct sk_buff *skb)
{
TCP_SKB_CB(skb)->flags &= ~TCPCB_FLAG_CWR;
@@ -283,6 +287,7 @@ static inline void TCP_ECN_send_synack(struct tcp_sock *tp, struct sk_buff *skb)
TCP_SKB_CB(skb)->flags &= ~TCPCB_FLAG_ECE;
}
+/* Packet ECN state for a SYN. */
static inline void TCP_ECN_send_syn(struct sock *sk, struct sk_buff *skb)
{
struct tcp_sock *tp = tcp_sk(sk);
@@ -301,6 +306,9 @@ TCP_ECN_make_synack(struct request_sock *req, struct tcphdr *th)
th->ece = 1;
}
+/* Set up ECN state for a packet on a ESTABLISHED socket that is about to
+ * be sent.
+ */
static inline void TCP_ECN_send(struct sock *sk, struct sk_buff *skb,
int tcp_header_len)
{
@@ -362,7 +370,9 @@ struct tcp_out_options {
__u32 tsval, tsecr; /* need to include OPTION_TS */
};
-/* Beware: Something in the Internet is very sensitive to the ordering of
+/* Write previously computed TCP options to the packet.
+ *
+ * Beware: Something in the Internet is very sensitive to the ordering of
* TCP options, we learned this through the hard way, so be careful here.
* Luckily we can at least blame others for their non-compliance but from
* inter-operatibility perspective it seems that we're somewhat stuck with
@@ -445,6 +455,9 @@ static void tcp_options_write(__be32 *ptr, struct tcp_sock *tp,
}
}
+/* Compute TCP options for SYN packets. This is not the final
+ * network wire format yet.
+ */
static unsigned tcp_syn_options(struct sock *sk, struct sk_buff *skb,
struct tcp_out_options *opts,
struct tcp_md5sig_key **md5) {
@@ -493,6 +506,7 @@ static unsigned tcp_syn_options(struct sock *sk, struct sk_buff *skb,
return size;
}
+/* Set up TCP options for SYN-ACKs. */
static unsigned tcp_synack_options(struct sock *sk,
struct request_sock *req,
unsigned mss, struct sk_buff *skb,
@@ -541,6 +555,9 @@ static unsigned tcp_synack_options(struct sock *sk,
return size;
}
+/* Compute TCP options for ESTABLISHED sockets. This is not the
+ * final wire format yet.
+ */
static unsigned tcp_established_options(struct sock *sk, struct sk_buff *skb,
struct tcp_out_options *opts,
struct tcp_md5sig_key **md5) {
@@ -705,7 +722,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
return net_xmit_eval(err);
}
-/* This routine just queue's the buffer
+/* This routine just queues the buffer for sending.
*
* NOTE: probe0 timer is not checked, do not forget tcp_push_pending_frames,
* otherwise socket can stall.
@@ -722,6 +739,7 @@ static void tcp_queue_skb(struct sock *sk, struct sk_buff *skb)
sk_mem_charge(sk, skb->truesize);
}
+/* Initialize TSO segments for a packet. */
static void tcp_set_skb_tso_segs(struct sock *sk, struct sk_buff *skb,
unsigned int mss_now)
{
@@ -909,6 +927,7 @@ static void __pskb_trim_head(struct sk_buff *skb, int len)
skb->len = skb->data_len;
}
+/* Remove acked data from a packet in the transmit queue. */
int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len)
{
if (skb_cloned(skb) && pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
@@ -937,7 +956,7 @@ int tcp_trim_head(struct sock *sk, struct sk_buff *skb, u32 len)
return 0;
}
-/* Not accounting for SACKs here. */
+/* Calculate MSS. Not accounting for SACKs here. */
int tcp_mtu_to_mss(struct sock *sk, int pmtu)
{
struct tcp_sock *tp = tcp_sk(sk);
@@ -981,6 +1000,7 @@ int tcp_mss_to_mtu(struct sock *sk, int mss)
return mtu;
}
+/* MTU probing init per socket */
void tcp_mtup_init(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
@@ -1143,7 +1163,8 @@ static inline unsigned int tcp_cwnd_test(struct tcp_sock *tp,
return 0;
}
-/* This must be invoked the first time we consider transmitting
+/* Intialize TSO state of a skb.
+ * This must be invoked the first time we consider transmitting
* SKB onto the wire.
*/
static int tcp_init_tso_segs(struct sock *sk, struct sk_buff *skb,
@@ -1158,6 +1179,7 @@ static int tcp_init_tso_segs(struct sock *sk, struct sk_buff *skb,
return tso_segs;
}
+/* Minshall's variant of the Nagle send check. */
static inline int tcp_minshall_check(const struct tcp_sock *tp)
{
return after(tp->snd_sml, tp->snd_una) &&
@@ -1242,6 +1264,7 @@ static unsigned int tcp_snd_test(struct sock *sk, struct sk_buff *skb,
return cwnd_quota;
}
+/* Test if sending is allowed right now. */
int tcp_may_send_now(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
@@ -1378,6 +1401,10 @@ send_now:
}
/* Create a new MTU probe if we are ready.
+ * MTU probe is regularly attempting to increase the path MTU by
+ * deliberately sending larger packets. This discovers routing
+ * changes resulting in larger path MTUs.
+ *
* Returns 0 if we should wait to probe (no cwnd available),
* 1 if a probe was sent,
* -1 otherwise
@@ -1790,6 +1817,7 @@ static void tcp_collapse_retrans(struct sock *sk, struct sk_buff *skb)
sk_wmem_free_skb(sk, next_skb);
}
+/* Check if coalescing SKBs is legal. */
static int tcp_can_collapse(struct sock *sk, struct sk_buff *skb)
{
if (tcp_skb_pcount(skb) > 1)
@@ -1808,6 +1836,9 @@ static int tcp_can_collapse(struct sock *sk, struct sk_buff *skb)
return 1;
}
+/* Collapse packets in the retransmit queue to make to create
+ * less packets on the wire. This is only done on retransmission.
+ */
static void tcp_retrans_try_collapse(struct sock *sk, struct sk_buff *to,
int space)
{
@@ -1957,6 +1988,9 @@ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb)
return err;
}
+/* Check if we forward retransmits are possible in the current
+ * window/congestion state.
+ */
static int tcp_can_forward_retransmit(struct sock *sk)
{
const struct inet_connection_sock *icsk = inet_csk(sk);
@@ -2101,7 +2135,8 @@ void tcp_send_fin(struct sock *sk)
} else {
/* Socket is locked, keep trying until memory is available. */
for (;;) {
- skb = alloc_skb_fclone(MAX_TCP_HEADER, GFP_KERNEL);
+ skb = alloc_skb_fclone(MAX_TCP_HEADER,
+ sk->sk_allocation);
if (skb)
break;
yield();
@@ -2145,7 +2180,8 @@ void tcp_send_active_reset(struct sock *sk, gfp_t priority)
TCP_INC_STATS(sock_net(sk), TCP_MIB_OUTRSTS);
}
-/* WARNING: This routine must only be called when we have already sent
+/* Send a crossed SYN-ACK during socket establishment.
+ * WARNING: This routine must only be called when we have already sent
* a SYN packet that crossed the incoming SYN that caused this routine
* to get called. If this assumption fails then the initial rcv_wnd
* and rcv_wscale values will not be correct.
@@ -2180,9 +2216,7 @@ int tcp_send_synack(struct sock *sk)
return tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC);
}
-/*
- * Prepare a SYN-ACK.
- */
+/* Prepare a SYN-ACK. */
struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
struct request_sock *req)
{
@@ -2269,9 +2303,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
return skb;
}
-/*
- * Do all connect socket setups that can be done AF independent.
- */
+/* Do all connect socket setups that can be done AF independent. */
static void tcp_connect_init(struct sock *sk)
{
struct dst_entry *dst = __sk_dst_get(sk);
@@ -2330,9 +2362,7 @@ static void tcp_connect_init(struct sock *sk)
tcp_clear_retrans(tp);
}
-/*
- * Build a SYN and send it off.
- */
+/* Build a SYN and send it off. */
int tcp_connect(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
@@ -2359,7 +2389,7 @@ int tcp_connect(struct sock *sk)
sk->sk_wmem_queued += buff->truesize;
sk_mem_charge(sk, buff->truesize);
tp->packets_out += tcp_skb_pcount(buff);
- tcp_transmit_skb(sk, buff, 1, GFP_KERNEL);
+ tcp_transmit_skb(sk, buff, 1, sk->sk_allocation);
/* We change tp->snd_nxt after the tcp_transmit_skb() call
* in order to make this packet get counted in tcpOutSegs.
@@ -2493,6 +2523,7 @@ static int tcp_xmit_probe_skb(struct sock *sk, int urgent)
return tcp_transmit_skb(sk, skb, 0, GFP_ATOMIC);
}
+/* Initiate keepalive or window probe from timer. */
int tcp_write_wakeup(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index b144a26359bc..cdb2ca7684d4 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -137,13 +137,14 @@ static int tcp_write_timeout(struct sock *sk)
{
struct inet_connection_sock *icsk = inet_csk(sk);
int retry_until;
+ bool do_reset;
if ((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV)) {
if (icsk->icsk_retransmits)
dst_negative_advice(&sk->sk_dst_cache);
retry_until = icsk->icsk_syn_retries ? : sysctl_tcp_syn_retries;
} else {
- if (icsk->icsk_retransmits >= sysctl_tcp_retries1) {
+ if (retransmits_timed_out(sk, sysctl_tcp_retries1)) {
/* Black hole detection */
tcp_mtu_probing(icsk, sk);
@@ -155,13 +156,15 @@ static int tcp_write_timeout(struct sock *sk)
const int alive = (icsk->icsk_rto < TCP_RTO_MAX);
retry_until = tcp_orphan_retries(sk, alive);
+ do_reset = alive ||
+ !retransmits_timed_out(sk, retry_until);
- if (tcp_out_of_resources(sk, alive || icsk->icsk_retransmits < retry_until))
+ if (tcp_out_of_resources(sk, do_reset))
return 1;
}
}
- if (icsk->icsk_retransmits >= retry_until) {
+ if (retransmits_timed_out(sk, retry_until)) {
/* Has it gone just too far? */
tcp_write_err(sk);
return 1;
@@ -279,7 +282,7 @@ static void tcp_probe_timer(struct sock *sk)
* The TCP retransmit timer.
*/
-static void tcp_retransmit_timer(struct sock *sk)
+void tcp_retransmit_timer(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
struct inet_connection_sock *icsk = inet_csk(sk);
@@ -385,7 +388,7 @@ static void tcp_retransmit_timer(struct sock *sk)
out_reset_timer:
icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX);
inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, TCP_RTO_MAX);
- if (icsk->icsk_retransmits > sysctl_tcp_retries1)
+ if (retransmits_timed_out(sk, sysctl_tcp_retries1 + 1))
__sk_dst_reset(sk);
out:;
@@ -499,8 +502,7 @@ static void tcp_keepalive_timer (unsigned long data)
elapsed = tcp_time_stamp - tp->rcv_tstamp;
if (elapsed >= keepalive_time_when(tp)) {
- if ((!tp->keepalive_probes && icsk->icsk_probes_out >= sysctl_tcp_keepalive_probes) ||
- (tp->keepalive_probes && icsk->icsk_probes_out >= tp->keepalive_probes)) {
+ if (icsk->icsk_probes_out >= keepalive_probes(tp)) {
tcp_send_active_reset(sk, GFP_ATOMIC);
tcp_write_err(sk);
goto out;
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index 80e3812837ad..ebaaa7f973d7 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -110,11 +110,12 @@ struct udp_table udp_table;
EXPORT_SYMBOL(udp_table);
int sysctl_udp_mem[3] __read_mostly;
-int sysctl_udp_rmem_min __read_mostly;
-int sysctl_udp_wmem_min __read_mostly;
-
EXPORT_SYMBOL(sysctl_udp_mem);
+
+int sysctl_udp_rmem_min __read_mostly;
EXPORT_SYMBOL(sysctl_udp_rmem_min);
+
+int sysctl_udp_wmem_min __read_mostly;
EXPORT_SYMBOL(sysctl_udp_wmem_min);
atomic_t udp_memory_allocated;
@@ -158,7 +159,7 @@ static int udp_lib_lport_inuse(struct net *net, __u16 num,
*/
int udp_lib_get_port(struct sock *sk, unsigned short snum,
int (*saddr_comp)(const struct sock *sk1,
- const struct sock *sk2 ) )
+ const struct sock *sk2))
{
struct udp_hslot *hslot;
struct udp_table *udptable = sk->sk_prot->h.udp_table;
@@ -221,14 +222,15 @@ fail_unlock:
fail:
return error;
}
+EXPORT_SYMBOL(udp_lib_get_port);
static int ipv4_rcv_saddr_equal(const struct sock *sk1, const struct sock *sk2)
{
struct inet_sock *inet1 = inet_sk(sk1), *inet2 = inet_sk(sk2);
- return ( !ipv6_only_sock(sk2) &&
- (!inet1->rcv_saddr || !inet2->rcv_saddr ||
- inet1->rcv_saddr == inet2->rcv_saddr ));
+ return (!ipv6_only_sock(sk2) &&
+ (!inet1->rcv_saddr || !inet2->rcv_saddr ||
+ inet1->rcv_saddr == inet2->rcv_saddr));
}
int udp_v4_get_port(struct sock *sk, unsigned short snum)
@@ -383,8 +385,8 @@ found:
void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable)
{
struct inet_sock *inet;
- struct iphdr *iph = (struct iphdr*)skb->data;
- struct udphdr *uh = (struct udphdr*)(skb->data+(iph->ihl<<2));
+ struct iphdr *iph = (struct iphdr *)skb->data;
+ struct udphdr *uh = (struct udphdr *)(skb->data+(iph->ihl<<2));
const int type = icmp_hdr(skb)->type;
const int code = icmp_hdr(skb)->code;
struct sock *sk;
@@ -439,7 +441,7 @@ void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable)
if (!harderr || sk->sk_state != TCP_ESTABLISHED)
goto out;
} else {
- ip_icmp_error(sk, skb, err, uh->dest, info, (u8*)(uh+1));
+ ip_icmp_error(sk, skb, err, uh->dest, info, (u8 *)(uh+1));
}
sk->sk_err = err;
sk->sk_error_report(sk);
@@ -474,7 +476,7 @@ EXPORT_SYMBOL(udp_flush_pending_frames);
* (checksum field must be zeroed out)
*/
static void udp4_hwcsum_outgoing(struct sock *sk, struct sk_buff *skb,
- __be32 src, __be32 dst, int len )
+ __be32 src, __be32 dst, int len)
{
unsigned int offset;
struct udphdr *uh = udp_hdr(skb);
@@ -545,7 +547,7 @@ static int udp_push_pending_frames(struct sock *sk)
} else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */
- udp4_hwcsum_outgoing(sk, skb, fl->fl4_src,fl->fl4_dst, up->len);
+ udp4_hwcsum_outgoing(sk, skb, fl->fl4_src, fl->fl4_dst, up->len);
goto send;
} else /* `normal' UDP */
@@ -553,18 +555,24 @@ static int udp_push_pending_frames(struct sock *sk)
/* add protocol-dependent pseudo-header */
uh->check = csum_tcpudp_magic(fl->fl4_src, fl->fl4_dst, up->len,
- sk->sk_protocol, csum );
+ sk->sk_protocol, csum);
if (uh->check == 0)
uh->check = CSUM_MANGLED_0;
send:
err = ip_push_pending_frames(sk);
+ if (err) {
+ if (err == -ENOBUFS && !inet->recverr) {
+ UDP_INC_STATS_USER(sock_net(sk),
+ UDP_MIB_SNDBUFERRORS, is_udplite);
+ err = 0;
+ }
+ } else
+ UDP_INC_STATS_USER(sock_net(sk),
+ UDP_MIB_OUTDATAGRAMS, is_udplite);
out:
up->len = 0;
up->pending = 0;
- if (!err)
- UDP_INC_STATS_USER(sock_net(sk),
- UDP_MIB_OUTDATAGRAMS, is_udplite);
return err;
}
@@ -592,7 +600,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
* Check the flags.
*/
- if (msg->msg_flags&MSG_OOB) /* Mirror BSD error message compatibility */
+ if (msg->msg_flags & MSG_OOB) /* Mirror BSD error message compatibility */
return -EOPNOTSUPP;
ipc.opt = NULL;
@@ -619,7 +627,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
* Get and verify the address.
*/
if (msg->msg_name) {
- struct sockaddr_in * usin = (struct sockaddr_in*)msg->msg_name;
+ struct sockaddr_in * usin = (struct sockaddr_in *)msg->msg_name;
if (msg->msg_namelen < sizeof(*usin))
return -EINVAL;
if (usin->sin_family != AF_INET) {
@@ -684,7 +692,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
}
if (connected)
- rt = (struct rtable*)sk_dst_check(sk, 0);
+ rt = (struct rtable *)sk_dst_check(sk, 0);
if (rt == NULL) {
struct flowi fl = { .oif = ipc.oif,
@@ -782,6 +790,7 @@ do_confirm:
err = 0;
goto out;
}
+EXPORT_SYMBOL(udp_sendmsg);
int udp_sendpage(struct sock *sk, struct page *page, int offset,
size_t size, int flags)
@@ -871,6 +880,7 @@ int udp_ioctl(struct sock *sk, int cmd, unsigned long arg)
return 0;
}
+EXPORT_SYMBOL(udp_ioctl);
/*
* This should be easy, if there is something there we
@@ -892,7 +902,7 @@ int udp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
* Check any passed addresses
*/
if (addr_len)
- *addr_len=sizeof(*sin);
+ *addr_len = sizeof(*sin);
if (flags & MSG_ERRQUEUE)
return ip_recv_error(sk, msg, len);
@@ -923,9 +933,11 @@ try_again:
if (skb_csum_unnecessary(skb))
err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr),
- msg->msg_iov, copied );
+ msg->msg_iov, copied);
else {
- err = skb_copy_and_csum_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov);
+ err = skb_copy_and_csum_datagram_iovec(skb,
+ sizeof(struct udphdr),
+ msg->msg_iov);
if (err == -EINVAL)
goto csum_copy_err;
@@ -941,8 +953,7 @@ try_again:
sock_recv_timestamp(msg, sk, skb);
/* Copy the address. */
- if (sin)
- {
+ if (sin) {
sin->sin_family = AF_INET;
sin->sin_port = udp_hdr(skb)->source;
sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
@@ -995,6 +1006,7 @@ int udp_disconnect(struct sock *sk, int flags)
sk_dst_reset(sk);
return 0;
}
+EXPORT_SYMBOL(udp_disconnect);
void udp_lib_unhash(struct sock *sk)
{
@@ -1044,7 +1056,7 @@ drop:
* Note that in the success and error cases, the skb is assumed to
* have either been requeued or freed.
*/
-int udp_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
+int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
struct udp_sock *up = udp_sk(sk);
int rc;
@@ -1214,7 +1226,7 @@ static inline int udp4_csum_init(struct sk_buff *skb, struct udphdr *uh,
if (uh->check == 0) {
skb->ip_summed = CHECKSUM_UNNECESSARY;
} else if (skb->ip_summed == CHECKSUM_COMPLETE) {
- if (!csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len,
+ if (!csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len,
proto, skb->csum))
skb->ip_summed = CHECKSUM_UNNECESSARY;
}
@@ -1355,7 +1367,7 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
int err = 0;
int is_udplite = IS_UDPLITE(sk);
- if (optlen<sizeof(int))
+ if (optlen < sizeof(int))
return -EINVAL;
if (get_user(val, (int __user *)optval))
@@ -1426,6 +1438,7 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
return err;
}
+EXPORT_SYMBOL(udp_lib_setsockopt);
int udp_setsockopt(struct sock *sk, int level, int optname,
char __user *optval, int optlen)
@@ -1453,7 +1466,7 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname,
struct udp_sock *up = udp_sk(sk);
int val, len;
- if (get_user(len,optlen))
+ if (get_user(len, optlen))
return -EFAULT;
len = min_t(unsigned int, len, sizeof(int));
@@ -1486,10 +1499,11 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname,
if (put_user(len, optlen))
return -EFAULT;
- if (copy_to_user(optval, &val,len))
+ if (copy_to_user(optval, &val, len))
return -EFAULT;
return 0;
}
+EXPORT_SYMBOL(udp_lib_getsockopt);
int udp_getsockopt(struct sock *sk, int level, int optname,
char __user *optval, int __user *optlen)
@@ -1528,9 +1542,9 @@ unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait)
int is_lite = IS_UDPLITE(sk);
/* Check for false positives due to checksum errors */
- if ( (mask & POLLRDNORM) &&
- !(file->f_flags & O_NONBLOCK) &&
- !(sk->sk_shutdown & RCV_SHUTDOWN)){
+ if ((mask & POLLRDNORM) &&
+ !(file->f_flags & O_NONBLOCK) &&
+ !(sk->sk_shutdown & RCV_SHUTDOWN)) {
struct sk_buff_head *rcvq = &sk->sk_receive_queue;
struct sk_buff *skb;
@@ -1552,6 +1566,7 @@ unsigned int udp_poll(struct file *file, struct socket *sock, poll_table *wait)
return mask;
}
+EXPORT_SYMBOL(udp_poll);
struct proto udp_prot = {
.name = "UDP",
@@ -1582,6 +1597,7 @@ struct proto udp_prot = {
.compat_getsockopt = compat_udp_getsockopt,
#endif
};
+EXPORT_SYMBOL(udp_prot);
/* ------------------------------------------------------------------------ */
#ifdef CONFIG_PROC_FS
@@ -1703,11 +1719,13 @@ int udp_proc_register(struct net *net, struct udp_seq_afinfo *afinfo)
rc = -ENOMEM;
return rc;
}
+EXPORT_SYMBOL(udp_proc_register);
void udp_proc_unregister(struct net *net, struct udp_seq_afinfo *afinfo)
{
proc_net_remove(net, afinfo->name);
}
+EXPORT_SYMBOL(udp_proc_unregister);
/* ------------------------------------------------------------------------ */
static void udp4_format_sock(struct sock *sp, struct seq_file *f,
@@ -1741,7 +1759,7 @@ int udp4_seq_show(struct seq_file *seq, void *v)
int len;
udp4_format_sock(v, seq, state->bucket, &len);
- seq_printf(seq, "%*s\n", 127 - len ,"");
+ seq_printf(seq, "%*s\n", 127 - len, "");
}
return 0;
}
@@ -1816,16 +1834,64 @@ void __init udp_init(void)
sysctl_udp_wmem_min = SK_MEM_QUANTUM;
}
-EXPORT_SYMBOL(udp_disconnect);
-EXPORT_SYMBOL(udp_ioctl);
-EXPORT_SYMBOL(udp_prot);
-EXPORT_SYMBOL(udp_sendmsg);
-EXPORT_SYMBOL(udp_lib_getsockopt);
-EXPORT_SYMBOL(udp_lib_setsockopt);
-EXPORT_SYMBOL(udp_poll);
-EXPORT_SYMBOL(udp_lib_get_port);
+int udp4_ufo_send_check(struct sk_buff *skb)
+{
+ const struct iphdr *iph;
+ struct udphdr *uh;
+
+ if (!pskb_may_pull(skb, sizeof(*uh)))
+ return -EINVAL;
+
+ iph = ip_hdr(skb);
+ uh = udp_hdr(skb);
+
+ uh->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len,
+ IPPROTO_UDP, 0);
+ skb->csum_start = skb_transport_header(skb) - skb->head;
+ skb->csum_offset = offsetof(struct udphdr, check);
+ skb->ip_summed = CHECKSUM_PARTIAL;
+ return 0;
+}
+
+struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, int features)
+{
+ struct sk_buff *segs = ERR_PTR(-EINVAL);
+ unsigned int mss;
+ int offset;
+ __wsum csum;
+
+ mss = skb_shinfo(skb)->gso_size;
+ if (unlikely(skb->len <= mss))
+ goto out;
+
+ if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) {
+ /* Packet is from an untrusted source, reset gso_segs. */
+ int type = skb_shinfo(skb)->gso_type;
+
+ if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY) ||
+ !(type & (SKB_GSO_UDP))))
+ goto out;
+
+ skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss);
+
+ segs = NULL;
+ goto out;
+ }
+
+ /* Do software UFO. Complete and fill in the UDP checksum as HW cannot
+ * do checksum of UDP packets sent as multiple IP fragments.
+ */
+ offset = skb->csum_start - skb_headroom(skb);
+ csum = skb_checksum(skb, offset, skb->len - offset, 0);
+ offset += skb->csum_offset;
+ *(__sum16 *)(skb->data + offset) = csum_fold(csum);
+ skb->ip_summed = CHECKSUM_NONE;
+
+ /* Fragment the skb. IP headers of the fragments are updated in
+ * inet_gso_segment()
+ */
+ segs = skb_segment(skb, features);
+out:
+ return segs;
+}
-#ifdef CONFIG_PROC_FS
-EXPORT_SYMBOL(udp_proc_register);
-EXPORT_SYMBOL(udp_proc_unregister);
-#endif
diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c
index 0071ee6f441f..74fb2eb833ec 100644
--- a/net/ipv4/xfrm4_policy.c
+++ b/net/ipv4/xfrm4_policy.c
@@ -264,6 +264,22 @@ static struct xfrm_policy_afinfo xfrm4_policy_afinfo = {
.fill_dst = xfrm4_fill_dst,
};
+#ifdef CONFIG_SYSCTL
+static struct ctl_table xfrm4_policy_table[] = {
+ {
+ .ctl_name = CTL_UNNUMBERED,
+ .procname = "xfrm4_gc_thresh",
+ .data = &xfrm4_dst_ops.gc_thresh,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ { }
+};
+
+static struct ctl_table_header *sysctl_hdr;
+#endif
+
static void __init xfrm4_policy_init(void)
{
xfrm_policy_register_afinfo(&xfrm4_policy_afinfo);
@@ -271,12 +287,31 @@ static void __init xfrm4_policy_init(void)
static void __exit xfrm4_policy_fini(void)
{
+#ifdef CONFIG_SYSCTL
+ if (sysctl_hdr)
+ unregister_net_sysctl_table(sysctl_hdr);
+#endif
xfrm_policy_unregister_afinfo(&xfrm4_policy_afinfo);
}
-void __init xfrm4_init(void)
+void __init xfrm4_init(int rt_max_size)
{
xfrm4_state_init();
xfrm4_policy_init();
+ /*
+ * Select a default value for the gc_thresh based on the main route
+ * table hash size. It seems to me the worst case scenario is when
+ * we have ipsec operating in transport mode, in which we create a
+ * dst_entry per socket. The xfrm gc algorithm starts trying to remove
+ * entries at gc_thresh, and prevents new allocations as 2*gc_thresh
+ * so lets set an initial xfrm gc_thresh value at the rt_max_size/2.
+ * That will let us store an ipsec connection per route table entry,
+ * and start cleaning when were 1/2 full
+ */
+ xfrm4_dst_ops.gc_thresh = rt_max_size/2;
+#ifdef CONFIG_SYSCTL
+ sysctl_hdr = register_net_sysctl_table(&init_net, net_ipv4_ctl_path,
+ xfrm4_policy_table);
+#endif
}
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 43b3c9f89c12..c9b369034a40 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1371,12 +1371,14 @@ struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, const struct in6_addr *add
/* Gets referenced address, destroys ifaddr */
-static void addrconf_dad_stop(struct inet6_ifaddr *ifp)
+static void addrconf_dad_stop(struct inet6_ifaddr *ifp, int dad_failed)
{
if (ifp->flags&IFA_F_PERMANENT) {
spin_lock_bh(&ifp->lock);
addrconf_del_timer(ifp);
ifp->flags |= IFA_F_TENTATIVE;
+ if (dad_failed)
+ ifp->flags |= IFA_F_DADFAILED;
spin_unlock_bh(&ifp->lock);
in6_ifa_put(ifp);
#ifdef CONFIG_IPV6_PRIVACY
@@ -1422,7 +1424,7 @@ void addrconf_dad_failure(struct inet6_ifaddr *ifp)
}
}
- addrconf_dad_stop(ifp);
+ addrconf_dad_stop(ifp, 1);
}
/* Join to solicited addr multicast group. */
@@ -2778,7 +2780,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
idev->cnf.accept_dad < 1 ||
!(ifp->flags&IFA_F_TENTATIVE) ||
ifp->flags & IFA_F_NODAD) {
- ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
+ ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED);
spin_unlock_bh(&ifp->lock);
read_unlock_bh(&idev->lock);
@@ -2795,7 +2797,7 @@ static void addrconf_dad_start(struct inet6_ifaddr *ifp, u32 flags)
* - otherwise, kill it.
*/
in6_ifa_hold(ifp);
- addrconf_dad_stop(ifp);
+ addrconf_dad_stop(ifp, 0);
return;
}
@@ -2829,7 +2831,7 @@ static void addrconf_dad_timer(unsigned long data)
* DAD was successful
*/
- ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC);
+ ifp->flags &= ~(IFA_F_TENTATIVE|IFA_F_OPTIMISTIC|IFA_F_DADFAILED);
spin_unlock_bh(&ifp->lock);
read_unlock_bh(&idev->lock);
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 45f9a2a42d56..a123a328aeb3 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -774,6 +774,11 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features)
struct sk_buff *segs = ERR_PTR(-EINVAL);
struct ipv6hdr *ipv6h;
struct inet6_protocol *ops;
+ int proto;
+ struct frag_hdr *fptr;
+ unsigned int unfrag_ip6hlen;
+ u8 *prevhdr;
+ int offset = 0;
if (!(features & NETIF_F_V6_CSUM))
features &= ~NETIF_F_SG;
@@ -793,10 +798,9 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features)
__skb_pull(skb, sizeof(*ipv6h));
segs = ERR_PTR(-EPROTONOSUPPORT);
+ proto = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr);
rcu_read_lock();
- ops = rcu_dereference(inet6_protos[
- ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr)]);
-
+ ops = rcu_dereference(inet6_protos[proto]);
if (likely(ops && ops->gso_segment)) {
skb_reset_transport_header(skb);
segs = ops->gso_segment(skb, features);
@@ -810,6 +814,16 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features)
ipv6h = ipv6_hdr(skb);
ipv6h->payload_len = htons(skb->len - skb->mac_len -
sizeof(*ipv6h));
+ if (proto == IPPROTO_UDP) {
+ unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
+ fptr = (struct frag_hdr *)(skb_network_header(skb) +
+ unfrag_ip6hlen);
+ fptr->frag_off = htons(offset);
+ if (skb->next != NULL)
+ fptr->frag_off |= htons(IP6_MF);
+ offset += (ntohs(ipv6h->payload_len) -
+ sizeof(struct frag_hdr));
+ }
}
out:
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index eab62a7a8f06..e2325f6a05fb 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -323,7 +323,7 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
int iif = 0;
int addr_type = 0;
int len;
- int hlimit, tclass;
+ int hlimit;
int err = 0;
if ((u8 *)hdr < skb->head ||
@@ -469,10 +469,6 @@ route_done:
if (hlimit < 0)
hlimit = ip6_dst_hoplimit(dst);
- tclass = np->tclass;
- if (tclass < 0)
- tclass = 0;
-
msg.skb = skb;
msg.offset = skb_network_offset(skb);
msg.type = type;
@@ -488,8 +484,8 @@ route_done:
err = ip6_append_data(sk, icmpv6_getfrag, &msg,
len + sizeof(struct icmp6hdr),
- sizeof(struct icmp6hdr),
- hlimit, tclass, NULL, &fl, (struct rt6_info*)dst,
+ sizeof(struct icmp6hdr), hlimit,
+ np->tclass, NULL, &fl, (struct rt6_info*)dst,
MSG_DONTWAIT);
if (err) {
ip6_flush_pending_frames(sk);
@@ -522,7 +518,6 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
struct dst_entry *dst;
int err = 0;
int hlimit;
- int tclass;
saddr = &ipv6_hdr(skb)->daddr;
@@ -562,10 +557,6 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
if (hlimit < 0)
hlimit = ip6_dst_hoplimit(dst);
- tclass = np->tclass;
- if (tclass < 0)
- tclass = 0;
-
idev = in6_dev_get(skb->dev);
msg.skb = skb;
@@ -573,7 +564,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
msg.type = ICMPV6_ECHO_REPLY;
err = ip6_append_data(sk, icmpv6_getfrag, &msg, skb->len + sizeof(struct icmp6hdr),
- sizeof(struct icmp6hdr), hlimit, tclass, NULL, &fl,
+ sizeof(struct icmp6hdr), hlimit, np->tclass, NULL, &fl,
(struct rt6_info*)dst, MSG_DONTWAIT);
if (err) {
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 52ee1dced2ff..0e93ca56eb69 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -164,12 +164,6 @@ static __inline__ void rt6_release(struct rt6_info *rt)
dst_free(&rt->u.dst);
}
-#ifdef CONFIG_IPV6_MULTIPLE_TABLES
-#define FIB_TABLE_HASHSZ 256
-#else
-#define FIB_TABLE_HASHSZ 1
-#endif
-
static void fib6_link_table(struct net *net, struct fib6_table *tb)
{
unsigned int h;
@@ -180,7 +174,7 @@ static void fib6_link_table(struct net *net, struct fib6_table *tb)
*/
rwlock_init(&tb->tb6_lock);
- h = tb->tb6_id & (FIB_TABLE_HASHSZ - 1);
+ h = tb->tb6_id & (FIB6_TABLE_HASHSZ - 1);
/*
* No protection necessary, this is the only list mutatation
@@ -231,7 +225,7 @@ struct fib6_table *fib6_get_table(struct net *net, u32 id)
if (id == 0)
id = RT6_TABLE_MAIN;
- h = id & (FIB_TABLE_HASHSZ - 1);
+ h = id & (FIB6_TABLE_HASHSZ - 1);
rcu_read_lock();
head = &net->ipv6.fib_table_hash[h];
hlist_for_each_entry_rcu(tb, node, head, tb6_hlist) {
@@ -382,7 +376,7 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
arg.net = net;
w->args = &arg;
- for (h = s_h; h < FIB_TABLE_HASHSZ; h++, s_e = 0) {
+ for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_e = 0) {
e = 0;
head = &net->ipv6.fib_table_hash[h];
hlist_for_each_entry(tb, node, head, tb6_hlist) {
@@ -1368,7 +1362,7 @@ void fib6_clean_all(struct net *net, int (*func)(struct rt6_info *, void *arg),
unsigned int h;
rcu_read_lock();
- for (h = 0; h < FIB_TABLE_HASHSZ; h++) {
+ for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
head = &net->ipv6.fib_table_hash[h];
hlist_for_each_entry_rcu(table, node, head, tb6_hlist) {
write_lock_bh(&table->tb6_lock);
@@ -1483,7 +1477,7 @@ static int fib6_net_init(struct net *net)
if (!net->ipv6.rt6_stats)
goto out_timer;
- net->ipv6.fib_table_hash = kcalloc(FIB_TABLE_HASHSZ,
+ net->ipv6.fib_table_hash = kcalloc(FIB6_TABLE_HASHSZ,
sizeof(*net->ipv6.fib_table_hash),
GFP_KERNEL);
if (!net->ipv6.fib_table_hash)
diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c
index 6d6a4277c677..2d9cbaa67edb 100644
--- a/net/ipv6/ip6_input.c
+++ b/net/ipv6/ip6_input.c
@@ -63,7 +63,7 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt
if (skb->pkt_type == PACKET_OTHERHOST) {
kfree_skb(skb);
- return 0;
+ return NET_RX_DROP;
}
rcu_read_lock();
@@ -133,7 +133,7 @@ int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt
if (ipv6_parse_hopopts(skb) < 0) {
IP6_INC_STATS_BH(net, idev, IPSTATS_MIB_INHDRERRORS);
rcu_read_unlock();
- return 0;
+ return NET_RX_DROP;
}
}
@@ -149,7 +149,7 @@ err:
drop:
rcu_read_unlock();
kfree_skb(skb);
- return 0;
+ return NET_RX_DROP;
}
/*
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 87f8419a68fd..cd48801a8d6f 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -57,18 +57,6 @@
static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *));
-static __inline__ void ipv6_select_ident(struct sk_buff *skb, struct frag_hdr *fhdr)
-{
- static u32 ipv6_fragmentation_id = 1;
- static DEFINE_SPINLOCK(ip6_id_lock);
-
- spin_lock_bh(&ip6_id_lock);
- fhdr->identification = htonl(ipv6_fragmentation_id);
- if (++ipv6_fragmentation_id == 0)
- ipv6_fragmentation_id = 1;
- spin_unlock_bh(&ip6_id_lock);
-}
-
int __ip6_local_out(struct sk_buff *skb)
{
int len;
@@ -206,7 +194,8 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
struct ipv6hdr *hdr;
u8 proto = fl->proto;
int seg_len = skb->len;
- int hlimit, tclass;
+ int hlimit = -1;
+ int tclass = 0;
u32 mtu;
if (opt) {
@@ -249,19 +238,13 @@ int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
/*
* Fill in the IPv6 header
*/
-
- hlimit = -1;
- if (np)
+ if (np) {
+ tclass = np->tclass;
hlimit = np->hop_limit;
+ }
if (hlimit < 0)
hlimit = ip6_dst_hoplimit(dst);
- tclass = -1;
- if (np)
- tclass = np->tclass;
- if (tclass < 0)
- tclass = 0;
-
*(__be32 *)hdr = htonl(0x60000000 | (tclass << 20)) | fl->fl6_flowlabel;
hdr->payload_len = htons(seg_len);
@@ -706,7 +689,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
skb_reset_network_header(skb);
memcpy(skb_network_header(skb), tmp_hdr, hlen);
- ipv6_select_ident(skb, fh);
+ ipv6_select_ident(fh);
fh->nexthdr = nexthdr;
fh->reserved = 0;
fh->frag_off = htons(IP6_MF);
@@ -844,7 +827,7 @@ slow_path:
fh->nexthdr = nexthdr;
fh->reserved = 0;
if (!frag_id) {
- ipv6_select_ident(skb, fh);
+ ipv6_select_ident(fh);
frag_id = fh->identification;
} else
fh->identification = frag_id;
@@ -1087,11 +1070,13 @@ static inline int ip6_ufo_append_data(struct sock *sk,
if (!err) {
struct frag_hdr fhdr;
- /* specify the length of each IP datagram fragment*/
- skb_shinfo(skb)->gso_size = mtu - fragheaderlen -
- sizeof(struct frag_hdr);
+ /* Specify the length of each IPv6 datagram fragment.
+ * It has to be a multiple of 8.
+ */
+ skb_shinfo(skb)->gso_size = (mtu - fragheaderlen -
+ sizeof(struct frag_hdr)) & ~7;
skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
- ipv6_select_ident(skb, &fhdr);
+ ipv6_select_ident(&fhdr);
skb_shinfo(skb)->ip6_frag_id = fhdr.identification;
__skb_queue_tail(&sk->sk_write_queue, skb);
@@ -1526,7 +1511,7 @@ int ip6_push_pending_frames(struct sock *sk)
err = ip6_local_out(skb);
if (err) {
if (err > 0)
- err = np->recverr ? net_xmit_errno(err) : 0;
+ err = net_xmit_errno(err);
if (err)
goto error;
}
@@ -1535,6 +1520,7 @@ out:
ip6_cork_release(inet, np);
return err;
error:
+ IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS);
goto out;
}
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 51f410e7775a..7d25bbe32110 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -1036,7 +1036,7 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
return 0;
}
-static int
+static netdev_tx_t
ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct ip6_tnl *t = netdev_priv(dev);
@@ -1063,14 +1063,14 @@ ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
goto tx_err;
t->recursion--;
- return 0;
+ return NETDEV_TX_OK;
tx_err:
stats->tx_errors++;
stats->tx_dropped++;
kfree_skb(skb);
t->recursion--;
- return 0;
+ return NETDEV_TX_OK;
}
static void ip6_tnl_set_cap(struct ip6_tnl *t)
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index c769f155c698..5c8d73730c75 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -204,7 +204,7 @@ static int ip6mr_vif_seq_show(struct seq_file *seq, void *v)
return 0;
}
-static struct seq_operations ip6mr_vif_seq_ops = {
+static const struct seq_operations ip6mr_vif_seq_ops = {
.start = ip6mr_vif_seq_start,
.next = ip6mr_vif_seq_next,
.stop = ip6mr_vif_seq_stop,
@@ -217,7 +217,7 @@ static int ip6mr_vif_open(struct inode *inode, struct file *file)
sizeof(struct ipmr_vif_iter));
}
-static struct file_operations ip6mr_vif_fops = {
+static const struct file_operations ip6mr_vif_fops = {
.owner = THIS_MODULE,
.open = ip6mr_vif_open,
.read = seq_read,
@@ -341,7 +341,7 @@ static int ipmr_mfc_open(struct inode *inode, struct file *file)
sizeof(struct ipmr_mfc_iter));
}
-static struct file_operations ip6mr_mfc_fops = {
+static const struct file_operations ip6mr_mfc_fops = {
.owner = THIS_MODULE,
.open = ipmr_mfc_open,
.read = seq_read,
@@ -416,7 +416,8 @@ static struct inet6_protocol pim6_protocol = {
/* Service routines creating virtual interfaces: PIMREG */
-static int reg_vif_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t reg_vif_xmit(struct sk_buff *skb,
+ struct net_device *dev)
{
struct net *net = dev_net(dev);
@@ -427,7 +428,7 @@ static int reg_vif_xmit(struct sk_buff *skb, struct net_device *dev)
MRT6MSG_WHOLEPKT);
read_unlock(&mrt_lock);
kfree_skb(skb);
- return 0;
+ return NETDEV_TX_OK;
}
static const struct net_device_ops reg_vif_netdev_ops = {
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index a7fdf9a27f15..f5e0682b402d 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -315,6 +315,9 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
goto e_inval;
if (val < -1 || val > 0xff)
goto e_inval;
+ /* RFC 3542, 6.5: default traffic class of 0x0 */
+ if (val == -1)
+ val = 0;
np->tclass = val;
retv = 0;
break;
@@ -1037,8 +1040,6 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
case IPV6_TCLASS:
val = np->tclass;
- if (val < 0)
- val = 0;
break;
case IPV6_RECVTCLASS:
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 4b264ed40a8c..71c3dacec1ed 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -2107,7 +2107,6 @@ static int ip6_mc_add_src(struct inet6_dev *idev, struct in6_addr *pmca,
for (j=0; j<i; j++)
(void) ip6_mc_del1_src(pmc, sfmode, &psfsrc[i]);
} else if (isexclude != (pmc->mca_sfcount[MCAST_EXCLUDE] != 0)) {
- struct inet6_dev *idev = pmc->idev;
struct ip6_sf_list *psf;
/* filter mode change */
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 9eb68e92cc18..7015478797f6 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -98,7 +98,7 @@ static int pndisc_constructor(struct pneigh_entry *n);
static void pndisc_destructor(struct pneigh_entry *n);
static void pndisc_redo(struct sk_buff *skb);
-static struct neigh_ops ndisc_generic_ops = {
+static const struct neigh_ops ndisc_generic_ops = {
.family = AF_INET6,
.solicit = ndisc_solicit,
.error_report = ndisc_error_report,
@@ -108,7 +108,7 @@ static struct neigh_ops ndisc_generic_ops = {
.queue_xmit = dev_queue_xmit,
};
-static struct neigh_ops ndisc_hh_ops = {
+static const struct neigh_ops ndisc_hh_ops = {
.family = AF_INET6,
.solicit = ndisc_solicit,
.error_report = ndisc_error_report,
@@ -119,7 +119,7 @@ static struct neigh_ops ndisc_hh_ops = {
};
-static struct neigh_ops ndisc_direct_ops = {
+static const struct neigh_ops ndisc_direct_ops = {
.family = AF_INET6,
.output = dev_queue_xmit,
.connected_output = dev_queue_xmit,
@@ -955,8 +955,8 @@ static void ndisc_recv_na(struct sk_buff *skb)
*/
if (skb->pkt_type != PACKET_LOOPBACK)
ND_PRINTK1(KERN_WARNING
- "ICMPv6 NA: someone advertises our address on %s!\n",
- ifp->idev->dev->name);
+ "ICMPv6 NA: someone advertises our address %pI6 on %s!\n",
+ &ifp->addr, ifp->idev->dev->name);
in6_ifa_put(ifp);
return;
}
@@ -1151,10 +1151,6 @@ static void ndisc_router_discovery(struct sk_buff *skb)
skb->dev->name);
return;
}
- if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_ra) {
- in6_dev_put(in6_dev);
- return;
- }
if (!ndisc_parse_options(opt, optlen, &ndopts)) {
in6_dev_put(in6_dev);
@@ -1163,6 +1159,10 @@ static void ndisc_router_discovery(struct sk_buff *skb)
return;
}
+ /* skip route and link configuration on routers */
+ if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_ra)
+ goto skip_linkparms;
+
#ifdef CONFIG_IPV6_NDISC_NODETYPE
/* skip link-specific parameters from interior routers */
if (skb->ndisc_nodetype == NDISC_NODETYPE_NODEFAULT)
@@ -1283,9 +1283,7 @@ skip_defrtr:
}
}
-#ifdef CONFIG_IPV6_NDISC_NODETYPE
skip_linkparms:
-#endif
/*
* Process options.
@@ -1312,6 +1310,10 @@ skip_linkparms:
NEIGH_UPDATE_F_ISROUTER);
}
+ /* skip route and link configuration on routers */
+ if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_ra)
+ goto out;
+
#ifdef CONFIG_IPV6_ROUTE_INFO
if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {
struct nd_opt_hdr *p;
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
index ced1f2c0cb65..cc9f8ef303fd 100644
--- a/net/ipv6/netfilter/ip6_tables.c
+++ b/net/ipv6/netfilter/ip6_tables.c
@@ -8,7 +8,7 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
-
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/capability.h>
#include <linux/in.h>
#include <linux/skbuff.h>
@@ -222,16 +222,11 @@ get_entry(void *base, unsigned int offset)
/* All zeroes == unconditional rule. */
/* Mildly perf critical (only if packet tracing is on) */
-static inline int
-unconditional(const struct ip6t_ip6 *ipv6)
+static inline bool unconditional(const struct ip6t_ip6 *ipv6)
{
- unsigned int i;
-
- for (i = 0; i < sizeof(*ipv6); i++)
- if (((char *)ipv6)[i])
- break;
+ static const struct ip6t_ip6 uncond;
- return (i == sizeof(*ipv6));
+ return memcmp(ipv6, &uncond, sizeof(uncond)) == 0;
}
#if defined(CONFIG_NETFILTER_XT_TARGET_TRACE) || \
@@ -745,6 +740,21 @@ find_check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
return ret;
}
+static bool check_underflow(struct ip6t_entry *e)
+{
+ const struct ip6t_entry_target *t;
+ unsigned int verdict;
+
+ if (!unconditional(&e->ipv6))
+ return false;
+ t = ip6t_get_target(e);
+ if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
+ return false;
+ verdict = ((struct ip6t_standard_target *)t)->verdict;
+ verdict = -verdict - 1;
+ return verdict == NF_DROP || verdict == NF_ACCEPT;
+}
+
static int
check_entry_size_and_hooks(struct ip6t_entry *e,
struct xt_table_info *newinfo,
@@ -752,6 +762,7 @@ check_entry_size_and_hooks(struct ip6t_entry *e,
unsigned char *limit,
const unsigned int *hook_entries,
const unsigned int *underflows,
+ unsigned int valid_hooks,
unsigned int *i)
{
unsigned int h;
@@ -771,15 +782,21 @@ check_entry_size_and_hooks(struct ip6t_entry *e,
/* Check hooks & underflows */
for (h = 0; h < NF_INET_NUMHOOKS; h++) {
+ if (!(valid_hooks & (1 << h)))
+ continue;
if ((unsigned char *)e - base == hook_entries[h])
newinfo->hook_entry[h] = hook_entries[h];
- if ((unsigned char *)e - base == underflows[h])
+ if ((unsigned char *)e - base == underflows[h]) {
+ if (!check_underflow(e)) {
+ pr_err("Underflows must be unconditional and "
+ "use the STANDARD target with "
+ "ACCEPT/DROP\n");
+ return -EINVAL;
+ }
newinfo->underflow[h] = underflows[h];
+ }
}
- /* FIXME: underflows must be unconditional, standard verdicts
- < 0 (not IP6T_RETURN). --RR */
-
/* Clear counters and comefrom */
e->counters = ((struct xt_counters) { 0, 0 });
e->comefrom = 0;
@@ -842,7 +859,7 @@ translate_table(const char *name,
newinfo,
entry0,
entry0 + size,
- hook_entries, underflows, &i);
+ hook_entries, underflows, valid_hooks, &i);
if (ret != 0)
return ret;
@@ -2083,7 +2100,8 @@ do_ip6t_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
return ret;
}
-struct xt_table *ip6t_register_table(struct net *net, struct xt_table *table,
+struct xt_table *ip6t_register_table(struct net *net,
+ const struct xt_table *table,
const struct ip6t_replace *repl)
{
int ret;
diff --git a/net/ipv6/netfilter/ip6t_eui64.c b/net/ipv6/netfilter/ip6t_eui64.c
index db610bacbcce..ca287f6d2bce 100644
--- a/net/ipv6/netfilter/ip6t_eui64.c
+++ b/net/ipv6/netfilter/ip6t_eui64.c
@@ -23,7 +23,6 @@ static bool
eui64_mt6(const struct sk_buff *skb, const struct xt_match_param *par)
{
unsigned char eui64[8];
- int i = 0;
if (!(skb_mac_header(skb) >= skb->head &&
skb_mac_header(skb) + ETH_HLEN <= skb->data) &&
@@ -42,12 +41,8 @@ eui64_mt6(const struct sk_buff *skb, const struct xt_match_param *par)
eui64[4] = 0xfe;
eui64[0] ^= 0x02;
- i = 0;
- while (ipv6_hdr(skb)->saddr.s6_addr[8 + i] == eui64[i]
- && i < 8)
- i++;
-
- if (i == 8)
+ if (!memcmp(ipv6_hdr(skb)->saddr.s6_addr + 8, eui64,
+ sizeof(eui64)))
return true;
}
}
diff --git a/net/ipv6/netfilter/ip6table_filter.c b/net/ipv6/netfilter/ip6table_filter.c
index ef5a0a32bf8e..6f4383ad86f9 100644
--- a/net/ipv6/netfilter/ip6table_filter.c
+++ b/net/ipv6/netfilter/ip6table_filter.c
@@ -51,11 +51,11 @@ static struct
.term = IP6T_ERROR_INIT, /* ERROR */
};
-static struct xt_table packet_filter = {
+static const struct xt_table packet_filter = {
.name = "filter",
.valid_hooks = FILTER_VALID_HOOKS,
.me = THIS_MODULE,
- .af = AF_INET6,
+ .af = NFPROTO_IPV6,
};
/* The work comes in here from netfilter.c. */
@@ -95,21 +95,21 @@ static struct nf_hook_ops ip6t_ops[] __read_mostly = {
{
.hook = ip6t_in_hook,
.owner = THIS_MODULE,
- .pf = PF_INET6,
+ .pf = NFPROTO_IPV6,
.hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP6_PRI_FILTER,
},
{
.hook = ip6t_in_hook,
.owner = THIS_MODULE,
- .pf = PF_INET6,
+ .pf = NFPROTO_IPV6,
.hooknum = NF_INET_FORWARD,
.priority = NF_IP6_PRI_FILTER,
},
{
.hook = ip6t_local_out_hook,
.owner = THIS_MODULE,
- .pf = PF_INET6,
+ .pf = NFPROTO_IPV6,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP6_PRI_FILTER,
},
diff --git a/net/ipv6/netfilter/ip6table_mangle.c b/net/ipv6/netfilter/ip6table_mangle.c
index ab0d398a2ba7..0ad91433ed61 100644
--- a/net/ipv6/netfilter/ip6table_mangle.c
+++ b/net/ipv6/netfilter/ip6table_mangle.c
@@ -21,7 +21,7 @@ MODULE_DESCRIPTION("ip6tables mangle table");
(1 << NF_INET_LOCAL_OUT) | \
(1 << NF_INET_POST_ROUTING))
-static struct
+static const struct
{
struct ip6t_replace repl;
struct ip6t_standard entries[5];
@@ -57,11 +57,11 @@ static struct
.term = IP6T_ERROR_INIT, /* ERROR */
};
-static struct xt_table packet_mangler = {
+static const struct xt_table packet_mangler = {
.name = "mangle",
.valid_hooks = MANGLE_VALID_HOOKS,
.me = THIS_MODULE,
- .af = AF_INET6,
+ .af = NFPROTO_IPV6,
};
/* The work comes in here from netfilter.c. */
@@ -136,35 +136,35 @@ static struct nf_hook_ops ip6t_ops[] __read_mostly = {
{
.hook = ip6t_in_hook,
.owner = THIS_MODULE,
- .pf = PF_INET6,
+ .pf = NFPROTO_IPV6,
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP6_PRI_MANGLE,
},
{
.hook = ip6t_in_hook,
.owner = THIS_MODULE,
- .pf = PF_INET6,
+ .pf = NFPROTO_IPV6,
.hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP6_PRI_MANGLE,
},
{
.hook = ip6t_in_hook,
.owner = THIS_MODULE,
- .pf = PF_INET6,
+ .pf = NFPROTO_IPV6,
.hooknum = NF_INET_FORWARD,
.priority = NF_IP6_PRI_MANGLE,
},
{
.hook = ip6t_local_out_hook,
.owner = THIS_MODULE,
- .pf = PF_INET6,
+ .pf = NFPROTO_IPV6,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP6_PRI_MANGLE,
},
{
.hook = ip6t_post_routing_hook,
.owner = THIS_MODULE,
- .pf = PF_INET6,
+ .pf = NFPROTO_IPV6,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP6_PRI_MANGLE,
},
diff --git a/net/ipv6/netfilter/ip6table_raw.c b/net/ipv6/netfilter/ip6table_raw.c
index 4b792b6ca321..ed1a1180f3b3 100644
--- a/net/ipv6/netfilter/ip6table_raw.c
+++ b/net/ipv6/netfilter/ip6table_raw.c
@@ -8,7 +8,7 @@
#define RAW_VALID_HOOKS ((1 << NF_INET_PRE_ROUTING) | (1 << NF_INET_LOCAL_OUT))
-static struct
+static const struct
{
struct ip6t_replace repl;
struct ip6t_standard entries[2];
@@ -35,11 +35,11 @@ static struct
.term = IP6T_ERROR_INIT, /* ERROR */
};
-static struct xt_table packet_raw = {
+static const struct xt_table packet_raw = {
.name = "raw",
.valid_hooks = RAW_VALID_HOOKS,
.me = THIS_MODULE,
- .af = AF_INET6,
+ .af = NFPROTO_IPV6,
};
/* The work comes in here from netfilter.c. */
@@ -68,14 +68,14 @@ ip6t_local_out_hook(unsigned int hook,
static struct nf_hook_ops ip6t_ops[] __read_mostly = {
{
.hook = ip6t_pre_routing_hook,
- .pf = PF_INET6,
+ .pf = NFPROTO_IPV6,
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP6_PRI_FIRST,
.owner = THIS_MODULE,
},
{
.hook = ip6t_local_out_hook,
- .pf = PF_INET6,
+ .pf = NFPROTO_IPV6,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP6_PRI_FIRST,
.owner = THIS_MODULE,
diff --git a/net/ipv6/netfilter/ip6table_security.c b/net/ipv6/netfilter/ip6table_security.c
index 0ea37ff15d56..41b444c60934 100644
--- a/net/ipv6/netfilter/ip6table_security.c
+++ b/net/ipv6/netfilter/ip6table_security.c
@@ -26,7 +26,7 @@ MODULE_DESCRIPTION("ip6tables security table, for MAC rules");
(1 << NF_INET_FORWARD) | \
(1 << NF_INET_LOCAL_OUT)
-static struct
+static const struct
{
struct ip6t_replace repl;
struct ip6t_standard entries[3];
@@ -56,11 +56,11 @@ static struct
.term = IP6T_ERROR_INIT, /* ERROR */
};
-static struct xt_table security_table = {
+static const struct xt_table security_table = {
.name = "security",
.valid_hooks = SECURITY_VALID_HOOKS,
.me = THIS_MODULE,
- .af = AF_INET6,
+ .af = NFPROTO_IPV6,
};
static unsigned int
@@ -101,21 +101,21 @@ static struct nf_hook_ops ip6t_ops[] __read_mostly = {
{
.hook = ip6t_local_in_hook,
.owner = THIS_MODULE,
- .pf = PF_INET6,
+ .pf = NFPROTO_IPV6,
.hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP6_PRI_SECURITY,
},
{
.hook = ip6t_forward_hook,
.owner = THIS_MODULE,
- .pf = PF_INET6,
+ .pf = NFPROTO_IPV6,
.hooknum = NF_INET_FORWARD,
.priority = NF_IP6_PRI_SECURITY,
},
{
.hook = ip6t_local_out_hook,
.owner = THIS_MODULE,
- .pf = PF_INET6,
+ .pf = NFPROTO_IPV6,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP6_PRI_SECURITY,
},
diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
index 2a15c2d66c69..5f2ec208a8c3 100644
--- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
+++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
@@ -27,6 +27,7 @@
#include <net/netfilter/nf_conntrack_l3proto.h>
#include <net/netfilter/nf_conntrack_core.h>
#include <net/netfilter/ipv6/nf_conntrack_ipv6.h>
+#include <net/netfilter/nf_log.h>
static bool ipv6_pkt_to_tuple(const struct sk_buff *skb, unsigned int nhoff,
struct nf_conntrack_tuple *tuple)
@@ -176,8 +177,11 @@ static unsigned int ipv6_confirm(unsigned int hooknum,
}
ret = helper->help(skb, protoff, ct, ctinfo);
- if (ret != NF_ACCEPT)
+ if (ret != NF_ACCEPT) {
+ nf_log_packet(NFPROTO_IPV6, hooknum, skb, in, out, NULL,
+ "nf_ct_%s: dropping packet", helper->name);
return ret;
+ }
out:
/* We've seen it coming out the other side: confirm it */
return nf_conntrack_confirm(skb);
@@ -265,42 +269,42 @@ static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = {
{
.hook = ipv6_defrag,
.owner = THIS_MODULE,
- .pf = PF_INET6,
+ .pf = NFPROTO_IPV6,
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP6_PRI_CONNTRACK_DEFRAG,
},
{
.hook = ipv6_conntrack_in,
.owner = THIS_MODULE,
- .pf = PF_INET6,
+ .pf = NFPROTO_IPV6,
.hooknum = NF_INET_PRE_ROUTING,
.priority = NF_IP6_PRI_CONNTRACK,
},
{
.hook = ipv6_conntrack_local,
.owner = THIS_MODULE,
- .pf = PF_INET6,
+ .pf = NFPROTO_IPV6,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP6_PRI_CONNTRACK,
},
{
.hook = ipv6_defrag,
.owner = THIS_MODULE,
- .pf = PF_INET6,
+ .pf = NFPROTO_IPV6,
.hooknum = NF_INET_LOCAL_OUT,
.priority = NF_IP6_PRI_CONNTRACK_DEFRAG,
},
{
.hook = ipv6_confirm,
.owner = THIS_MODULE,
- .pf = PF_INET6,
+ .pf = NFPROTO_IPV6,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP6_PRI_LAST,
},
{
.hook = ipv6_confirm,
.owner = THIS_MODULE,
- .pf = PF_INET6,
+ .pf = NFPROTO_IPV6,
.hooknum = NF_INET_LOCAL_IN,
.priority = NF_IP6_PRI_LAST-1,
},
diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c
index 590ddefb7ffc..c9605c3ad91f 100644
--- a/net/ipv6/proc.c
+++ b/net/ipv6/proc.c
@@ -101,7 +101,7 @@ static struct snmp_mib snmp6_icmp6_list[] = {
};
/* RFC 4293 v6 ICMPMsgStatsTable; named items for RFC 2466 compatibility */
-static char *icmp6type2name[256] = {
+static const char *const icmp6type2name[256] = {
[ICMPV6_DEST_UNREACH] = "DestUnreachs",
[ICMPV6_PKT_TOOBIG] = "PktTooBigs",
[ICMPV6_TIME_EXCEED] = "TimeExcds",
@@ -144,7 +144,7 @@ static void snmp6_seq_show_icmpv6msg(struct seq_file *seq, void **mib)
/* print by name -- deprecated items */
for (i = 0; i < ICMP6MSG_MIB_MAX; i++) {
int icmptype;
- char *p;
+ const char *p;
icmptype = i & 0xff;
p = icmp6type2name[icmptype];
diff --git a/net/ipv6/protocol.c b/net/ipv6/protocol.c
index 9ab789159913..568864f722ca 100644
--- a/net/ipv6/protocol.c
+++ b/net/ipv6/protocol.c
@@ -20,20 +20,9 @@
* - Removed unused variable 'inet6_protocol_base'
* - Modified inet6_del_protocol() to correctly maintain copy bit.
*/
-
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <linux/in6.h>
+#include <linux/module.h>
#include <linux/netdevice.h>
-#include <linux/if_arp.h>
-
-#include <net/sock.h>
-#include <net/snmp.h>
-
-#include <net/ipv6.h>
+#include <linux/spinlock.h>
#include <net/protocol.h>
struct inet6_protocol *inet6_protos[MAX_INET_PROTOS];
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index d6c3c1c34b2d..7d675b8d82d3 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -642,7 +642,7 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,
err = NF_HOOK(PF_INET6, NF_INET_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
dst_output);
if (err > 0)
- err = np->recverr ? net_xmit_errno(err) : 0;
+ err = net_xmit_errno(err);
if (err)
goto error;
out:
@@ -653,6 +653,8 @@ error_fault:
kfree_skb(skb);
error:
IP6_INC_STATS(sock_net(sk), rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS);
+ if (err == -ENOBUFS && !np->recverr)
+ err = 0;
return err;
}
@@ -877,11 +879,8 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
hlimit = ip6_dst_hoplimit(dst);
}
- if (tclass < 0) {
+ if (tclass < 0)
tclass = np->tclass;
- if (tclass < 0)
- tclass = 0;
- }
if (msg->msg_flags&MSG_CONFIRM)
goto do_confirm;
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 1473ee0a1f51..9ccfef345560 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -665,7 +665,7 @@ static struct rt6_info *rt6_alloc_cow(struct rt6_info *ort, struct in6_addr *dad
net->ipv6.sysctl.ip6_rt_gc_elasticity = 1;
net->ipv6.sysctl.ip6_rt_gc_min_interval = 0;
- ip6_dst_gc(net->ipv6.ip6_dst_ops);
+ ip6_dst_gc(&net->ipv6.ip6_dst_ops);
net->ipv6.sysctl.ip6_rt_gc_elasticity =
saved_rt_elasticity;
@@ -970,7 +970,7 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev,
if (unlikely(idev == NULL))
return NULL;
- rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops);
+ rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops);
if (unlikely(rt == NULL)) {
in6_dev_put(idev);
goto out;
@@ -1060,7 +1060,7 @@ static void icmp6_clean_all(int (*func)(struct rt6_info *rt, void *arg),
static int ip6_dst_gc(struct dst_ops *ops)
{
unsigned long now = jiffies;
- struct net *net = ops->dst_net;
+ struct net *net = container_of(ops, struct net, ipv6.ip6_dst_ops);
int rt_min_interval = net->ipv6.sysctl.ip6_rt_gc_min_interval;
int rt_max_size = net->ipv6.sysctl.ip6_rt_max_size;
int rt_elasticity = net->ipv6.sysctl.ip6_rt_gc_elasticity;
@@ -1154,7 +1154,7 @@ int ip6_route_add(struct fib6_config *cfg)
goto out;
}
- rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops);
+ rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops);
if (rt == NULL) {
err = -ENOMEM;
@@ -1643,7 +1643,7 @@ out:
static struct rt6_info * ip6_rt_copy(struct rt6_info *ort)
{
struct net *net = dev_net(ort->rt6i_dev);
- struct rt6_info *rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops);
+ struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops);
if (rt) {
rt->u.dst.input = ort->u.dst.input;
@@ -1923,7 +1923,7 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
int anycast)
{
struct net *net = dev_net(idev->dev);
- struct rt6_info *rt = ip6_dst_alloc(net->ipv6.ip6_dst_ops);
+ struct rt6_info *rt = ip6_dst_alloc(&net->ipv6.ip6_dst_ops);
struct neighbour *neigh;
if (rt == NULL)
@@ -2501,7 +2501,7 @@ static int rt6_stats_seq_show(struct seq_file *seq, void *v)
net->ipv6.rt6_stats->fib_rt_alloc,
net->ipv6.rt6_stats->fib_rt_entries,
net->ipv6.rt6_stats->fib_rt_cache,
- atomic_read(&net->ipv6.ip6_dst_ops->entries),
+ atomic_read(&net->ipv6.ip6_dst_ops.entries),
net->ipv6.rt6_stats->fib_discarded_routes);
return 0;
@@ -2637,7 +2637,7 @@ struct ctl_table *ipv6_route_sysctl_init(struct net *net)
if (table) {
table[0].data = &net->ipv6.sysctl.flush_delay;
- table[1].data = &net->ipv6.ip6_dst_ops->gc_thresh;
+ table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh;
table[2].data = &net->ipv6.sysctl.ip6_rt_max_size;
table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval;
table[4].data = &net->ipv6.sysctl.ip6_rt_gc_timeout;
@@ -2655,12 +2655,8 @@ static int ip6_route_net_init(struct net *net)
{
int ret = -ENOMEM;
- net->ipv6.ip6_dst_ops = kmemdup(&ip6_dst_ops_template,
- sizeof(*net->ipv6.ip6_dst_ops),
- GFP_KERNEL);
- if (!net->ipv6.ip6_dst_ops)
- goto out;
- net->ipv6.ip6_dst_ops->dst_net = hold_net(net);
+ memcpy(&net->ipv6.ip6_dst_ops, &ip6_dst_ops_template,
+ sizeof(net->ipv6.ip6_dst_ops));
net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
sizeof(*net->ipv6.ip6_null_entry),
@@ -2669,7 +2665,7 @@ static int ip6_route_net_init(struct net *net)
goto out_ip6_dst_ops;
net->ipv6.ip6_null_entry->u.dst.path =
(struct dst_entry *)net->ipv6.ip6_null_entry;
- net->ipv6.ip6_null_entry->u.dst.ops = net->ipv6.ip6_dst_ops;
+ net->ipv6.ip6_null_entry->u.dst.ops = &net->ipv6.ip6_dst_ops;
#ifdef CONFIG_IPV6_MULTIPLE_TABLES
net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template,
@@ -2679,7 +2675,7 @@ static int ip6_route_net_init(struct net *net)
goto out_ip6_null_entry;
net->ipv6.ip6_prohibit_entry->u.dst.path =
(struct dst_entry *)net->ipv6.ip6_prohibit_entry;
- net->ipv6.ip6_prohibit_entry->u.dst.ops = net->ipv6.ip6_dst_ops;
+ net->ipv6.ip6_prohibit_entry->u.dst.ops = &net->ipv6.ip6_dst_ops;
net->ipv6.ip6_blk_hole_entry = kmemdup(&ip6_blk_hole_entry_template,
sizeof(*net->ipv6.ip6_blk_hole_entry),
@@ -2688,7 +2684,7 @@ static int ip6_route_net_init(struct net *net)
goto out_ip6_prohibit_entry;
net->ipv6.ip6_blk_hole_entry->u.dst.path =
(struct dst_entry *)net->ipv6.ip6_blk_hole_entry;
- net->ipv6.ip6_blk_hole_entry->u.dst.ops = net->ipv6.ip6_dst_ops;
+ net->ipv6.ip6_blk_hole_entry->u.dst.ops = &net->ipv6.ip6_dst_ops;
#endif
net->ipv6.sysctl.flush_delay = 0;
@@ -2717,8 +2713,6 @@ out_ip6_null_entry:
kfree(net->ipv6.ip6_null_entry);
#endif
out_ip6_dst_ops:
- release_net(net->ipv6.ip6_dst_ops->dst_net);
- kfree(net->ipv6.ip6_dst_ops);
goto out;
}
@@ -2733,8 +2727,6 @@ static void ip6_route_net_exit(struct net *net)
kfree(net->ipv6.ip6_prohibit_entry);
kfree(net->ipv6.ip6_blk_hole_entry);
#endif
- release_net(net->ipv6.ip6_dst_ops->dst_net);
- kfree(net->ipv6.ip6_dst_ops);
}
static struct pernet_operations ip6_route_net_ops = {
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index 98b7327d0949..0ae4f6448187 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -609,7 +609,8 @@ static inline __be32 try_6to4(struct in6_addr *v6dst)
* and that skb is filled properly by that function.
*/
-static int ipip6_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb,
+ struct net_device *dev)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
struct net_device_stats *stats = &tunnel->dev->stats;
@@ -753,7 +754,7 @@ static int ipip6_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
stats->tx_dropped++;
dev_kfree_skb(skb);
tunnel->recursion--;
- return 0;
+ return NETDEV_TX_OK;
}
if (skb->sk)
skb_set_owner_w(new_skb, skb->sk);
@@ -778,7 +779,7 @@ static int ipip6_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
iph->version = 4;
iph->ihl = sizeof(struct iphdr)>>2;
if (mtu > IPV6_MIN_MTU)
- iph->frag_off = htons(IP_DF);
+ iph->frag_off = tiph->frag_off;
else
iph->frag_off = 0;
@@ -794,7 +795,7 @@ static int ipip6_tunnel_xmit(struct sk_buff *skb, struct net_device *dev)
IPTUNNEL_XMIT();
tunnel->recursion--;
- return 0;
+ return NETDEV_TX_OK;
tx_error_icmp:
dst_link_failure(skb);
@@ -802,7 +803,7 @@ tx_error:
stats->tx_errors++;
dev_kfree_skb(skb);
tunnel->recursion--;
- return 0;
+ return NETDEV_TX_OK;
}
static void ipip6_tunnel_bind_dev(struct net_device *dev)
diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c
index a031034720b4..0dc6a4e5ed4a 100644
--- a/net/ipv6/sysctl_net_ipv6.c
+++ b/net/ipv6/sysctl_net_ipv6.c
@@ -40,7 +40,7 @@ static ctl_table ipv6_table_template[] = {
{ .ctl_name = 0 }
};
-static ctl_table ipv6_table[] = {
+static ctl_table ipv6_rotable[] = {
{
.ctl_name = NET_IPV6_MLD_MAX_MSF,
.procname = "mld_max_msf",
@@ -130,7 +130,7 @@ int ipv6_sysctl_register(void)
{
int err = -ENOMEM;
- ip6_header = register_net_sysctl_rotable(net_ipv6_ctl_path, ipv6_table);
+ ip6_header = register_net_sysctl_rotable(net_ipv6_ctl_path, ipv6_rotable);
if (ip6_header == NULL)
goto out;
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index d849dd53b788..3aae0f217d61 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -75,11 +75,11 @@ static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb);
-static struct inet_connection_sock_af_ops ipv6_mapped;
-static struct inet_connection_sock_af_ops ipv6_specific;
+static const struct inet_connection_sock_af_ops ipv6_mapped;
+static const struct inet_connection_sock_af_ops ipv6_specific;
#ifdef CONFIG_TCP_MD5SIG
-static struct tcp_sock_af_ops tcp_sock_ipv6_specific;
-static struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific;
+static const struct tcp_sock_af_ops tcp_sock_ipv6_specific;
+static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific;
#else
static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(struct sock *sk,
struct in6_addr *addr)
@@ -591,7 +591,7 @@ static int tcp_v6_md5_do_add(struct sock *sk, struct in6_addr *peer,
}
sk->sk_route_caps &= ~NETIF_F_GSO_MASK;
}
- if (tcp_alloc_md5sig_pool() == NULL) {
+ if (tcp_alloc_md5sig_pool(sk) == NULL) {
kfree(newkey);
return -ENOMEM;
}
@@ -894,7 +894,7 @@ struct request_sock_ops tcp6_request_sock_ops __read_mostly = {
};
#ifdef CONFIG_TCP_MD5SIG
-static struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = {
+static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = {
.md5_lookup = tcp_v6_reqsk_md5_lookup,
.calc_md5_hash = tcp_v6_md5_hash_skb,
};
@@ -1003,6 +1003,7 @@ static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win,
skb_reserve(buff, MAX_HEADER + sizeof(struct ipv6hdr) + tot_len);
t1 = (struct tcphdr *) skb_push(buff, tot_len);
+ skb_reset_transport_header(skb);
/* Swap the send and the receive. */
memset(t1, 0, sizeof(*t1));
@@ -1760,7 +1761,7 @@ static int tcp_v6_remember_stamp(struct sock *sk)
return 0;
}
-static struct inet_connection_sock_af_ops ipv6_specific = {
+static const struct inet_connection_sock_af_ops ipv6_specific = {
.queue_xmit = inet6_csk_xmit,
.send_check = tcp_v6_send_check,
.rebuild_header = inet6_sk_rebuild_header,
@@ -1780,7 +1781,7 @@ static struct inet_connection_sock_af_ops ipv6_specific = {
};
#ifdef CONFIG_TCP_MD5SIG
-static struct tcp_sock_af_ops tcp_sock_ipv6_specific = {
+static const struct tcp_sock_af_ops tcp_sock_ipv6_specific = {
.md5_lookup = tcp_v6_md5_lookup,
.calc_md5_hash = tcp_v6_md5_hash_skb,
.md5_add = tcp_v6_md5_add_func,
@@ -1792,7 +1793,7 @@ static struct tcp_sock_af_ops tcp_sock_ipv6_specific = {
* TCP over IPv4 via INET6 API
*/
-static struct inet_connection_sock_af_ops ipv6_mapped = {
+static const struct inet_connection_sock_af_ops ipv6_mapped = {
.queue_xmit = ip_queue_xmit,
.send_check = tcp_v4_send_check,
.rebuild_header = inet_sk_rebuild_header,
@@ -1812,7 +1813,7 @@ static struct inet_connection_sock_af_ops ipv6_mapped = {
};
#ifdef CONFIG_TCP_MD5SIG
-static struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific = {
+static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific = {
.md5_lookup = tcp_v4_md5_lookup,
.calc_md5_hash = tcp_v4_md5_hash_skb,
.md5_add = tcp_v6_md5_add_func,
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 33b59bd92c4d..164040613c2e 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -638,6 +638,47 @@ static void udp_v6_flush_pending_frames(struct sock *sk)
}
}
+/**
+ * udp6_hwcsum_outgoing - handle outgoing HW checksumming
+ * @sk: socket we are sending on
+ * @skb: sk_buff containing the filled-in UDP header
+ * (checksum field must be zeroed out)
+ */
+static void udp6_hwcsum_outgoing(struct sock *sk, struct sk_buff *skb,
+ const struct in6_addr *saddr,
+ const struct in6_addr *daddr, int len)
+{
+ unsigned int offset;
+ struct udphdr *uh = udp_hdr(skb);
+ __wsum csum = 0;
+
+ if (skb_queue_len(&sk->sk_write_queue) == 1) {
+ /* Only one fragment on the socket. */
+ skb->csum_start = skb_transport_header(skb) - skb->head;
+ skb->csum_offset = offsetof(struct udphdr, check);
+ uh->check = ~csum_ipv6_magic(saddr, daddr, len, IPPROTO_UDP, 0);
+ } else {
+ /*
+ * HW-checksum won't work as there are two or more
+ * fragments on the socket so that all csums of sk_buffs
+ * should be together
+ */
+ offset = skb_transport_offset(skb);
+ skb->csum = skb_checksum(skb, offset, skb->len - offset, 0);
+
+ skb->ip_summed = CHECKSUM_NONE;
+
+ skb_queue_walk(&sk->sk_write_queue, skb) {
+ csum = csum_add(csum, skb->csum);
+ }
+
+ uh->check = csum_ipv6_magic(saddr, daddr, len, IPPROTO_UDP,
+ csum);
+ if (uh->check == 0)
+ uh->check = CSUM_MANGLED_0;
+ }
+}
+
/*
* Sending
*/
@@ -668,7 +709,11 @@ static int udp_v6_push_pending_frames(struct sock *sk)
if (is_udplite)
csum = udplite_csum_outgoing(sk, skb);
- else
+ else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */
+ udp6_hwcsum_outgoing(sk, skb, &fl->fl6_src, &fl->fl6_dst,
+ up->len);
+ goto send;
+ } else
csum = udp_csum_outgoing(sk, skb);
/* add protocol-dependent pseudo-header */
@@ -677,13 +722,20 @@ static int udp_v6_push_pending_frames(struct sock *sk)
if (uh->check == 0)
uh->check = CSUM_MANGLED_0;
+send:
err = ip6_push_pending_frames(sk);
+ if (err) {
+ if (err == -ENOBUFS && !inet6_sk(sk)->recverr) {
+ UDP6_INC_STATS_USER(sock_net(sk),
+ UDP_MIB_SNDBUFERRORS, is_udplite);
+ err = 0;
+ }
+ } else
+ UDP6_INC_STATS_USER(sock_net(sk),
+ UDP_MIB_OUTDATAGRAMS, is_udplite);
out:
up->len = 0;
up->pending = 0;
- if (!err)
- UDP6_INC_STATS_USER(sock_net(sk),
- UDP_MIB_OUTDATAGRAMS, is_udplite);
return err;
}
@@ -900,11 +952,8 @@ do_udp_sendmsg:
hlimit = ip6_dst_hoplimit(dst);
}
- if (tclass < 0) {
+ if (tclass < 0)
tclass = np->tclass;
- if (tclass < 0)
- tclass = 0;
- }
if (msg->msg_flags&MSG_CONFIRM)
goto do_confirm;
@@ -1032,9 +1081,102 @@ int compat_udpv6_getsockopt(struct sock *sk, int level, int optname,
}
#endif
+static int udp6_ufo_send_check(struct sk_buff *skb)
+{
+ struct ipv6hdr *ipv6h;
+ struct udphdr *uh;
+
+ if (!pskb_may_pull(skb, sizeof(*uh)))
+ return -EINVAL;
+
+ ipv6h = ipv6_hdr(skb);
+ uh = udp_hdr(skb);
+
+ uh->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len,
+ IPPROTO_UDP, 0);
+ skb->csum_start = skb_transport_header(skb) - skb->head;
+ skb->csum_offset = offsetof(struct udphdr, check);
+ skb->ip_summed = CHECKSUM_PARTIAL;
+ return 0;
+}
+
+static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, int features)
+{
+ struct sk_buff *segs = ERR_PTR(-EINVAL);
+ unsigned int mss;
+ unsigned int unfrag_ip6hlen, unfrag_len;
+ struct frag_hdr *fptr;
+ u8 *mac_start, *prevhdr;
+ u8 nexthdr;
+ u8 frag_hdr_sz = sizeof(struct frag_hdr);
+ int offset;
+ __wsum csum;
+
+ mss = skb_shinfo(skb)->gso_size;
+ if (unlikely(skb->len <= mss))
+ goto out;
+
+ if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) {
+ /* Packet is from an untrusted source, reset gso_segs. */
+ int type = skb_shinfo(skb)->gso_type;
+
+ if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY) ||
+ !(type & (SKB_GSO_UDP))))
+ goto out;
+
+ skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss);
+
+ segs = NULL;
+ goto out;
+ }
+
+ /* Do software UFO. Complete and fill in the UDP checksum as HW cannot
+ * do checksum of UDP packets sent as multiple IP fragments.
+ */
+ offset = skb->csum_start - skb_headroom(skb);
+ csum = skb_checksum(skb, offset, skb->len- offset, 0);
+ offset += skb->csum_offset;
+ *(__sum16 *)(skb->data + offset) = csum_fold(csum);
+ skb->ip_summed = CHECKSUM_NONE;
+
+ /* Check if there is enough headroom to insert fragment header. */
+ if ((skb_headroom(skb) < frag_hdr_sz) &&
+ pskb_expand_head(skb, frag_hdr_sz, 0, GFP_ATOMIC))
+ goto out;
+
+ /* Find the unfragmentable header and shift it left by frag_hdr_sz
+ * bytes to insert fragment header.
+ */
+ unfrag_ip6hlen = ip6_find_1stfragopt(skb, &prevhdr);
+ nexthdr = *prevhdr;
+ *prevhdr = NEXTHDR_FRAGMENT;
+ unfrag_len = skb_network_header(skb) - skb_mac_header(skb) +
+ unfrag_ip6hlen;
+ mac_start = skb_mac_header(skb);
+ memmove(mac_start-frag_hdr_sz, mac_start, unfrag_len);
+
+ skb->mac_header -= frag_hdr_sz;
+ skb->network_header -= frag_hdr_sz;
+
+ fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen);
+ fptr->nexthdr = nexthdr;
+ fptr->reserved = 0;
+ ipv6_select_ident(fptr);
+
+ /* Fragment the skb. ipv6 header and the remaining fields of the
+ * fragment header are updated in ipv6_gso_segment()
+ */
+ segs = skb_segment(skb, features);
+
+out:
+ return segs;
+}
+
static struct inet6_protocol udpv6_protocol = {
.handler = udpv6_rcv,
.err_handler = udpv6_err,
+ .gso_send_check = udp6_ufo_send_check,
+ .gso_segment = udp6_ufo_fragment,
.flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL,
};
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index 3a3c677bc0f2..8ec3d45cd1d9 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c
@@ -306,9 +306,26 @@ static void xfrm6_policy_fini(void)
xfrm_policy_unregister_afinfo(&xfrm6_policy_afinfo);
}
+#ifdef CONFIG_SYSCTL
+static struct ctl_table xfrm6_policy_table[] = {
+ {
+ .ctl_name = CTL_UNNUMBERED,
+ .procname = "xfrm6_gc_thresh",
+ .data = &xfrm6_dst_ops.gc_thresh,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
+ { }
+};
+
+static struct ctl_table_header *sysctl_hdr;
+#endif
+
int __init xfrm6_init(void)
{
int ret;
+ unsigned int gc_thresh;
ret = xfrm6_policy_init();
if (ret)
@@ -317,6 +334,23 @@ int __init xfrm6_init(void)
ret = xfrm6_state_init();
if (ret)
goto out_policy;
+ /*
+ * We need a good default value for the xfrm6 gc threshold.
+ * In ipv4 we set it to the route hash table size * 8, which
+ * is half the size of the maximaum route cache for ipv4. It
+ * would be good to do the same thing for v6, except the table is
+ * constructed differently here. Here each table for a net namespace
+ * can have FIB_TABLE_HASHSZ entries, so lets go with the same
+ * computation that we used for ipv4 here. Also, lets keep the initial
+ * gc_thresh to a minimum of 1024, since, the ipv6 route cache defaults
+ * to that as a minimum as well
+ */
+ gc_thresh = FIB6_TABLE_HASHSZ * 8;
+ xfrm6_dst_ops.gc_thresh = (gc_thresh < 1024) ? 1024 : gc_thresh;
+#ifdef CONFIG_SYSCTL
+ sysctl_hdr = register_net_sysctl_table(&init_net, net_ipv6_ctl_path,
+ xfrm6_policy_table);
+#endif
out:
return ret;
out_policy:
@@ -326,6 +360,10 @@ out_policy:
void xfrm6_fini(void)
{
+#ifdef CONFIG_SYSCTL
+ if (sysctl_hdr)
+ unregister_net_sysctl_table(sysctl_hdr);
+#endif
//xfrm6_input_fini();
xfrm6_policy_fini();
xfrm6_state_fini();
diff --git a/net/irda/ircomm/ircomm_event.c b/net/irda/ircomm/ircomm_event.c
index c35b3ef5c2f0..d78554fedbac 100644
--- a/net/irda/ircomm/ircomm_event.c
+++ b/net/irda/ircomm/ircomm_event.c
@@ -49,7 +49,7 @@ static int ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event,
static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event,
struct sk_buff *skb, struct ircomm_info *info);
-char *ircomm_state[] = {
+const char *const ircomm_state[] = {
"IRCOMM_IDLE",
"IRCOMM_WAITI",
"IRCOMM_WAITR",
@@ -57,7 +57,7 @@ char *ircomm_state[] = {
};
#ifdef CONFIG_IRDA_DEBUG
-static char *ircomm_event[] = {
+static const char *const ircomm_event[] = {
"IRCOMM_CONNECT_REQUEST",
"IRCOMM_CONNECT_RESPONSE",
"IRCOMM_TTP_CONNECT_INDICATION",
diff --git a/net/irda/ircomm/ircomm_tty_attach.c b/net/irda/ircomm/ircomm_tty_attach.c
index 9032a1d1190d..eafc010907c2 100644
--- a/net/irda/ircomm/ircomm_tty_attach.c
+++ b/net/irda/ircomm/ircomm_tty_attach.c
@@ -80,7 +80,7 @@ static int ircomm_tty_state_ready(struct ircomm_tty_cb *self,
struct sk_buff *skb,
struct ircomm_tty_info *info);
-char *ircomm_tty_state[] = {
+const char *const ircomm_tty_state[] = {
"IRCOMM_TTY_IDLE",
"IRCOMM_TTY_SEARCH",
"IRCOMM_TTY_QUERY_PARAMETERS",
@@ -91,7 +91,7 @@ char *ircomm_tty_state[] = {
};
#ifdef CONFIG_IRDA_DEBUG
-static char *ircomm_tty_event[] = {
+static const char *const ircomm_tty_event[] = {
"IRCOMM_TTY_ATTACH_CABLE",
"IRCOMM_TTY_DETACH_CABLE",
"IRCOMM_TTY_DATA_REQUEST",
diff --git a/net/irda/iriap.c b/net/irda/iriap.c
index 4a105dc32dcd..294e34d3517c 100644
--- a/net/irda/iriap.c
+++ b/net/irda/iriap.c
@@ -44,7 +44,7 @@
#ifdef CONFIG_IRDA_DEBUG
/* FIXME: This one should go in irlmp.c */
-static const char *ias_charset_types[] = {
+static const char *const ias_charset_types[] = {
"CS_ASCII",
"CS_ISO_8859_1",
"CS_ISO_8859_2",
@@ -966,7 +966,7 @@ static void iriap_watchdog_timer_expired(void *data)
#ifdef CONFIG_PROC_FS
-static const char *ias_value_types[] = {
+static const char *const ias_value_types[] = {
"IAS_MISSING",
"IAS_INTEGER",
"IAS_OCT_SEQ",
diff --git a/net/irda/irlan/irlan_common.c b/net/irda/irlan/irlan_common.c
index 774d73a76852..62116829b817 100644
--- a/net/irda/irlan/irlan_common.c
+++ b/net/irda/irlan/irlan_common.c
@@ -69,14 +69,14 @@ static int eth; /* Use "eth" or "irlan" name for devices */
static int access = ACCESS_PEER; /* PEER, DIRECT or HOSTED */
#ifdef CONFIG_PROC_FS
-static const char *irlan_access[] = {
+static const char *const irlan_access[] = {
"UNKNOWN",
"DIRECT",
"PEER",
"HOSTED"
};
-static const char *irlan_media[] = {
+static const char *const irlan_media[] = {
"UNKNOWN",
"802.3",
"802.5"
diff --git a/net/irda/irlan/irlan_eth.c b/net/irda/irlan/irlan_eth.c
index 724bcf951b80..7b6b631f647f 100644
--- a/net/irda/irlan/irlan_eth.c
+++ b/net/irda/irlan/irlan_eth.c
@@ -41,7 +41,8 @@
static int irlan_eth_open(struct net_device *dev);
static int irlan_eth_close(struct net_device *dev);
-static int irlan_eth_xmit(struct sk_buff *skb, struct net_device *dev);
+static netdev_tx_t irlan_eth_xmit(struct sk_buff *skb,
+ struct net_device *dev);
static void irlan_eth_set_multicast_list( struct net_device *dev);
static struct net_device_stats *irlan_eth_get_stats(struct net_device *dev);
@@ -162,7 +163,8 @@ static int irlan_eth_close(struct net_device *dev)
* Transmits ethernet frames over IrDA link.
*
*/
-static int irlan_eth_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t irlan_eth_xmit(struct sk_buff *skb,
+ struct net_device *dev)
{
struct irlan_cb *self = netdev_priv(dev);
int ret;
@@ -177,7 +179,7 @@ static int irlan_eth_xmit(struct sk_buff *skb, struct net_device *dev)
/* Did the realloc succeed? */
if (new_skb == NULL)
- return 0;
+ return NETDEV_TX_OK;
/* Use the new skb instead */
skb = new_skb;
@@ -209,7 +211,7 @@ static int irlan_eth_xmit(struct sk_buff *skb, struct net_device *dev)
self->stats.tx_bytes += skb->len;
}
- return 0;
+ return NETDEV_TX_OK;
}
/*
diff --git a/net/irda/irlap.c b/net/irda/irlap.c
index e4965b764b9b..356e65b1dc42 100644
--- a/net/irda/irlap.c
+++ b/net/irda/irlap.c
@@ -63,7 +63,7 @@ static void irlap_init_qos_capabilities(struct irlap_cb *self,
struct qos_info *qos_user);
#ifdef CONFIG_IRDA_DEBUG
-static char *lap_reasons[] = {
+static const char *const lap_reasons[] = {
"ERROR, NOT USED",
"LAP_DISC_INDICATION",
"LAP_NO_RESPONSE",
diff --git a/net/irda/irlap_event.c b/net/irda/irlap_event.c
index 16c4ef0f5c1a..c5c51959e3ce 100644
--- a/net/irda/irlap_event.c
+++ b/net/irda/irlap_event.c
@@ -78,7 +78,7 @@ static int irlap_state_reset_check(struct irlap_cb *, IRLAP_EVENT event,
struct sk_buff *, struct irlap_info *);
#ifdef CONFIG_IRDA_DEBUG
-static const char *irlap_event[] = {
+static const char *const irlap_event[] = {
"DISCOVERY_REQUEST",
"CONNECT_REQUEST",
"CONNECT_RESPONSE",
@@ -120,7 +120,7 @@ static const char *irlap_event[] = {
};
#endif /* CONFIG_IRDA_DEBUG */
-const char *irlap_state[] = {
+const char *const irlap_state[] = {
"LAP_NDM",
"LAP_QUERY",
"LAP_REPLY",
diff --git a/net/irda/irlmp_event.c b/net/irda/irlmp_event.c
index 78cce0cb073f..c1fb5db81042 100644
--- a/net/irda/irlmp_event.c
+++ b/net/irda/irlmp_event.c
@@ -33,13 +33,13 @@
#include <net/irda/irlmp_frame.h>
#include <net/irda/irlmp_event.h>
-const char *irlmp_state[] = {
+const char *const irlmp_state[] = {
"LAP_STANDBY",
"LAP_U_CONNECT",
"LAP_ACTIVE",
};
-const char *irlsap_state[] = {
+const char *const irlsap_state[] = {
"LSAP_DISCONNECTED",
"LSAP_CONNECT",
"LSAP_CONNECT_PEND",
@@ -49,7 +49,7 @@ const char *irlsap_state[] = {
};
#ifdef CONFIG_IRDA_DEBUG
-static const char *irlmp_event[] = {
+static const char *const irlmp_event[] = {
"LM_CONNECT_REQUEST",
"LM_CONNECT_CONFIRM",
"LM_CONNECT_RESPONSE",
diff --git a/net/irda/irnet/irnet_ppp.h b/net/irda/irnet/irnet_ppp.h
index d9f8bd4ebd05..b5df2418f90c 100644
--- a/net/irda/irnet/irnet_ppp.h
+++ b/net/irda/irnet/irnet_ppp.h
@@ -95,7 +95,7 @@ static int
/**************************** VARIABLES ****************************/
/* Filesystem callbacks (to call us) */
-static struct file_operations irnet_device_fops =
+static const struct file_operations irnet_device_fops =
{
.owner = THIS_MODULE,
.read = dev_irnet_read,
diff --git a/net/irda/irnetlink.c b/net/irda/irnetlink.c
index 8dd7ed7e7c1f..476b307bd801 100644
--- a/net/irda/irnetlink.c
+++ b/net/irda/irnetlink.c
@@ -115,7 +115,7 @@ static int irda_nl_get_mode(struct sk_buff *skb, struct genl_info *info)
genlmsg_end(msg, hdr);
- return genlmsg_unicast(msg, info->snd_pid);
+ return genlmsg_reply(msg, info);
err_out:
nlmsg_free(msg);
diff --git a/net/irda/irproc.c b/net/irda/irproc.c
index 8ff1861649e8..318766e5dbdf 100644
--- a/net/irda/irproc.c
+++ b/net/irda/irproc.c
@@ -34,21 +34,21 @@
#include <net/irda/irlap.h>
#include <net/irda/irlmp.h>
-extern struct file_operations discovery_seq_fops;
-extern struct file_operations irlap_seq_fops;
-extern struct file_operations irlmp_seq_fops;
-extern struct file_operations irttp_seq_fops;
-extern struct file_operations irias_seq_fops;
+extern const struct file_operations discovery_seq_fops;
+extern const struct file_operations irlap_seq_fops;
+extern const struct file_operations irlmp_seq_fops;
+extern const struct file_operations irttp_seq_fops;
+extern const struct file_operations irias_seq_fops;
struct irda_entry {
const char *name;
- struct file_operations *fops;
+ const struct file_operations *fops;
};
struct proc_dir_entry *proc_irda;
EXPORT_SYMBOL(proc_irda);
-static struct irda_entry irda_dirs[] = {
+static const struct irda_entry irda_dirs[] = {
{"discovery", &discovery_seq_fops},
{"irttp", &irttp_seq_fops},
{"irlmp", &irlmp_seq_fops},
diff --git a/net/key/af_key.c b/net/key/af_key.c
index dba9abd27f90..4e98193dfa0f 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -3705,7 +3705,7 @@ static void pfkey_seq_stop(struct seq_file *f, void *v)
read_unlock(&pfkey_table_lock);
}
-static struct seq_operations pfkey_seq_ops = {
+static const struct seq_operations pfkey_seq_ops = {
.start = pfkey_seq_start,
.next = pfkey_seq_next,
.stop = pfkey_seq_stop,
@@ -3718,7 +3718,7 @@ static int pfkey_seq_open(struct inode *inode, struct file *file)
sizeof(struct seq_net_private));
}
-static struct file_operations pfkey_proc_ops = {
+static const struct file_operations pfkey_proc_ops = {
.open = pfkey_seq_open,
.read = seq_read,
.llseek = seq_lseek,
diff --git a/net/lapb/lapb_iface.c b/net/lapb/lapb_iface.c
index 2ba1bc4f3c3a..bda96d18fd98 100644
--- a/net/lapb/lapb_iface.c
+++ b/net/lapb/lapb_iface.c
@@ -407,7 +407,7 @@ int lapb_data_indication(struct lapb_cb *lapb, struct sk_buff *skb)
return lapb->callbacks.data_indication(lapb->dev, skb);
kfree_skb(skb);
- return NET_RX_CN_HIGH; /* For now; must be != NET_RX_DROP */
+ return NET_RX_SUCCESS; /* For now; must be != NET_RX_DROP */
}
int lapb_data_transmit(struct lapb_cb *lapb, struct sk_buff *skb)
diff --git a/net/llc/llc_proc.c b/net/llc/llc_proc.c
index f97be471fe2e..be47ac427f6b 100644
--- a/net/llc/llc_proc.c
+++ b/net/llc/llc_proc.c
@@ -143,7 +143,7 @@ out:
return 0;
}
-static char *llc_conn_state_names[] = {
+static const char *const llc_conn_state_names[] = {
[LLC_CONN_STATE_ADM] = "adm",
[LLC_CONN_STATE_SETUP] = "setup",
[LLC_CONN_STATE_NORMAL] = "normal",
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index 7836ee928983..4d5543af3123 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -6,7 +6,6 @@ config MAC80211
select CRYPTO_ARC4
select CRYPTO_AES
select CRC32
- select WIRELESS_EXT
---help---
This option enables the hardware independent IEEE 802.11
networking stack.
@@ -14,24 +13,7 @@ config MAC80211
comment "CFG80211 needs to be enabled for MAC80211"
depends on CFG80211=n
-config MAC80211_DEFAULT_PS
- bool "enable powersave by default"
- depends on MAC80211
- default y
- help
- This option enables powersave mode by default.
-
- If this causes your applications to misbehave you should fix your
- applications instead -- they need to register their network
- latency requirement, see Documentation/power/pm_qos_interface.txt.
-
-config MAC80211_DEFAULT_PS_VALUE
- int
- default 1 if MAC80211_DEFAULT_PS
- default 0
-
-menu "Rate control algorithm selection"
- depends on MAC80211 != n
+if MAC80211 != n
config MAC80211_RC_PID
bool "PID controller based rate control algorithm" if EMBEDDED
@@ -78,17 +60,17 @@ config MAC80211_RC_DEFAULT
default "pid" if MAC80211_RC_DEFAULT_PID
default ""
-endmenu
+endif
config MAC80211_MESH
bool "Enable mac80211 mesh networking (pre-802.11s) support"
depends on MAC80211 && EXPERIMENTAL
- depends on BROKEN
---help---
This options enables support of Draft 802.11s mesh networking.
- The implementation is based on Draft 1.08 of the Mesh Networking
- amendment. For more information visit http://o11s.org/.
-
+ The implementation is based on Draft 2.08 of the Mesh Networking
+ amendment. However, no compliance with that draft is claimed or even
+ possible, as drafts leave a number of identifiers to be defined after
+ ratification. For more information visit http://o11s.org/.
config MAC80211_LEDS
bool "Enable LED triggers"
@@ -222,3 +204,15 @@ config MAC80211_DEBUG_COUNTERS
and show them in debugfs.
If unsure, say N.
+
+config MAC80211_DRIVER_API_TRACER
+ bool "Driver API tracer"
+ depends on MAC80211_DEBUG_MENU
+ depends on EVENT_TRACING
+ help
+ Say Y here to make mac80211 register with the ftrace
+ framework for the driver API -- you can see which
+ driver methods it is calling then by looking at the
+ trace.
+
+ If unsure, say N.
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index 0e3ab88bb706..9f3cf7129324 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -3,7 +3,6 @@ obj-$(CONFIG_MAC80211) += mac80211.o
# mac80211 objects
mac80211-y := \
main.o \
- wext.o \
sta_info.o \
wep.o \
wpa.o \
@@ -41,6 +40,9 @@ mac80211-$(CONFIG_MAC80211_MESH) += \
mac80211-$(CONFIG_PM) += pm.o
+mac80211-$(CONFIG_MAC80211_DRIVER_API_TRACER) += driver-trace.o
+CFLAGS_driver-trace.o := -I$(src)
+
# objects for PID algorithm
rc80211_pid-y := rc80211_pid_algo.o
rc80211_pid-$(CONFIG_MAC80211_DEBUGFS) += rc80211_pid_debugfs.o
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index a24e59816b93..bd765f30dba2 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -391,9 +391,6 @@ static void ieee80211_agg_splice_packets(struct ieee80211_local *local,
if (!skb_queue_empty(&sta->ampdu_mlme.tid_tx[tid]->pending)) {
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
- /* mark queue as pending, it is stopped already */
- __set_bit(IEEE80211_QUEUE_STOP_REASON_PENDING,
- &local->queue_stop_reasons[queue]);
/* copy over remaining packets */
skb_queue_splice_tail_init(
&sta->ampdu_mlme.tid_tx[tid]->pending,
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 3f47276caeb8..5608f6c68413 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -57,36 +57,21 @@ static int ieee80211_add_iface(struct wiphy *wiphy, char *name,
return 0;
}
-static int ieee80211_del_iface(struct wiphy *wiphy, int ifindex)
+static int ieee80211_del_iface(struct wiphy *wiphy, struct net_device *dev)
{
- struct net_device *dev;
- struct ieee80211_sub_if_data *sdata;
-
- /* we're under RTNL */
- dev = __dev_get_by_index(&init_net, ifindex);
- if (!dev)
- return -ENODEV;
-
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- ieee80211_if_remove(sdata);
+ ieee80211_if_remove(IEEE80211_DEV_TO_SUB_IF(dev));
return 0;
}
-static int ieee80211_change_iface(struct wiphy *wiphy, int ifindex,
+static int ieee80211_change_iface(struct wiphy *wiphy,
+ struct net_device *dev,
enum nl80211_iftype type, u32 *flags,
struct vif_params *params)
{
- struct net_device *dev;
struct ieee80211_sub_if_data *sdata;
int ret;
- /* we're under RTNL */
- dev = __dev_get_by_index(&init_net, ifindex);
- if (!dev)
- return -ENODEV;
-
if (!nl80211_type_check(type))
return -EINVAL;
@@ -338,6 +323,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
+ sinfo->generation = sdata->local->sta_generation;
+
sinfo->filled = STATION_INFO_INACTIVE_TIME |
STATION_INFO_RX_BYTES |
STATION_INFO_TX_BYTES |
@@ -924,6 +911,8 @@ static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
else
memset(next_hop, 0, ETH_ALEN);
+ pinfo->generation = mesh_paths_generation;
+
pinfo->filled = MPATH_INFO_FRAME_QLEN |
MPATH_INFO_DSN |
MPATH_INFO_METRIC |
@@ -1177,123 +1166,29 @@ static int ieee80211_scan(struct wiphy *wiphy,
static int ieee80211_auth(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_auth_request *req)
{
- struct ieee80211_sub_if_data *sdata;
-
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- switch (req->auth_type) {
- case NL80211_AUTHTYPE_OPEN_SYSTEM:
- sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_OPEN;
- break;
- case NL80211_AUTHTYPE_SHARED_KEY:
- sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_SHARED_KEY;
- break;
- case NL80211_AUTHTYPE_FT:
- sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_FT;
- break;
- case NL80211_AUTHTYPE_NETWORK_EAP:
- sdata->u.mgd.auth_algs = IEEE80211_AUTH_ALG_LEAP;
- break;
- default:
- return -EOPNOTSUPP;
- }
-
- memcpy(sdata->u.mgd.bssid, req->peer_addr, ETH_ALEN);
- sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
- sdata->u.mgd.flags |= IEEE80211_STA_BSSID_SET;
-
- /* TODO: req->chan */
- sdata->u.mgd.flags |= IEEE80211_STA_AUTO_CHANNEL_SEL;
-
- if (req->ssid) {
- sdata->u.mgd.flags |= IEEE80211_STA_SSID_SET;
- memcpy(sdata->u.mgd.ssid, req->ssid, req->ssid_len);
- sdata->u.mgd.ssid_len = req->ssid_len;
- sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL;
- }
-
- kfree(sdata->u.mgd.sme_auth_ie);
- sdata->u.mgd.sme_auth_ie = NULL;
- sdata->u.mgd.sme_auth_ie_len = 0;
- if (req->ie) {
- sdata->u.mgd.sme_auth_ie = kmalloc(req->ie_len, GFP_KERNEL);
- if (sdata->u.mgd.sme_auth_ie == NULL)
- return -ENOMEM;
- memcpy(sdata->u.mgd.sme_auth_ie, req->ie, req->ie_len);
- sdata->u.mgd.sme_auth_ie_len = req->ie_len;
- }
-
- sdata->u.mgd.flags |= IEEE80211_STA_EXT_SME;
- sdata->u.mgd.state = IEEE80211_STA_MLME_DIRECT_PROBE;
- ieee80211_sta_req_auth(sdata);
- return 0;
+ return ieee80211_mgd_auth(IEEE80211_DEV_TO_SUB_IF(dev), req);
}
static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_assoc_request *req)
{
- struct ieee80211_sub_if_data *sdata;
- int ret;
-
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- if (memcmp(sdata->u.mgd.bssid, req->peer_addr, ETH_ALEN) != 0 ||
- !(sdata->u.mgd.flags & IEEE80211_STA_AUTHENTICATED))
- return -ENOLINK; /* not authenticated */
-
- sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_BSSID_SEL;
- sdata->u.mgd.flags |= IEEE80211_STA_BSSID_SET;
-
- /* TODO: req->chan */
- sdata->u.mgd.flags |= IEEE80211_STA_AUTO_CHANNEL_SEL;
-
- if (req->ssid) {
- sdata->u.mgd.flags |= IEEE80211_STA_SSID_SET;
- memcpy(sdata->u.mgd.ssid, req->ssid, req->ssid_len);
- sdata->u.mgd.ssid_len = req->ssid_len;
- sdata->u.mgd.flags &= ~IEEE80211_STA_AUTO_SSID_SEL;
- } else
- sdata->u.mgd.flags |= IEEE80211_STA_AUTO_SSID_SEL;
-
- ret = ieee80211_sta_set_extra_ie(sdata, req->ie, req->ie_len);
- if (ret && ret != -EALREADY)
- return ret;
-
- if (req->use_mfp) {
- sdata->u.mgd.mfp = IEEE80211_MFP_REQUIRED;
- sdata->u.mgd.flags |= IEEE80211_STA_MFP_ENABLED;
- } else {
- sdata->u.mgd.mfp = IEEE80211_MFP_DISABLED;
- sdata->u.mgd.flags &= ~IEEE80211_STA_MFP_ENABLED;
- }
-
- if (req->control_port)
- sdata->u.mgd.flags |= IEEE80211_STA_CONTROL_PORT;
- else
- sdata->u.mgd.flags &= ~IEEE80211_STA_CONTROL_PORT;
-
- sdata->u.mgd.flags |= IEEE80211_STA_EXT_SME;
- sdata->u.mgd.state = IEEE80211_STA_MLME_ASSOCIATE;
- ieee80211_sta_req_auth(sdata);
- return 0;
+ return ieee80211_mgd_assoc(IEEE80211_DEV_TO_SUB_IF(dev), req);
}
static int ieee80211_deauth(struct wiphy *wiphy, struct net_device *dev,
- struct cfg80211_deauth_request *req)
+ struct cfg80211_deauth_request *req,
+ void *cookie)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- /* TODO: req->ie, req->peer_addr */
- return ieee80211_sta_deauthenticate(sdata, req->reason_code);
+ return ieee80211_mgd_deauth(IEEE80211_DEV_TO_SUB_IF(dev),
+ req, cookie);
}
static int ieee80211_disassoc(struct wiphy *wiphy, struct net_device *dev,
- struct cfg80211_disassoc_request *req)
+ struct cfg80211_disassoc_request *req,
+ void *cookie)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
-
- /* TODO: req->ie, req->peer_addr */
- return ieee80211_sta_disassociate(sdata, req->reason_code);
+ return ieee80211_mgd_disassoc(IEEE80211_DEV_TO_SUB_IF(dev),
+ req, cookie);
}
static int ieee80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
@@ -1374,6 +1269,16 @@ static int ieee80211_get_tx_power(struct wiphy *wiphy, int *dbm)
return 0;
}
+static int ieee80211_set_wds_peer(struct wiphy *wiphy, struct net_device *dev,
+ u8 *addr)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ memcpy(&sdata->u.wds.remote_addr, addr, ETH_ALEN);
+
+ return 0;
+}
+
static void ieee80211_rfkill_poll(struct wiphy *wiphy)
{
struct ieee80211_local *local = wiphy_priv(wiphy);
@@ -1381,6 +1286,85 @@ static void ieee80211_rfkill_poll(struct wiphy *wiphy)
drv_rfkill_poll(local);
}
+#ifdef CONFIG_NL80211_TESTMODE
+static int ieee80211_testmode_cmd(struct wiphy *wiphy, void *data, int len)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+
+ if (!local->ops->testmode_cmd)
+ return -EOPNOTSUPP;
+
+ return local->ops->testmode_cmd(&local->hw, data, len);
+}
+#endif
+
+static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
+ bool enabled, int timeout)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_conf *conf = &local->hw.conf;
+
+ if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS))
+ return -EOPNOTSUPP;
+
+ if (enabled == sdata->u.mgd.powersave &&
+ timeout == conf->dynamic_ps_timeout)
+ return 0;
+
+ sdata->u.mgd.powersave = enabled;
+ conf->dynamic_ps_timeout = timeout;
+
+ if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS)
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
+
+ ieee80211_recalc_ps(local, -1);
+
+ return 0;
+}
+
+static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
+ struct net_device *dev,
+ const u8 *addr,
+ const struct cfg80211_bitrate_mask *mask)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ int i, err = -EINVAL;
+ u32 target_rate;
+ struct ieee80211_supported_band *sband;
+
+ sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
+
+ /* target_rate = -1, rate->fixed = 0 means auto only, so use all rates
+ * target_rate = X, rate->fixed = 1 means only rate X
+ * target_rate = X, rate->fixed = 0 means all rates <= X */
+ sdata->max_ratectrl_rateidx = -1;
+ sdata->force_unicast_rateidx = -1;
+
+ if (mask->fixed)
+ target_rate = mask->fixed / 100;
+ else if (mask->maxrate)
+ target_rate = mask->maxrate / 100;
+ else
+ return 0;
+
+ for (i=0; i< sband->n_bitrates; i++) {
+ struct ieee80211_rate *brate = &sband->bitrates[i];
+ int this_rate = brate->bitrate;
+
+ if (target_rate == this_rate) {
+ sdata->max_ratectrl_rateidx = i;
+ if (mask->fixed)
+ sdata->force_unicast_rateidx = i;
+ err = 0;
+ break;
+ }
+ }
+
+ return err;
+}
+
struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
@@ -1422,5 +1406,9 @@ struct cfg80211_ops mac80211_config_ops = {
.set_wiphy_params = ieee80211_set_wiphy_params,
.set_tx_power = ieee80211_set_tx_power,
.get_tx_power = ieee80211_get_tx_power,
+ .set_wds_peer = ieee80211_set_wds_peer,
.rfkill_poll = ieee80211_rfkill_poll,
+ CFG80211_TESTMODE_CMD(ieee80211_testmode_cmd)
+ .set_power_mgmt = ieee80211_set_power_mgmt,
+ .set_bitrate_mask = ieee80211_set_bitrate_mask,
};
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 6c439cd5ccea..96991b68f048 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -175,7 +175,7 @@ static ssize_t queues_read(struct file *file, char __user *user_buf,
for (q = 0; q < local->hw.queues; q++)
res += sprintf(buf + res, "%02d: %#.8lx/%d\n", q,
local->queue_stop_reasons[q],
- __netif_subqueue_stopped(local->mdev, q));
+ skb_queue_len(&local->pending[q]));
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
return simple_read_from_buffer(user_buf, count, ppos, buf, res);
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index e3420329f4e6..61234e79022b 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -95,33 +95,9 @@ IEEE80211_IF_FILE(force_unicast_rateidx, force_unicast_rateidx, DEC);
IEEE80211_IF_FILE(max_ratectrl_rateidx, max_ratectrl_rateidx, DEC);
/* STA attributes */
-IEEE80211_IF_FILE(state, u.mgd.state, DEC);
IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC);
-IEEE80211_IF_FILE(prev_bssid, u.mgd.prev_bssid, MAC);
-IEEE80211_IF_FILE(ssid_len, u.mgd.ssid_len, SIZE);
IEEE80211_IF_FILE(aid, u.mgd.aid, DEC);
-IEEE80211_IF_FILE(ap_capab, u.mgd.ap_capab, HEX);
IEEE80211_IF_FILE(capab, u.mgd.capab, HEX);
-IEEE80211_IF_FILE(extra_ie_len, u.mgd.extra_ie_len, SIZE);
-IEEE80211_IF_FILE(auth_tries, u.mgd.auth_tries, DEC);
-IEEE80211_IF_FILE(assoc_tries, u.mgd.assoc_tries, DEC);
-IEEE80211_IF_FILE(auth_algs, u.mgd.auth_algs, HEX);
-IEEE80211_IF_FILE(auth_alg, u.mgd.auth_alg, DEC);
-IEEE80211_IF_FILE(auth_transaction, u.mgd.auth_transaction, DEC);
-
-static ssize_t ieee80211_if_fmt_flags(
- const struct ieee80211_sub_if_data *sdata, char *buf, int buflen)
-{
- return scnprintf(buf, buflen, "%s%s%s%s%s%s%s\n",
- sdata->u.mgd.flags & IEEE80211_STA_SSID_SET ? "SSID\n" : "",
- sdata->u.mgd.flags & IEEE80211_STA_BSSID_SET ? "BSSID\n" : "",
- sdata->u.mgd.flags & IEEE80211_STA_PREV_BSSID_SET ? "prev BSSID\n" : "",
- sdata->u.mgd.flags & IEEE80211_STA_AUTHENTICATED ? "AUTH\n" : "",
- sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED ? "ASSOC\n" : "",
- sdata->u.mgd.flags & IEEE80211_STA_PROBEREQ_POLL ? "PROBEREQ POLL\n" : "",
- sdata->vif.bss_conf.use_cts_prot ? "CTS prot\n" : "");
-}
-__IEEE80211_IF_FILE(flags);
/* AP attributes */
IEEE80211_IF_FILE(num_sta_ps, u.ap.num_sta_ps, ATOMIC);
@@ -140,6 +116,8 @@ IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC);
#ifdef CONFIG_MAC80211_MESH
/* Mesh stats attributes */
+IEEE80211_IF_FILE(fwded_mcast, u.mesh.mshstats.fwded_mcast, DEC);
+IEEE80211_IF_FILE(fwded_unicast, u.mesh.mshstats.fwded_unicast, DEC);
IEEE80211_IF_FILE(fwded_frames, u.mesh.mshstats.fwded_frames, DEC);
IEEE80211_IF_FILE(dropped_frames_ttl, u.mesh.mshstats.dropped_frames_ttl, DEC);
IEEE80211_IF_FILE(dropped_frames_no_route,
@@ -184,20 +162,9 @@ static void add_sta_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_ADD(force_unicast_rateidx, sta);
DEBUGFS_ADD(max_ratectrl_rateidx, sta);
- DEBUGFS_ADD(state, sta);
DEBUGFS_ADD(bssid, sta);
- DEBUGFS_ADD(prev_bssid, sta);
- DEBUGFS_ADD(ssid_len, sta);
DEBUGFS_ADD(aid, sta);
- DEBUGFS_ADD(ap_capab, sta);
DEBUGFS_ADD(capab, sta);
- DEBUGFS_ADD(extra_ie_len, sta);
- DEBUGFS_ADD(auth_tries, sta);
- DEBUGFS_ADD(assoc_tries, sta);
- DEBUGFS_ADD(auth_algs, sta);
- DEBUGFS_ADD(auth_alg, sta);
- DEBUGFS_ADD(auth_transaction, sta);
- DEBUGFS_ADD(flags, sta);
}
static void add_ap_files(struct ieee80211_sub_if_data *sdata)
@@ -240,6 +207,8 @@ static void add_mesh_stats(struct ieee80211_sub_if_data *sdata)
{
sdata->mesh_stats_dir = debugfs_create_dir("mesh_stats",
sdata->debugfsdir);
+ MESHSTATS_ADD(fwded_mcast);
+ MESHSTATS_ADD(fwded_unicast);
MESHSTATS_ADD(fwded_frames);
MESHSTATS_ADD(dropped_frames_ttl);
MESHSTATS_ADD(dropped_frames_no_route);
@@ -317,20 +286,9 @@ static void del_sta_files(struct ieee80211_sub_if_data *sdata)
DEBUGFS_DEL(force_unicast_rateidx, sta);
DEBUGFS_DEL(max_ratectrl_rateidx, sta);
- DEBUGFS_DEL(state, sta);
DEBUGFS_DEL(bssid, sta);
- DEBUGFS_DEL(prev_bssid, sta);
- DEBUGFS_DEL(ssid_len, sta);
DEBUGFS_DEL(aid, sta);
- DEBUGFS_DEL(ap_capab, sta);
DEBUGFS_DEL(capab, sta);
- DEBUGFS_DEL(extra_ie_len, sta);
- DEBUGFS_DEL(auth_tries, sta);
- DEBUGFS_DEL(assoc_tries, sta);
- DEBUGFS_DEL(auth_algs, sta);
- DEBUGFS_DEL(auth_alg, sta);
- DEBUGFS_DEL(auth_transaction, sta);
- DEBUGFS_DEL(flags, sta);
}
static void del_ap_files(struct ieee80211_sub_if_data *sdata)
@@ -373,6 +331,8 @@ static void del_monitor_files(struct ieee80211_sub_if_data *sdata)
static void del_mesh_stats(struct ieee80211_sub_if_data *sdata)
{
+ MESHSTATS_DEL(fwded_mcast);
+ MESHSTATS_DEL(fwded_unicast);
MESHSTATS_DEL(fwded_frames);
MESHSTATS_DEL(dropped_frames_ttl);
MESHSTATS_DEL(dropped_frames_no_route);
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 90230c718b5b..33a2e892115b 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -120,45 +120,38 @@ STA_OPS(last_seq_ctrl);
static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf,
size_t count, loff_t *ppos)
{
- char buf[768], *p = buf;
+ char buf[30 + STA_TID_NUM * 70], *p = buf;
int i;
struct sta_info *sta = file->private_data;
- p += scnprintf(p, sizeof(buf)+buf-p, "Agg state for STA is:\n");
- p += scnprintf(p, sizeof(buf)+buf-p, " STA next dialog_token is %d \n "
- "TIDs info is: \n TID :",
- (sta->ampdu_mlme.dialog_token_allocator + 1));
- for (i = 0; i < STA_TID_NUM; i++)
- p += scnprintf(p, sizeof(buf)+buf-p, "%5d", i);
-
- p += scnprintf(p, sizeof(buf)+buf-p, "\n RX :");
- for (i = 0; i < STA_TID_NUM; i++)
- p += scnprintf(p, sizeof(buf)+buf-p, "%5d",
- sta->ampdu_mlme.tid_state_rx[i]);
-
- p += scnprintf(p, sizeof(buf)+buf-p, "\n DTKN:");
- for (i = 0; i < STA_TID_NUM; i++)
- p += scnprintf(p, sizeof(buf)+buf-p, "%5d",
- sta->ampdu_mlme.tid_state_rx[i] ?
- sta->ampdu_mlme.tid_rx[i]->dialog_token : 0);
-
- p += scnprintf(p, sizeof(buf)+buf-p, "\n TX :");
- for (i = 0; i < STA_TID_NUM; i++)
- p += scnprintf(p, sizeof(buf)+buf-p, "%5d",
- sta->ampdu_mlme.tid_state_tx[i]);
-
- p += scnprintf(p, sizeof(buf)+buf-p, "\n DTKN:");
- for (i = 0; i < STA_TID_NUM; i++)
- p += scnprintf(p, sizeof(buf)+buf-p, "%5d",
- sta->ampdu_mlme.tid_state_tx[i] ?
- sta->ampdu_mlme.tid_tx[i]->dialog_token : 0);
-
- p += scnprintf(p, sizeof(buf)+buf-p, "\n SSN :");
- for (i = 0; i < STA_TID_NUM; i++)
- p += scnprintf(p, sizeof(buf)+buf-p, "%5d",
- sta->ampdu_mlme.tid_state_tx[i] ?
- sta->ampdu_mlme.tid_tx[i]->ssn : 0);
- p += scnprintf(p, sizeof(buf)+buf-p, "\n");
+ spin_lock_bh(&sta->lock);
+ p += scnprintf(p, sizeof(buf)+buf-p, "next dialog_token is %#02x\n",
+ sta->ampdu_mlme.dialog_token_allocator + 1);
+ for (i = 0; i < STA_TID_NUM; i++) {
+ p += scnprintf(p, sizeof(buf)+buf-p, "TID %02d:", i);
+ p += scnprintf(p, sizeof(buf)+buf-p, " RX=%x",
+ sta->ampdu_mlme.tid_state_rx[i]);
+ p += scnprintf(p, sizeof(buf)+buf-p, "/DTKN=%#.2x",
+ sta->ampdu_mlme.tid_state_rx[i] ?
+ sta->ampdu_mlme.tid_rx[i]->dialog_token : 0);
+ p += scnprintf(p, sizeof(buf)+buf-p, "/SSN=%#.3x",
+ sta->ampdu_mlme.tid_state_rx[i] ?
+ sta->ampdu_mlme.tid_rx[i]->ssn : 0);
+
+ p += scnprintf(p, sizeof(buf)+buf-p, " TX=%x",
+ sta->ampdu_mlme.tid_state_tx[i]);
+ p += scnprintf(p, sizeof(buf)+buf-p, "/DTKN=%#.2x",
+ sta->ampdu_mlme.tid_state_tx[i] ?
+ sta->ampdu_mlme.tid_tx[i]->dialog_token : 0);
+ p += scnprintf(p, sizeof(buf)+buf-p, "/SSN=%#.3x",
+ sta->ampdu_mlme.tid_state_tx[i] ?
+ sta->ampdu_mlme.tid_tx[i]->ssn : 0);
+ p += scnprintf(p, sizeof(buf)+buf-p, "/pending=%03d",
+ sta->ampdu_mlme.tid_state_tx[i] ?
+ skb_queue_len(&sta->ampdu_mlme.tid_tx[i]->pending) : 0);
+ p += scnprintf(p, sizeof(buf)+buf-p, "\n");
+ }
+ spin_unlock_bh(&sta->lock);
return simple_read_from_buffer(userbuf, count, ppos, buf, p - buf);
}
@@ -203,6 +196,22 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
DEBUGFS_ADD(inactive_ms);
DEBUGFS_ADD(last_seq_ctrl);
DEBUGFS_ADD(agg_status);
+ DEBUGFS_ADD(dev);
+ DEBUGFS_ADD(rx_packets);
+ DEBUGFS_ADD(tx_packets);
+ DEBUGFS_ADD(rx_bytes);
+ DEBUGFS_ADD(tx_bytes);
+ DEBUGFS_ADD(rx_duplicates);
+ DEBUGFS_ADD(rx_fragments);
+ DEBUGFS_ADD(rx_dropped);
+ DEBUGFS_ADD(tx_fragments);
+ DEBUGFS_ADD(tx_filtered);
+ DEBUGFS_ADD(tx_retry_failed);
+ DEBUGFS_ADD(tx_retry_count);
+ DEBUGFS_ADD(last_signal);
+ DEBUGFS_ADD(last_qual);
+ DEBUGFS_ADD(last_noise);
+ DEBUGFS_ADD(wep_weak_iv_count);
}
void ieee80211_sta_debugfs_remove(struct sta_info *sta)
@@ -212,6 +221,23 @@ void ieee80211_sta_debugfs_remove(struct sta_info *sta)
DEBUGFS_DEL(inactive_ms);
DEBUGFS_DEL(last_seq_ctrl);
DEBUGFS_DEL(agg_status);
+ DEBUGFS_DEL(aid);
+ DEBUGFS_DEL(dev);
+ DEBUGFS_DEL(rx_packets);
+ DEBUGFS_DEL(tx_packets);
+ DEBUGFS_DEL(rx_bytes);
+ DEBUGFS_DEL(tx_bytes);
+ DEBUGFS_DEL(rx_duplicates);
+ DEBUGFS_DEL(rx_fragments);
+ DEBUGFS_DEL(rx_dropped);
+ DEBUGFS_DEL(tx_fragments);
+ DEBUGFS_DEL(tx_filtered);
+ DEBUGFS_DEL(tx_retry_failed);
+ DEBUGFS_DEL(tx_retry_count);
+ DEBUGFS_DEL(last_signal);
+ DEBUGFS_DEL(last_qual);
+ DEBUGFS_DEL(last_noise);
+ DEBUGFS_DEL(wep_weak_iv_count);
debugfs_remove(sta->debugfs.dir);
sta->debugfs.dir = NULL;
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index b13446afd48f..020a94a31106 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -3,6 +3,7 @@
#include <net/mac80211.h>
#include "ieee80211_i.h"
+#include "driver-trace.h"
static inline int drv_tx(struct ieee80211_local *local, struct sk_buff *skb)
{
@@ -11,29 +12,49 @@ static inline int drv_tx(struct ieee80211_local *local, struct sk_buff *skb)
static inline int drv_start(struct ieee80211_local *local)
{
- return local->ops->start(&local->hw);
+ int ret;
+
+ local->started = true;
+ smp_mb();
+ ret = local->ops->start(&local->hw);
+ trace_drv_start(local, ret);
+ return ret;
}
static inline void drv_stop(struct ieee80211_local *local)
{
local->ops->stop(&local->hw);
+ trace_drv_stop(local);
+
+ /* sync away all work on the tasklet before clearing started */
+ tasklet_disable(&local->tasklet);
+ tasklet_enable(&local->tasklet);
+
+ barrier();
+
+ local->started = false;
}
static inline int drv_add_interface(struct ieee80211_local *local,
struct ieee80211_if_init_conf *conf)
{
- return local->ops->add_interface(&local->hw, conf);
+ int ret = local->ops->add_interface(&local->hw, conf);
+ trace_drv_add_interface(local, conf->mac_addr, conf->vif, ret);
+ return ret;
}
static inline void drv_remove_interface(struct ieee80211_local *local,
struct ieee80211_if_init_conf *conf)
{
local->ops->remove_interface(&local->hw, conf);
+ trace_drv_remove_interface(local, conf->mac_addr, conf->vif);
}
static inline int drv_config(struct ieee80211_local *local, u32 changed)
{
- return local->ops->config(&local->hw, changed);
+ int ret = local->ops->config(&local->hw, changed);
+ trace_drv_config(local, changed, ret);
+ return ret;
}
static inline void drv_bss_info_changed(struct ieee80211_local *local,
@@ -43,24 +64,45 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local,
{
if (local->ops->bss_info_changed)
local->ops->bss_info_changed(&local->hw, vif, info, changed);
+ trace_drv_bss_info_changed(local, vif, info, changed);
+}
+
+static inline u64 drv_prepare_multicast(struct ieee80211_local *local,
+ int mc_count,
+ struct dev_addr_list *mc_list)
+{
+ u64 ret = 0;
+
+ if (local->ops->prepare_multicast)
+ ret = local->ops->prepare_multicast(&local->hw, mc_count,
+ mc_list);
+
+ trace_drv_prepare_multicast(local, mc_count, ret);
+
+ return ret;
}
static inline void drv_configure_filter(struct ieee80211_local *local,
unsigned int changed_flags,
unsigned int *total_flags,
- int mc_count,
- struct dev_addr_list *mc_list)
+ u64 multicast)
{
+ might_sleep();
+
local->ops->configure_filter(&local->hw, changed_flags, total_flags,
- mc_count, mc_list);
+ multicast);
+ trace_drv_configure_filter(local, changed_flags, total_flags,
+ multicast);
}
static inline int drv_set_tim(struct ieee80211_local *local,
struct ieee80211_sta *sta, bool set)
{
+ int ret = 0;
if (local->ops->set_tim)
- return local->ops->set_tim(&local->hw, sta, set);
- return 0;
+ ret = local->ops->set_tim(&local->hw, sta, set);
+ trace_drv_set_tim(local, sta, set, ret);
+ return ret;
}
static inline int drv_set_key(struct ieee80211_local *local,
@@ -68,7 +110,9 @@ static inline int drv_set_key(struct ieee80211_local *local,
struct ieee80211_sta *sta,
struct ieee80211_key_conf *key)
{
- return local->ops->set_key(&local->hw, cmd, vif, sta, key);
+ int ret = local->ops->set_key(&local->hw, cmd, vif, sta, key);
+ trace_drv_set_key(local, cmd, vif, sta, key, ret);
+ return ret;
}
static inline void drv_update_tkip_key(struct ieee80211_local *local,
@@ -79,32 +123,41 @@ static inline void drv_update_tkip_key(struct ieee80211_local *local,
if (local->ops->update_tkip_key)
local->ops->update_tkip_key(&local->hw, conf, address,
iv32, phase1key);
+ trace_drv_update_tkip_key(local, conf, address, iv32);
}
static inline int drv_hw_scan(struct ieee80211_local *local,
struct cfg80211_scan_request *req)
{
- return local->ops->hw_scan(&local->hw, req);
+ int ret = local->ops->hw_scan(&local->hw, req);
+ trace_drv_hw_scan(local, req, ret);
+ return ret;
}
static inline void drv_sw_scan_start(struct ieee80211_local *local)
{
if (local->ops->sw_scan_start)
local->ops->sw_scan_start(&local->hw);
+ trace_drv_sw_scan_start(local);
}
static inline void drv_sw_scan_complete(struct ieee80211_local *local)
{
if (local->ops->sw_scan_complete)
local->ops->sw_scan_complete(&local->hw);
+ trace_drv_sw_scan_complete(local);
}
static inline int drv_get_stats(struct ieee80211_local *local,
struct ieee80211_low_level_stats *stats)
{
- if (!local->ops->get_stats)
- return -EOPNOTSUPP;
- return local->ops->get_stats(&local->hw, stats);
+ int ret = -EOPNOTSUPP;
+
+ if (local->ops->get_stats)
+ ret = local->ops->get_stats(&local->hw, stats);
+ trace_drv_get_stats(local, stats, ret);
+
+ return ret;
}
static inline void drv_get_tkip_seq(struct ieee80211_local *local,
@@ -112,14 +165,17 @@ static inline void drv_get_tkip_seq(struct ieee80211_local *local,
{
if (local->ops->get_tkip_seq)
local->ops->get_tkip_seq(&local->hw, hw_key_idx, iv32, iv16);
+ trace_drv_get_tkip_seq(local, hw_key_idx, iv32, iv16);
}
static inline int drv_set_rts_threshold(struct ieee80211_local *local,
u32 value)
{
+ int ret = 0;
if (local->ops->set_rts_threshold)
- return local->ops->set_rts_threshold(&local->hw, value);
- return 0;
+ ret = local->ops->set_rts_threshold(&local->hw, value);
+ trace_drv_set_rts_threshold(local, value, ret);
+ return ret;
}
static inline void drv_sta_notify(struct ieee80211_local *local,
@@ -129,46 +185,57 @@ static inline void drv_sta_notify(struct ieee80211_local *local,
{
if (local->ops->sta_notify)
local->ops->sta_notify(&local->hw, vif, cmd, sta);
+ trace_drv_sta_notify(local, vif, cmd, sta);
}
static inline int drv_conf_tx(struct ieee80211_local *local, u16 queue,
const struct ieee80211_tx_queue_params *params)
{
+ int ret = -EOPNOTSUPP;
if (local->ops->conf_tx)
- return local->ops->conf_tx(&local->hw, queue, params);
- return -EOPNOTSUPP;
+ ret = local->ops->conf_tx(&local->hw, queue, params);
+ trace_drv_conf_tx(local, queue, params, ret);
+ return ret;
}
static inline int drv_get_tx_stats(struct ieee80211_local *local,
struct ieee80211_tx_queue_stats *stats)
{
- return local->ops->get_tx_stats(&local->hw, stats);
+ int ret = local->ops->get_tx_stats(&local->hw, stats);
+ trace_drv_get_tx_stats(local, stats, ret);
+ return ret;
}
static inline u64 drv_get_tsf(struct ieee80211_local *local)
{
+ u64 ret = -1ULL;
if (local->ops->get_tsf)
- return local->ops->get_tsf(&local->hw);
- return -1ULL;
+ ret = local->ops->get_tsf(&local->hw);
+ trace_drv_get_tsf(local, ret);
+ return ret;
}
static inline void drv_set_tsf(struct ieee80211_local *local, u64 tsf)
{
if (local->ops->set_tsf)
local->ops->set_tsf(&local->hw, tsf);
+ trace_drv_set_tsf(local, tsf);
}
static inline void drv_reset_tsf(struct ieee80211_local *local)
{
if (local->ops->reset_tsf)
local->ops->reset_tsf(&local->hw);
+ trace_drv_reset_tsf(local);
}
static inline int drv_tx_last_beacon(struct ieee80211_local *local)
{
+ int ret = 1;
if (local->ops->tx_last_beacon)
- return local->ops->tx_last_beacon(&local->hw);
- return 1;
+ ret = local->ops->tx_last_beacon(&local->hw);
+ trace_drv_tx_last_beacon(local, ret);
+ return ret;
}
static inline int drv_ampdu_action(struct ieee80211_local *local,
@@ -176,10 +243,12 @@ static inline int drv_ampdu_action(struct ieee80211_local *local,
struct ieee80211_sta *sta, u16 tid,
u16 *ssn)
{
+ int ret = -EOPNOTSUPP;
if (local->ops->ampdu_action)
- return local->ops->ampdu_action(&local->hw, action,
- sta, tid, ssn);
- return -EOPNOTSUPP;
+ ret = local->ops->ampdu_action(&local->hw, action,
+ sta, tid, ssn);
+ trace_drv_ampdu_action(local, action, sta, tid, ssn, ret);
+ return ret;
}
diff --git a/net/mac80211/driver-trace.c b/net/mac80211/driver-trace.c
new file mode 100644
index 000000000000..8ed8711b1a6d
--- /dev/null
+++ b/net/mac80211/driver-trace.c
@@ -0,0 +1,9 @@
+/* bug in tracepoint.h, it should include this */
+#include <linux/module.h>
+
+/* sparse isn't too happy with all macros... */
+#ifndef __CHECKER__
+#include "driver-ops.h"
+#define CREATE_TRACE_POINTS
+#include "driver-trace.h"
+#endif
diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h
new file mode 100644
index 000000000000..37b9051afcf3
--- /dev/null
+++ b/net/mac80211/driver-trace.h
@@ -0,0 +1,672 @@
+#if !defined(__MAC80211_DRIVER_TRACE) || defined(TRACE_HEADER_MULTI_READ)
+#define __MAC80211_DRIVER_TRACE
+
+#include <linux/tracepoint.h>
+#include <net/mac80211.h>
+#include "ieee80211_i.h"
+
+#if !defined(CONFIG_MAC80211_DRIVER_API_TRACER) || defined(__CHECKER__)
+#undef TRACE_EVENT
+#define TRACE_EVENT(name, proto, ...) \
+static inline void trace_ ## name(proto) {}
+#endif
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM mac80211
+
+#define MAXNAME 32
+#define LOCAL_ENTRY __array(char, wiphy_name, 32)
+#define LOCAL_ASSIGN strlcpy(__entry->wiphy_name, wiphy_name(local->hw.wiphy), MAXNAME)
+#define LOCAL_PR_FMT "%s"
+#define LOCAL_PR_ARG __entry->wiphy_name
+
+#define STA_ENTRY __array(char, sta_addr, ETH_ALEN)
+#define STA_ASSIGN (sta ? memcpy(__entry->sta_addr, sta->addr, ETH_ALEN) : memset(__entry->sta_addr, 0, ETH_ALEN))
+#define STA_PR_FMT " sta:%pM"
+#define STA_PR_ARG __entry->sta_addr
+
+#define VIF_ENTRY __field(enum nl80211_iftype, vif_type) __field(void *, vif)
+#define VIF_ASSIGN __entry->vif_type = vif ? vif->type : 0; __entry->vif = vif
+#define VIF_PR_FMT " vif:%p(%d)"
+#define VIF_PR_ARG __entry->vif, __entry->vif_type
+
+TRACE_EVENT(drv_start,
+ TP_PROTO(struct ieee80211_local *local, int ret),
+
+ TP_ARGS(local, ret),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ __entry->ret = ret;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT, LOCAL_PR_ARG
+ )
+);
+
+TRACE_EVENT(drv_stop,
+ TP_PROTO(struct ieee80211_local *local),
+
+ TP_ARGS(local),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT, LOCAL_PR_ARG
+ )
+);
+
+TRACE_EVENT(drv_add_interface,
+ TP_PROTO(struct ieee80211_local *local,
+ const u8 *addr,
+ struct ieee80211_vif *vif,
+ int ret),
+
+ TP_ARGS(local, addr, vif, ret),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ __array(char, addr, 6)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ memcpy(__entry->addr, addr, 6);
+ __entry->ret = ret;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT " addr:%pM ret:%d",
+ LOCAL_PR_ARG, VIF_PR_ARG, __entry->addr, __entry->ret
+ )
+);
+
+TRACE_EVENT(drv_remove_interface,
+ TP_PROTO(struct ieee80211_local *local,
+ const u8 *addr, struct ieee80211_vif *vif),
+
+ TP_ARGS(local, addr, vif),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ __array(char, addr, 6)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ memcpy(__entry->addr, addr, 6);
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT " addr:%pM",
+ LOCAL_PR_ARG, VIF_PR_ARG, __entry->addr
+ )
+);
+
+TRACE_EVENT(drv_config,
+ TP_PROTO(struct ieee80211_local *local,
+ u32 changed,
+ int ret),
+
+ TP_ARGS(local, changed, ret),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __field(u32, changed)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ __entry->changed = changed;
+ __entry->ret = ret;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT " ch:%#x ret:%d",
+ LOCAL_PR_ARG, __entry->changed, __entry->ret
+ )
+);
+
+TRACE_EVENT(drv_bss_info_changed,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+ u32 changed),
+
+ TP_ARGS(local, vif, info, changed),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ __field(bool, assoc)
+ __field(u16, aid)
+ __field(bool, cts)
+ __field(bool, shortpre)
+ __field(bool, shortslot)
+ __field(u8, dtimper)
+ __field(u16, bcnint)
+ __field(u16, assoc_cap)
+ __field(u64, timestamp)
+ __field(u32, basic_rates)
+ __field(u32, changed)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ __entry->changed = changed;
+ __entry->aid = info->aid;
+ __entry->assoc = info->assoc;
+ __entry->shortpre = info->use_short_preamble;
+ __entry->cts = info->use_cts_prot;
+ __entry->shortslot = info->use_short_slot;
+ __entry->dtimper = info->dtim_period;
+ __entry->bcnint = info->beacon_int;
+ __entry->assoc_cap = info->assoc_capability;
+ __entry->timestamp = info->timestamp;
+ __entry->basic_rates = info->basic_rates;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT " changed:%#x",
+ LOCAL_PR_ARG, VIF_PR_ARG, __entry->changed
+ )
+);
+
+TRACE_EVENT(drv_prepare_multicast,
+ TP_PROTO(struct ieee80211_local *local, int mc_count, u64 ret),
+
+ TP_ARGS(local, mc_count, ret),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __field(int, mc_count)
+ __field(u64, ret)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ __entry->mc_count = mc_count;
+ __entry->ret = ret;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT " prepare mc (%d): %llx",
+ LOCAL_PR_ARG, __entry->mc_count,
+ (unsigned long long) __entry->ret
+ )
+);
+
+TRACE_EVENT(drv_configure_filter,
+ TP_PROTO(struct ieee80211_local *local,
+ unsigned int changed_flags,
+ unsigned int *total_flags,
+ u64 multicast),
+
+ TP_ARGS(local, changed_flags, total_flags, multicast),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __field(unsigned int, changed)
+ __field(unsigned int, total)
+ __field(u64, multicast)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ __entry->changed = changed_flags;
+ __entry->total = *total_flags;
+ __entry->multicast = multicast;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT " changed:%#x total:%#x",
+ LOCAL_PR_ARG, __entry->changed, __entry->total
+ )
+);
+
+TRACE_EVENT(drv_set_tim,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sta *sta, bool set, int ret),
+
+ TP_ARGS(local, sta, set, ret),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ STA_ENTRY
+ __field(bool, set)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ STA_ASSIGN;
+ __entry->set = set;
+ __entry->ret = ret;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT STA_PR_FMT " set:%d ret:%d",
+ LOCAL_PR_ARG, STA_PR_FMT, __entry->set, __entry->ret
+ )
+);
+
+TRACE_EVENT(drv_set_key,
+ TP_PROTO(struct ieee80211_local *local,
+ enum set_key_cmd cmd, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key, int ret),
+
+ TP_ARGS(local, cmd, vif, sta, key, ret),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ STA_ENTRY
+ __field(enum ieee80211_key_alg, alg)
+ __field(u8, hw_key_idx)
+ __field(u8, flags)
+ __field(s8, keyidx)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ STA_ASSIGN;
+ __entry->alg = key->alg;
+ __entry->flags = key->flags;
+ __entry->keyidx = key->keyidx;
+ __entry->hw_key_idx = key->hw_key_idx;
+ __entry->ret = ret;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " ret:%d",
+ LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->ret
+ )
+);
+
+TRACE_EVENT(drv_update_tkip_key,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_key_conf *conf,
+ const u8 *address, u32 iv32),
+
+ TP_ARGS(local, conf, address, iv32),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __array(u8, addr, 6)
+ __field(u32, iv32)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ memcpy(__entry->addr, address, 6);
+ __entry->iv32 = iv32;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT " addr:%pM iv32:%#x",
+ LOCAL_PR_ARG, __entry->addr, __entry->iv32
+ )
+);
+
+TRACE_EVENT(drv_hw_scan,
+ TP_PROTO(struct ieee80211_local *local,
+ struct cfg80211_scan_request *req, int ret),
+
+ TP_ARGS(local, req, ret),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ __entry->ret = ret;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT " ret:%d",
+ LOCAL_PR_ARG, __entry->ret
+ )
+);
+
+TRACE_EVENT(drv_sw_scan_start,
+ TP_PROTO(struct ieee80211_local *local),
+
+ TP_ARGS(local),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT, LOCAL_PR_ARG
+ )
+);
+
+TRACE_EVENT(drv_sw_scan_complete,
+ TP_PROTO(struct ieee80211_local *local),
+
+ TP_ARGS(local),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT, LOCAL_PR_ARG
+ )
+);
+
+TRACE_EVENT(drv_get_stats,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_low_level_stats *stats,
+ int ret),
+
+ TP_ARGS(local, stats, ret),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __field(int, ret)
+ __field(unsigned int, ackfail)
+ __field(unsigned int, rtsfail)
+ __field(unsigned int, fcserr)
+ __field(unsigned int, rtssucc)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ __entry->ret = ret;
+ __entry->ackfail = stats->dot11ACKFailureCount;
+ __entry->rtsfail = stats->dot11RTSFailureCount;
+ __entry->fcserr = stats->dot11FCSErrorCount;
+ __entry->rtssucc = stats->dot11RTSSuccessCount;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT " ret:%d",
+ LOCAL_PR_ARG, __entry->ret
+ )
+);
+
+TRACE_EVENT(drv_get_tkip_seq,
+ TP_PROTO(struct ieee80211_local *local,
+ u8 hw_key_idx, u32 *iv32, u16 *iv16),
+
+ TP_ARGS(local, hw_key_idx, iv32, iv16),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __field(u8, hw_key_idx)
+ __field(u32, iv32)
+ __field(u16, iv16)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ __entry->hw_key_idx = hw_key_idx;
+ __entry->iv32 = *iv32;
+ __entry->iv16 = *iv16;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT, LOCAL_PR_ARG
+ )
+);
+
+TRACE_EVENT(drv_set_rts_threshold,
+ TP_PROTO(struct ieee80211_local *local, u32 value, int ret),
+
+ TP_ARGS(local, value, ret),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __field(u32, value)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ __entry->ret = ret;
+ __entry->value = value;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT " value:%d ret:%d",
+ LOCAL_PR_ARG, __entry->value, __entry->ret
+ )
+);
+
+TRACE_EVENT(drv_sta_notify,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_vif *vif,
+ enum sta_notify_cmd cmd,
+ struct ieee80211_sta *sta),
+
+ TP_ARGS(local, vif, cmd, sta),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ STA_ENTRY
+ __field(u32, cmd)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ STA_ASSIGN;
+ __entry->cmd = cmd;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " cmd:%d",
+ LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->cmd
+ )
+);
+
+TRACE_EVENT(drv_conf_tx,
+ TP_PROTO(struct ieee80211_local *local, u16 queue,
+ const struct ieee80211_tx_queue_params *params,
+ int ret),
+
+ TP_ARGS(local, queue, params, ret),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __field(u16, queue)
+ __field(u16, txop)
+ __field(u16, cw_min)
+ __field(u16, cw_max)
+ __field(u8, aifs)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ __entry->queue = queue;
+ __entry->ret = ret;
+ __entry->txop = params->txop;
+ __entry->cw_max = params->cw_max;
+ __entry->cw_min = params->cw_min;
+ __entry->aifs = params->aifs;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT " queue:%d ret:%d",
+ LOCAL_PR_ARG, __entry->queue, __entry->ret
+ )
+);
+
+TRACE_EVENT(drv_get_tx_stats,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_tx_queue_stats *stats,
+ int ret),
+
+ TP_ARGS(local, stats, ret),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ __entry->ret = ret;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT " ret:%d",
+ LOCAL_PR_ARG, __entry->ret
+ )
+);
+
+TRACE_EVENT(drv_get_tsf,
+ TP_PROTO(struct ieee80211_local *local, u64 ret),
+
+ TP_ARGS(local, ret),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __field(u64, ret)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ __entry->ret = ret;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT " ret:%llu",
+ LOCAL_PR_ARG, (unsigned long long)__entry->ret
+ )
+);
+
+TRACE_EVENT(drv_set_tsf,
+ TP_PROTO(struct ieee80211_local *local, u64 tsf),
+
+ TP_ARGS(local, tsf),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __field(u64, tsf)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ __entry->tsf = tsf;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT " tsf:%llu",
+ LOCAL_PR_ARG, (unsigned long long)__entry->tsf
+ )
+);
+
+TRACE_EVENT(drv_reset_tsf,
+ TP_PROTO(struct ieee80211_local *local),
+
+ TP_ARGS(local),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT, LOCAL_PR_ARG
+ )
+);
+
+TRACE_EVENT(drv_tx_last_beacon,
+ TP_PROTO(struct ieee80211_local *local, int ret),
+
+ TP_ARGS(local, ret),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ __entry->ret = ret;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT " ret:%d",
+ LOCAL_PR_ARG, __entry->ret
+ )
+);
+
+TRACE_EVENT(drv_ampdu_action,
+ TP_PROTO(struct ieee80211_local *local,
+ enum ieee80211_ampdu_mlme_action action,
+ struct ieee80211_sta *sta, u16 tid,
+ u16 *ssn, int ret),
+
+ TP_ARGS(local, action, sta, tid, ssn, ret),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ STA_ENTRY
+ __field(u32, action)
+ __field(u16, tid)
+ __field(u16, ssn)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ STA_ASSIGN;
+ __entry->ret = ret;
+ __entry->action = action;
+ __entry->tid = tid;
+ __entry->ssn = *ssn;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT STA_PR_FMT " action:%d tid:%d ret:%d",
+ LOCAL_PR_ARG, STA_PR_ARG, __entry->action, __entry->tid, __entry->ret
+ )
+);
+#endif /* !__MAC80211_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE driver-trace
+#include <trace/define_trace.h>
diff --git a/net/mac80211/event.c b/net/mac80211/event.c
index f288d01a6344..01ae759518f6 100644
--- a/net/mac80211/event.c
+++ b/net/mac80211/event.c
@@ -7,8 +7,7 @@
*
* mac80211 - events
*/
-
-#include <net/iw_handler.h>
+#include <net/cfg80211.h>
#include "ieee80211_i.h"
/*
@@ -17,26 +16,12 @@
* driver or is still in the frame), it should provide that information.
*/
void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx,
- struct ieee80211_hdr *hdr, const u8 *tsc)
+ struct ieee80211_hdr *hdr, const u8 *tsc,
+ gfp_t gfp)
{
- union iwreq_data wrqu;
- char *buf = kmalloc(128, GFP_ATOMIC);
-
- if (buf) {
- /* TODO: needed parameters: count, key type, TSC */
- sprintf(buf, "MLME-MICHAELMICFAILURE.indication("
- "keyid=%d %scast addr=%pM)",
- keyidx, hdr->addr1[0] & 0x01 ? "broad" : "uni",
- hdr->addr2);
- memset(&wrqu, 0, sizeof(wrqu));
- wrqu.data.length = strlen(buf);
- wireless_send_event(sdata->dev, IWEVCUSTOM, &wrqu, buf);
- kfree(buf);
- }
-
cfg80211_michael_mic_failure(sdata->dev, hdr->addr2,
(hdr->addr1[0] & 0x01) ?
NL80211_KEYTYPE_GROUP :
NL80211_KEYTYPE_PAIRWISE,
- keyidx, tsc);
+ keyidx, tsc, gfp);
}
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 0b30277eb366..920ec8792f4b 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -57,7 +57,7 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata,
*/
if (auth_alg == WLAN_AUTH_OPEN && auth_transaction == 1)
ieee80211_send_auth(sdata, 2, WLAN_AUTH_OPEN, NULL, 0,
- sdata->u.ibss.bssid, 0);
+ sdata->u.ibss.bssid, NULL, 0, 0);
}
static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata,
@@ -494,7 +494,7 @@ static void ieee80211_sta_create_ibss(struct ieee80211_sub_if_data *sdata)
capability = WLAN_CAPABILITY_IBSS;
- if (sdata->default_key)
+ if (ifibss->privacy)
capability |= WLAN_CAPABILITY_PRIVACY;
else
sdata->drop_unencrypted = 0;
@@ -524,9 +524,8 @@ static void ieee80211_sta_find_ibss(struct ieee80211_sub_if_data *sdata)
return;
capability = WLAN_CAPABILITY_IBSS;
- if (sdata->default_key)
+ if (ifibss->privacy)
capability |= WLAN_CAPABILITY_PRIVACY;
-
if (ifibss->fixed_bssid)
bssid = ifibss->bssid;
if (ifibss->fixed_channel)
@@ -705,7 +704,7 @@ static void ieee80211_ibss_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt;
u16 fc;
- rx_status = (struct ieee80211_rx_status *) skb->cb;
+ rx_status = IEEE80211_SKB_RXCB(skb);
mgmt = (struct ieee80211_mgmt *) skb->data;
fc = le16_to_cpu(mgmt->frame_control);
@@ -743,7 +742,7 @@ static void ieee80211_ibss_work(struct work_struct *work)
if (!netif_running(sdata->dev))
return;
- if (local->sw_scanning || local->hw_scanning)
+ if (local->scanning)
return;
if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_ADHOC))
@@ -782,7 +781,7 @@ static void ieee80211_ibss_timer(unsigned long data)
}
set_bit(IEEE80211_IBSS_REQ_RUN, &ifibss->request);
- queue_work(local->hw.workqueue, &ifibss->work);
+ ieee80211_queue_work(&local->hw, &ifibss->work);
}
#ifdef CONFIG_PM
@@ -836,8 +835,7 @@ void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local)
}
ieee80211_rx_result
-ieee80211_ibss_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
- struct ieee80211_rx_status *rx_status)
+ieee80211_ibss_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_mgmt *mgmt;
@@ -852,11 +850,10 @@ ieee80211_ibss_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
switch (fc & IEEE80211_FCTL_STYPE) {
case IEEE80211_STYPE_PROBE_RESP:
case IEEE80211_STYPE_BEACON:
- memcpy(skb->cb, rx_status, sizeof(*rx_status));
case IEEE80211_STYPE_PROBE_REQ:
case IEEE80211_STYPE_AUTH:
skb_queue_tail(&sdata->u.ibss.skb_queue, skb);
- queue_work(local->hw.workqueue, &sdata->u.ibss.work);
+ ieee80211_queue_work(&local->hw, &sdata->u.ibss.work);
return RX_QUEUED;
}
@@ -874,6 +871,8 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
} else
sdata->u.ibss.fixed_bssid = false;
+ sdata->u.ibss.privacy = params->privacy;
+
sdata->vif.bss_conf.beacon_int = params->beacon_interval;
sdata->u.ibss.channel = params->channel;
@@ -913,7 +912,7 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
ieee80211_recalc_idle(sdata->local);
set_bit(IEEE80211_IBSS_REQ_RUN, &sdata->u.ibss.request);
- queue_work(sdata->local->hw.workqueue, &sdata->u.ibss.work);
+ ieee80211_queue_work(&sdata->local->hw, &sdata->u.ibss.work);
return 0;
}
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 68eb5052179a..588005c84a6d 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -24,7 +24,6 @@
#include <linux/spinlock.h>
#include <linux/etherdevice.h>
#include <net/cfg80211.h>
-#include <net/iw_handler.h>
#include <net/mac80211.h>
#include "key.h"
#include "sta_info.h"
@@ -213,7 +212,9 @@ struct ieee80211_if_vlan {
};
struct mesh_stats {
- __u32 fwded_frames; /* Mesh forwarded frames */
+ __u32 fwded_mcast; /* Mesh forwarded multicast frames */
+ __u32 fwded_unicast; /* Mesh forwarded unicast frames */
+ __u32 fwded_frames; /* Mesh total forwarded frames */
__u32 dropped_frames_ttl; /* Not transmitted since mesh_ttl == 0*/
__u32 dropped_frames_no_route; /* Not transmitted, no route found */
atomic_t estab_plinks;
@@ -227,86 +228,81 @@ struct mesh_preq_queue {
u8 flags;
};
+enum ieee80211_mgd_state {
+ IEEE80211_MGD_STATE_IDLE,
+ IEEE80211_MGD_STATE_PROBE,
+ IEEE80211_MGD_STATE_AUTH,
+ IEEE80211_MGD_STATE_ASSOC,
+};
+
+struct ieee80211_mgd_work {
+ struct list_head list;
+ struct ieee80211_bss *bss;
+ int ie_len;
+ u8 prev_bssid[ETH_ALEN];
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+ u8 ssid_len;
+ unsigned long timeout;
+ enum ieee80211_mgd_state state;
+ u16 auth_alg, auth_transaction;
+
+ int tries;
+
+ u8 key[WLAN_KEY_LEN_WEP104];
+ u8 key_len, key_idx;
+
+ /* must be last */
+ u8 ie[0]; /* for auth or assoc frame, not probe */
+};
+
/* flags used in struct ieee80211_if_managed.flags */
-#define IEEE80211_STA_SSID_SET BIT(0)
-#define IEEE80211_STA_BSSID_SET BIT(1)
-#define IEEE80211_STA_PREV_BSSID_SET BIT(2)
-#define IEEE80211_STA_AUTHENTICATED BIT(3)
-#define IEEE80211_STA_ASSOCIATED BIT(4)
-#define IEEE80211_STA_PROBEREQ_POLL BIT(5)
-#define IEEE80211_STA_CREATE_IBSS BIT(6)
-#define IEEE80211_STA_CONTROL_PORT BIT(7)
-#define IEEE80211_STA_WMM_ENABLED BIT(8)
-/* hole at 9, please re-use */
-#define IEEE80211_STA_AUTO_SSID_SEL BIT(10)
-#define IEEE80211_STA_AUTO_BSSID_SEL BIT(11)
-#define IEEE80211_STA_AUTO_CHANNEL_SEL BIT(12)
-#define IEEE80211_STA_PRIVACY_INVOKED BIT(13)
-#define IEEE80211_STA_TKIP_WEP_USED BIT(14)
-#define IEEE80211_STA_CSA_RECEIVED BIT(15)
-#define IEEE80211_STA_MFP_ENABLED BIT(16)
-#define IEEE80211_STA_EXT_SME BIT(17)
-/* flags for MLME request */
-#define IEEE80211_STA_REQ_SCAN 0
-#define IEEE80211_STA_REQ_AUTH 1
-#define IEEE80211_STA_REQ_RUN 2
+enum ieee80211_sta_flags {
+ IEEE80211_STA_BEACON_POLL = BIT(0),
+ IEEE80211_STA_CONNECTION_POLL = BIT(1),
+ IEEE80211_STA_CONTROL_PORT = BIT(2),
+ IEEE80211_STA_WMM_ENABLED = BIT(3),
+ IEEE80211_STA_DISABLE_11N = BIT(4),
+ IEEE80211_STA_CSA_RECEIVED = BIT(5),
+ IEEE80211_STA_MFP_ENABLED = BIT(6),
+};
-/* bitfield of allowed auth algs */
-#define IEEE80211_AUTH_ALG_OPEN BIT(0)
-#define IEEE80211_AUTH_ALG_SHARED_KEY BIT(1)
-#define IEEE80211_AUTH_ALG_LEAP BIT(2)
-#define IEEE80211_AUTH_ALG_FT BIT(3)
+/* flags for MLME request */
+enum ieee80211_sta_request {
+ IEEE80211_STA_REQ_SCAN,
+};
struct ieee80211_if_managed {
struct timer_list timer;
+ struct timer_list conn_mon_timer;
+ struct timer_list bcn_mon_timer;
struct timer_list chswitch_timer;
struct work_struct work;
+ struct work_struct monitor_work;
struct work_struct chswitch_work;
struct work_struct beacon_loss_work;
- u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
+ unsigned long probe_timeout;
+ int probe_send_count;
- u8 ssid[IEEE80211_MAX_SSID_LEN];
- size_t ssid_len;
+ struct mutex mtx;
+ struct ieee80211_bss *associated;
+ struct ieee80211_mgd_work *old_associate_work;
+ struct list_head work_list;
- enum {
- IEEE80211_STA_MLME_DISABLED,
- IEEE80211_STA_MLME_DIRECT_PROBE,
- IEEE80211_STA_MLME_AUTHENTICATE,
- IEEE80211_STA_MLME_ASSOCIATE,
- IEEE80211_STA_MLME_ASSOCIATED,
- } state;
+ u8 bssid[ETH_ALEN];
u16 aid;
- u16 ap_capab, capab;
- u8 *extra_ie; /* to be added to the end of AssocReq */
- size_t extra_ie_len;
-
- /* The last AssocReq/Resp IEs */
- u8 *assocreq_ies, *assocresp_ies;
- size_t assocreq_ies_len, assocresp_ies_len;
+ u16 capab;
struct sk_buff_head skb_queue;
- int assoc_scan_tries; /* number of scans done pre-association */
- int direct_probe_tries; /* retries for direct probes */
- int auth_tries; /* retries for auth req */
- int assoc_tries; /* retries for assoc req */
-
unsigned long timers_running; /* used for quiesce/restart */
bool powersave; /* powersave requested for this iface */
unsigned long request;
- unsigned long last_probe;
- unsigned long last_beacon;
-
unsigned int flags;
- unsigned int auth_algs; /* bitfield of allowed auth algs */
- int auth_alg; /* currently used IEEE 802.11 authentication algorithm */
- int auth_transaction;
-
u32 beacon_crc;
enum {
@@ -316,10 +312,6 @@ struct ieee80211_if_managed {
} mfp; /* management frame protection */
int wmm_last_param_set;
-
- /* Extra IE data for management frames */
- u8 *sme_auth_ie;
- size_t sme_auth_ie_len;
};
enum ieee80211_ibss_request {
@@ -339,6 +331,7 @@ struct ieee80211_if_ibss {
bool fixed_bssid;
bool fixed_channel;
+ bool privacy;
u8 bssid[ETH_ALEN];
u8 ssid[IEEE80211_MAX_SSID_LEN];
@@ -364,7 +357,7 @@ struct ieee80211_if_mesh {
unsigned long timers_running;
- bool housekeeping;
+ unsigned long wrkq_flags;
u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN];
size_t mesh_id_len;
@@ -374,6 +367,10 @@ struct ieee80211_if_mesh {
u8 mesh_pm_id[4];
/* Congestion Control Mode Identifier */
u8 mesh_cc_id[4];
+ /* Synchronization Protocol Identifier */
+ u8 mesh_sp_id[4];
+ /* Authentication Protocol Identifier */
+ u8 mesh_auth_id[4];
/* Local mesh Destination Sequence Number */
u32 dsn;
/* Last used PREQ ID */
@@ -478,20 +475,9 @@ struct ieee80211_sub_if_data {
union {
struct {
struct dentry *drop_unencrypted;
- struct dentry *state;
struct dentry *bssid;
- struct dentry *prev_bssid;
- struct dentry *ssid_len;
struct dentry *aid;
- struct dentry *ap_capab;
struct dentry *capab;
- struct dentry *extra_ie_len;
- struct dentry *auth_tries;
- struct dentry *assoc_tries;
- struct dentry *auth_algs;
- struct dentry *auth_alg;
- struct dentry *auth_transaction;
- struct dentry *flags;
struct dentry *force_unicast_rateidx;
struct dentry *max_ratectrl_rateidx;
} sta;
@@ -526,6 +512,8 @@ struct ieee80211_sub_if_data {
#ifdef CONFIG_MAC80211_MESH
struct dentry *mesh_stats_dir;
struct {
+ struct dentry *fwded_mcast;
+ struct dentry *fwded_unicast;
struct dentry *fwded_frames;
struct dentry *dropped_frames_ttl;
struct dentry *dropped_frames_no_route;
@@ -588,12 +576,44 @@ enum queue_stop_reason {
IEEE80211_QUEUE_STOP_REASON_CSA,
IEEE80211_QUEUE_STOP_REASON_AGGREGATION,
IEEE80211_QUEUE_STOP_REASON_SUSPEND,
- IEEE80211_QUEUE_STOP_REASON_PENDING,
IEEE80211_QUEUE_STOP_REASON_SKB_ADD,
};
-struct ieee80211_master_priv {
- struct ieee80211_local *local;
+/**
+ * mac80211 scan flags - currently active scan mode
+ *
+ * @SCAN_SW_SCANNING: We're currently in the process of scanning but may as
+ * well be on the operating channel
+ * @SCAN_HW_SCANNING: The hardware is scanning for us, we have no way to
+ * determine if we are on the operating channel or not
+ * @SCAN_OFF_CHANNEL: We're off our operating channel for scanning,
+ * gets only set in conjunction with SCAN_SW_SCANNING
+ */
+enum {
+ SCAN_SW_SCANNING,
+ SCAN_HW_SCANNING,
+ SCAN_OFF_CHANNEL,
+};
+
+/**
+ * enum mac80211_scan_state - scan state machine states
+ *
+ * @SCAN_DECISION: Main entry point to the scan state machine, this state
+ * determines if we should keep on scanning or switch back to the
+ * operating channel
+ * @SCAN_SET_CHANNEL: Set the next channel to be scanned
+ * @SCAN_SEND_PROBE: Send probe requests and wait for probe responses
+ * @SCAN_LEAVE_OPER_CHANNEL: Leave the operating channel, notify the AP
+ * about us leaving the channel and stop all associated STA interfaces
+ * @SCAN_ENTER_OPER_CHANNEL: Enter the operating channel again, notify the
+ * AP about us being back and restart all associated STA interfaces
+ */
+enum mac80211_scan_state {
+ SCAN_DECISION,
+ SCAN_SET_CHANNEL,
+ SCAN_SEND_PROBE,
+ SCAN_LEAVE_OPER_CHANNEL,
+ SCAN_ENTER_OPER_CHANNEL,
};
struct ieee80211_local {
@@ -604,17 +624,33 @@ struct ieee80211_local {
const struct ieee80211_ops *ops;
+ /*
+ * private workqueue to mac80211. mac80211 makes this accessible
+ * via ieee80211_queue_work()
+ */
+ struct workqueue_struct *workqueue;
+
unsigned long queue_stop_reasons[IEEE80211_MAX_QUEUES];
/* also used to protect ampdu_ac_queue and amdpu_ac_stop_refcnt */
spinlock_t queue_stop_reason_lock;
- struct net_device *mdev; /* wmaster# - "master" 802.11 device */
int open_count;
int monitors, cooked_mntrs;
/* number of interfaces with corresponding FIF_ flags */
- int fif_fcsfail, fif_plcpfail, fif_control, fif_other_bss;
+ int fif_fcsfail, fif_plcpfail, fif_control, fif_other_bss, fif_pspoll;
unsigned int filter_flags; /* FIF_* */
struct iw_statistics wstats;
+
+ /* protects the aggregated multicast list and filter calls */
+ spinlock_t filter_lock;
+
+ /* used for uploading changed mc list */
+ struct work_struct reconfig_filter;
+
+ /* aggregated multicast list */
+ struct dev_addr_list *mc_list;
+ int mc_count;
+
bool tim_in_locked_section; /* see ieee80211_beacon_get() */
/*
@@ -631,6 +667,9 @@ struct ieee80211_local {
*/
bool quiescing;
+ /* device is started */
+ bool started;
+
int tx_headroom; /* required headroom for hardware/radiotap */
/* Tasklet and skb queue to process calls from IRQ mode. All frames
@@ -653,6 +692,7 @@ struct ieee80211_local {
struct list_head sta_list;
struct sta_info *sta_hash[STA_HASH_SIZE];
struct timer_list sta_cleanup;
+ int sta_generation;
struct sk_buff_head pending[IEEE80211_MAX_QUEUES];
struct tasklet_struct tx_pending_tasklet;
@@ -687,9 +727,9 @@ struct ieee80211_local {
/* Scanning and BSS list */
struct mutex scan_mtx;
- bool sw_scanning, hw_scanning;
+ unsigned long scanning;
struct cfg80211_ssid scan_ssid;
- struct cfg80211_scan_request int_scan_req;
+ struct cfg80211_scan_request *int_scan_req;
struct cfg80211_scan_request *scan_req;
struct ieee80211_channel *scan_channel;
const u8 *orig_ies;
@@ -697,7 +737,7 @@ struct ieee80211_local {
int scan_channel_idx;
int scan_ies_len;
- enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state;
+ enum mac80211_scan_state next_scan_state;
struct delayed_work scan_work;
struct ieee80211_sub_if_data *scan_sdata;
enum nl80211_channel_type oper_channel_type;
@@ -834,10 +874,6 @@ struct ieee80211_local {
static inline struct ieee80211_sub_if_data *
IEEE80211_DEV_TO_SUB_IF(struct net_device *dev)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
-
- BUG_ON(!local || local->mdev == dev);
-
return netdev_priv(dev);
}
@@ -937,21 +973,20 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
void ieee80211_configure_filter(struct ieee80211_local *local);
u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata);
-/* wireless extensions */
-extern const struct iw_handler_def ieee80211_iw_handler_def;
-
/* STA code */
void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata);
+int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_auth_request *req);
+int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_assoc_request *req);
+int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_deauth_request *req,
+ void *cookie);
+int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_disassoc_request *req,
+ void *cookie);
ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb,
- struct ieee80211_rx_status *rx_status);
-int ieee80211_sta_commit(struct ieee80211_sub_if_data *sdata);
-int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t len);
-int ieee80211_sta_get_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t *len);
-int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid);
-void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata);
-int ieee80211_sta_deauthenticate(struct ieee80211_sub_if_data *sdata, u16 reason);
-int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason);
+ struct sk_buff *skb);
void ieee80211_send_pspoll(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata);
void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency);
@@ -967,8 +1002,7 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);
void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local);
void ieee80211_ibss_setup_sdata(struct ieee80211_sub_if_data *sdata);
ieee80211_rx_result
-ieee80211_ibss_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
- struct ieee80211_rx_status *rx_status);
+ieee80211_ibss_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb);
struct sta_info *ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata,
u8 *bssid, u8 *addr, u32 supp_rates);
int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata,
@@ -983,16 +1017,9 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
const u8 *ssid, u8 ssid_len);
int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
struct cfg80211_scan_request *req);
-int ieee80211_scan_results(struct ieee80211_local *local,
- struct iw_request_info *info,
- char *buf, size_t len);
void ieee80211_scan_cancel(struct ieee80211_local *local);
ieee80211_rx_result
-ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb,
- struct ieee80211_rx_status *rx_status);
-int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata,
- const char *ie, size_t len);
+ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb);
void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local);
struct ieee80211_bss *
@@ -1008,8 +1035,6 @@ ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq,
u8 *ssid, u8 ssid_len);
void ieee80211_rx_bss_put(struct ieee80211_local *local,
struct ieee80211_bss *bss);
-void ieee80211_rx_bss_remove(struct ieee80211_sub_if_data *sdata, u8 *bssid,
- int freq, u8 *ssid, u8 ssid_len);
/* interface handling */
int ieee80211_if_add(struct ieee80211_local *local, const char *name,
@@ -1025,9 +1050,10 @@ void ieee80211_recalc_idle(struct ieee80211_local *local);
/* tx handling */
void ieee80211_clear_tx_pending(struct ieee80211_local *local);
void ieee80211_tx_pending(unsigned long data);
-int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev);
-int ieee80211_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev);
-int ieee80211_subif_start_xmit(struct sk_buff *skb, struct net_device *dev);
+netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
+ struct net_device *dev);
+netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
+ struct net_device *dev);
/* HT */
void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband,
@@ -1065,6 +1091,7 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
/* Suspend/resume and hw reconfiguration */
int ieee80211_reconfig(struct ieee80211_local *local);
+void ieee80211_stop_device(struct ieee80211_local *local);
#ifdef CONFIG_PM
int __ieee80211_suspend(struct ieee80211_hw *hw);
@@ -1092,7 +1119,8 @@ u8 *ieee80211_get_bssid(struct ieee80211_hdr *hdr, size_t len,
int ieee80211_frame_duration(struct ieee80211_local *local, size_t len,
int rate, int erp, int short_preamble);
void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int keyidx,
- struct ieee80211_hdr *hdr, const u8 *tsc);
+ struct ieee80211_hdr *hdr, const u8 *tsc,
+ gfp_t gfp);
void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata);
void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
int encrypt);
@@ -1129,8 +1157,8 @@ int ieee80211_add_pending_skbs(struct ieee80211_local *local,
void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
u16 transaction, u16 auth_alg,
- u8 *extra, size_t extra_len,
- const u8 *bssid, int encrypt);
+ u8 *extra, size_t extra_len, const u8 *bssid,
+ const u8 *key, u8 key_len, u8 key_idx);
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
const u8 *ie, size_t ie_len);
void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index b7c8a4484298..b8295cbd7e8f 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -190,10 +190,6 @@ static int ieee80211_open(struct net_device *dev)
ETH_ALEN);
}
- if (compare_ether_addr(null_addr, local->mdev->dev_addr) == 0)
- memcpy(local->mdev->dev_addr, local->hw.wiphy->perm_addr,
- ETH_ALEN);
-
/*
* Validate the MAC address for this device.
*/
@@ -224,18 +220,15 @@ static int ieee80211_open(struct net_device *dev)
local->fif_fcsfail++;
if (sdata->u.mntr_flags & MONITOR_FLAG_PLCPFAIL)
local->fif_plcpfail++;
- if (sdata->u.mntr_flags & MONITOR_FLAG_CONTROL)
+ if (sdata->u.mntr_flags & MONITOR_FLAG_CONTROL) {
local->fif_control++;
+ local->fif_pspoll++;
+ }
if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS)
local->fif_other_bss++;
- netif_addr_lock_bh(local->mdev);
ieee80211_configure_filter(local);
- netif_addr_unlock_bh(local->mdev);
break;
- case NL80211_IFTYPE_STATION:
- sdata->u.mgd.flags &= ~IEEE80211_STA_PREV_BSSID_SET;
- /* fall through */
default:
conf.vif = &sdata->vif;
conf.type = sdata->vif.type;
@@ -246,12 +239,15 @@ static int ieee80211_open(struct net_device *dev)
if (ieee80211_vif_is_mesh(&sdata->vif)) {
local->fif_other_bss++;
- netif_addr_lock_bh(local->mdev);
ieee80211_configure_filter(local);
- netif_addr_unlock_bh(local->mdev);
ieee80211_start_mesh(sdata);
+ } else if (sdata->vif.type == NL80211_IFTYPE_AP) {
+ local->fif_pspoll++;
+
+ ieee80211_configure_filter(local);
}
+
changed |= ieee80211_reset_erp_info(sdata);
ieee80211_bss_info_change_notify(sdata, changed);
ieee80211_enable_keys(sdata);
@@ -281,15 +277,6 @@ static int ieee80211_open(struct net_device *dev)
}
}
- if (local->open_count == 0) {
- res = dev_open(local->mdev);
- WARN_ON(res);
- if (res)
- goto err_del_interface;
- tasklet_enable(&local->tx_pending_tasklet);
- tasklet_enable(&local->tasklet);
- }
-
/*
* set_multicast_list will be invoked by the networking core
* which will check whether any increments here were done in
@@ -323,7 +310,7 @@ static int ieee80211_open(struct net_device *dev)
* to fix this.
*/
if (sdata->vif.type == NL80211_IFTYPE_STATION)
- queue_work(local->hw.workqueue, &sdata->u.mgd.work);
+ ieee80211_queue_work(&local->hw, &sdata->u.mgd.work);
netif_tx_start_all_queues(dev);
@@ -346,7 +333,10 @@ static int ieee80211_stop(struct net_device *dev)
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_init_conf conf;
struct sta_info *sta;
+ unsigned long flags;
+ struct sk_buff *skb, *tmp;
u32 hw_reconf_flags = 0;
+ int i;
/*
* Stop TX on this interface first.
@@ -366,18 +356,6 @@ static int ieee80211_stop(struct net_device *dev)
rcu_read_unlock();
/*
- * Announce that we are leaving the network, in case we are a
- * station interface type. This must be done before removing
- * all stations associated with sta_info_flush, otherwise STA
- * information will be gone and no announce being done.
- */
- if (sdata->vif.type == NL80211_IFTYPE_STATION) {
- if (sdata->u.mgd.state != IEEE80211_STA_MLME_DISABLED)
- ieee80211_sta_deauthenticate(sdata,
- WLAN_REASON_DEAUTH_LEAVING);
- }
-
- /*
* Remove all stations associated with this interface.
*
* This must be done before calling ops->remove_interface()
@@ -408,13 +386,24 @@ static int ieee80211_stop(struct net_device *dev)
if (sdata->flags & IEEE80211_SDATA_PROMISC)
atomic_dec(&local->iff_promiscs);
- dev_mc_unsync(local->mdev, dev);
+ if (sdata->vif.type == NL80211_IFTYPE_AP)
+ local->fif_pspoll--;
+
+ netif_addr_lock_bh(dev);
+ spin_lock_bh(&local->filter_lock);
+ __dev_addr_unsync(&local->mc_list, &local->mc_count,
+ &dev->mc_list, &dev->mc_count);
+ spin_unlock_bh(&local->filter_lock);
+ netif_addr_unlock_bh(dev);
+
+ ieee80211_configure_filter(local);
+
del_timer_sync(&local->dynamic_ps_timer);
cancel_work_sync(&local->dynamic_ps_enable_work);
/* APs need special treatment */
if (sdata->vif.type == NL80211_IFTYPE_AP) {
- struct ieee80211_sub_if_data *vlan, *tmp;
+ struct ieee80211_sub_if_data *vlan, *tmpsdata;
struct beacon_data *old_beacon = sdata->u.ap.beacon;
/* remove beacon */
@@ -423,7 +412,7 @@ static int ieee80211_stop(struct net_device *dev)
kfree(old_beacon);
/* down all dependent devices, that is VLANs */
- list_for_each_entry_safe(vlan, tmp, &sdata->u.ap.vlans,
+ list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans,
u.vlan.list)
dev_close(vlan->dev);
WARN_ON(!list_empty(&sdata->u.ap.vlans));
@@ -452,29 +441,30 @@ static int ieee80211_stop(struct net_device *dev)
local->fif_fcsfail--;
if (sdata->u.mntr_flags & MONITOR_FLAG_PLCPFAIL)
local->fif_plcpfail--;
- if (sdata->u.mntr_flags & MONITOR_FLAG_CONTROL)
+ if (sdata->u.mntr_flags & MONITOR_FLAG_CONTROL) {
+ local->fif_pspoll--;
local->fif_control--;
+ }
if (sdata->u.mntr_flags & MONITOR_FLAG_OTHER_BSS)
local->fif_other_bss--;
- netif_addr_lock_bh(local->mdev);
ieee80211_configure_filter(local);
- netif_addr_unlock_bh(local->mdev);
break;
case NL80211_IFTYPE_STATION:
- memset(sdata->u.mgd.bssid, 0, ETH_ALEN);
del_timer_sync(&sdata->u.mgd.chswitch_timer);
del_timer_sync(&sdata->u.mgd.timer);
+ del_timer_sync(&sdata->u.mgd.conn_mon_timer);
+ del_timer_sync(&sdata->u.mgd.bcn_mon_timer);
/*
- * If the timer fired while we waited for it, it will have
- * requeued the work. Now the work will be running again
+ * If any of the timers fired while we waited for it, it will
+ * have queued its work. Now the work will be running again
* but will not rearm the timer again because it checks
* whether the interface is running, which, at this point,
* it no longer is.
*/
cancel_work_sync(&sdata->u.mgd.work);
cancel_work_sync(&sdata->u.mgd.chswitch_work);
-
+ cancel_work_sync(&sdata->u.mgd.monitor_work);
cancel_work_sync(&sdata->u.mgd.beacon_loss_work);
/*
@@ -485,12 +475,6 @@ static int ieee80211_stop(struct net_device *dev)
*/
synchronize_rcu();
skb_queue_purge(&sdata->u.mgd.skb_queue);
-
- sdata->u.mgd.flags &= ~(IEEE80211_STA_PRIVACY_INVOKED |
- IEEE80211_STA_TKIP_WEP_USED);
- kfree(sdata->u.mgd.extra_ie);
- sdata->u.mgd.extra_ie = NULL;
- sdata->u.mgd.extra_ie_len = 0;
/* fall through */
case NL80211_IFTYPE_ADHOC:
if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
@@ -507,37 +491,23 @@ static int ieee80211_stop(struct net_device *dev)
local->fif_other_bss--;
atomic_dec(&local->iff_allmultis);
- netif_addr_lock_bh(local->mdev);
ieee80211_configure_filter(local);
- netif_addr_unlock_bh(local->mdev);
ieee80211_stop_mesh(sdata);
}
/* fall through */
default:
- if (local->scan_sdata == sdata) {
- if (!local->ops->hw_scan)
- cancel_delayed_work_sync(&local->scan_work);
- /*
- * The software scan can no longer run now, so we can
- * clear out the scan_sdata reference. However, the
- * hardware scan may still be running. The complete
- * function must be prepared to handle a NULL value.
- */
- local->scan_sdata = NULL;
- /*
- * The memory barrier guarantees that another CPU
- * that is hardware-scanning will now see the fact
- * that this interface is gone.
- */
- smp_mb();
- /*
- * If software scanning, complete the scan but since
- * the scan_sdata is NULL already don't send out a
- * scan event to userspace -- the scan is incomplete.
- */
- if (local->sw_scanning)
- ieee80211_scan_completed(&local->hw, true);
+ if (local->scan_sdata == sdata)
+ ieee80211_scan_cancel(local);
+
+ /*
+ * Disable beaconing for AP and mesh, IBSS can't
+ * still be joined to a network at this point.
+ */
+ if (sdata->vif.type == NL80211_IFTYPE_AP ||
+ sdata->vif.type == NL80211_IFTYPE_MESH_POINT) {
+ ieee80211_bss_info_change_notify(sdata,
+ BSS_CHANGED_BEACON_ENABLED);
}
conf.vif = &sdata->vif;
@@ -555,17 +525,8 @@ static int ieee80211_stop(struct net_device *dev)
ieee80211_recalc_ps(local, -1);
if (local->open_count == 0) {
- if (netif_running(local->mdev))
- dev_close(local->mdev);
-
- drv_stop(local);
-
- ieee80211_led_radio(local, false);
-
- flush_workqueue(local->hw.workqueue);
-
- tasklet_disable(&local->tx_pending_tasklet);
- tasklet_disable(&local->tasklet);
+ ieee80211_clear_tx_pending(local);
+ ieee80211_stop_device(local);
/* no reconfiguring after stop! */
hw_reconf_flags = 0;
@@ -575,6 +536,18 @@ static int ieee80211_stop(struct net_device *dev)
if (hw_reconf_flags)
ieee80211_hw_config(local, hw_reconf_flags);
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+ for (i = 0; i < IEEE80211_MAX_QUEUES; i++) {
+ skb_queue_walk_safe(&local->pending[i], skb, tmp) {
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ if (info->control.vif == &sdata->vif) {
+ __skb_unlink(skb, &local->pending[i]);
+ dev_kfree_skb_irq(skb);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+
return 0;
}
@@ -604,8 +577,11 @@ static void ieee80211_set_multicast_list(struct net_device *dev)
atomic_dec(&local->iff_promiscs);
sdata->flags ^= IEEE80211_SDATA_PROMISC;
}
-
- dev_mc_sync(local->mdev, dev);
+ spin_lock_bh(&local->filter_lock);
+ __dev_addr_sync(&local->mc_list, &local->mc_count,
+ &dev->mc_list, &dev->mc_count);
+ spin_unlock_bh(&local->filter_lock);
+ ieee80211_queue_work(&local->hw, &local->reconfig_filter);
}
/*
@@ -652,11 +628,6 @@ static void ieee80211_teardown_sdata(struct net_device *dev)
kfree_skb(sdata->u.ibss.presp);
break;
case NL80211_IFTYPE_STATION:
- kfree(sdata->u.mgd.extra_ie);
- kfree(sdata->u.mgd.assocreq_ies);
- kfree(sdata->u.mgd.assocresp_ies);
- kfree(sdata->u.mgd.sme_auth_ie);
- break;
case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_MONITOR:
@@ -695,7 +666,6 @@ static void ieee80211_if_setup(struct net_device *dev)
{
ether_setup(dev);
dev->netdev_ops = &ieee80211_dataif_ops;
- dev->wireless_handlers = &ieee80211_iw_handler_def;
dev->destructor = free_netdev;
}
@@ -784,6 +754,10 @@ int ieee80211_if_change_type(struct ieee80211_sub_if_data *sdata,
return 0;
}
+static struct device_type wiphy_type = {
+ .name = "wlan",
+};
+
int ieee80211_if_add(struct ieee80211_local *local, const char *name,
struct net_device **new_dev, enum nl80211_iftype type,
struct vif_params *params)
@@ -798,6 +772,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
name, ieee80211_if_setup);
if (!ndev)
return -ENOMEM;
+ dev_net_set(ndev, wiphy_net(local->hw.wiphy));
ndev->needed_headroom = local->tx_headroom +
4*6 /* four MAC addresses */
@@ -814,7 +789,7 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
memcpy(ndev->dev_addr, local->hw.wiphy->perm_addr, ETH_ALEN);
SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy));
- ndev->features |= NETIF_F_NETNS_LOCAL;
+ SET_NETDEV_DEVTYPE(ndev, &wiphy_type);
/* don't use IEEE80211_DEV_TO_SUB_IF because it checks too much */
sdata = netdev_priv(ndev);
@@ -931,7 +906,7 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local)
struct ieee80211_sub_if_data *sdata;
int count = 0;
- if (local->hw_scanning || local->sw_scanning)
+ if (local->scanning)
return ieee80211_idle_off(local, "scanning");
list_for_each_entry(sdata, &local->interfaces, list) {
@@ -939,7 +914,8 @@ u32 __ieee80211_recalc_idle(struct ieee80211_local *local)
continue;
/* do not count disabled managed interfaces */
if (sdata->vif.type == NL80211_IFTYPE_STATION &&
- sdata->u.mgd.state == IEEE80211_STA_MLME_DISABLED)
+ !sdata->u.mgd.associated &&
+ list_empty(&sdata->u.mgd.work_list))
continue;
/* do not count unused IBSS interfaces */
if (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 092a017b237e..797f53942e5f 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -50,9 +50,9 @@ struct ieee80211_tx_status_rtap_hdr {
} __attribute__ ((packed));
-/* must be called under mdev tx lock */
void ieee80211_configure_filter(struct ieee80211_local *local)
{
+ u64 mc;
unsigned int changed_flags;
unsigned int new_flags = 0;
@@ -62,7 +62,7 @@ void ieee80211_configure_filter(struct ieee80211_local *local)
if (atomic_read(&local->iff_allmultis))
new_flags |= FIF_ALLMULTI;
- if (local->monitors)
+ if (local->monitors || local->scanning)
new_flags |= FIF_BCN_PRBRESP_PROMISC;
if (local->fif_fcsfail)
@@ -77,77 +77,29 @@ void ieee80211_configure_filter(struct ieee80211_local *local)
if (local->fif_other_bss)
new_flags |= FIF_OTHER_BSS;
+ if (local->fif_pspoll)
+ new_flags |= FIF_PSPOLL;
+
+ spin_lock_bh(&local->filter_lock);
changed_flags = local->filter_flags ^ new_flags;
+ mc = drv_prepare_multicast(local, local->mc_count, local->mc_list);
+ spin_unlock_bh(&local->filter_lock);
+
/* be a bit nasty */
new_flags |= (1<<31);
- drv_configure_filter(local, changed_flags, &new_flags,
- local->mdev->mc_count,
- local->mdev->mc_list);
+ drv_configure_filter(local, changed_flags, &new_flags, mc);
WARN_ON(new_flags & (1<<31));
local->filter_flags = new_flags & ~(1<<31);
}
-/* master interface */
-
-static int header_parse_80211(const struct sk_buff *skb, unsigned char *haddr)
-{
- memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); /* addr2 */
- return ETH_ALEN;
-}
-
-static const struct header_ops ieee80211_header_ops = {
- .create = eth_header,
- .parse = header_parse_80211,
- .rebuild = eth_rebuild_header,
- .cache = eth_header_cache,
- .cache_update = eth_header_cache_update,
-};
-
-static int ieee80211_master_open(struct net_device *dev)
+static void ieee80211_reconfig_filter(struct work_struct *work)
{
- struct ieee80211_master_priv *mpriv = netdev_priv(dev);
- struct ieee80211_local *local = mpriv->local;
- struct ieee80211_sub_if_data *sdata;
- int res = -EOPNOTSUPP;
-
- /* we hold the RTNL here so can safely walk the list */
- list_for_each_entry(sdata, &local->interfaces, list) {
- if (netif_running(sdata->dev)) {
- res = 0;
- break;
- }
- }
-
- if (res)
- return res;
-
- netif_tx_start_all_queues(local->mdev);
-
- return 0;
-}
-
-static int ieee80211_master_stop(struct net_device *dev)
-{
- struct ieee80211_master_priv *mpriv = netdev_priv(dev);
- struct ieee80211_local *local = mpriv->local;
- struct ieee80211_sub_if_data *sdata;
-
- /* we hold the RTNL here so can safely walk the list */
- list_for_each_entry(sdata, &local->interfaces, list)
- if (netif_running(sdata->dev))
- dev_close(sdata->dev);
-
- return 0;
-}
-
-static void ieee80211_master_set_multicast_list(struct net_device *dev)
-{
- struct ieee80211_master_priv *mpriv = netdev_priv(dev);
- struct ieee80211_local *local = mpriv->local;
+ struct ieee80211_local *local =
+ container_of(work, struct ieee80211_local, reconfig_filter);
ieee80211_configure_filter(local);
}
@@ -259,7 +211,8 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
}
if (changed & BSS_CHANGED_BEACON_ENABLED) {
- if (local->sw_scanning) {
+ if (local->quiescing || !netif_running(sdata->dev) ||
+ test_bit(SCAN_SW_SCANNING, &local->scanning)) {
sdata->vif.bss_conf.enable_beacon = false;
} else {
/*
@@ -288,9 +241,6 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
drv_bss_info_changed(local, &sdata->vif,
&sdata->vif.bss_conf, changed);
-
- /* DEPRECATED */
- local->hw.conf.beacon_int = sdata->vif.bss_conf.beacon_int;
}
u32 ieee80211_reset_erp_info(struct ieee80211_sub_if_data *sdata)
@@ -310,7 +260,6 @@ void ieee80211_tx_status_irqsafe(struct ieee80211_hw *hw,
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
int tmp;
- skb->dev = local->mdev;
skb->pkt_type = IEEE80211_TX_STATUS_MSG;
skb_queue_tail(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS ?
&local->skb_queue : &local->skb_queue_unreliable, skb);
@@ -330,19 +279,16 @@ static void ieee80211_tasklet_handler(unsigned long data)
{
struct ieee80211_local *local = (struct ieee80211_local *) data;
struct sk_buff *skb;
- struct ieee80211_rx_status rx_status;
struct ieee80211_ra_tid *ra_tid;
while ((skb = skb_dequeue(&local->skb_queue)) ||
(skb = skb_dequeue(&local->skb_queue_unreliable))) {
switch (skb->pkt_type) {
case IEEE80211_RX_MSG:
- /* status is in skb->cb */
- memcpy(&rx_status, skb->cb, sizeof(rx_status));
/* Clear skb->pkt_type in order to not confuse kernel
* netstack. */
skb->pkt_type = 0;
- __ieee80211_rx(local_to_hw(local), skb, &rx_status);
+ ieee80211_rx(local_to_hw(local), skb);
break;
case IEEE80211_TX_STATUS_MSG:
skb->pkt_type = 0;
@@ -375,6 +321,31 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ /*
+ * XXX: This is temporary!
+ *
+ * The problem here is that when we get here, the driver will
+ * quite likely have pretty much overwritten info->control by
+ * using info->driver_data or info->rate_driver_data. Thus,
+ * when passing out the frame to the driver again, we would be
+ * passing completely bogus data since the driver would then
+ * expect a properly filled info->control. In mac80211 itself
+ * the same problem occurs, since we need info->control.vif
+ * internally.
+ *
+ * To fix this, we should send the frame through TX processing
+ * again. However, it's not that simple, since the frame will
+ * have been software-encrypted (if applicable) already, and
+ * encrypting it again doesn't do much good. So to properly do
+ * that, we not only have to skip the actual 'raw' encryption
+ * (key selection etc. still has to be done!) but also the
+ * sequence number assignment since that impacts the crypto
+ * encapsulation, of course.
+ *
+ * Hence, for now, fix the bug by just dropping the frame.
+ */
+ goto drop;
+
sta->tx_filtered_count++;
/*
@@ -428,6 +399,7 @@ static void ieee80211_handle_filtered_frame(struct ieee80211_local *local,
return;
}
+ drop:
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
if (net_ratelimit())
printk(KERN_DEBUG "%s: dropped TX filtered frame, "
@@ -510,6 +482,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
}
rate_control_tx_status(local, sband, sta, skb);
+ if (ieee80211_vif_is_mesh(&sta->sdata->vif))
+ ieee80211s_update_metric(local, sta, skb);
}
rcu_read_unlock();
@@ -685,6 +659,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
if (!wiphy)
return NULL;
+ wiphy->netnsok = true;
wiphy->privid = mac80211_wiphy_privid;
/* Yes, putting cfg80211_bss into ieee80211_bss is a hack */
@@ -711,7 +686,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
local->hw.max_rates = 1;
local->hw.conf.long_frame_max_tx_count = wiphy->retry_long;
local->hw.conf.short_frame_max_tx_count = wiphy->retry_short;
- local->hw.conf.radio_enabled = true;
local->user_power_level = -1;
INIT_LIST_HEAD(&local->interfaces);
@@ -719,13 +693,15 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
mutex_init(&local->scan_mtx);
spin_lock_init(&local->key_lock);
-
+ spin_lock_init(&local->filter_lock);
spin_lock_init(&local->queue_stop_reason_lock);
INIT_DELAYED_WORK(&local->scan_work, ieee80211_scan_work);
INIT_WORK(&local->restart_work, ieee80211_restart_work);
+ INIT_WORK(&local->reconfig_filter, ieee80211_reconfig_filter);
+
INIT_WORK(&local->dynamic_ps_enable_work,
ieee80211_dynamic_ps_enable_work);
INIT_WORK(&local->dynamic_ps_disable_work,
@@ -739,12 +715,10 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
skb_queue_head_init(&local->pending[i]);
tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending,
(unsigned long)local);
- tasklet_disable(&local->tx_pending_tasklet);
tasklet_init(&local->tasklet,
ieee80211_tasklet_handler,
(unsigned long) local);
- tasklet_disable(&local->tasklet);
skb_queue_head_init(&local->skb_queue);
skb_queue_head_init(&local->skb_queue_unreliable);
@@ -755,30 +729,11 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
}
EXPORT_SYMBOL(ieee80211_alloc_hw);
-static const struct net_device_ops ieee80211_master_ops = {
- .ndo_start_xmit = ieee80211_master_start_xmit,
- .ndo_open = ieee80211_master_open,
- .ndo_stop = ieee80211_master_stop,
- .ndo_set_multicast_list = ieee80211_master_set_multicast_list,
- .ndo_select_queue = ieee80211_select_queue,
-};
-
-static void ieee80211_master_setup(struct net_device *mdev)
-{
- mdev->type = ARPHRD_IEEE80211;
- mdev->netdev_ops = &ieee80211_master_ops;
- mdev->header_ops = &ieee80211_header_ops;
- mdev->tx_queue_len = 1000;
- mdev->addr_len = ETH_ALEN;
-}
-
int ieee80211_register_hw(struct ieee80211_hw *hw)
{
struct ieee80211_local *local = hw_to_local(hw);
int result;
enum ieee80211_band band;
- struct net_device *mdev;
- struct ieee80211_master_priv *mpriv;
int channels, i, j, max_bitrates;
bool supp_ht;
static const u32 cipher_suites[] = {
@@ -818,9 +773,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
supp_ht = supp_ht || sband->ht_cap.ht_supported;
}
- local->int_scan_req.n_channels = channels;
- local->int_scan_req.channels = kzalloc(sizeof(void *) * channels, GFP_KERNEL);
- if (!local->int_scan_req.channels)
+ local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) +
+ sizeof(void *) * channels, GFP_KERNEL);
+ if (!local->int_scan_req)
return -ENOMEM;
/* if low-level driver supports AP, we also support VLAN */
@@ -877,19 +832,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
if (hw->queues > IEEE80211_MAX_QUEUES)
hw->queues = IEEE80211_MAX_QUEUES;
- mdev = alloc_netdev_mq(sizeof(struct ieee80211_master_priv),
- "wmaster%d", ieee80211_master_setup,
- hw->queues);
- if (!mdev)
- goto fail_mdev_alloc;
-
- mpriv = netdev_priv(mdev);
- mpriv->local = local;
- local->mdev = mdev;
-
- local->hw.workqueue =
+ local->workqueue =
create_singlethread_workqueue(wiphy_name(local->hw.wiphy));
- if (!local->hw.workqueue) {
+ if (!local->workqueue) {
result = -ENOMEM;
goto fail_workqueue;
}
@@ -921,17 +866,6 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
}
rtnl_lock();
- result = dev_alloc_name(local->mdev, local->mdev->name);
- if (result < 0)
- goto fail_dev;
-
- memcpy(local->mdev->dev_addr, local->hw.wiphy->perm_addr, ETH_ALEN);
- SET_NETDEV_DEV(local->mdev, wiphy_dev(local->hw.wiphy));
- local->mdev->features |= NETIF_F_NETNS_LOCAL;
-
- result = register_netdevice(local->mdev);
- if (result < 0)
- goto fail_dev;
result = ieee80211_init_rate_ctrl_alg(local,
hw->rate_control_algorithm);
@@ -956,13 +890,13 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
/* alloc internal scan request */
i = 0;
- local->int_scan_req.ssids = &local->scan_ssid;
- local->int_scan_req.n_ssids = 1;
+ local->int_scan_req->ssids = &local->scan_ssid;
+ local->int_scan_req->n_ssids = 1;
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
if (!hw->wiphy->bands[band])
continue;
for (j = 0; j < hw->wiphy->bands[band]->n_channels; j++) {
- local->int_scan_req.channels[i] =
+ local->int_scan_req->channels[i] =
&hw->wiphy->bands[band]->channels[j];
i++;
}
@@ -984,23 +918,17 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
ieee80211_led_exit(local);
ieee80211_remove_interfaces(local);
fail_rate:
- unregister_netdevice(local->mdev);
- local->mdev = NULL;
- fail_dev:
rtnl_unlock();
ieee80211_wep_free(local);
fail_wep:
sta_info_stop(local);
fail_sta_info:
debugfs_hw_del(local);
- destroy_workqueue(local->hw.workqueue);
+ destroy_workqueue(local->workqueue);
fail_workqueue:
- if (local->mdev)
- free_netdev(local->mdev);
- fail_mdev_alloc:
wiphy_unregister(local->hw.wiphy);
fail_wiphy_register:
- kfree(local->int_scan_req.channels);
+ kfree(local->int_scan_req);
return result;
}
EXPORT_SYMBOL(ieee80211_register_hw);
@@ -1022,15 +950,12 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
* because the driver cannot be handing us frames any
* more and the tasklet is killed.
*/
-
- /* First, we remove all virtual interfaces. */
ieee80211_remove_interfaces(local);
- /* then, finally, remove the master interface */
- unregister_netdevice(local->mdev);
-
rtnl_unlock();
+ cancel_work_sync(&local->reconfig_filter);
+
ieee80211_clear_tx_pending(local);
sta_info_stop(local);
rate_control_deinitialize(local);
@@ -1043,12 +968,11 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
skb_queue_purge(&local->skb_queue);
skb_queue_purge(&local->skb_queue_unreliable);
- destroy_workqueue(local->hw.workqueue);
+ destroy_workqueue(local->workqueue);
wiphy_unregister(local->hw.wiphy);
ieee80211_wep_free(local);
ieee80211_led_exit(local);
- free_netdev(local->mdev);
- kfree(local->int_scan_req.channels);
+ kfree(local->int_scan_req);
}
EXPORT_SYMBOL(ieee80211_unregister_hw);
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 11cf45bce38a..f7364e56f1ee 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -18,8 +18,11 @@
#define PP_OFFSET 1 /* Path Selection Protocol */
#define PM_OFFSET 5 /* Path Selection Metric */
#define CC_OFFSET 9 /* Congestion Control Mode */
-#define CAPAB_OFFSET 17
-#define ACCEPT_PLINKS 0x80
+#define SP_OFFSET 13 /* Synchronization Protocol */
+#define AUTH_OFFSET 17 /* Authentication Protocol */
+#define CAPAB_OFFSET 22
+#define CAPAB_ACCEPT_PLINKS 0x80
+#define CAPAB_FORWARDING 0x10
#define TMR_RUNNING_HK 0
#define TMR_RUNNING_MP 1
@@ -47,14 +50,14 @@ static void ieee80211_mesh_housekeeping_timer(unsigned long data)
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
- ifmsh->housekeeping = true;
+ ifmsh->wrkq_flags |= MESH_WORK_HOUSEKEEPING;
if (local->quiescing) {
set_bit(TMR_RUNNING_HK, &ifmsh->timers_running);
return;
}
- queue_work(local->hw.workqueue, &ifmsh->work);
+ ieee80211_queue_work(&local->hw, &ifmsh->work);
}
/**
@@ -84,7 +87,9 @@ bool mesh_matches_local(struct ieee802_11_elems *ie, struct ieee80211_sub_if_dat
memcmp(ifmsh->mesh_id, ie->mesh_id, ie->mesh_id_len) == 0 &&
memcmp(ifmsh->mesh_pp_id, ie->mesh_config + PP_OFFSET, 4) == 0 &&
memcmp(ifmsh->mesh_pm_id, ie->mesh_config + PM_OFFSET, 4) == 0 &&
- memcmp(ifmsh->mesh_cc_id, ie->mesh_config + CC_OFFSET, 4) == 0)
+ memcmp(ifmsh->mesh_cc_id, ie->mesh_config + CC_OFFSET, 4) == 0 &&
+ memcmp(ifmsh->mesh_sp_id, ie->mesh_config + SP_OFFSET, 4) == 0 &&
+ memcmp(ifmsh->mesh_auth_id, ie->mesh_config + AUTH_OFFSET, 4) == 0)
return true;
return false;
@@ -97,7 +102,7 @@ bool mesh_matches_local(struct ieee802_11_elems *ie, struct ieee80211_sub_if_dat
*/
bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie)
{
- return (*(ie->mesh_config + CAPAB_OFFSET) & ACCEPT_PLINKS) != 0;
+ return (*(ie->mesh_config + CAPAB_OFFSET) & CAPAB_ACCEPT_PLINKS) != 0;
}
/**
@@ -123,11 +128,18 @@ void mesh_accept_plinks_update(struct ieee80211_sub_if_data *sdata)
void mesh_ids_set_default(struct ieee80211_if_mesh *sta)
{
- u8 def_id[4] = {0x00, 0x0F, 0xAC, 0xff};
-
- memcpy(sta->mesh_pp_id, def_id, 4);
- memcpy(sta->mesh_pm_id, def_id, 4);
- memcpy(sta->mesh_cc_id, def_id, 4);
+ u8 oui[3] = {0x00, 0x0F, 0xAC};
+
+ memcpy(sta->mesh_pp_id, oui, sizeof(oui));
+ memcpy(sta->mesh_pm_id, oui, sizeof(oui));
+ memcpy(sta->mesh_cc_id, oui, sizeof(oui));
+ memcpy(sta->mesh_sp_id, oui, sizeof(oui));
+ memcpy(sta->mesh_auth_id, oui, sizeof(oui));
+ sta->mesh_pp_id[sizeof(oui)] = 0;
+ sta->mesh_pm_id[sizeof(oui)] = 0;
+ sta->mesh_cc_id[sizeof(oui)] = 0xff;
+ sta->mesh_sp_id[sizeof(oui)] = 0xff;
+ sta->mesh_auth_id[sizeof(oui)] = 0x0;
}
int mesh_rmc_init(struct ieee80211_sub_if_data *sdata)
@@ -245,7 +257,7 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
if (sdata->u.mesh.mesh_id_len)
memcpy(pos, sdata->u.mesh.mesh_id, sdata->u.mesh.mesh_id_len);
- pos = skb_put(skb, 21);
+ pos = skb_put(skb, 2 + IEEE80211_MESH_CONFIG_LEN);
*pos++ = WLAN_EID_MESH_CONFIG;
*pos++ = IEEE80211_MESH_CONFIG_LEN;
/* Version */
@@ -263,15 +275,22 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
memcpy(pos, sdata->u.mesh.mesh_cc_id, 4);
pos += 4;
- /* Channel precedence:
- * Not running simple channel unification protocol
- */
- memset(pos, 0x00, 4);
+ /* Synchronization protocol identifier */
+ memcpy(pos, sdata->u.mesh.mesh_sp_id, 4);
pos += 4;
+ /* Authentication Protocol identifier */
+ memcpy(pos, sdata->u.mesh.mesh_auth_id, 4);
+ pos += 4;
+
+ /* Mesh Formation Info */
+ memset(pos, 0x00, 1);
+ pos += 1;
+
/* Mesh capability */
sdata->u.mesh.accepting_plinks = mesh_plink_availables(sdata);
- *pos++ = sdata->u.mesh.accepting_plinks ? ACCEPT_PLINKS : 0x00;
+ *pos = CAPAB_FORWARDING;
+ *pos++ |= sdata->u.mesh.accepting_plinks ? CAPAB_ACCEPT_PLINKS : 0x00;
*pos++ = 0x00;
return;
@@ -320,30 +339,6 @@ struct mesh_table *mesh_table_alloc(int size_order)
return newtbl;
}
-static void __mesh_table_free(struct mesh_table *tbl)
-{
- kfree(tbl->hash_buckets);
- kfree(tbl->hashwlock);
- kfree(tbl);
-}
-
-void mesh_table_free(struct mesh_table *tbl, bool free_leafs)
-{
- struct hlist_head *mesh_hash;
- struct hlist_node *p, *q;
- int i;
-
- mesh_hash = tbl->hash_buckets;
- for (i = 0; i <= tbl->hash_mask; i++) {
- spin_lock(&tbl->hashwlock[i]);
- hlist_for_each_safe(p, q, &mesh_hash[i]) {
- tbl->free_node(p, free_leafs);
- atomic_dec(&tbl->entries);
- }
- spin_unlock(&tbl->hashwlock[i]);
- }
- __mesh_table_free(tbl);
-}
static void ieee80211_mesh_path_timer(unsigned long data)
{
@@ -357,63 +352,79 @@ static void ieee80211_mesh_path_timer(unsigned long data)
return;
}
- queue_work(local->hw.workqueue, &ifmsh->work);
+ ieee80211_queue_work(&local->hw, &ifmsh->work);
}
-struct mesh_table *mesh_table_grow(struct mesh_table *tbl)
-{
- struct mesh_table *newtbl;
- struct hlist_head *oldhash;
- struct hlist_node *p, *q;
- int i;
-
- if (atomic_read(&tbl->entries)
- < tbl->mean_chain_len * (tbl->hash_mask + 1))
- goto endgrow;
-
- newtbl = mesh_table_alloc(tbl->size_order + 1);
- if (!newtbl)
- goto endgrow;
-
- newtbl->free_node = tbl->free_node;
- newtbl->mean_chain_len = tbl->mean_chain_len;
- newtbl->copy_node = tbl->copy_node;
- atomic_set(&newtbl->entries, atomic_read(&tbl->entries));
-
- oldhash = tbl->hash_buckets;
- for (i = 0; i <= tbl->hash_mask; i++)
- hlist_for_each(p, &oldhash[i])
- if (tbl->copy_node(p, newtbl) < 0)
- goto errcopy;
-
- return newtbl;
-
-errcopy:
- for (i = 0; i <= newtbl->hash_mask; i++) {
- hlist_for_each_safe(p, q, &newtbl->hash_buckets[i])
- tbl->free_node(p, 0);
+/**
+ * ieee80211_fill_mesh_addresses - fill addresses of a locally originated mesh frame
+ * @hdr: 802.11 frame header
+ * @fc: frame control field
+ * @meshda: destination address in the mesh
+ * @meshsa: source address address in the mesh. Same as TA, as frame is
+ * locally originated.
+ *
+ * Return the length of the 802.11 (does not include a mesh control header)
+ */
+int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc, char
+ *meshda, char *meshsa) {
+ if (is_multicast_ether_addr(meshda)) {
+ *fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
+ /* DA TA SA */
+ memcpy(hdr->addr1, meshda, ETH_ALEN);
+ memcpy(hdr->addr2, meshsa, ETH_ALEN);
+ memcpy(hdr->addr3, meshsa, ETH_ALEN);
+ return 24;
+ } else {
+ *fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS |
+ IEEE80211_FCTL_TODS);
+ /* RA TA DA SA */
+ memset(hdr->addr1, 0, ETH_ALEN); /* RA is resolved later */
+ memcpy(hdr->addr2, meshsa, ETH_ALEN);
+ memcpy(hdr->addr3, meshda, ETH_ALEN);
+ memcpy(hdr->addr4, meshsa, ETH_ALEN);
+ return 30;
}
- __mesh_table_free(newtbl);
-endgrow:
- return NULL;
}
/**
* ieee80211_new_mesh_header - create a new mesh header
* @meshhdr: uninitialized mesh header
* @sdata: mesh interface to be used
+ * @addr4: addr4 of the mesh frame (1st in ae header)
+ * may be NULL
+ * @addr5: addr5 of the mesh frame (1st or 2nd in ae header)
+ * may be NULL unless addr6 is present
+ * @addr6: addr6 of the mesh frame (2nd or 3rd in ae header)
+ * may be NULL unless addr5 is present
*
* Return the header length.
*/
int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr,
- struct ieee80211_sub_if_data *sdata)
+ struct ieee80211_sub_if_data *sdata, char *addr4,
+ char *addr5, char *addr6)
{
- meshhdr->flags = 0;
+ int aelen = 0;
+ memset(meshhdr, 0, sizeof(meshhdr));
meshhdr->ttl = sdata->u.mesh.mshcfg.dot11MeshTTL;
put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &meshhdr->seqnum);
sdata->u.mesh.mesh_seqnum++;
-
- return 6;
+ if (addr4) {
+ meshhdr->flags |= MESH_FLAGS_AE_A4;
+ aelen += ETH_ALEN;
+ memcpy(meshhdr->eaddr1, addr4, ETH_ALEN);
+ }
+ if (addr5 && addr6) {
+ meshhdr->flags |= MESH_FLAGS_AE_A5_A6;
+ aelen += 2 * ETH_ALEN;
+ if (!addr4) {
+ memcpy(meshhdr->eaddr1, addr5, ETH_ALEN);
+ memcpy(meshhdr->eaddr2, addr6, ETH_ALEN);
+ } else {
+ memcpy(meshhdr->eaddr2, addr5, ETH_ALEN);
+ memcpy(meshhdr->eaddr3, addr6, ETH_ALEN);
+ }
+ }
+ return 6 + aelen;
}
static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata,
@@ -433,7 +444,6 @@ static void ieee80211_mesh_housekeeping(struct ieee80211_sub_if_data *sdata,
if (free_plinks != sdata->u.mesh.accepting_plinks)
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON);
- ifmsh->housekeeping = false;
mod_timer(&ifmsh->housekeeping_timer,
round_jiffies(jiffies + IEEE80211_MESH_HOUSEKEEPING_INTERVAL));
}
@@ -470,10 +480,12 @@ void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata)
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct ieee80211_local *local = sdata->local;
- ifmsh->housekeeping = true;
- queue_work(local->hw.workqueue, &ifmsh->work);
+ ifmsh->wrkq_flags |= MESH_WORK_HOUSEKEEPING;
+ ieee80211_queue_work(&local->hw, &ifmsh->work);
+ sdata->vif.bss_conf.beacon_int = MESH_DEFAULT_BEACON_INTERVAL;
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON |
- BSS_CHANGED_BEACON_ENABLED);
+ BSS_CHANGED_BEACON_ENABLED |
+ BSS_CHANGED_BEACON_INT);
}
void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata)
@@ -568,7 +580,7 @@ static void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
ifmsh = &sdata->u.mesh;
- rx_status = (struct ieee80211_rx_status *) skb->cb;
+ rx_status = IEEE80211_SKB_RXCB(skb);
mgmt = (struct ieee80211_mgmt *) skb->data;
stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE;
@@ -597,7 +609,7 @@ static void ieee80211_mesh_work(struct work_struct *work)
if (!netif_running(sdata->dev))
return;
- if (local->sw_scanning || local->hw_scanning)
+ if (local->scanning)
return;
while ((skb = skb_dequeue(&ifmsh->skb_queue)))
@@ -608,7 +620,13 @@ static void ieee80211_mesh_work(struct work_struct *work)
ifmsh->last_preq + msecs_to_jiffies(ifmsh->mshcfg.dot11MeshHWMPpreqMinInterval)))
mesh_path_start_discovery(sdata);
- if (ifmsh->housekeeping)
+ if (test_and_clear_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags))
+ mesh_mpath_table_grow();
+
+ if (test_and_clear_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags))
+ mesh_mpp_table_grow();
+
+ if (test_and_clear_bit(MESH_WORK_HOUSEKEEPING, &ifmsh->wrkq_flags))
ieee80211_mesh_housekeeping(sdata, ifmsh);
}
@@ -619,7 +637,7 @@ void ieee80211_mesh_notify_scan_completed(struct ieee80211_local *local)
rcu_read_lock();
list_for_each_entry_rcu(sdata, &local->interfaces, list)
if (ieee80211_vif_is_mesh(&sdata->vif))
- queue_work(local->hw.workqueue, &sdata->u.mesh.work);
+ ieee80211_queue_work(&local->hw, &sdata->u.mesh.work);
rcu_read_unlock();
}
@@ -671,8 +689,7 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
}
ieee80211_rx_result
-ieee80211_mesh_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
- struct ieee80211_rx_status *rx_status)
+ieee80211_mesh_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
@@ -686,12 +703,14 @@ ieee80211_mesh_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
fc = le16_to_cpu(mgmt->frame_control);
switch (fc & IEEE80211_FCTL_STYPE) {
+ case IEEE80211_STYPE_ACTION:
+ if (skb->len < IEEE80211_MIN_ACTION_SIZE)
+ return RX_DROP_MONITOR;
+ /* fall through */
case IEEE80211_STYPE_PROBE_RESP:
case IEEE80211_STYPE_BEACON:
- case IEEE80211_STYPE_ACTION:
- memcpy(skb->cb, rx_status, sizeof(*rx_status));
skb_queue_tail(&ifmsh->skb_queue, skb);
- queue_work(local->hw.workqueue, &ifmsh->work);
+ ieee80211_queue_work(&local->hw, &ifmsh->work);
return RX_QUEUED;
}
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index c7d72819cdd2..dd1c19319f0a 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -44,6 +44,23 @@ enum mesh_path_flags {
};
/**
+ * enum mesh_deferred_task_flags - mac80211 mesh deferred tasks
+ *
+ *
+ *
+ * @MESH_WORK_HOUSEKEEPING: run the periodic mesh housekeeping tasks
+ * @MESH_WORK_GROW_MPATH_TABLE: the mesh path table is full and needs
+ * to grow.
+ * @MESH_WORK_GROW_MPP_TABLE: the mesh portals table is full and needs to
+ * grow
+ */
+enum mesh_deferred_task_flags {
+ MESH_WORK_HOUSEKEEPING,
+ MESH_WORK_GROW_MPATH_TABLE,
+ MESH_WORK_GROW_MPP_TABLE,
+};
+
+/**
* struct mesh_path - mac80211 mesh path structure
*
* @dst: mesh path destination mac address
@@ -61,7 +78,7 @@ enum mesh_path_flags {
* retry
* @discovery_retries: number of discovery retries
* @flags: mesh path flags, as specified on &enum mesh_path_flags
- * @state_lock: mesh pat state lock
+ * @state_lock: mesh path state lock
*
*
* The combination of dst and sdata is unique in the mesh path table. Since the
@@ -174,6 +191,7 @@ struct mesh_rmc {
*/
#define MESH_PATH_REFRESH_TIME 1000
#define MESH_MIN_DISCOVERY_TIMEOUT (2 * MESH_DIAM_TRAVERSAL_TIME)
+#define MESH_DEFAULT_BEACON_INTERVAL 1000 /* in 1024 us units */
#define MESH_MAX_PREQ_RETRIES 4
#define MESH_PATH_EXPIRE (600 * HZ)
@@ -193,8 +211,11 @@ struct mesh_rmc {
/* Public interfaces */
/* Various */
+int ieee80211_fill_mesh_addresses(struct ieee80211_hdr *hdr, __le16 *fc,
+ char *da, char *sa);
int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr,
- struct ieee80211_sub_if_data *sdata);
+ struct ieee80211_sub_if_data *sdata, char *addr4,
+ char *addr5, char *addr6);
int mesh_rmc_check(u8 *addr, struct ieee80211s_hdr *mesh_hdr,
struct ieee80211_sub_if_data *sdata);
bool mesh_matches_local(struct ieee802_11_elems *ie,
@@ -205,11 +226,12 @@ void mesh_mgmt_ies_add(struct sk_buff *skb,
void mesh_rmc_free(struct ieee80211_sub_if_data *sdata);
int mesh_rmc_init(struct ieee80211_sub_if_data *sdata);
void ieee80211s_init(void);
+void ieee80211s_update_metric(struct ieee80211_local *local,
+ struct sta_info *stainfo, struct sk_buff *skb);
void ieee80211s_stop(void);
void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata);
ieee80211_rx_result
-ieee80211_mesh_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
- struct ieee80211_rx_status *rx_status);
+ieee80211_mesh_rx_mgmt(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb);
void ieee80211_start_mesh(struct ieee80211_sub_if_data *sdata);
void ieee80211_stop_mesh(struct ieee80211_sub_if_data *sdata);
@@ -247,7 +269,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
/* Mesh tables */
struct mesh_table *mesh_table_alloc(int size_order);
void mesh_table_free(struct mesh_table *tbl, bool free_leafs);
-struct mesh_table *mesh_table_grow(struct mesh_table *tbl);
+void mesh_mpath_table_grow(void);
+void mesh_mpp_table_grow(void);
u32 mesh_table_hash(u8 *addr, struct ieee80211_sub_if_data *sdata,
struct mesh_table *tbl);
/* Mesh paths */
@@ -266,6 +289,8 @@ void mesh_path_discard_frame(struct sk_buff *skb,
void mesh_path_quiesce(struct ieee80211_sub_if_data *sdata);
void mesh_path_restart(struct ieee80211_sub_if_data *sdata);
+extern int mesh_paths_generation;
+
#ifdef CONFIG_MAC80211_MESH
extern int mesh_allocated;
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index f49ef288e2e2..e12a786e26b8 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -201,6 +201,24 @@ int mesh_path_error_tx(u8 *dst, __le32 dst_dsn, u8 *ra,
return 0;
}
+void ieee80211s_update_metric(struct ieee80211_local *local,
+ struct sta_info *stainfo, struct sk_buff *skb)
+{
+ struct ieee80211_tx_info *txinfo = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ int failed;
+
+ if (!ieee80211_is_data(hdr->frame_control))
+ return;
+
+ failed = !(txinfo->flags & IEEE80211_TX_STAT_ACK);
+
+ /* moving average, scaled to 100 */
+ stainfo->fail_avg = ((80 * stainfo->fail_avg + 5) / 100 + 20 * failed);
+ if (stainfo->fail_avg > 95)
+ mesh_plink_broken(stainfo);
+}
+
static u32 airtime_link_metric_get(struct ieee80211_local *local,
struct sta_info *sta)
{
@@ -397,7 +415,8 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt,
- u8 *preq_elem, u32 metric) {
+ u8 *preq_elem, u32 metric)
+{
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
struct mesh_path *mpath;
u8 *dst_addr, *orig_addr;
@@ -430,7 +449,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
if ((!(mpath->flags & MESH_PATH_DSN_VALID)) ||
DSN_LT(mpath->dsn, dst_dsn)) {
mpath->dsn = dst_dsn;
- mpath->flags &= MESH_PATH_DSN_VALID;
+ mpath->flags |= MESH_PATH_DSN_VALID;
} else if ((!(dst_flags & MP_F_DO)) &&
(mpath->flags & MESH_PATH_ACTIVE)) {
reply = true;
@@ -478,6 +497,7 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
hopcount, ttl, cpu_to_le32(lifetime),
cpu_to_le32(metric), cpu_to_le32(preq_id),
sdata);
+ ifmsh->mshstats.fwded_mcast++;
ifmsh->mshstats.fwded_frames++;
}
}
@@ -536,6 +556,8 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
cpu_to_le32(lifetime), cpu_to_le32(metric),
0, sdata);
rcu_read_unlock();
+
+ sdata->u.mesh.mshstats.fwded_unicast++;
sdata->u.mesh.mshstats.fwded_frames++;
return;
@@ -660,14 +682,14 @@ static void mesh_queue_preq(struct mesh_path *mpath, u8 flags)
spin_unlock(&ifmsh->mesh_preq_queue_lock);
if (time_after(jiffies, ifmsh->last_preq + min_preq_int_jiff(sdata)))
- queue_work(sdata->local->hw.workqueue, &ifmsh->work);
+ ieee80211_queue_work(&sdata->local->hw, &ifmsh->work);
else if (time_before(jiffies, ifmsh->last_preq)) {
/* avoid long wait if did not send preqs for a long time
* and jiffies wrapped around
*/
ifmsh->last_preq = jiffies - min_preq_int_jiff(sdata) - 1;
- queue_work(sdata->local->hw.workqueue, &ifmsh->work);
+ ieee80211_queue_work(&sdata->local->hw, &ifmsh->work);
} else
mod_timer(&ifmsh->mesh_path_timer, ifmsh->last_preq +
min_preq_int_jiff(sdata));
@@ -686,11 +708,11 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)
u8 ttl, dst_flags;
u32 lifetime;
- spin_lock(&ifmsh->mesh_preq_queue_lock);
+ spin_lock_bh(&ifmsh->mesh_preq_queue_lock);
if (!ifmsh->preq_queue_len ||
time_before(jiffies, ifmsh->last_preq +
min_preq_int_jiff(sdata))) {
- spin_unlock(&ifmsh->mesh_preq_queue_lock);
+ spin_unlock_bh(&ifmsh->mesh_preq_queue_lock);
return;
}
@@ -698,7 +720,7 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)
struct mesh_preq_queue, list);
list_del(&preq_node->list);
--ifmsh->preq_queue_len;
- spin_unlock(&ifmsh->mesh_preq_queue_lock);
+ spin_unlock_bh(&ifmsh->mesh_preq_queue_lock);
rcu_read_lock();
mpath = mesh_path_lookup(preq_node->dst, sdata);
@@ -784,7 +806,6 @@ int mesh_nexthop_lookup(struct sk_buff *skb,
mesh_path_add(dst_addr, sdata);
mpath = mesh_path_lookup(dst_addr, sdata);
if (!mpath) {
- dev_kfree_skb(skb);
sdata->u.mesh.mshstats.dropped_frames_no_route++;
err = -ENOSPC;
goto endlookup;
@@ -792,7 +813,7 @@ int mesh_nexthop_lookup(struct sk_buff *skb,
}
if (mpath->flags & MESH_PATH_ACTIVE) {
- if (time_after(jiffies, mpath->exp_time -
+ if (time_after(jiffies, mpath->exp_time +
msecs_to_jiffies(sdata->u.mesh.mshcfg.path_refresh_time))
&& !memcmp(sdata->dev->dev_addr, hdr->addr4,
ETH_ALEN)
@@ -804,17 +825,17 @@ int mesh_nexthop_lookup(struct sk_buff *skb,
memcpy(hdr->addr1, mpath->next_hop->sta.addr,
ETH_ALEN);
} else {
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
if (!(mpath->flags & MESH_PATH_RESOLVING)) {
/* Start discovery only if it is not running yet */
mesh_queue_preq(mpath, PREQ_Q_F_START);
}
if (skb_queue_len(&mpath->frame_queue) >=
- MESH_FRAME_QUEUE_LEN) {
- skb_to_free = mpath->frame_queue.next;
- skb_unlink(skb_to_free, &mpath->frame_queue);
- }
+ MESH_FRAME_QUEUE_LEN)
+ skb_to_free = skb_dequeue(&mpath->frame_queue);
+ info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
skb_queue_tail(&mpath->frame_queue, skb);
if (skb_to_free)
mesh_path_discard_frame(skb_to_free, sdata);
diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c
index 479597e88583..751c4d0e2b36 100644
--- a/net/mac80211/mesh_pathtbl.c
+++ b/net/mac80211/mesh_pathtbl.c
@@ -38,6 +38,71 @@ struct mpath_node {
static struct mesh_table *mesh_paths;
static struct mesh_table *mpp_paths; /* Store paths for MPP&MAP */
+int mesh_paths_generation;
+static void __mesh_table_free(struct mesh_table *tbl)
+{
+ kfree(tbl->hash_buckets);
+ kfree(tbl->hashwlock);
+ kfree(tbl);
+}
+
+void mesh_table_free(struct mesh_table *tbl, bool free_leafs)
+{
+ struct hlist_head *mesh_hash;
+ struct hlist_node *p, *q;
+ int i;
+
+ mesh_hash = tbl->hash_buckets;
+ for (i = 0; i <= tbl->hash_mask; i++) {
+ spin_lock(&tbl->hashwlock[i]);
+ hlist_for_each_safe(p, q, &mesh_hash[i]) {
+ tbl->free_node(p, free_leafs);
+ atomic_dec(&tbl->entries);
+ }
+ spin_unlock(&tbl->hashwlock[i]);
+ }
+ __mesh_table_free(tbl);
+}
+
+static struct mesh_table *mesh_table_grow(struct mesh_table *tbl)
+{
+ struct mesh_table *newtbl;
+ struct hlist_head *oldhash;
+ struct hlist_node *p, *q;
+ int i;
+
+ if (atomic_read(&tbl->entries)
+ < tbl->mean_chain_len * (tbl->hash_mask + 1))
+ goto endgrow;
+
+ newtbl = mesh_table_alloc(tbl->size_order + 1);
+ if (!newtbl)
+ goto endgrow;
+
+ newtbl->free_node = tbl->free_node;
+ newtbl->mean_chain_len = tbl->mean_chain_len;
+ newtbl->copy_node = tbl->copy_node;
+ atomic_set(&newtbl->entries, atomic_read(&tbl->entries));
+
+ oldhash = tbl->hash_buckets;
+ for (i = 0; i <= tbl->hash_mask; i++)
+ hlist_for_each(p, &oldhash[i])
+ if (tbl->copy_node(p, newtbl) < 0)
+ goto errcopy;
+
+ return newtbl;
+
+errcopy:
+ for (i = 0; i <= newtbl->hash_mask; i++) {
+ hlist_for_each_safe(p, q, &newtbl->hash_buckets[i])
+ tbl->free_node(p, 0);
+ }
+ __mesh_table_free(newtbl);
+endgrow:
+ return NULL;
+}
+
+
/* This lock will have the grow table function as writer and add / delete nodes
* as readers. When reading the table (i.e. doing lookups) we are well protected
* by RCU
@@ -55,7 +120,25 @@ static DEFINE_RWLOCK(pathtbl_resize_lock);
*/
void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta)
{
+ struct sk_buff *skb;
+ struct ieee80211_hdr *hdr;
+ struct sk_buff_head tmpq;
+ unsigned long flags;
+
rcu_assign_pointer(mpath->next_hop, sta);
+
+ __skb_queue_head_init(&tmpq);
+
+ spin_lock_irqsave(&mpath->frame_queue.lock, flags);
+
+ while ((skb = __skb_dequeue(&mpath->frame_queue)) != NULL) {
+ hdr = (struct ieee80211_hdr *) skb->data;
+ memcpy(hdr->addr1, sta->sta.addr, ETH_ALEN);
+ __skb_queue_tail(&tmpq, skb);
+ }
+
+ skb_queue_splice(&tmpq, &mpath->frame_queue);
+ spin_unlock_irqrestore(&mpath->frame_queue.lock, flags);
}
@@ -167,6 +250,8 @@ struct mesh_path *mesh_path_lookup_by_idx(int idx, struct ieee80211_sub_if_data
*/
int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata)
{
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+ struct ieee80211_local *local = sdata->local;
struct mesh_path *mpath, *new_mpath;
struct mpath_node *node, *new_node;
struct hlist_head *bucket;
@@ -175,8 +260,6 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata)
int err = 0;
u32 hash_idx;
- might_sleep();
-
if (memcmp(dst, sdata->dev->dev_addr, ETH_ALEN) == 0)
/* never add ourselves as neighbours */
return -ENOTSUPP;
@@ -188,11 +271,11 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata)
return -ENOSPC;
err = -ENOMEM;
- new_mpath = kzalloc(sizeof(struct mesh_path), GFP_KERNEL);
+ new_mpath = kzalloc(sizeof(struct mesh_path), GFP_ATOMIC);
if (!new_mpath)
goto err_path_alloc;
- new_node = kmalloc(sizeof(struct mpath_node), GFP_KERNEL);
+ new_node = kmalloc(sizeof(struct mpath_node), GFP_ATOMIC);
if (!new_node)
goto err_node_alloc;
@@ -225,23 +308,13 @@ int mesh_path_add(u8 *dst, struct ieee80211_sub_if_data *sdata)
mesh_paths->mean_chain_len * (mesh_paths->hash_mask + 1))
grow = 1;
+ mesh_paths_generation++;
+
spin_unlock(&mesh_paths->hashwlock[hash_idx]);
read_unlock(&pathtbl_resize_lock);
if (grow) {
- struct mesh_table *oldtbl, *newtbl;
-
- write_lock(&pathtbl_resize_lock);
- oldtbl = mesh_paths;
- newtbl = mesh_table_grow(mesh_paths);
- if (!newtbl) {
- write_unlock(&pathtbl_resize_lock);
- return 0;
- }
- rcu_assign_pointer(mesh_paths, newtbl);
- write_unlock(&pathtbl_resize_lock);
-
- synchronize_rcu();
- mesh_table_free(oldtbl, false);
+ set_bit(MESH_WORK_GROW_MPATH_TABLE, &ifmsh->wrkq_flags);
+ ieee80211_queue_work(&local->hw, &ifmsh->work);
}
return 0;
@@ -256,9 +329,46 @@ err_path_alloc:
return err;
}
+void mesh_mpath_table_grow(void)
+{
+ struct mesh_table *oldtbl, *newtbl;
+
+ write_lock(&pathtbl_resize_lock);
+ oldtbl = mesh_paths;
+ newtbl = mesh_table_grow(mesh_paths);
+ if (!newtbl) {
+ write_unlock(&pathtbl_resize_lock);
+ return;
+ }
+ rcu_assign_pointer(mesh_paths, newtbl);
+ write_unlock(&pathtbl_resize_lock);
+
+ synchronize_rcu();
+ mesh_table_free(oldtbl, false);
+}
+
+void mesh_mpp_table_grow(void)
+{
+ struct mesh_table *oldtbl, *newtbl;
+
+ write_lock(&pathtbl_resize_lock);
+ oldtbl = mpp_paths;
+ newtbl = mesh_table_grow(mpp_paths);
+ if (!newtbl) {
+ write_unlock(&pathtbl_resize_lock);
+ return;
+ }
+ rcu_assign_pointer(mpp_paths, newtbl);
+ write_unlock(&pathtbl_resize_lock);
+
+ synchronize_rcu();
+ mesh_table_free(oldtbl, false);
+}
int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata)
{
+ struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+ struct ieee80211_local *local = sdata->local;
struct mesh_path *mpath, *new_mpath;
struct mpath_node *node, *new_node;
struct hlist_head *bucket;
@@ -267,8 +377,6 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata)
int err = 0;
u32 hash_idx;
- might_sleep();
-
if (memcmp(dst, sdata->dev->dev_addr, ETH_ALEN) == 0)
/* never add ourselves as neighbours */
return -ENOTSUPP;
@@ -277,11 +385,11 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata)
return -ENOTSUPP;
err = -ENOMEM;
- new_mpath = kzalloc(sizeof(struct mesh_path), GFP_KERNEL);
+ new_mpath = kzalloc(sizeof(struct mesh_path), GFP_ATOMIC);
if (!new_mpath)
goto err_path_alloc;
- new_node = kmalloc(sizeof(struct mpath_node), GFP_KERNEL);
+ new_node = kmalloc(sizeof(struct mpath_node), GFP_ATOMIC);
if (!new_node)
goto err_node_alloc;
@@ -315,20 +423,8 @@ int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata)
spin_unlock(&mpp_paths->hashwlock[hash_idx]);
read_unlock(&pathtbl_resize_lock);
if (grow) {
- struct mesh_table *oldtbl, *newtbl;
-
- write_lock(&pathtbl_resize_lock);
- oldtbl = mpp_paths;
- newtbl = mesh_table_grow(mpp_paths);
- if (!newtbl) {
- write_unlock(&pathtbl_resize_lock);
- return 0;
- }
- rcu_assign_pointer(mpp_paths, newtbl);
- write_unlock(&pathtbl_resize_lock);
-
- synchronize_rcu();
- mesh_table_free(oldtbl, false);
+ set_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags);
+ ieee80211_queue_work(&local->hw, &ifmsh->work);
}
return 0;
@@ -466,6 +562,7 @@ int mesh_path_del(u8 *addr, struct ieee80211_sub_if_data *sdata)
err = -ENXIO;
enddel:
+ mesh_paths_generation++;
spin_unlock(&mesh_paths->hashwlock[hash_idx]);
read_unlock(&pathtbl_resize_lock);
return err;
@@ -481,11 +578,9 @@ enddel:
*/
void mesh_path_tx_pending(struct mesh_path *mpath)
{
- struct sk_buff *skb;
-
- while ((skb = skb_dequeue(&mpath->frame_queue)) &&
- (mpath->flags & MESH_PATH_ACTIVE))
- dev_queue_xmit(skb);
+ if (mpath->flags & MESH_PATH_ACTIVE)
+ ieee80211_add_pending_skbs(mpath->sdata->local,
+ &mpath->frame_queue);
}
/**
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index cb14253587f1..ffcbad75e09b 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -409,7 +409,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m
baselen = (u8 *) mgmt->u.action.u.plink_action.variable - (u8 *) mgmt;
if (mgmt->u.action.u.plink_action.action_code == PLINK_CONFIRM) {
baseaddr += 4;
- baselen -= 4;
+ baselen += 4;
}
ieee802_11_parse_elems(baseaddr, len - baselen, &elems);
if (!elems.peer_link) {
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 07e7e41816be..97a278a2f48e 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -27,43 +27,99 @@
#include "rate.h"
#include "led.h"
-#define IEEE80211_ASSOC_SCANS_MAX_TRIES 2
#define IEEE80211_AUTH_TIMEOUT (HZ / 5)
#define IEEE80211_AUTH_MAX_TRIES 3
#define IEEE80211_ASSOC_TIMEOUT (HZ / 5)
#define IEEE80211_ASSOC_MAX_TRIES 3
-#define IEEE80211_MONITORING_INTERVAL (2 * HZ)
-#define IEEE80211_PROBE_WAIT (HZ / 5)
-#define IEEE80211_PROBE_IDLE_TIME (60 * HZ)
-#define IEEE80211_RETRY_AUTH_INTERVAL (1 * HZ)
+#define IEEE80211_MAX_PROBE_TRIES 5
+
+/*
+ * beacon loss detection timeout
+ * XXX: should depend on beacon interval
+ */
+#define IEEE80211_BEACON_LOSS_TIME (2 * HZ)
+/*
+ * Time the connection can be idle before we probe
+ * it to see if we can still talk to the AP.
+ */
+#define IEEE80211_CONNECTION_IDLE_TIME (30 * HZ)
+/*
+ * Time we wait for a probe response after sending
+ * a probe request because of beacon loss or for
+ * checking the connection still works.
+ */
+#define IEEE80211_PROBE_WAIT (HZ / 2)
#define TMR_RUNNING_TIMER 0
#define TMR_RUNNING_CHANSW 1
+/*
+ * All cfg80211 functions have to be called outside a locked
+ * section so that they can acquire a lock themselves... This
+ * is much simpler than queuing up things in cfg80211, but we
+ * do need some indirection for that here.
+ */
+enum rx_mgmt_action {
+ /* no action required */
+ RX_MGMT_NONE,
+
+ /* caller must call cfg80211_send_rx_auth() */
+ RX_MGMT_CFG80211_AUTH,
+
+ /* caller must call cfg80211_send_rx_assoc() */
+ RX_MGMT_CFG80211_ASSOC,
+
+ /* caller must call cfg80211_send_deauth() */
+ RX_MGMT_CFG80211_DEAUTH,
+
+ /* caller must call cfg80211_send_disassoc() */
+ RX_MGMT_CFG80211_DISASSOC,
+
+ /* caller must call cfg80211_auth_timeout() & free work */
+ RX_MGMT_CFG80211_AUTH_TO,
+
+ /* caller must call cfg80211_assoc_timeout() & free work */
+ RX_MGMT_CFG80211_ASSOC_TO,
+};
+
/* utils */
-static int ecw2cw(int ecw)
+static inline void ASSERT_MGD_MTX(struct ieee80211_if_managed *ifmgd)
{
- return (1 << ecw) - 1;
+ WARN_ON(!mutex_is_locked(&ifmgd->mtx));
}
-static u8 *ieee80211_bss_get_ie(struct ieee80211_bss *bss, u8 ie)
+/*
+ * We can have multiple work items (and connection probing)
+ * scheduling this timer, but we need to take care to only
+ * reschedule it when it should fire _earlier_ than it was
+ * asked for before, or if it's not pending right now. This
+ * function ensures that. Note that it then is required to
+ * run this function for all timeouts after the first one
+ * has happened -- the work that runs from this timer will
+ * do that.
+ */
+static void run_again(struct ieee80211_if_managed *ifmgd,
+ unsigned long timeout)
{
- u8 *end, *pos;
+ ASSERT_MGD_MTX(ifmgd);
- pos = bss->cbss.information_elements;
- if (pos == NULL)
- return NULL;
- end = pos + bss->cbss.len_information_elements;
+ if (!timer_pending(&ifmgd->timer) ||
+ time_before(timeout, ifmgd->timer.expires))
+ mod_timer(&ifmgd->timer, timeout);
+}
- while (pos + 1 < end) {
- if (pos + 2 + pos[1] > end)
- break;
- if (pos[0] == ie)
- return pos;
- pos += 2 + pos[1];
- }
+static void mod_beacon_timer(struct ieee80211_sub_if_data *sdata)
+{
+ if (sdata->local->hw.flags & IEEE80211_HW_BEACON_FILTER)
+ return;
+
+ mod_timer(&sdata->u.mgd.bcn_mon_timer,
+ round_jiffies_up(jiffies + IEEE80211_BEACON_LOSS_TIME));
+}
- return NULL;
+static int ecw2cw(int ecw)
+{
+ return (1 << ecw) - 1;
}
static int ieee80211_compatible_rates(struct ieee80211_bss *bss,
@@ -94,11 +150,10 @@ static int ieee80211_compatible_rates(struct ieee80211_bss *bss,
*/
static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
struct ieee80211_ht_info *hti,
- u16 ap_ht_cap_flags)
+ const u8 *bssid, u16 ap_ht_cap_flags)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct sta_info *sta;
u32 changed = 0;
u16 ht_opmode;
@@ -147,12 +202,10 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
ieee80211_hw_config(local, 0);
rcu_read_lock();
-
- sta = sta_info_get(local, ifmgd->bssid);
+ sta = sta_info_get(local, bssid);
if (sta)
rate_control_rate_update(local, sband, sta,
IEEE80211_RC_HT_CHANGED);
-
rcu_read_unlock();
}
@@ -175,23 +228,24 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
/* frame sending functions */
-static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
+static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgd_work *wk)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
- u8 *pos, *ies, *ht_ie;
+ u8 *pos;
+ const u8 *ies, *ht_ie;
int i, len, count, rates_len, supp_rates_len;
u16 capab;
- struct ieee80211_bss *bss;
int wmm = 0;
struct ieee80211_supported_band *sband;
u32 rates = 0;
skb = dev_alloc_skb(local->hw.extra_tx_headroom +
- sizeof(*mgmt) + 200 + ifmgd->extra_ie_len +
- ifmgd->ssid_len);
+ sizeof(*mgmt) + 200 + wk->ie_len +
+ wk->ssid_len);
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer for assoc "
"frame\n", sdata->dev->name);
@@ -210,45 +264,35 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
}
- bss = ieee80211_rx_bss_get(local, ifmgd->bssid,
- local->hw.conf.channel->center_freq,
- ifmgd->ssid, ifmgd->ssid_len);
- if (bss) {
- if (bss->cbss.capability & WLAN_CAPABILITY_PRIVACY)
- capab |= WLAN_CAPABILITY_PRIVACY;
- if (bss->wmm_used)
- wmm = 1;
+ if (wk->bss->cbss.capability & WLAN_CAPABILITY_PRIVACY)
+ capab |= WLAN_CAPABILITY_PRIVACY;
+ if (wk->bss->wmm_used)
+ wmm = 1;
- /* get all rates supported by the device and the AP as
- * some APs don't like getting a superset of their rates
- * in the association request (e.g. D-Link DAP 1353 in
- * b-only mode) */
- rates_len = ieee80211_compatible_rates(bss, sband, &rates);
+ /* get all rates supported by the device and the AP as
+ * some APs don't like getting a superset of their rates
+ * in the association request (e.g. D-Link DAP 1353 in
+ * b-only mode) */
+ rates_len = ieee80211_compatible_rates(wk->bss, sband, &rates);
- if ((bss->cbss.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
- (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT))
- capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
-
- ieee80211_rx_bss_put(local, bss);
- } else {
- rates = ~0;
- rates_len = sband->n_bitrates;
- }
+ if ((wk->bss->cbss.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
+ (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT))
+ capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
memset(mgmt, 0, 24);
- memcpy(mgmt->da, ifmgd->bssid, ETH_ALEN);
+ memcpy(mgmt->da, wk->bss->cbss.bssid, ETH_ALEN);
memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
- memcpy(mgmt->bssid, ifmgd->bssid, ETH_ALEN);
+ memcpy(mgmt->bssid, wk->bss->cbss.bssid, ETH_ALEN);
- if (ifmgd->flags & IEEE80211_STA_PREV_BSSID_SET) {
+ if (!is_zero_ether_addr(wk->prev_bssid)) {
skb_put(skb, 10);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_REASSOC_REQ);
mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab);
mgmt->u.reassoc_req.listen_interval =
cpu_to_le16(local->hw.conf.listen_interval);
- memcpy(mgmt->u.reassoc_req.current_ap, ifmgd->prev_bssid,
+ memcpy(mgmt->u.reassoc_req.current_ap, wk->prev_bssid,
ETH_ALEN);
} else {
skb_put(skb, 4);
@@ -260,10 +304,10 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
}
/* SSID */
- ies = pos = skb_put(skb, 2 + ifmgd->ssid_len);
+ ies = pos = skb_put(skb, 2 + wk->ssid_len);
*pos++ = WLAN_EID_SSID;
- *pos++ = ifmgd->ssid_len;
- memcpy(pos, ifmgd->ssid, ifmgd->ssid_len);
+ *pos++ = wk->ssid_len;
+ memcpy(pos, wk->ssid, wk->ssid_len);
/* add all rates which were marked to be used above */
supp_rates_len = rates_len;
@@ -318,9 +362,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
}
}
- if (ifmgd->extra_ie) {
- pos = skb_put(skb, ifmgd->extra_ie_len);
- memcpy(pos, ifmgd->extra_ie, ifmgd->extra_ie_len);
+ if (wk->ie_len && wk->ie) {
+ pos = skb_put(skb, wk->ie_len);
+ memcpy(pos, wk->ie, wk->ie_len);
}
if (wmm && (ifmgd->flags & IEEE80211_STA_WMM_ENABLED)) {
@@ -345,9 +389,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
*/
if (wmm && (ifmgd->flags & IEEE80211_STA_WMM_ENABLED) &&
sband->ht_cap.ht_supported &&
- (ht_ie = ieee80211_bss_get_ie(bss, WLAN_EID_HT_INFORMATION)) &&
+ (ht_ie = ieee80211_bss_get_ie(&wk->bss->cbss, WLAN_EID_HT_INFORMATION)) &&
ht_ie[1] >= sizeof(struct ieee80211_ht_info) &&
- (!(ifmgd->flags & IEEE80211_STA_TKIP_WEP_USED))) {
+ (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))) {
struct ieee80211_ht_info *ht_info =
(struct ieee80211_ht_info *)(ht_ie + 2);
u16 cap = sband->ht_cap.cap;
@@ -382,18 +426,13 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
}
- kfree(ifmgd->assocreq_ies);
- ifmgd->assocreq_ies_len = (skb->data + skb->len) - ies;
- ifmgd->assocreq_ies = kmalloc(ifmgd->assocreq_ies_len, GFP_KERNEL);
- if (ifmgd->assocreq_ies)
- memcpy(ifmgd->assocreq_ies, ies, ifmgd->assocreq_ies_len);
-
ieee80211_tx_skb(sdata, skb, 0);
}
static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
- u16 stype, u16 reason)
+ const u8 *bssid, u16 stype, u16 reason,
+ void *cookie)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
@@ -410,18 +449,18 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
memset(mgmt, 0, 24);
- memcpy(mgmt->da, ifmgd->bssid, ETH_ALEN);
+ memcpy(mgmt->da, bssid, ETH_ALEN);
memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
- memcpy(mgmt->bssid, ifmgd->bssid, ETH_ALEN);
+ memcpy(mgmt->bssid, bssid, ETH_ALEN);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
skb_put(skb, 2);
/* u.deauth.reason_code == u.disassoc.reason_code */
mgmt->u.deauth.reason_code = cpu_to_le16(reason);
if (stype == IEEE80211_STYPE_DEAUTH)
- cfg80211_send_deauth(sdata->dev, (u8 *) mgmt, skb->len);
+ cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len, cookie);
else
- cfg80211_send_disassoc(sdata->dev, (u8 *) mgmt, skb->len);
+ cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len, cookie);
ieee80211_tx_skb(sdata, skb, ifmgd->flags & IEEE80211_STA_MFP_ENABLED);
}
@@ -494,28 +533,26 @@ static void ieee80211_chswitch_work(struct work_struct *work)
{
struct ieee80211_sub_if_data *sdata =
container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work);
- struct ieee80211_bss *bss;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
if (!netif_running(sdata->dev))
return;
- bss = ieee80211_rx_bss_get(sdata->local, ifmgd->bssid,
- sdata->local->hw.conf.channel->center_freq,
- ifmgd->ssid, ifmgd->ssid_len);
- if (!bss)
- goto exit;
+ mutex_lock(&ifmgd->mtx);
+ if (!ifmgd->associated)
+ goto out;
sdata->local->oper_channel = sdata->local->csa_channel;
+ ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL);
+
/* XXX: shouldn't really modify cfg80211-owned data! */
- if (!ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL))
- bss->cbss.channel = sdata->local->oper_channel;
+ ifmgd->associated->cbss.channel = sdata->local->oper_channel;
- ieee80211_rx_bss_put(sdata->local, bss);
-exit:
- ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
ieee80211_wake_queues_by_reason(&sdata->local->hw,
IEEE80211_QUEUE_STOP_REASON_CSA);
+ out:
+ ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
+ mutex_unlock(&ifmgd->mtx);
}
static void ieee80211_chswitch_timer(unsigned long data)
@@ -529,7 +566,7 @@ static void ieee80211_chswitch_timer(unsigned long data)
return;
}
- queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work);
+ ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
}
void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
@@ -540,10 +577,12 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num);
- if (ifmgd->state != IEEE80211_STA_MLME_ASSOCIATED)
+ ASSERT_MGD_MTX(ifmgd);
+
+ if (!ifmgd->associated)
return;
- if (sdata->local->sw_scanning || sdata->local->hw_scanning)
+ if (sdata->local->scanning)
return;
/* Disregard subsequent beacons if we are already running a timer
@@ -559,7 +598,7 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
sdata->local->csa_channel = new_ch;
if (sw_elem->count <= 1) {
- queue_work(sdata->local->hw.workqueue, &ifmgd->chswitch_work);
+ ieee80211_queue_work(&sdata->local->hw, &ifmgd->chswitch_work);
} else {
ieee80211_stop_queues_by_reason(&sdata->local->hw,
IEEE80211_QUEUE_STOP_REASON_CSA);
@@ -601,7 +640,7 @@ static void ieee80211_enable_ps(struct ieee80211_local *local,
* If we are scanning right now then the parameters will
* take effect when scan finishes.
*/
- if (local->hw_scanning || local->sw_scanning)
+ if (local->scanning)
return;
if (conf->dynamic_ps_timeout > 0 &&
@@ -651,8 +690,9 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
}
if (count == 1 && found->u.mgd.powersave &&
- (found->u.mgd.flags & IEEE80211_STA_ASSOCIATED) &&
- !(found->u.mgd.flags & IEEE80211_STA_PROBEREQ_POLL)) {
+ found->u.mgd.associated && list_empty(&found->u.mgd.work_list) &&
+ !(found->u.mgd.flags & (IEEE80211_STA_BEACON_POLL |
+ IEEE80211_STA_CONNECTION_POLL))) {
s32 beaconint_us;
if (latency < 0)
@@ -724,7 +764,7 @@ void ieee80211_dynamic_ps_timer(unsigned long data)
if (local->quiescing || local->suspended)
return;
- queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work);
+ ieee80211_queue_work(&local->hw, &local->dynamic_ps_enable_work);
}
/* MLME */
@@ -806,9 +846,6 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
u16 capab, bool erp_valid, u8 erp)
{
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-#endif
u32 changed = 0;
bool use_protection;
bool use_short_preamble;
@@ -825,42 +862,16 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME);
if (use_protection != bss_conf->use_cts_prot) {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: CTS protection %s (BSSID=%pM)\n",
- sdata->dev->name,
- use_protection ? "enabled" : "disabled",
- ifmgd->bssid);
- }
-#endif
bss_conf->use_cts_prot = use_protection;
changed |= BSS_CHANGED_ERP_CTS_PROT;
}
if (use_short_preamble != bss_conf->use_short_preamble) {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: switched to %s barker preamble"
- " (BSSID=%pM)\n",
- sdata->dev->name,
- use_short_preamble ? "short" : "long",
- ifmgd->bssid);
- }
-#endif
bss_conf->use_short_preamble = use_short_preamble;
changed |= BSS_CHANGED_ERP_PREAMBLE;
}
if (use_short_slot != bss_conf->use_short_slot) {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: switched to %s slot time"
- " (BSSID=%pM)\n",
- sdata->dev->name,
- use_short_slot ? "short" : "long",
- ifmgd->bssid);
- }
-#endif
bss_conf->use_short_slot = use_short_slot;
changed |= BSS_CHANGED_ERP_SLOT;
}
@@ -868,105 +879,31 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
return changed;
}
-static void ieee80211_sta_send_apinfo(struct ieee80211_sub_if_data *sdata)
-{
- union iwreq_data wrqu;
-
- memset(&wrqu, 0, sizeof(wrqu));
- if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED)
- memcpy(wrqu.ap_addr.sa_data, sdata->u.mgd.bssid, ETH_ALEN);
- wrqu.ap_addr.sa_family = ARPHRD_ETHER;
- wireless_send_event(sdata->dev, SIOCGIWAP, &wrqu, NULL);
-}
-
-static void ieee80211_sta_send_associnfo(struct ieee80211_sub_if_data *sdata)
-{
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- char *buf;
- size_t len;
- int i;
- union iwreq_data wrqu;
-
- if (!ifmgd->assocreq_ies && !ifmgd->assocresp_ies)
- return;
-
- buf = kmalloc(50 + 2 * (ifmgd->assocreq_ies_len +
- ifmgd->assocresp_ies_len), GFP_KERNEL);
- if (!buf)
- return;
-
- len = sprintf(buf, "ASSOCINFO(");
- if (ifmgd->assocreq_ies) {
- len += sprintf(buf + len, "ReqIEs=");
- for (i = 0; i < ifmgd->assocreq_ies_len; i++) {
- len += sprintf(buf + len, "%02x",
- ifmgd->assocreq_ies[i]);
- }
- }
- if (ifmgd->assocresp_ies) {
- if (ifmgd->assocreq_ies)
- len += sprintf(buf + len, " ");
- len += sprintf(buf + len, "RespIEs=");
- for (i = 0; i < ifmgd->assocresp_ies_len; i++) {
- len += sprintf(buf + len, "%02x",
- ifmgd->assocresp_ies[i]);
- }
- }
- len += sprintf(buf + len, ")");
-
- if (len > IW_CUSTOM_MAX) {
- len = sprintf(buf, "ASSOCRESPIE=");
- for (i = 0; i < ifmgd->assocresp_ies_len; i++) {
- len += sprintf(buf + len, "%02x",
- ifmgd->assocresp_ies[i]);
- }
- }
-
- if (len <= IW_CUSTOM_MAX) {
- memset(&wrqu, 0, sizeof(wrqu));
- wrqu.data.length = len;
- wireless_send_event(sdata->dev, IWEVCUSTOM, &wrqu, buf);
- }
-
- kfree(buf);
-}
-
-
static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgd_work *wk,
u32 bss_info_changed)
{
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
- struct ieee80211_conf *conf = &local_to_hw(local)->conf;
-
- struct ieee80211_bss *bss;
+ struct ieee80211_bss *bss = wk->bss;
bss_info_changed |= BSS_CHANGED_ASSOC;
- ifmgd->flags |= IEEE80211_STA_ASSOCIATED;
+ /* set timing information */
+ sdata->vif.bss_conf.beacon_int = bss->cbss.beacon_interval;
+ sdata->vif.bss_conf.timestamp = bss->cbss.tsf;
+ sdata->vif.bss_conf.dtim_period = bss->dtim_period;
- bss = ieee80211_rx_bss_get(local, ifmgd->bssid,
- conf->channel->center_freq,
- ifmgd->ssid, ifmgd->ssid_len);
- if (bss) {
- /* set timing information */
- sdata->vif.bss_conf.beacon_int = bss->cbss.beacon_interval;
- sdata->vif.bss_conf.timestamp = bss->cbss.tsf;
- sdata->vif.bss_conf.dtim_period = bss->dtim_period;
+ bss_info_changed |= BSS_CHANGED_BEACON_INT;
+ bss_info_changed |= ieee80211_handle_bss_capability(sdata,
+ bss->cbss.capability, bss->has_erp_value, bss->erp_value);
- bss_info_changed |= BSS_CHANGED_BEACON_INT;
- bss_info_changed |= ieee80211_handle_bss_capability(sdata,
- bss->cbss.capability, bss->has_erp_value, bss->erp_value);
-
- cfg80211_hold_bss(&bss->cbss);
-
- ieee80211_rx_bss_put(local, bss);
- }
+ sdata->u.mgd.associated = bss;
+ sdata->u.mgd.old_associate_work = wk;
+ memcpy(sdata->u.mgd.bssid, bss->cbss.bssid, ETH_ALEN);
- ifmgd->flags |= IEEE80211_STA_PREV_BSSID_SET;
- memcpy(ifmgd->prev_bssid, sdata->u.mgd.bssid, ETH_ALEN);
- ieee80211_sta_send_associnfo(sdata);
+ /* just to be sure */
+ sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL |
+ IEEE80211_STA_BEACON_POLL);
- ifmgd->last_probe = jiffies;
ieee80211_led_assoc(local, 1);
sdata->vif.bss_conf.assoc = 1;
@@ -982,176 +919,157 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
ieee80211_bss_info_change_notify(sdata, bss_info_changed);
- /* will be same as sdata */
- if (local->ps_sdata) {
- mutex_lock(&local->iflist_mtx);
- ieee80211_recalc_ps(local, -1);
- mutex_unlock(&local->iflist_mtx);
- }
+ mutex_lock(&local->iflist_mtx);
+ ieee80211_recalc_ps(local, -1);
+ mutex_unlock(&local->iflist_mtx);
netif_tx_start_all_queues(sdata->dev);
netif_carrier_on(sdata->dev);
-
- ieee80211_sta_send_apinfo(sdata);
}
-static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata)
+static enum rx_mgmt_action __must_check
+ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgd_work *wk)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
- ifmgd->direct_probe_tries++;
- if (ifmgd->direct_probe_tries > IEEE80211_AUTH_MAX_TRIES) {
+ wk->tries++;
+ if (wk->tries > IEEE80211_AUTH_MAX_TRIES) {
printk(KERN_DEBUG "%s: direct probe to AP %pM timed out\n",
- sdata->dev->name, ifmgd->bssid);
- ifmgd->state = IEEE80211_STA_MLME_DISABLED;
- ieee80211_recalc_idle(local);
- cfg80211_send_auth_timeout(sdata->dev, ifmgd->bssid);
+ sdata->dev->name, wk->bss->cbss.bssid);
/*
* Most likely AP is not in the range so remove the
- * bss information associated to the AP
+ * bss struct for that AP.
*/
- ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
- sdata->local->hw.conf.channel->center_freq,
- ifmgd->ssid, ifmgd->ssid_len);
+ cfg80211_unlink_bss(local->hw.wiphy, &wk->bss->cbss);
/*
* We might have a pending scan which had no chance to run yet
- * due to state == IEEE80211_STA_MLME_DIRECT_PROBE.
- * Hence, queue the STAs work again
+ * due to work needing to be done. Hence, queue the STAs work
+ * again for that.
*/
- queue_work(local->hw.workqueue, &ifmgd->work);
- return;
+ ieee80211_queue_work(&local->hw, &ifmgd->work);
+ return RX_MGMT_CFG80211_AUTH_TO;
}
- printk(KERN_DEBUG "%s: direct probe to AP %pM try %d\n",
- sdata->dev->name, ifmgd->bssid,
- ifmgd->direct_probe_tries);
+ printk(KERN_DEBUG "%s: direct probe to AP %pM (try %d)\n",
+ sdata->dev->name, wk->bss->cbss.bssid,
+ wk->tries);
- ifmgd->state = IEEE80211_STA_MLME_DIRECT_PROBE;
-
- /* Direct probe is sent to broadcast address as some APs
+ /*
+ * Direct probe is sent to broadcast address as some APs
* will not answer to direct packet in unassociated state.
*/
- ieee80211_send_probe_req(sdata, NULL,
- ifmgd->ssid, ifmgd->ssid_len, NULL, 0);
+ ieee80211_send_probe_req(sdata, NULL, wk->ssid, wk->ssid_len, NULL, 0);
+
+ wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
+ run_again(ifmgd, wk->timeout);
- mod_timer(&ifmgd->timer, jiffies + IEEE80211_AUTH_TIMEOUT);
+ return RX_MGMT_NONE;
}
-static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata)
+static enum rx_mgmt_action __must_check
+ieee80211_authenticate(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgd_work *wk)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
- u8 *ies;
- size_t ies_len;
- ifmgd->auth_tries++;
- if (ifmgd->auth_tries > IEEE80211_AUTH_MAX_TRIES) {
+ wk->tries++;
+ if (wk->tries > IEEE80211_AUTH_MAX_TRIES) {
printk(KERN_DEBUG "%s: authentication with AP %pM"
" timed out\n",
- sdata->dev->name, ifmgd->bssid);
- ifmgd->state = IEEE80211_STA_MLME_DISABLED;
- ieee80211_recalc_idle(local);
- cfg80211_send_auth_timeout(sdata->dev, ifmgd->bssid);
- ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
- sdata->local->hw.conf.channel->center_freq,
- ifmgd->ssid, ifmgd->ssid_len);
+ sdata->dev->name, wk->bss->cbss.bssid);
+
+ /*
+ * Most likely AP is not in the range so remove the
+ * bss struct for that AP.
+ */
+ cfg80211_unlink_bss(local->hw.wiphy, &wk->bss->cbss);
/*
* We might have a pending scan which had no chance to run yet
- * due to state == IEEE80211_STA_MLME_AUTHENTICATE.
- * Hence, queue the STAs work again
+ * due to work needing to be done. Hence, queue the STAs work
+ * again for that.
*/
- queue_work(local->hw.workqueue, &ifmgd->work);
- return;
+ ieee80211_queue_work(&local->hw, &ifmgd->work);
+ return RX_MGMT_CFG80211_AUTH_TO;
}
- ifmgd->state = IEEE80211_STA_MLME_AUTHENTICATE;
- printk(KERN_DEBUG "%s: authenticate with AP %pM\n",
- sdata->dev->name, ifmgd->bssid);
+ printk(KERN_DEBUG "%s: authenticate with AP %pM (try %d)\n",
+ sdata->dev->name, wk->bss->cbss.bssid, wk->tries);
- if (ifmgd->flags & IEEE80211_STA_EXT_SME) {
- ies = ifmgd->sme_auth_ie;
- ies_len = ifmgd->sme_auth_ie_len;
- } else {
- ies = NULL;
- ies_len = 0;
- }
- ieee80211_send_auth(sdata, 1, ifmgd->auth_alg, ies, ies_len,
- ifmgd->bssid, 0);
- ifmgd->auth_transaction = 2;
+ ieee80211_send_auth(sdata, 1, wk->auth_alg, wk->ie, wk->ie_len,
+ wk->bss->cbss.bssid, NULL, 0, 0);
+ wk->auth_transaction = 2;
+
+ wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
+ run_again(ifmgd, wk->timeout);
- mod_timer(&ifmgd->timer, jiffies + IEEE80211_AUTH_TIMEOUT);
+ return RX_MGMT_NONE;
}
-/*
- * The disassoc 'reason' argument can be either our own reason
- * if self disconnected or a reason code from the AP.
- */
static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
- bool deauth, bool self_disconnected,
- u16 reason)
+ bool deauth)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
- struct ieee80211_conf *conf = &local_to_hw(local)->conf;
- struct ieee80211_bss *bss;
struct sta_info *sta;
u32 changed = 0, config_changed = 0;
+ u8 bssid[ETH_ALEN];
+
+ ASSERT_MGD_MTX(ifmgd);
+
+ if (WARN_ON(!ifmgd->associated))
+ return;
+
+ memcpy(bssid, ifmgd->associated->cbss.bssid, ETH_ALEN);
+
+ ifmgd->associated = NULL;
+ memset(ifmgd->bssid, 0, ETH_ALEN);
if (deauth) {
- ifmgd->direct_probe_tries = 0;
- ifmgd->auth_tries = 0;
+ kfree(ifmgd->old_associate_work);
+ ifmgd->old_associate_work = NULL;
+ } else {
+ struct ieee80211_mgd_work *wk = ifmgd->old_associate_work;
+
+ wk->state = IEEE80211_MGD_STATE_IDLE;
+ list_add(&wk->list, &ifmgd->work_list);
}
- ifmgd->assoc_scan_tries = 0;
- ifmgd->assoc_tries = 0;
+
+ /*
+ * we need to commit the associated = NULL change because the
+ * scan code uses that to determine whether this iface should
+ * go to/wake up from powersave or not -- and could otherwise
+ * wake the queues erroneously.
+ */
+ smp_mb();
+
+ /*
+ * Thus, we can only afterwards stop the queues -- to account
+ * for the case where another CPU is finishing a scan at this
+ * time -- we don't want the scan code to enable queues.
+ */
netif_tx_stop_all_queues(sdata->dev);
netif_carrier_off(sdata->dev);
rcu_read_lock();
- sta = sta_info_get(local, ifmgd->bssid);
+ sta = sta_info_get(local, bssid);
if (sta)
ieee80211_sta_tear_down_BA_sessions(sta);
rcu_read_unlock();
- bss = ieee80211_rx_bss_get(local, ifmgd->bssid,
- conf->channel->center_freq,
- ifmgd->ssid, ifmgd->ssid_len);
-
- if (bss) {
- cfg80211_unhold_bss(&bss->cbss);
- ieee80211_rx_bss_put(local, bss);
- }
-
- if (self_disconnected) {
- if (deauth)
- ieee80211_send_deauth_disassoc(sdata,
- IEEE80211_STYPE_DEAUTH, reason);
- else
- ieee80211_send_deauth_disassoc(sdata,
- IEEE80211_STYPE_DISASSOC, reason);
- }
-
- ifmgd->flags &= ~IEEE80211_STA_ASSOCIATED;
changed |= ieee80211_reset_erp_info(sdata);
ieee80211_led_assoc(local, 0);
changed |= BSS_CHANGED_ASSOC;
sdata->vif.bss_conf.assoc = false;
- ieee80211_sta_send_apinfo(sdata);
-
- if (self_disconnected || reason == WLAN_REASON_DISASSOC_STA_HAS_LEFT) {
- ifmgd->state = IEEE80211_STA_MLME_DISABLED;
- ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
- sdata->local->hw.conf.channel->center_freq,
- ifmgd->ssid, ifmgd->ssid_len);
- }
-
ieee80211_set_wmm_default(sdata);
ieee80211_recalc_idle(local);
@@ -1180,7 +1098,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
rcu_read_lock();
- sta = sta_info_get(local, ifmgd->bssid);
+ sta = sta_info_get(local, bssid);
if (!sta) {
rcu_read_unlock();
return;
@@ -1193,83 +1111,42 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
sta_info_destroy(sta);
}
-static int ieee80211_sta_wep_configured(struct ieee80211_sub_if_data *sdata)
-{
- if (!sdata || !sdata->default_key ||
- sdata->default_key->conf.alg != ALG_WEP)
- return 0;
- return 1;
-}
-
-static int ieee80211_privacy_mismatch(struct ieee80211_sub_if_data *sdata)
-{
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- struct ieee80211_local *local = sdata->local;
- struct ieee80211_bss *bss;
- int bss_privacy;
- int wep_privacy;
- int privacy_invoked;
-
- if (!ifmgd || (ifmgd->flags & IEEE80211_STA_EXT_SME))
- return 0;
-
- bss = ieee80211_rx_bss_get(local, ifmgd->bssid,
- local->hw.conf.channel->center_freq,
- ifmgd->ssid, ifmgd->ssid_len);
- if (!bss)
- return 0;
-
- bss_privacy = !!(bss->cbss.capability & WLAN_CAPABILITY_PRIVACY);
- wep_privacy = !!ieee80211_sta_wep_configured(sdata);
- privacy_invoked = !!(ifmgd->flags & IEEE80211_STA_PRIVACY_INVOKED);
-
- ieee80211_rx_bss_put(local, bss);
-
- if ((bss_privacy == wep_privacy) || (bss_privacy == privacy_invoked))
- return 0;
-
- return 1;
-}
-
-static void ieee80211_associate(struct ieee80211_sub_if_data *sdata)
+static enum rx_mgmt_action __must_check
+ieee80211_associate(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgd_work *wk)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
- ifmgd->assoc_tries++;
- if (ifmgd->assoc_tries > IEEE80211_ASSOC_MAX_TRIES) {
+ wk->tries++;
+ if (wk->tries > IEEE80211_ASSOC_MAX_TRIES) {
printk(KERN_DEBUG "%s: association with AP %pM"
" timed out\n",
- sdata->dev->name, ifmgd->bssid);
- ifmgd->state = IEEE80211_STA_MLME_DISABLED;
- ieee80211_recalc_idle(local);
- cfg80211_send_assoc_timeout(sdata->dev, ifmgd->bssid);
- ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
- sdata->local->hw.conf.channel->center_freq,
- ifmgd->ssid, ifmgd->ssid_len);
+ sdata->dev->name, wk->bss->cbss.bssid);
+
+ /*
+ * Most likely AP is not in the range so remove the
+ * bss struct for that AP.
+ */
+ cfg80211_unlink_bss(local->hw.wiphy, &wk->bss->cbss);
+
/*
* We might have a pending scan which had no chance to run yet
- * due to state == IEEE80211_STA_MLME_ASSOCIATE.
- * Hence, queue the STAs work again
+ * due to work needing to be done. Hence, queue the STAs work
+ * again for that.
*/
- queue_work(local->hw.workqueue, &ifmgd->work);
- return;
+ ieee80211_queue_work(&local->hw, &ifmgd->work);
+ return RX_MGMT_CFG80211_ASSOC_TO;
}
- ifmgd->state = IEEE80211_STA_MLME_ASSOCIATE;
- printk(KERN_DEBUG "%s: associate with AP %pM\n",
- sdata->dev->name, ifmgd->bssid);
- if (ieee80211_privacy_mismatch(sdata)) {
- printk(KERN_DEBUG "%s: mismatch in privacy configuration and "
- "mixed-cell disabled - abort association\n", sdata->dev->name);
- ifmgd->state = IEEE80211_STA_MLME_DISABLED;
- ieee80211_recalc_idle(local);
- return;
- }
+ printk(KERN_DEBUG "%s: associate with AP %pM (try %d)\n",
+ sdata->dev->name, wk->bss->cbss.bssid, wk->tries);
+ ieee80211_send_assoc(sdata, wk);
- ieee80211_send_assoc(sdata);
+ wk->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
+ run_again(ifmgd, wk->timeout);
- mod_timer(&ifmgd->timer, jiffies + IEEE80211_ASSOC_TIMEOUT);
+ return RX_MGMT_NONE;
}
void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
@@ -1280,160 +1157,113 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
* from AP because we know that the connection is working both ways
* at that time. But multicast frames (and hence also beacons) must
* be ignored here, because we need to trigger the timer during
- * data idle periods for sending the periodical probe request to
- * the AP.
+ * data idle periods for sending the periodic probe request to the
+ * AP we're connected to.
*/
- if (!is_multicast_ether_addr(hdr->addr1))
- mod_timer(&sdata->u.mgd.timer,
- jiffies + IEEE80211_MONITORING_INTERVAL);
-}
-
-void ieee80211_beacon_loss_work(struct work_struct *work)
-{
- struct ieee80211_sub_if_data *sdata =
- container_of(work, struct ieee80211_sub_if_data,
- u.mgd.beacon_loss_work);
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-
- /*
- * The driver has already reported this event and we have
- * already sent a probe request. Maybe the AP died and the
- * driver keeps reporting until we disassociate... We have
- * to ignore that because otherwise we would continually
- * reset the timer and never check whether we received a
- * probe response!
- */
- if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL)
+ if (is_multicast_ether_addr(hdr->addr1))
return;
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: driver reports beacon loss from AP %pM "
- "- sending probe request\n", sdata->dev->name,
- sdata->u.mgd.bssid);
- }
-#endif
-
- ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL;
-
- mutex_lock(&sdata->local->iflist_mtx);
- ieee80211_recalc_ps(sdata->local, -1);
- mutex_unlock(&sdata->local->iflist_mtx);
-
- ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid,
- ifmgd->ssid_len, NULL, 0);
-
- mod_timer(&ifmgd->timer, jiffies + IEEE80211_PROBE_WAIT);
+ mod_timer(&sdata->u.mgd.conn_mon_timer,
+ round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME));
}
-void ieee80211_beacon_loss(struct ieee80211_vif *vif)
+static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
{
- struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ const u8 *ssid;
+
+ ssid = ieee80211_bss_get_ie(&ifmgd->associated->cbss, WLAN_EID_SSID);
+ ieee80211_send_probe_req(sdata, ifmgd->associated->cbss.bssid,
+ ssid + 2, ssid[1], NULL, 0);
- queue_work(sdata->local->hw.workqueue,
- &sdata->u.mgd.beacon_loss_work);
+ ifmgd->probe_send_count++;
+ ifmgd->probe_timeout = jiffies + IEEE80211_PROBE_WAIT;
+ run_again(ifmgd, ifmgd->probe_timeout);
}
-EXPORT_SYMBOL(ieee80211_beacon_loss);
-static void ieee80211_associated(struct ieee80211_sub_if_data *sdata)
+static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
+ bool beacon)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- struct ieee80211_local *local = sdata->local;
- struct sta_info *sta;
- unsigned long last_rx;
- bool disassoc = false;
+ bool already = false;
- /* TODO: start monitoring current AP signal quality and number of
- * missed beacons. Scan other channels every now and then and search
- * for better APs. */
- /* TODO: remove expired BSSes */
+ if (!netif_running(sdata->dev))
+ return;
- ifmgd->state = IEEE80211_STA_MLME_ASSOCIATED;
+ if (sdata->local->scanning)
+ return;
- rcu_read_lock();
+ mutex_lock(&ifmgd->mtx);
- sta = sta_info_get(local, ifmgd->bssid);
- if (!sta) {
- printk(KERN_DEBUG "%s: No STA entry for own AP %pM\n",
- sdata->dev->name, ifmgd->bssid);
- disassoc = true;
- rcu_read_unlock();
+ if (!ifmgd->associated)
goto out;
- }
- last_rx = sta->last_rx;
- rcu_read_unlock();
-
- if ((ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) &&
- time_after(jiffies, last_rx + IEEE80211_PROBE_WAIT)) {
- printk(KERN_DEBUG "%s: no probe response from AP %pM "
- "- disassociating\n",
- sdata->dev->name, ifmgd->bssid);
- disassoc = true;
- ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL;
- goto out;
- }
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+ if (beacon && net_ratelimit())
+ printk(KERN_DEBUG "%s: detected beacon loss from AP "
+ "- sending probe request\n", sdata->dev->name);
+#endif
/*
- * Beacon filtering is only enabled with power save and then the
- * stack should not check for beacon loss.
+ * The driver/our work has already reported this event or the
+ * connection monitoring has kicked in and we have already sent
+ * a probe request. Or maybe the AP died and the driver keeps
+ * reporting until we disassociate...
+ *
+ * In either case we have to ignore the current call to this
+ * function (except for setting the correct probe reason bit)
+ * because otherwise we would reset the timer every time and
+ * never check whether we received a probe response!
*/
- if (!((local->hw.flags & IEEE80211_HW_BEACON_FILTER) &&
- (local->hw.conf.flags & IEEE80211_CONF_PS)) &&
- time_after(jiffies,
- ifmgd->last_beacon + IEEE80211_MONITORING_INTERVAL)) {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: beacon loss from AP %pM "
- "- sending probe request\n",
- sdata->dev->name, ifmgd->bssid);
- }
-#endif
- ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL;
- mutex_lock(&local->iflist_mtx);
- ieee80211_recalc_ps(local, -1);
- mutex_unlock(&local->iflist_mtx);
- ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid,
- ifmgd->ssid_len, NULL, 0);
- mod_timer(&ifmgd->timer, jiffies + IEEE80211_PROBE_WAIT);
+ if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
+ IEEE80211_STA_CONNECTION_POLL))
+ already = true;
+
+ if (beacon)
+ ifmgd->flags |= IEEE80211_STA_BEACON_POLL;
+ else
+ ifmgd->flags |= IEEE80211_STA_CONNECTION_POLL;
+
+ if (already)
goto out;
- }
- if (time_after(jiffies, last_rx + IEEE80211_PROBE_IDLE_TIME)) {
- ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL;
- mutex_lock(&local->iflist_mtx);
- ieee80211_recalc_ps(local, -1);
- mutex_unlock(&local->iflist_mtx);
- ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid,
- ifmgd->ssid_len, NULL, 0);
- }
+ mutex_lock(&sdata->local->iflist_mtx);
+ ieee80211_recalc_ps(sdata->local, -1);
+ mutex_unlock(&sdata->local->iflist_mtx);
+ ifmgd->probe_send_count = 0;
+ ieee80211_mgd_probe_ap_send(sdata);
out:
- if (!disassoc)
- mod_timer(&ifmgd->timer,
- jiffies + IEEE80211_MONITORING_INTERVAL);
- else
- ieee80211_set_disassoc(sdata, true, true,
- WLAN_REASON_PREV_AUTH_NOT_VALID);
+ mutex_unlock(&ifmgd->mtx);
}
+void ieee80211_beacon_loss_work(struct work_struct *work)
+{
+ struct ieee80211_sub_if_data *sdata =
+ container_of(work, struct ieee80211_sub_if_data,
+ u.mgd.beacon_loss_work);
+
+ ieee80211_mgd_probe_ap(sdata, true);
+}
-static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata)
+void ieee80211_beacon_loss(struct ieee80211_vif *vif)
{
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+ ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work);
+}
+EXPORT_SYMBOL(ieee80211_beacon_loss);
+
+static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgd_work *wk)
+{
+ wk->state = IEEE80211_MGD_STATE_IDLE;
printk(KERN_DEBUG "%s: authenticated\n", sdata->dev->name);
- ifmgd->flags |= IEEE80211_STA_AUTHENTICATED;
- if (ifmgd->flags & IEEE80211_STA_EXT_SME) {
- /* Wait for SME to request association */
- ifmgd->state = IEEE80211_STA_MLME_DISABLED;
- ieee80211_recalc_idle(sdata->local);
- } else
- ieee80211_associate(sdata);
}
static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgd_work *wk,
struct ieee80211_mgmt *mgmt,
size_t len)
{
@@ -1444,161 +1274,133 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
if (!elems.challenge)
return;
- ieee80211_send_auth(sdata, 3, sdata->u.mgd.auth_alg,
+ ieee80211_send_auth(sdata, 3, wk->auth_alg,
elems.challenge - 2, elems.challenge_len + 2,
- sdata->u.mgd.bssid, 1);
- sdata->u.mgd.auth_transaction = 4;
+ wk->bss->cbss.bssid,
+ wk->key, wk->key_len, wk->key_idx);
+ wk->auth_transaction = 4;
}
-static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt,
- size_t len)
+static enum rx_mgmt_action __must_check
+ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgd_work *wk,
+ struct ieee80211_mgmt *mgmt, size_t len)
{
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u16 auth_alg, auth_transaction, status_code;
- if (ifmgd->state != IEEE80211_STA_MLME_AUTHENTICATE)
- return;
+ if (wk->state != IEEE80211_MGD_STATE_AUTH)
+ return RX_MGMT_NONE;
if (len < 24 + 6)
- return;
+ return RX_MGMT_NONE;
- if (memcmp(ifmgd->bssid, mgmt->sa, ETH_ALEN) != 0)
- return;
+ if (memcmp(wk->bss->cbss.bssid, mgmt->sa, ETH_ALEN) != 0)
+ return RX_MGMT_NONE;
- if (memcmp(ifmgd->bssid, mgmt->bssid, ETH_ALEN) != 0)
- return;
+ if (memcmp(wk->bss->cbss.bssid, mgmt->bssid, ETH_ALEN) != 0)
+ return RX_MGMT_NONE;
auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
status_code = le16_to_cpu(mgmt->u.auth.status_code);
- if (auth_alg != ifmgd->auth_alg ||
- auth_transaction != ifmgd->auth_transaction)
- return;
+ if (auth_alg != wk->auth_alg ||
+ auth_transaction != wk->auth_transaction)
+ return RX_MGMT_NONE;
if (status_code != WLAN_STATUS_SUCCESS) {
- if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) {
- u8 algs[3];
- const int num_algs = ARRAY_SIZE(algs);
- int i, pos;
- algs[0] = algs[1] = algs[2] = 0xff;
- if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_OPEN)
- algs[0] = WLAN_AUTH_OPEN;
- if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_SHARED_KEY)
- algs[1] = WLAN_AUTH_SHARED_KEY;
- if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_LEAP)
- algs[2] = WLAN_AUTH_LEAP;
- if (ifmgd->auth_alg == WLAN_AUTH_OPEN)
- pos = 0;
- else if (ifmgd->auth_alg == WLAN_AUTH_SHARED_KEY)
- pos = 1;
- else
- pos = 2;
- for (i = 0; i < num_algs; i++) {
- pos++;
- if (pos >= num_algs)
- pos = 0;
- if (algs[pos] == ifmgd->auth_alg ||
- algs[pos] == 0xff)
- continue;
- if (algs[pos] == WLAN_AUTH_SHARED_KEY &&
- !ieee80211_sta_wep_configured(sdata))
- continue;
- ifmgd->auth_alg = algs[pos];
- break;
- }
- }
- return;
+ list_del(&wk->list);
+ kfree(wk);
+ return RX_MGMT_CFG80211_AUTH;
}
- switch (ifmgd->auth_alg) {
+ switch (wk->auth_alg) {
case WLAN_AUTH_OPEN:
case WLAN_AUTH_LEAP:
case WLAN_AUTH_FT:
- ieee80211_auth_completed(sdata);
- cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len);
- break;
+ ieee80211_auth_completed(sdata, wk);
+ return RX_MGMT_CFG80211_AUTH;
case WLAN_AUTH_SHARED_KEY:
- if (ifmgd->auth_transaction == 4) {
- ieee80211_auth_completed(sdata);
- cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len);
+ if (wk->auth_transaction == 4) {
+ ieee80211_auth_completed(sdata, wk);
+ return RX_MGMT_CFG80211_AUTH;
} else
- ieee80211_auth_challenge(sdata, mgmt, len);
+ ieee80211_auth_challenge(sdata, wk, mgmt, len);
break;
}
+
+ return RX_MGMT_NONE;
}
-static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt,
- size_t len)
+static enum rx_mgmt_action __must_check
+ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgd_work *wk,
+ struct ieee80211_mgmt *mgmt, size_t len)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ const u8 *bssid = NULL;
u16 reason_code;
if (len < 24 + 2)
- return;
+ return RX_MGMT_NONE;
- if (memcmp(ifmgd->bssid, mgmt->sa, ETH_ALEN))
- return;
+ ASSERT_MGD_MTX(ifmgd);
+
+ if (wk)
+ bssid = wk->bss->cbss.bssid;
+ else
+ bssid = ifmgd->associated->cbss.bssid;
reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
- if (ifmgd->flags & IEEE80211_STA_AUTHENTICATED)
- printk(KERN_DEBUG "%s: deauthenticated (Reason: %u)\n",
- sdata->dev->name, reason_code);
+ printk(KERN_DEBUG "%s: deauthenticated from %pM (Reason: %u)\n",
+ sdata->dev->name, bssid, reason_code);
- if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) &&
- (ifmgd->state == IEEE80211_STA_MLME_AUTHENTICATE ||
- ifmgd->state == IEEE80211_STA_MLME_ASSOCIATE ||
- ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED)) {
- ifmgd->state = IEEE80211_STA_MLME_DIRECT_PROBE;
- mod_timer(&ifmgd->timer, jiffies +
- IEEE80211_RETRY_AUTH_INTERVAL);
+ if (!wk) {
+ ieee80211_set_disassoc(sdata, true);
+ } else {
+ list_del(&wk->list);
+ kfree(wk);
}
- ieee80211_set_disassoc(sdata, true, false, 0);
- ifmgd->flags &= ~IEEE80211_STA_AUTHENTICATED;
- cfg80211_send_deauth(sdata->dev, (u8 *) mgmt, len);
+ return RX_MGMT_CFG80211_DEAUTH;
}
-static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt,
- size_t len)
+static enum rx_mgmt_action __must_check
+ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt, size_t len)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u16 reason_code;
if (len < 24 + 2)
- return;
+ return RX_MGMT_NONE;
- if (memcmp(ifmgd->bssid, mgmt->sa, ETH_ALEN))
- return;
+ ASSERT_MGD_MTX(ifmgd);
- reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
+ if (WARN_ON(!ifmgd->associated))
+ return RX_MGMT_NONE;
- if (ifmgd->flags & IEEE80211_STA_ASSOCIATED)
- printk(KERN_DEBUG "%s: disassociated (Reason: %u)\n",
- sdata->dev->name, reason_code);
+ if (WARN_ON(memcmp(ifmgd->associated->cbss.bssid, mgmt->sa, ETH_ALEN)))
+ return RX_MGMT_NONE;
- if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) &&
- ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED) {
- ifmgd->state = IEEE80211_STA_MLME_ASSOCIATE;
- mod_timer(&ifmgd->timer, jiffies +
- IEEE80211_RETRY_AUTH_INTERVAL);
- }
+ reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
- ieee80211_set_disassoc(sdata, false, false, reason_code);
- cfg80211_send_disassoc(sdata->dev, (u8 *) mgmt, len);
+ printk(KERN_DEBUG "%s: disassociated (Reason: %u)\n",
+ sdata->dev->name, reason_code);
+
+ ieee80211_set_disassoc(sdata, false);
+ return RX_MGMT_CFG80211_DISASSOC;
}
-static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt,
- size_t len,
- int reassoc)
+static enum rx_mgmt_action __must_check
+ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgd_work *wk,
+ struct ieee80211_mgmt *mgmt, size_t len,
+ bool reassoc)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
@@ -1614,17 +1416,16 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
bool have_higher_than_11mbit = false, newsta = false;
u16 ap_ht_cap_flags;
- /* AssocResp and ReassocResp have identical structure, so process both
- * of them in this function. */
-
- if (ifmgd->state != IEEE80211_STA_MLME_ASSOCIATE)
- return;
+ /*
+ * AssocResp and ReassocResp have identical structure, so process both
+ * of them in this function.
+ */
if (len < 24 + 6)
- return;
+ return RX_MGMT_NONE;
- if (memcmp(ifmgd->bssid, mgmt->sa, ETH_ALEN) != 0)
- return;
+ if (memcmp(wk->bss->cbss.bssid, mgmt->sa, ETH_ALEN) != 0)
+ return RX_MGMT_NONE;
capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
@@ -1647,26 +1448,18 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
printk(KERN_DEBUG "%s: AP rejected association temporarily; "
"comeback duration %u TU (%u ms)\n",
sdata->dev->name, tu, ms);
+ wk->timeout = jiffies + msecs_to_jiffies(ms);
if (ms > IEEE80211_ASSOC_TIMEOUT)
- mod_timer(&ifmgd->timer,
- jiffies + msecs_to_jiffies(ms));
- return;
+ run_again(ifmgd, jiffies + msecs_to_jiffies(ms));
+ return RX_MGMT_NONE;
}
if (status_code != WLAN_STATUS_SUCCESS) {
printk(KERN_DEBUG "%s: AP denied association (code=%d)\n",
sdata->dev->name, status_code);
- /* if this was a reassociation, ensure we try a "full"
- * association next time. This works around some broken APs
- * which do not correctly reject reassociation requests. */
- ifmgd->flags &= ~IEEE80211_STA_PREV_BSSID_SET;
- cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, len);
- if (ifmgd->flags & IEEE80211_STA_EXT_SME) {
- /* Wait for SME to decide what to do next */
- ifmgd->state = IEEE80211_STA_MLME_DISABLED;
- ieee80211_recalc_idle(local);
- }
- return;
+ list_del(&wk->list);
+ kfree(wk);
+ return RX_MGMT_CFG80211_ASSOC;
}
if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14)))
@@ -1677,51 +1470,35 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
if (!elems.supp_rates) {
printk(KERN_DEBUG "%s: no SuppRates element in AssocResp\n",
sdata->dev->name);
- return;
+ return RX_MGMT_NONE;
}
printk(KERN_DEBUG "%s: associated\n", sdata->dev->name);
ifmgd->aid = aid;
- ifmgd->ap_capab = capab_info;
-
- kfree(ifmgd->assocresp_ies);
- ifmgd->assocresp_ies_len = len - (pos - (u8 *) mgmt);
- ifmgd->assocresp_ies = kmalloc(ifmgd->assocresp_ies_len, GFP_KERNEL);
- if (ifmgd->assocresp_ies)
- memcpy(ifmgd->assocresp_ies, pos, ifmgd->assocresp_ies_len);
rcu_read_lock();
/* Add STA entry for the AP */
- sta = sta_info_get(local, ifmgd->bssid);
+ sta = sta_info_get(local, wk->bss->cbss.bssid);
if (!sta) {
newsta = true;
- sta = sta_info_alloc(sdata, ifmgd->bssid, GFP_ATOMIC);
+ rcu_read_unlock();
+
+ sta = sta_info_alloc(sdata, wk->bss->cbss.bssid, GFP_KERNEL);
if (!sta) {
printk(KERN_DEBUG "%s: failed to alloc STA entry for"
" the AP\n", sdata->dev->name);
- rcu_read_unlock();
- return;
+ return RX_MGMT_NONE;
}
- /* update new sta with its last rx activity */
- sta->last_rx = jiffies;
- }
+ set_sta_flags(sta, WLAN_STA_AUTH | WLAN_STA_ASSOC |
+ WLAN_STA_ASSOC_AP);
+ if (!(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
+ set_sta_flags(sta, WLAN_STA_AUTHORIZED);
- /*
- * FIXME: Do we really need to update the sta_info's information here?
- * We already know about the AP (we found it in our list) so it
- * should already be filled with the right info, no?
- * As is stands, all this is racy because typically we assume
- * the information that is filled in here (except flags) doesn't
- * change while a STA structure is alive. As such, it should move
- * to between the sta_info_alloc() and sta_info_insert() above.
- */
-
- set_sta_flags(sta, WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_AP);
- if (!(ifmgd->flags & IEEE80211_STA_CONTROL_PORT))
- set_sta_flags(sta, WLAN_STA_AUTHORIZED);
+ rcu_read_lock();
+ }
rates = 0;
basic_rates = 0;
@@ -1771,8 +1548,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
else
sdata->flags &= ~IEEE80211_SDATA_OPERATING_GMODE;
- /* If TKIP/WEP is used, no need to parse AP's HT capabilities */
- if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_TKIP_WEP_USED))
+ if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
ieee80211_ht_cap_ie_to_sta_ht_cap(sband,
elems.ht_cap_elem, &sta->sta.ht_cap);
@@ -1792,7 +1568,7 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
printk(KERN_DEBUG "%s: failed to insert STA entry for"
" the AP (error %d)\n", sdata->dev->name, err);
rcu_read_unlock();
- return;
+ return RX_MGMT_NONE;
}
}
@@ -1806,24 +1582,29 @@ static void ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
if (elems.ht_info_elem && elems.wmm_param &&
(ifmgd->flags & IEEE80211_STA_WMM_ENABLED) &&
- !(ifmgd->flags & IEEE80211_STA_TKIP_WEP_USED))
+ !(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
+ wk->bss->cbss.bssid,
ap_ht_cap_flags);
+ /* delete work item -- must be before set_associated for PS */
+ list_del(&wk->list);
+
/* set AID and assoc capability,
* ieee80211_set_associated() will tell the driver */
bss_conf->aid = aid;
bss_conf->assoc_capability = capab_info;
- ieee80211_set_associated(sdata, changed);
+ /* this will take ownership of wk */
+ ieee80211_set_associated(sdata, wk, changed);
/*
- * initialise the time of last beacon to be the association time,
- * otherwise beacon loss check will trigger immediately
+ * Start timer to probe the connection to the AP now.
+ * Also start the timer that will detect beacon loss.
*/
- ifmgd->last_beacon = jiffies;
+ ieee80211_sta_rx_notify(sdata, (struct ieee80211_hdr *)mgmt);
+ mod_beacon_timer(sdata);
- ieee80211_associated(sdata);
- cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, len);
+ return RX_MGMT_CFG80211_ASSOC;
}
@@ -1851,23 +1632,25 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
bss = ieee80211_bss_info_update(local, rx_status, mgmt, len, elems,
channel, beacon);
- if (!bss)
+ if (bss)
+ ieee80211_rx_bss_put(local, bss);
+
+ if (!sdata->u.mgd.associated)
return;
if (elems->ch_switch_elem && (elems->ch_switch_elem_len == 3) &&
- (memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN) == 0)) {
+ (memcmp(mgmt->bssid, sdata->u.mgd.associated->cbss.bssid,
+ ETH_ALEN) == 0)) {
struct ieee80211_channel_sw_ie *sw_elem =
(struct ieee80211_channel_sw_ie *)elems->ch_switch_elem;
ieee80211_sta_process_chanswitch(sdata, sw_elem, bss);
}
-
- ieee80211_rx_bss_put(local, bss);
}
static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt,
- size_t len,
+ struct ieee80211_mgd_work *wk,
+ struct ieee80211_mgmt *mgmt, size_t len,
struct ieee80211_rx_status *rx_status)
{
struct ieee80211_if_managed *ifmgd;
@@ -1876,6 +1659,8 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
ifmgd = &sdata->u.mgd;
+ ASSERT_MGD_MTX(ifmgd);
+
if (memcmp(mgmt->da, sdata->dev->dev_addr, ETH_ALEN))
return; /* ignore ProbeResp to foreign address */
@@ -1889,17 +1674,32 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems, false);
/* direct probe may be part of the association flow */
- if (ifmgd->state == IEEE80211_STA_MLME_DIRECT_PROBE) {
+ if (wk && wk->state == IEEE80211_MGD_STATE_PROBE) {
printk(KERN_DEBUG "%s direct probe responded\n",
sdata->dev->name);
- ieee80211_authenticate(sdata);
+ wk->tries = 0;
+ wk->state = IEEE80211_MGD_STATE_AUTH;
+ WARN_ON(ieee80211_authenticate(sdata, wk) != RX_MGMT_NONE);
}
- if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) {
- ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL;
+ if (ifmgd->associated &&
+ memcmp(mgmt->bssid, ifmgd->associated->cbss.bssid, ETH_ALEN) == 0 &&
+ ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
+ IEEE80211_STA_CONNECTION_POLL)) {
+ ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL |
+ IEEE80211_STA_BEACON_POLL);
mutex_lock(&sdata->local->iflist_mtx);
ieee80211_recalc_ps(sdata->local, -1);
mutex_unlock(&sdata->local->iflist_mtx);
+ /*
+ * We've received a probe response, but are not sure whether
+ * we have or will be receiving any beacons or data, so let's
+ * schedule the timers again, just in case.
+ */
+ mod_beacon_timer(sdata);
+ mod_timer(&ifmgd->conn_mon_timer,
+ round_jiffies_up(jiffies +
+ IEEE80211_CONNECTION_IDLE_TIME));
}
}
@@ -1937,6 +1737,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
bool erp_valid, directed_tim = false;
u8 erp_value = 0;
u32 ncrc;
+ u8 *bssid;
+
+ ASSERT_MGD_MTX(ifmgd);
/* Process beacon from the current BSS */
baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
@@ -1946,23 +1749,41 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
if (rx_status->freq != local->hw.conf.channel->center_freq)
return;
- if (!(ifmgd->flags & IEEE80211_STA_ASSOCIATED) ||
- memcmp(ifmgd->bssid, mgmt->bssid, ETH_ALEN) != 0)
+ /*
+ * We might have received a number of frames, among them a
+ * disassoc frame and a beacon...
+ */
+ if (!ifmgd->associated)
+ return;
+
+ bssid = ifmgd->associated->cbss.bssid;
+
+ /*
+ * And in theory even frames from a different AP we were just
+ * associated to a split-second ago!
+ */
+ if (memcmp(bssid, mgmt->bssid, ETH_ALEN) != 0)
return;
- if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) {
+ if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
if (net_ratelimit()) {
printk(KERN_DEBUG "%s: cancelling probereq poll due "
"to a received beacon\n", sdata->dev->name);
}
#endif
- ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL;
+ ifmgd->flags &= ~IEEE80211_STA_BEACON_POLL;
mutex_lock(&local->iflist_mtx);
ieee80211_recalc_ps(local, -1);
mutex_unlock(&local->iflist_mtx);
}
+ /*
+ * Push the beacon loss detection into the future since
+ * we are processing a beacon from the AP just now.
+ */
+ mod_beacon_timer(sdata);
+
ncrc = crc32_be(0, (void *)&mgmt->u.beacon.beacon_int, 4);
ncrc = ieee802_11_parse_elems_crc(mgmt->u.beacon.variable,
len - baselen, &elems,
@@ -2019,15 +1840,15 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
if (elems.ht_cap_elem && elems.ht_info_elem && elems.wmm_param &&
- !(ifmgd->flags & IEEE80211_STA_TKIP_WEP_USED)) {
+ !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) {
struct sta_info *sta;
struct ieee80211_supported_band *sband;
u16 ap_ht_cap_flags;
rcu_read_lock();
- sta = sta_info_get(local, ifmgd->bssid);
- if (!sta) {
+ sta = sta_info_get(local, bssid);
+ if (WARN_ON(!sta)) {
rcu_read_unlock();
return;
}
@@ -2042,15 +1863,11 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
rcu_read_unlock();
changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
- ap_ht_cap_flags);
+ bssid, ap_ht_cap_flags);
}
+ /* Note: country IE parsing is done for us by cfg80211 */
if (elems.country_elem) {
- /* Note we are only reviewing this on beacons
- * for the BSSID we are associated to */
- regulatory_hint_11d(local->hw.wiphy,
- elems.country_elem, elems.country_elem_len);
-
/* TODO: IBSS also needs this */
if (elems.pwr_constr_elem)
ieee80211_handle_pwr_constr(sdata,
@@ -2063,8 +1880,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
}
ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb,
- struct ieee80211_rx_status *rx_status)
+ struct sk_buff *skb)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_mgmt *mgmt;
@@ -2080,14 +1896,14 @@ ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata,
case IEEE80211_STYPE_PROBE_REQ:
case IEEE80211_STYPE_PROBE_RESP:
case IEEE80211_STYPE_BEACON:
- memcpy(skb->cb, rx_status, sizeof(*rx_status));
case IEEE80211_STYPE_AUTH:
case IEEE80211_STYPE_ASSOC_RESP:
case IEEE80211_STYPE_REASSOC_RESP:
case IEEE80211_STYPE_DEAUTH:
case IEEE80211_STYPE_DISASSOC:
+ case IEEE80211_STYPE_ACTION:
skb_queue_tail(&sdata->u.mgd.skb_queue, skb);
- queue_work(local->hw.workqueue, &sdata->u.mgd.work);
+ ieee80211_queue_work(&local->hw, &sdata->u.mgd.work);
return RX_QUEUED;
}
@@ -2097,40 +1913,119 @@ ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata,
static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_rx_status *rx_status;
struct ieee80211_mgmt *mgmt;
+ struct ieee80211_mgd_work *wk;
+ enum rx_mgmt_action rma = RX_MGMT_NONE;
u16 fc;
rx_status = (struct ieee80211_rx_status *) skb->cb;
mgmt = (struct ieee80211_mgmt *) skb->data;
fc = le16_to_cpu(mgmt->frame_control);
- switch (fc & IEEE80211_FCTL_STYPE) {
- case IEEE80211_STYPE_PROBE_RESP:
- ieee80211_rx_mgmt_probe_resp(sdata, mgmt, skb->len,
- rx_status);
- break;
- case IEEE80211_STYPE_BEACON:
- ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len,
- rx_status);
- break;
- case IEEE80211_STYPE_AUTH:
- ieee80211_rx_mgmt_auth(sdata, mgmt, skb->len);
+ mutex_lock(&ifmgd->mtx);
+
+ if (ifmgd->associated &&
+ memcmp(ifmgd->associated->cbss.bssid, mgmt->bssid,
+ ETH_ALEN) == 0) {
+ switch (fc & IEEE80211_FCTL_STYPE) {
+ case IEEE80211_STYPE_BEACON:
+ ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len,
+ rx_status);
+ break;
+ case IEEE80211_STYPE_PROBE_RESP:
+ ieee80211_rx_mgmt_probe_resp(sdata, NULL, mgmt,
+ skb->len, rx_status);
+ break;
+ case IEEE80211_STYPE_DEAUTH:
+ rma = ieee80211_rx_mgmt_deauth(sdata, NULL,
+ mgmt, skb->len);
+ break;
+ case IEEE80211_STYPE_DISASSOC:
+ rma = ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len);
+ break;
+ case IEEE80211_STYPE_ACTION:
+ /* XXX: differentiate, can only happen for CSA now! */
+ ieee80211_sta_process_chanswitch(sdata,
+ &mgmt->u.action.u.chan_switch.sw_elem,
+ ifmgd->associated);
+ break;
+ }
+ mutex_unlock(&ifmgd->mtx);
+
+ switch (rma) {
+ case RX_MGMT_NONE:
+ /* no action */
+ break;
+ case RX_MGMT_CFG80211_DEAUTH:
+ cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len,
+ NULL);
+ break;
+ case RX_MGMT_CFG80211_DISASSOC:
+ cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len,
+ NULL);
+ break;
+ default:
+ WARN(1, "unexpected: %d", rma);
+ }
+ goto out;
+ }
+
+ list_for_each_entry(wk, &ifmgd->work_list, list) {
+ if (memcmp(wk->bss->cbss.bssid, mgmt->bssid, ETH_ALEN) != 0)
+ continue;
+
+ switch (fc & IEEE80211_FCTL_STYPE) {
+ case IEEE80211_STYPE_PROBE_RESP:
+ ieee80211_rx_mgmt_probe_resp(sdata, wk, mgmt, skb->len,
+ rx_status);
+ break;
+ case IEEE80211_STYPE_AUTH:
+ rma = ieee80211_rx_mgmt_auth(sdata, wk, mgmt, skb->len);
+ break;
+ case IEEE80211_STYPE_ASSOC_RESP:
+ rma = ieee80211_rx_mgmt_assoc_resp(sdata, wk, mgmt,
+ skb->len, false);
+ break;
+ case IEEE80211_STYPE_REASSOC_RESP:
+ rma = ieee80211_rx_mgmt_assoc_resp(sdata, wk, mgmt,
+ skb->len, true);
+ break;
+ case IEEE80211_STYPE_DEAUTH:
+ rma = ieee80211_rx_mgmt_deauth(sdata, wk, mgmt,
+ skb->len);
+ break;
+ }
+ /*
+ * We've processed this frame for that work, so it can't
+ * belong to another work struct.
+ * NB: this is also required for correctness because the
+ * called functions can free 'wk', and for 'rma'!
+ */
break;
- case IEEE80211_STYPE_ASSOC_RESP:
- ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len, 0);
+ }
+
+ mutex_unlock(&ifmgd->mtx);
+
+ switch (rma) {
+ case RX_MGMT_NONE:
+ /* no action */
break;
- case IEEE80211_STYPE_REASSOC_RESP:
- ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len, 1);
+ case RX_MGMT_CFG80211_AUTH:
+ cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, skb->len);
break;
- case IEEE80211_STYPE_DEAUTH:
- ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len);
+ case RX_MGMT_CFG80211_ASSOC:
+ cfg80211_send_rx_assoc(sdata->dev, (u8 *) mgmt, skb->len);
break;
- case IEEE80211_STYPE_DISASSOC:
- ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len);
+ case RX_MGMT_CFG80211_DEAUTH:
+ cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len, NULL);
break;
+ default:
+ WARN(1, "unexpected: %d", rma);
}
+ out:
kfree_skb(skb);
}
@@ -2146,215 +2041,216 @@ static void ieee80211_sta_timer(unsigned long data)
return;
}
- set_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request);
- queue_work(local->hw.workqueue, &ifmgd->work);
+ ieee80211_queue_work(&local->hw, &ifmgd->work);
}
-static void ieee80211_sta_reset_auth(struct ieee80211_sub_if_data *sdata)
+static void ieee80211_sta_work(struct work_struct *work)
{
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ struct ieee80211_sub_if_data *sdata =
+ container_of(work, struct ieee80211_sub_if_data, u.mgd.work);
struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_managed *ifmgd;
+ struct sk_buff *skb;
+ struct ieee80211_mgd_work *wk, *tmp;
+ LIST_HEAD(free_work);
+ enum rx_mgmt_action rma;
+ bool anybusy = false;
- /* Reset own TSF to allow time synchronization work. */
- drv_reset_tsf(local);
+ if (!netif_running(sdata->dev))
+ return;
- ifmgd->wmm_last_param_set = -1; /* allow any WMM update */
+ if (local->scanning)
+ return;
+ if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
+ return;
- if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_OPEN)
- ifmgd->auth_alg = WLAN_AUTH_OPEN;
- else if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_SHARED_KEY)
- ifmgd->auth_alg = WLAN_AUTH_SHARED_KEY;
- else if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_LEAP)
- ifmgd->auth_alg = WLAN_AUTH_LEAP;
- else if (ifmgd->auth_algs & IEEE80211_AUTH_ALG_FT)
- ifmgd->auth_alg = WLAN_AUTH_FT;
- else
- ifmgd->auth_alg = WLAN_AUTH_OPEN;
- ifmgd->auth_transaction = -1;
- ifmgd->flags &= ~IEEE80211_STA_ASSOCIATED;
- ifmgd->assoc_scan_tries = 0;
- ifmgd->direct_probe_tries = 0;
- ifmgd->auth_tries = 0;
- ifmgd->assoc_tries = 0;
- netif_tx_stop_all_queues(sdata->dev);
- netif_carrier_off(sdata->dev);
-}
+ /*
+ * ieee80211_queue_work() should have picked up most cases,
+ * here we'll pick the the rest.
+ */
+ if (WARN(local->suspended, "STA MLME work scheduled while "
+ "going to suspend\n"))
+ return;
-static int ieee80211_sta_config_auth(struct ieee80211_sub_if_data *sdata)
-{
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- struct ieee80211_local *local = sdata->local;
- struct ieee80211_bss *bss;
- u8 *bssid = ifmgd->bssid, *ssid = ifmgd->ssid;
- u8 ssid_len = ifmgd->ssid_len;
- u16 capa_mask = WLAN_CAPABILITY_ESS;
- u16 capa_val = WLAN_CAPABILITY_ESS;
- struct ieee80211_channel *chan = local->oper_channel;
+ ifmgd = &sdata->u.mgd;
- if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) &&
- ifmgd->flags & (IEEE80211_STA_AUTO_SSID_SEL |
- IEEE80211_STA_AUTO_BSSID_SEL |
- IEEE80211_STA_AUTO_CHANNEL_SEL)) {
- capa_mask |= WLAN_CAPABILITY_PRIVACY;
- if (sdata->default_key)
- capa_val |= WLAN_CAPABILITY_PRIVACY;
- }
+ /* first process frames to avoid timing out while a frame is pending */
+ while ((skb = skb_dequeue(&ifmgd->skb_queue)))
+ ieee80211_sta_rx_queued_mgmt(sdata, skb);
+
+ /* then process the rest of the work */
+ mutex_lock(&ifmgd->mtx);
- if (ifmgd->flags & IEEE80211_STA_AUTO_CHANNEL_SEL)
- chan = NULL;
+ if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
+ IEEE80211_STA_CONNECTION_POLL) &&
+ ifmgd->associated) {
+ u8 bssid[ETH_ALEN];
- if (ifmgd->flags & IEEE80211_STA_AUTO_BSSID_SEL)
- bssid = NULL;
+ memcpy(bssid, ifmgd->associated->cbss.bssid, ETH_ALEN);
+ if (time_is_after_jiffies(ifmgd->probe_timeout))
+ run_again(ifmgd, ifmgd->probe_timeout);
- if (ifmgd->flags & IEEE80211_STA_AUTO_SSID_SEL) {
- ssid = NULL;
- ssid_len = 0;
+ else if (ifmgd->probe_send_count < IEEE80211_MAX_PROBE_TRIES) {
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
+ printk(KERN_DEBUG "No probe response from AP %pM"
+ " after %dms, try %d\n", bssid,
+ (1000 * IEEE80211_PROBE_WAIT)/HZ,
+ ifmgd->probe_send_count);
+#endif
+ ieee80211_mgd_probe_ap_send(sdata);
+ } else {
+ /*
+ * We actually lost the connection ... or did we?
+ * Let's make sure!
+ */
+ ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL |
+ IEEE80211_STA_BEACON_POLL);
+ printk(KERN_DEBUG "No probe response from AP %pM"
+ " after %dms, disconnecting.\n",
+ bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ);
+ ieee80211_set_disassoc(sdata, true);
+ mutex_unlock(&ifmgd->mtx);
+ /*
+ * must be outside lock due to cfg80211,
+ * but that's not a problem.
+ */
+ ieee80211_send_deauth_disassoc(sdata, bssid,
+ IEEE80211_STYPE_DEAUTH,
+ WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
+ NULL);
+ mutex_lock(&ifmgd->mtx);
+ }
}
- bss = (void *)cfg80211_get_bss(local->hw.wiphy, chan,
- bssid, ssid, ssid_len,
- capa_mask, capa_val);
- if (bss) {
- local->oper_channel = bss->cbss.channel;
- local->oper_channel_type = NL80211_CHAN_NO_HT;
- ieee80211_hw_config(local, 0);
+ ieee80211_recalc_idle(local);
- if (!(ifmgd->flags & IEEE80211_STA_SSID_SET))
- ieee80211_sta_set_ssid(sdata, bss->ssid,
- bss->ssid_len);
- ieee80211_sta_set_bssid(sdata, bss->cbss.bssid);
- ieee80211_sta_def_wmm_params(sdata, bss->supp_rates_len,
- bss->supp_rates);
- if (sdata->u.mgd.mfp == IEEE80211_MFP_REQUIRED)
- sdata->u.mgd.flags |= IEEE80211_STA_MFP_ENABLED;
- else
- sdata->u.mgd.flags &= ~IEEE80211_STA_MFP_ENABLED;
-
- /* Send out direct probe if no probe resp was received or
- * the one we have is outdated
- */
- if (!bss->last_probe_resp ||
- time_after(jiffies, bss->last_probe_resp
- + IEEE80211_SCAN_RESULT_EXPIRE))
- ifmgd->state = IEEE80211_STA_MLME_DIRECT_PROBE;
- else
- ifmgd->state = IEEE80211_STA_MLME_AUTHENTICATE;
+ list_for_each_entry_safe(wk, tmp, &ifmgd->work_list, list) {
+ if (time_is_after_jiffies(wk->timeout)) {
+ /*
+ * This work item isn't supposed to be worked on
+ * right now, but take care to adjust the timer
+ * properly.
+ */
+ run_again(ifmgd, wk->timeout);
+ continue;
+ }
- ieee80211_rx_bss_put(local, bss);
- ieee80211_sta_reset_auth(sdata);
- return 0;
- } else {
- if (ifmgd->assoc_scan_tries < IEEE80211_ASSOC_SCANS_MAX_TRIES) {
+ switch (wk->state) {
+ default:
+ WARN_ON(1);
+ /* fall through */
+ case IEEE80211_MGD_STATE_IDLE:
+ /* nothing */
+ rma = RX_MGMT_NONE;
+ break;
+ case IEEE80211_MGD_STATE_PROBE:
+ rma = ieee80211_direct_probe(sdata, wk);
+ break;
+ case IEEE80211_MGD_STATE_AUTH:
+ rma = ieee80211_authenticate(sdata, wk);
+ break;
+ case IEEE80211_MGD_STATE_ASSOC:
+ rma = ieee80211_associate(sdata, wk);
+ break;
+ }
+
+ switch (rma) {
+ case RX_MGMT_NONE:
+ /* no action required */
+ break;
+ case RX_MGMT_CFG80211_AUTH_TO:
+ case RX_MGMT_CFG80211_ASSOC_TO:
+ list_del(&wk->list);
+ list_add(&wk->list, &free_work);
+ wk->tries = rma; /* small abuse but only local */
+ break;
+ default:
+ WARN(1, "unexpected: %d", rma);
+ }
+ }
- ifmgd->assoc_scan_tries++;
+ list_for_each_entry(wk, &ifmgd->work_list, list) {
+ if (wk->state != IEEE80211_MGD_STATE_IDLE) {
+ anybusy = true;
+ break;
+ }
+ }
+ if (!anybusy &&
+ test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request))
+ ieee80211_queue_delayed_work(&local->hw,
+ &local->scan_work,
+ round_jiffies_relative(0));
- ieee80211_request_internal_scan(sdata, ifmgd->ssid,
- ssid_len);
+ mutex_unlock(&ifmgd->mtx);
- ifmgd->state = IEEE80211_STA_MLME_AUTHENTICATE;
- set_bit(IEEE80211_STA_REQ_AUTH, &ifmgd->request);
- } else {
- ifmgd->assoc_scan_tries = 0;
- ifmgd->state = IEEE80211_STA_MLME_DISABLED;
- ieee80211_recalc_idle(local);
+ list_for_each_entry_safe(wk, tmp, &free_work, list) {
+ switch (wk->tries) {
+ case RX_MGMT_CFG80211_AUTH_TO:
+ cfg80211_send_auth_timeout(sdata->dev,
+ wk->bss->cbss.bssid);
+ break;
+ case RX_MGMT_CFG80211_ASSOC_TO:
+ cfg80211_send_assoc_timeout(sdata->dev,
+ wk->bss->cbss.bssid);
+ break;
+ default:
+ WARN(1, "unexpected: %d", wk->tries);
}
+
+ list_del(&wk->list);
+ kfree(wk);
}
- return -1;
-}
+ ieee80211_recalc_idle(local);
+}
-static void ieee80211_sta_work(struct work_struct *work)
+static void ieee80211_sta_bcn_mon_timer(unsigned long data)
{
struct ieee80211_sub_if_data *sdata =
- container_of(work, struct ieee80211_sub_if_data, u.mgd.work);
+ (struct ieee80211_sub_if_data *) data;
struct ieee80211_local *local = sdata->local;
- struct ieee80211_if_managed *ifmgd;
- struct sk_buff *skb;
-
- if (!netif_running(sdata->dev))
- return;
- if (local->sw_scanning || local->hw_scanning)
+ if (local->quiescing)
return;
- if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
- return;
-
- /*
- * Nothing should have been stuffed into the workqueue during
- * the suspend->resume cycle. If this WARN is seen then there
- * is a bug with either the driver suspend or something in
- * mac80211 stuffing into the workqueue which we haven't yet
- * cleared during mac80211's suspend cycle.
- */
- if (WARN_ON(local->suspended))
- return;
-
- ifmgd = &sdata->u.mgd;
-
- while ((skb = skb_dequeue(&ifmgd->skb_queue)))
- ieee80211_sta_rx_queued_mgmt(sdata, skb);
+ ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work);
+}
- if (ifmgd->state != IEEE80211_STA_MLME_DIRECT_PROBE &&
- ifmgd->state != IEEE80211_STA_MLME_AUTHENTICATE &&
- ifmgd->state != IEEE80211_STA_MLME_ASSOCIATE &&
- test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request)) {
- queue_delayed_work(local->hw.workqueue, &local->scan_work,
- round_jiffies_relative(0));
- return;
- }
+static void ieee80211_sta_conn_mon_timer(unsigned long data)
+{
+ struct ieee80211_sub_if_data *sdata =
+ (struct ieee80211_sub_if_data *) data;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ struct ieee80211_local *local = sdata->local;
- if (test_and_clear_bit(IEEE80211_STA_REQ_AUTH, &ifmgd->request)) {
- if (ieee80211_sta_config_auth(sdata))
- return;
- clear_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request);
- } else if (!test_and_clear_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request))
+ if (local->quiescing)
return;
- ieee80211_recalc_idle(local);
-
- switch (ifmgd->state) {
- case IEEE80211_STA_MLME_DISABLED:
- break;
- case IEEE80211_STA_MLME_DIRECT_PROBE:
- ieee80211_direct_probe(sdata);
- break;
- case IEEE80211_STA_MLME_AUTHENTICATE:
- ieee80211_authenticate(sdata);
- break;
- case IEEE80211_STA_MLME_ASSOCIATE:
- ieee80211_associate(sdata);
- break;
- case IEEE80211_STA_MLME_ASSOCIATED:
- ieee80211_associated(sdata);
- break;
- default:
- WARN_ON(1);
- break;
- }
+ ieee80211_queue_work(&local->hw, &ifmgd->monitor_work);
+}
- if (ieee80211_privacy_mismatch(sdata)) {
- printk(KERN_DEBUG "%s: privacy configuration mismatch and "
- "mixed-cell disabled - disassociate\n", sdata->dev->name);
+static void ieee80211_sta_monitor_work(struct work_struct *work)
+{
+ struct ieee80211_sub_if_data *sdata =
+ container_of(work, struct ieee80211_sub_if_data,
+ u.mgd.monitor_work);
- ieee80211_set_disassoc(sdata, false, true,
- WLAN_REASON_UNSPECIFIED);
- }
+ ieee80211_mgd_probe_ap(sdata, false);
}
static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)
{
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
- /*
- * Need to update last_beacon to avoid beacon loss
- * test to trigger.
- */
- sdata->u.mgd.last_beacon = jiffies;
-
-
- queue_work(sdata->local->hw.workqueue,
+ sdata->u.mgd.flags &= ~(IEEE80211_STA_BEACON_POLL |
+ IEEE80211_STA_CONNECTION_POLL);
+
+ /* let's probe the connection once */
+ ieee80211_queue_work(&sdata->local->hw,
+ &sdata->u.mgd.monitor_work);
+ /* and do all the other regular work too */
+ ieee80211_queue_work(&sdata->local->hw,
&sdata->u.mgd.work);
}
}
@@ -2378,6 +2274,11 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata)
cancel_work_sync(&ifmgd->chswitch_work);
if (del_timer_sync(&ifmgd->chswitch_timer))
set_bit(TMR_RUNNING_CHANSW, &ifmgd->timers_running);
+
+ cancel_work_sync(&ifmgd->monitor_work);
+ /* these will just be re-established on connection */
+ del_timer_sync(&ifmgd->conn_mon_timer);
+ del_timer_sync(&ifmgd->bcn_mon_timer);
}
void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
@@ -2395,210 +2296,277 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_managed *ifmgd;
- u32 hw_flags;
ifmgd = &sdata->u.mgd;
INIT_WORK(&ifmgd->work, ieee80211_sta_work);
+ INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work);
INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work);
INIT_WORK(&ifmgd->beacon_loss_work, ieee80211_beacon_loss_work);
setup_timer(&ifmgd->timer, ieee80211_sta_timer,
(unsigned long) sdata);
+ setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
+ (unsigned long) sdata);
+ setup_timer(&ifmgd->conn_mon_timer, ieee80211_sta_conn_mon_timer,
+ (unsigned long) sdata);
setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer,
(unsigned long) sdata);
skb_queue_head_init(&ifmgd->skb_queue);
+ INIT_LIST_HEAD(&ifmgd->work_list);
+
ifmgd->capab = WLAN_CAPABILITY_ESS;
- ifmgd->auth_algs = IEEE80211_AUTH_ALG_OPEN |
- IEEE80211_AUTH_ALG_SHARED_KEY;
- ifmgd->flags |= IEEE80211_STA_CREATE_IBSS |
- IEEE80211_STA_AUTO_BSSID_SEL |
- IEEE80211_STA_AUTO_CHANNEL_SEL;
+ ifmgd->flags = 0;
if (sdata->local->hw.queues >= 4)
ifmgd->flags |= IEEE80211_STA_WMM_ENABLED;
- hw_flags = sdata->local->hw.flags;
-
- if (hw_flags & IEEE80211_HW_SUPPORTS_PS) {
- ifmgd->powersave = CONFIG_MAC80211_DEFAULT_PS_VALUE;
- sdata->local->hw.conf.dynamic_ps_timeout = 500;
- }
+ mutex_init(&ifmgd->mtx);
}
-/* configuration hooks */
-void ieee80211_sta_req_auth(struct ieee80211_sub_if_data *sdata)
+/* scan finished notification */
+void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
{
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- struct ieee80211_local *local = sdata->local;
-
- if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION))
- return;
-
- if ((ifmgd->flags & (IEEE80211_STA_BSSID_SET |
- IEEE80211_STA_AUTO_BSSID_SEL)) &&
- (ifmgd->flags & (IEEE80211_STA_SSID_SET |
- IEEE80211_STA_AUTO_SSID_SEL))) {
-
- if (ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED)
- ieee80211_set_disassoc(sdata, true, true,
- WLAN_REASON_DEAUTH_LEAVING);
-
- if (ifmgd->ssid_len == 0) {
- /*
- * Only allow association to be started if a valid SSID
- * is configured.
- */
- return;
- }
+ struct ieee80211_sub_if_data *sdata = local->scan_sdata;
- if (!(ifmgd->flags & IEEE80211_STA_EXT_SME) ||
- ifmgd->state != IEEE80211_STA_MLME_ASSOCIATE)
- set_bit(IEEE80211_STA_REQ_AUTH, &ifmgd->request);
- else if (ifmgd->flags & IEEE80211_STA_EXT_SME)
- set_bit(IEEE80211_STA_REQ_RUN, &ifmgd->request);
- queue_work(local->hw.workqueue, &ifmgd->work);
- }
+ /* Restart STA timers */
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list)
+ ieee80211_restart_sta_timer(sdata);
+ rcu_read_unlock();
}
-int ieee80211_sta_commit(struct ieee80211_sub_if_data *sdata)
+int ieee80211_max_network_latency(struct notifier_block *nb,
+ unsigned long data, void *dummy)
{
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ s32 latency_usec = (s32) data;
+ struct ieee80211_local *local =
+ container_of(nb, struct ieee80211_local,
+ network_latency_notifier);
- if (ifmgd->ssid_len)
- ifmgd->flags |= IEEE80211_STA_SSID_SET;
- else
- ifmgd->flags &= ~IEEE80211_STA_SSID_SET;
+ mutex_lock(&local->iflist_mtx);
+ ieee80211_recalc_ps(local, latency_usec);
+ mutex_unlock(&local->iflist_mtx);
return 0;
}
-int ieee80211_sta_set_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t len)
+/* config hooks */
+int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_auth_request *req)
{
- struct ieee80211_if_managed *ifmgd;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ const u8 *ssid;
+ struct ieee80211_mgd_work *wk;
+ u16 auth_alg;
- if (len > IEEE80211_MAX_SSID_LEN)
- return -EINVAL;
+ switch (req->auth_type) {
+ case NL80211_AUTHTYPE_OPEN_SYSTEM:
+ auth_alg = WLAN_AUTH_OPEN;
+ break;
+ case NL80211_AUTHTYPE_SHARED_KEY:
+ auth_alg = WLAN_AUTH_SHARED_KEY;
+ break;
+ case NL80211_AUTHTYPE_FT:
+ auth_alg = WLAN_AUTH_FT;
+ break;
+ case NL80211_AUTHTYPE_NETWORK_EAP:
+ auth_alg = WLAN_AUTH_LEAP;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
- ifmgd = &sdata->u.mgd;
+ wk = kzalloc(sizeof(*wk) + req->ie_len, GFP_KERNEL);
+ if (!wk)
+ return -ENOMEM;
- if (ifmgd->ssid_len != len || memcmp(ifmgd->ssid, ssid, len) != 0) {
- if (ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED)
- ieee80211_set_disassoc(sdata, true, true,
- WLAN_REASON_DEAUTH_LEAVING);
+ wk->bss = (void *)req->bss;
- /*
- * Do not use reassociation if SSID is changed (different ESS).
- */
- ifmgd->flags &= ~IEEE80211_STA_PREV_BSSID_SET;
- memset(ifmgd->ssid, 0, sizeof(ifmgd->ssid));
- memcpy(ifmgd->ssid, ssid, len);
- ifmgd->ssid_len = len;
+ if (req->ie && req->ie_len) {
+ memcpy(wk->ie, req->ie, req->ie_len);
+ wk->ie_len = req->ie_len;
}
- return ieee80211_sta_commit(sdata);
-}
+ if (req->key && req->key_len) {
+ wk->key_len = req->key_len;
+ wk->key_idx = req->key_idx;
+ memcpy(wk->key, req->key, req->key_len);
+ }
-int ieee80211_sta_get_ssid(struct ieee80211_sub_if_data *sdata, char *ssid, size_t *len)
-{
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- memcpy(ssid, ifmgd->ssid, ifmgd->ssid_len);
- *len = ifmgd->ssid_len;
+ ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
+ memcpy(wk->ssid, ssid + 2, ssid[1]);
+ wk->ssid_len = ssid[1];
+
+ wk->state = IEEE80211_MGD_STATE_PROBE;
+ wk->auth_alg = auth_alg;
+ wk->timeout = jiffies; /* run right away */
+
+ /*
+ * XXX: if still associated need to tell AP that we're going
+ * to sleep and then change channel etc.
+ */
+ sdata->local->oper_channel = req->bss->channel;
+ ieee80211_hw_config(sdata->local, 0);
+
+ mutex_lock(&ifmgd->mtx);
+ list_add(&wk->list, &sdata->u.mgd.work_list);
+ mutex_unlock(&ifmgd->mtx);
+
+ ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.work);
return 0;
}
-int ieee80211_sta_set_bssid(struct ieee80211_sub_if_data *sdata, u8 *bssid)
+int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_assoc_request *req)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ struct ieee80211_mgd_work *wk, *found = NULL;
+ int i, err;
- if (compare_ether_addr(bssid, ifmgd->bssid) != 0 &&
- ifmgd->state == IEEE80211_STA_MLME_ASSOCIATED)
- ieee80211_set_disassoc(sdata, true, true,
- WLAN_REASON_DEAUTH_LEAVING);
+ mutex_lock(&ifmgd->mtx);
- if (is_valid_ether_addr(bssid)) {
- memcpy(ifmgd->bssid, bssid, ETH_ALEN);
- ifmgd->flags |= IEEE80211_STA_BSSID_SET;
- } else {
- memset(ifmgd->bssid, 0, ETH_ALEN);
- ifmgd->flags &= ~IEEE80211_STA_BSSID_SET;
+ list_for_each_entry(wk, &ifmgd->work_list, list) {
+ if (&wk->bss->cbss == req->bss &&
+ wk->state == IEEE80211_MGD_STATE_IDLE) {
+ found = wk;
+ break;
+ }
}
- return ieee80211_sta_commit(sdata);
-}
+ if (!found) {
+ err = -ENOLINK;
+ goto out;
+ }
-int ieee80211_sta_set_extra_ie(struct ieee80211_sub_if_data *sdata,
- const char *ie, size_t len)
-{
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ list_del(&found->list);
- if (len == 0 && ifmgd->extra_ie_len == 0)
- return -EALREADY;
+ wk = krealloc(found, sizeof(*wk) + req->ie_len, GFP_KERNEL);
+ if (!wk) {
+ list_add(&found->list, &ifmgd->work_list);
+ err = -ENOMEM;
+ goto out;
+ }
- if (len == ifmgd->extra_ie_len && ifmgd->extra_ie &&
- memcmp(ifmgd->extra_ie, ie, len) == 0)
- return -EALREADY;
+ list_add(&wk->list, &ifmgd->work_list);
- kfree(ifmgd->extra_ie);
- if (len == 0) {
- ifmgd->extra_ie = NULL;
- ifmgd->extra_ie_len = 0;
- return 0;
- }
- ifmgd->extra_ie = kmalloc(len, GFP_KERNEL);
- if (!ifmgd->extra_ie) {
- ifmgd->extra_ie_len = 0;
- return -ENOMEM;
+ ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N;
+
+ for (i = 0; i < req->crypto.n_ciphers_pairwise; i++)
+ if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 ||
+ req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP ||
+ req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104)
+ ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
+
+ sdata->local->oper_channel = req->bss->channel;
+ ieee80211_hw_config(sdata->local, 0);
+
+ if (req->ie && req->ie_len) {
+ memcpy(wk->ie, req->ie, req->ie_len);
+ wk->ie_len = req->ie_len;
+ } else
+ wk->ie_len = 0;
+
+ if (req->prev_bssid)
+ memcpy(wk->prev_bssid, req->prev_bssid, ETH_ALEN);
+
+ wk->state = IEEE80211_MGD_STATE_ASSOC;
+ wk->tries = 0;
+ wk->timeout = jiffies; /* run right away */
+
+ if (req->use_mfp) {
+ ifmgd->mfp = IEEE80211_MFP_REQUIRED;
+ ifmgd->flags |= IEEE80211_STA_MFP_ENABLED;
+ } else {
+ ifmgd->mfp = IEEE80211_MFP_DISABLED;
+ ifmgd->flags &= ~IEEE80211_STA_MFP_ENABLED;
}
- memcpy(ifmgd->extra_ie, ie, len);
- ifmgd->extra_ie_len = len;
- return 0;
-}
-int ieee80211_sta_deauthenticate(struct ieee80211_sub_if_data *sdata, u16 reason)
-{
- printk(KERN_DEBUG "%s: deauthenticating by local choice (reason=%d)\n",
- sdata->dev->name, reason);
+ if (req->crypto.control_port)
+ ifmgd->flags |= IEEE80211_STA_CONTROL_PORT;
+ else
+ ifmgd->flags &= ~IEEE80211_STA_CONTROL_PORT;
- ieee80211_set_disassoc(sdata, true, true, reason);
- return 0;
+ ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.work);
+
+ err = 0;
+
+ out:
+ mutex_unlock(&ifmgd->mtx);
+ return err;
}
-int ieee80211_sta_disassociate(struct ieee80211_sub_if_data *sdata, u16 reason)
+int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_deauth_request *req,
+ void *cookie)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ struct ieee80211_mgd_work *wk;
+ const u8 *bssid = NULL;
- printk(KERN_DEBUG "%s: disassociating by local choice (reason=%d)\n",
- sdata->dev->name, reason);
+ printk(KERN_DEBUG "%s: deauthenticating by local choice (reason=%d)\n",
+ sdata->dev->name, req->reason_code);
+
+ mutex_lock(&ifmgd->mtx);
+
+ if (ifmgd->associated && &ifmgd->associated->cbss == req->bss) {
+ bssid = req->bss->bssid;
+ ieee80211_set_disassoc(sdata, true);
+ } else list_for_each_entry(wk, &ifmgd->work_list, list) {
+ if (&wk->bss->cbss == req->bss) {
+ bssid = req->bss->bssid;
+ list_del(&wk->list);
+ kfree(wk);
+ break;
+ }
+ }
- if (!(ifmgd->flags & IEEE80211_STA_ASSOCIATED))
+ /*
+ * cfg80211 should catch this ... but it's racy since
+ * we can receive a deauth frame, process it, hand it
+ * to cfg80211 while that's in a locked section already
+ * trying to tell us that the user wants to disconnect.
+ */
+ if (!bssid) {
+ mutex_unlock(&ifmgd->mtx);
return -ENOLINK;
+ }
+
+ mutex_unlock(&ifmgd->mtx);
+
+ ieee80211_send_deauth_disassoc(sdata, bssid,
+ IEEE80211_STYPE_DEAUTH, req->reason_code,
+ cookie);
- ieee80211_set_disassoc(sdata, false, true, reason);
return 0;
}
-/* scan finished notification */
-void ieee80211_mlme_notify_scan_completed(struct ieee80211_local *local)
+int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
+ struct cfg80211_disassoc_request *req,
+ void *cookie)
{
- struct ieee80211_sub_if_data *sdata = local->scan_sdata;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- /* Restart STA timers */
- rcu_read_lock();
- list_for_each_entry_rcu(sdata, &local->interfaces, list)
- ieee80211_restart_sta_timer(sdata);
- rcu_read_unlock();
-}
+ printk(KERN_DEBUG "%s: disassociating by local choice (reason=%d)\n",
+ sdata->dev->name, req->reason_code);
-int ieee80211_max_network_latency(struct notifier_block *nb,
- unsigned long data, void *dummy)
-{
- s32 latency_usec = (s32) data;
- struct ieee80211_local *local =
- container_of(nb, struct ieee80211_local,
- network_latency_notifier);
+ mutex_lock(&ifmgd->mtx);
- mutex_lock(&local->iflist_mtx);
- ieee80211_recalc_ps(local, latency_usec);
- mutex_unlock(&local->iflist_mtx);
+ /*
+ * cfg80211 should catch this ... but it's racy since
+ * we can receive a disassoc frame, process it, hand it
+ * to cfg80211 while that's in a locked section already
+ * trying to tell us that the user wants to disconnect.
+ */
+ if (&ifmgd->associated->cbss != req->bss) {
+ mutex_unlock(&ifmgd->mtx);
+ return -ENOLINK;
+ }
+
+ ieee80211_set_disassoc(sdata, false);
+
+ mutex_unlock(&ifmgd->mtx);
+ ieee80211_send_deauth_disassoc(sdata, req->bss->bssid,
+ IEEE80211_STYPE_DISASSOC, req->reason_code,
+ cookie);
return 0;
}
diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c
index 5e3d476972f9..e535f1c988fe 100644
--- a/net/mac80211/pm.c
+++ b/net/mac80211/pm.c
@@ -26,7 +26,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
/* make quiescing visible to timers everywhere */
mb();
- flush_workqueue(local->hw.workqueue);
+ flush_workqueue(local->workqueue);
/* Don't try to run timers while suspended. */
del_timer_sync(&local->sta_cleanup);
@@ -96,6 +96,10 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
if (!netif_running(sdata->dev))
continue;
+ /* disable beaconing */
+ ieee80211_bss_info_change_notify(sdata,
+ BSS_CHANGED_BEACON_ENABLED);
+
conf.vif = &sdata->vif;
conf.type = sdata->vif.type;
conf.mac_addr = sdata->dev->dev_addr;
@@ -103,17 +107,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw)
}
/* stop hardware - this must stop RX */
- if (local->open_count) {
- ieee80211_led_radio(local, false);
- drv_stop(local);
- }
-
- /*
- * flush again, in case driver queued work -- it
- * shouldn't be doing (or cancel everything in the
- * stop callback) that but better safe than sorry.
- */
- flush_workqueue(local->hw.workqueue);
+ if (local->open_count)
+ ieee80211_stop_device(local);
local->suspended = true;
/* need suspended to be visible before quiescing is false */
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index 4641f00a1e5c..b33efc4fc267 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -198,6 +198,35 @@ static void rate_control_release(struct kref *kref)
kfree(ctrl_ref);
}
+static bool rc_no_data_or_no_ack(struct ieee80211_tx_rate_control *txrc)
+{
+ struct sk_buff *skb = txrc->skb;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ __le16 fc;
+
+ fc = hdr->frame_control;
+
+ return ((info->flags & IEEE80211_TX_CTL_NO_ACK) || !ieee80211_is_data(fc));
+}
+
+bool rate_control_send_low(struct ieee80211_sta *sta,
+ void *priv_sta,
+ struct ieee80211_tx_rate_control *txrc)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb);
+
+ if (!sta || !priv_sta || rc_no_data_or_no_ack(txrc)) {
+ info->control.rates[0].idx = rate_lowest_index(txrc->sband, sta);
+ info->control.rates[0].count =
+ (info->flags & IEEE80211_TX_CTL_NO_ACK) ?
+ 1 : txrc->hw->max_rate_tries;
+ return true;
+ }
+ return false;
+}
+EXPORT_SYMBOL(rate_control_send_low);
+
void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta,
struct ieee80211_tx_rate_control *txrc)
@@ -258,7 +287,7 @@ int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
struct rate_control_ref *ref, *old;
ASSERT_RTNL();
- if (local->open_count || netif_running(local->mdev))
+ if (local->open_count)
return -EBUSY;
ref = rate_control_alloc(name, local);
diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c
index 37771abd8f5a..7c5142988bbb 100644
--- a/net/mac80211/rc80211_minstrel.c
+++ b/net/mac80211/rc80211_minstrel.c
@@ -70,20 +70,6 @@ rix_to_ndx(struct minstrel_sta_info *mi, int rix)
return i;
}
-static inline bool
-use_low_rate(struct sk_buff *skb)
-{
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- u16 fc;
-
- fc = le16_to_cpu(hdr->frame_control);
-
- return ((info->flags & IEEE80211_TX_CTL_NO_ACK) ||
- (fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA);
-}
-
-
static void
minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
{
@@ -232,7 +218,6 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
void *priv_sta, struct ieee80211_tx_rate_control *txrc)
{
struct sk_buff *skb = txrc->skb;
- struct ieee80211_supported_band *sband = txrc->sband;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct minstrel_sta_info *mi = priv_sta;
struct minstrel_priv *mp = priv;
@@ -245,14 +230,8 @@ minstrel_get_rate(void *priv, struct ieee80211_sta *sta,
int mrr_ndx[3];
int sample_rate;
- if (!sta || !mi || use_low_rate(skb)) {
- ar[0].idx = rate_lowest_index(sband, sta);
- if (info->flags & IEEE80211_TX_CTL_NO_ACK)
- ar[0].count = 1;
- else
- ar[0].count = mp->max_retry;
+ if (rate_control_send_low(sta, priv_sta, txrc))
return;
- }
mrr = mp->has_mrr && !txrc->rts && !txrc->bss_conf->use_cts_prot;
diff --git a/net/mac80211/rc80211_minstrel.h b/net/mac80211/rc80211_minstrel.h
index 869fe0ef951d..38bf4168fc3a 100644
--- a/net/mac80211/rc80211_minstrel.h
+++ b/net/mac80211/rc80211_minstrel.h
@@ -33,7 +33,6 @@ struct minstrel_rate {
/* per-rate throughput */
u32 cur_tp;
- u32 throughput;
u64 succ_hist;
u64 att_hist;
diff --git a/net/mac80211/rc80211_minstrel_debugfs.c b/net/mac80211/rc80211_minstrel_debugfs.c
index 98f480708050..a715d9454f64 100644
--- a/net/mac80211/rc80211_minstrel_debugfs.c
+++ b/net/mac80211/rc80211_minstrel_debugfs.c
@@ -83,7 +83,7 @@ minstrel_stats_open(struct inode *inode, struct file *file)
p += sprintf(p, "%3u%s", mr->bitrate / 2,
(mr->bitrate & 1 ? ".5" : " "));
- tp = ((mr->cur_tp * 96) / 18000) >> 10;
+ tp = mr->cur_tp / ((18000 << 10) / 96);
prob = mr->cur_prob / 18;
eprob = mr->probability / 18;
@@ -139,7 +139,7 @@ minstrel_stats_release(struct inode *inode, struct file *file)
return 0;
}
-static struct file_operations minstrel_stat_fops = {
+static const struct file_operations minstrel_stat_fops = {
.owner = THIS_MODULE,
.open = minstrel_stats_open,
.read = minstrel_stats_read,
diff --git a/net/mac80211/rc80211_pid_algo.c b/net/mac80211/rc80211_pid_algo.c
index a0bef767ceb5..699d3ed869c4 100644
--- a/net/mac80211/rc80211_pid_algo.c
+++ b/net/mac80211/rc80211_pid_algo.c
@@ -169,19 +169,9 @@ static void rate_control_pid_sample(struct rc_pid_info *pinfo,
* still a good measurement and copy it. */
if (unlikely(spinfo->tx_num_xmit == 0))
pf = spinfo->last_pf;
- else {
- /* XXX: BAD HACK!!! */
- struct sta_info *si = container_of(sta, struct sta_info, sta);
-
+ else
pf = spinfo->tx_num_failed * 100 / spinfo->tx_num_xmit;
- if (ieee80211_vif_is_mesh(&si->sdata->vif) && pf == 100)
- mesh_plink_broken(si);
- pf <<= RC_PID_ARITH_SHIFT;
- si->fail_avg = ((pf + (spinfo->last_pf << 3)) / 9)
- >> RC_PID_ARITH_SHIFT;
- }
-
spinfo->tx_num_xmit = 0;
spinfo->tx_num_failed = 0;
@@ -276,11 +266,9 @@ rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta,
{
struct sk_buff *skb = txrc->skb;
struct ieee80211_supported_band *sband = txrc->sband;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct rc_pid_sta_info *spinfo = priv_sta;
int rateidx;
- u16 fc;
if (txrc->rts)
info->control.rates[0].count =
@@ -290,16 +278,8 @@ rate_control_pid_get_rate(void *priv, struct ieee80211_sta *sta,
txrc->hw->conf.short_frame_max_tx_count;
/* Send management frames and NO_ACK data using lowest rate. */
- fc = le16_to_cpu(hdr->frame_control);
- if (!sta || !spinfo ||
- (fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_DATA ||
- info->flags & IEEE80211_TX_CTL_NO_ACK) {
- info->control.rates[0].idx = rate_lowest_index(sband, sta);
- if (info->flags & IEEE80211_TX_CTL_NO_ACK)
- info->control.rates[0].count = 1;
-
+ if (rate_control_send_low(sta, priv_sta, txrc))
return;
- }
rateidx = spinfo->txrate_idx;
@@ -321,7 +301,6 @@ rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband,
struct rc_pid_sta_info *spinfo = priv_sta;
struct rc_pid_info *pinfo = priv;
struct rc_pid_rateinfo *rinfo = pinfo->rinfo;
- struct sta_info *si;
int i, j, tmp;
bool s;
@@ -358,9 +337,6 @@ rate_control_pid_rate_init(void *priv, struct ieee80211_supported_band *sband,
}
spinfo->txrate_idx = rate_lowest_index(sband, sta);
- /* HACK */
- si = container_of(sta, struct sta_info, sta);
- si->fail_avg = 0;
}
static void *rate_control_pid_alloc(struct ieee80211_hw *hw,
diff --git a/net/mac80211/rc80211_pid_debugfs.c b/net/mac80211/rc80211_pid_debugfs.c
index a08a9b530347..a59043fbb0ff 100644
--- a/net/mac80211/rc80211_pid_debugfs.c
+++ b/net/mac80211/rc80211_pid_debugfs.c
@@ -198,7 +198,7 @@ static ssize_t rate_control_pid_events_read(struct file *file, char __user *buf,
#undef RC_PID_PRINT_BUF_SIZE
-static struct file_operations rc_pid_fop_events = {
+static const struct file_operations rc_pid_fop_events = {
.owner = THIS_MODULE,
.read = rate_control_pid_events_read,
.poll = rate_control_pid_events_poll,
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 0936fc24942d..c01588f9d453 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -30,7 +30,6 @@
static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
struct tid_ampdu_rx *tid_agg_rx,
struct sk_buff *skb,
- struct ieee80211_rx_status *status,
u16 mpdu_seq_num,
int bar_req);
/*
@@ -59,11 +58,11 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
return skb;
}
-static inline int should_drop_frame(struct ieee80211_rx_status *status,
- struct sk_buff *skb,
+static inline int should_drop_frame(struct sk_buff *skb,
int present_fcs_len,
int radiotap_len)
{
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC))
@@ -111,10 +110,10 @@ ieee80211_rx_radiotap_len(struct ieee80211_local *local,
static void
ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
struct sk_buff *skb,
- struct ieee80211_rx_status *status,
struct ieee80211_rate *rate,
int rtap_len)
{
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_radiotap_header *rthdr;
unsigned char *pos;
@@ -220,9 +219,9 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
*/
static struct sk_buff *
ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
- struct ieee80211_rx_status *status,
struct ieee80211_rate *rate)
{
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(origskb);
struct ieee80211_sub_if_data *sdata;
int needed_headroom = 0;
struct sk_buff *skb, *skb2;
@@ -248,8 +247,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
present_fcs_len = FCS_LEN;
if (!local->monitors) {
- if (should_drop_frame(status, origskb, present_fcs_len,
- rtap_len)) {
+ if (should_drop_frame(origskb, present_fcs_len, rtap_len)) {
dev_kfree_skb(origskb);
return NULL;
}
@@ -257,7 +255,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
return remove_monitor_info(local, origskb, rtap_len);
}
- if (should_drop_frame(status, origskb, present_fcs_len, rtap_len)) {
+ if (should_drop_frame(origskb, present_fcs_len, rtap_len)) {
/* only need to expand headroom if necessary */
skb = origskb;
origskb = NULL;
@@ -289,7 +287,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
/* if necessary, prepend radiotap information */
if (!(status->flag & RX_FLAG_RADIOTAP))
- ieee80211_add_rx_radiotap_header(local, skb, status, rate,
+ ieee80211_add_rx_radiotap_header(local, skb, rate,
needed_headroom);
skb_reset_mac_header(skb);
@@ -420,13 +418,13 @@ ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx)
struct ieee80211_local *local = rx->local;
struct sk_buff *skb = rx->skb;
- if (unlikely(local->hw_scanning))
- return ieee80211_scan_rx(rx->sdata, skb, rx->status);
+ if (unlikely(test_bit(SCAN_HW_SCANNING, &local->scanning)))
+ return ieee80211_scan_rx(rx->sdata, skb);
- if (unlikely(local->sw_scanning)) {
+ if (unlikely(test_bit(SCAN_SW_SCANNING, &local->scanning) &&
+ (rx->flags & IEEE80211_RX_IN_SCAN))) {
/* drop all the other packets during a software scan anyway */
- if (ieee80211_scan_rx(rx->sdata, skb, rx->status)
- != RX_QUEUED)
+ if (ieee80211_scan_rx(rx->sdata, skb) != RX_QUEUED)
dev_kfree_skb(skb);
return RX_QUEUED;
}
@@ -491,12 +489,21 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ char *dev_addr = rx->dev->dev_addr;
if (ieee80211_is_data(hdr->frame_control)) {
- if (!ieee80211_has_a4(hdr->frame_control))
- return RX_DROP_MONITOR;
- if (memcmp(hdr->addr4, rx->dev->dev_addr, ETH_ALEN) == 0)
- return RX_DROP_MONITOR;
+ if (is_multicast_ether_addr(hdr->addr1)) {
+ if (ieee80211_has_tods(hdr->frame_control) ||
+ !ieee80211_has_fromds(hdr->frame_control))
+ return RX_DROP_MONITOR;
+ if (memcmp(hdr->addr3, dev_addr, ETH_ALEN) == 0)
+ return RX_DROP_MONITOR;
+ } else {
+ if (!ieee80211_has_a4(hdr->frame_control))
+ return RX_DROP_MONITOR;
+ if (memcmp(hdr->addr4, dev_addr, ETH_ALEN) == 0)
+ return RX_DROP_MONITOR;
+ }
}
/* If there is not an established peer link and this is not a peer link
@@ -529,7 +536,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
if (ieee80211_is_data(hdr->frame_control) &&
is_multicast_ether_addr(hdr->addr1) &&
- mesh_rmc_check(hdr->addr4, msh_h_get(hdr, hdrlen), rx->sdata))
+ mesh_rmc_check(hdr->addr3, msh_h_get(hdr, hdrlen), rx->sdata))
return RX_DROP_MONITOR;
#undef msh_h_get
@@ -785,7 +792,7 @@ static void ap_sta_ps_start(struct sta_info *sta)
struct ieee80211_local *local = sdata->local;
atomic_inc(&sdata->bss->num_sta_ps);
- set_and_clear_sta_flags(sta, WLAN_STA_PS, WLAN_STA_PSPOLL);
+ set_sta_flags(sta, WLAN_STA_PS);
drv_sta_notify(local, &sdata->vif, STA_NOTIFY_SLEEP, &sta->sta);
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk(KERN_DEBUG "%s: STA %pM aid %d enters power save mode\n",
@@ -801,7 +808,7 @@ static int ap_sta_ps_end(struct sta_info *sta)
atomic_dec(&sdata->bss->num_sta_ps);
- clear_sta_flags(sta, WLAN_STA_PS | WLAN_STA_PSPOLL);
+ clear_sta_flags(sta, WLAN_STA_PS);
drv_sta_notify(local, &sdata->vif, STA_NOTIFY_AWAKE, &sta->sta);
if (!skb_queue_empty(&sta->ps_tx_buf))
@@ -836,28 +843,22 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
if (!sta)
return RX_CONTINUE;
- /* Update last_rx only for IBSS packets which are for the current
- * BSSID to avoid keeping the current IBSS network alive in cases where
- * other STAs are using different BSSID. */
+ /*
+ * Update last_rx only for IBSS packets which are for the current
+ * BSSID to avoid keeping the current IBSS network alive in cases
+ * where other STAs start using different BSSID.
+ */
if (rx->sdata->vif.type == NL80211_IFTYPE_ADHOC) {
u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len,
NL80211_IFTYPE_ADHOC);
if (compare_ether_addr(bssid, rx->sdata->u.ibss.bssid) == 0)
sta->last_rx = jiffies;
- } else
- if (!is_multicast_ether_addr(hdr->addr1) ||
- rx->sdata->vif.type == NL80211_IFTYPE_STATION) {
- /* Update last_rx only for unicast frames in order to prevent
- * the Probe Request frames (the only broadcast frames from a
- * STA in infrastructure mode) from keeping a connection alive.
+ } else if (!is_multicast_ether_addr(hdr->addr1)) {
+ /*
* Mesh beacons will update last_rx when if they are found to
* match the current local configuration when processed.
*/
- if (rx->sdata->vif.type == NL80211_IFTYPE_STATION &&
- ieee80211_is_beacon(hdr->frame_control)) {
- rx->sdata->u.mgd.last_beacon = jiffies;
- } else
- sta->last_rx = jiffies;
+ sta->last_rx = jiffies;
}
if (!(rx->flags & IEEE80211_RX_RA_MATCH))
@@ -1125,14 +1126,15 @@ ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
skb_queue_empty(&rx->sta->ps_tx_buf);
if (skb) {
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr =
(struct ieee80211_hdr *) skb->data;
/*
- * Tell TX path to send one frame even though the STA may
+ * Tell TX path to send this frame even though the STA may
* still remain is PS mode after this frame exchange.
*/
- set_sta_flags(rx->sta, WLAN_STA_PSPOLL);
+ info->flags |= IEEE80211_TX_CTL_PSPOLL_RESPONSE;
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk(KERN_DEBUG "STA %pM aid %d: PS Poll (entries after %d)\n",
@@ -1147,7 +1149,7 @@ ieee80211_rx_h_ps_poll(struct ieee80211_rx_data *rx)
else
hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);
- dev_queue_xmit(skb);
+ ieee80211_add_pending_skb(rx->local, skb);
if (no_pending_pkts)
sta_info_clear_tim_bit(rx->sta);
@@ -1487,10 +1489,13 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
struct ieee80211s_hdr *mesh_hdr;
unsigned int hdrlen;
struct sk_buff *skb = rx->skb, *fwd_skb;
+ struct ieee80211_local *local = rx->local;
+ struct ieee80211_sub_if_data *sdata;
hdr = (struct ieee80211_hdr *) skb->data;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
mesh_hdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);
+ sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
if (!ieee80211_is_data(hdr->frame_control))
return RX_CONTINUE;
@@ -1499,11 +1504,10 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
/* illegal frame */
return RX_DROP_MONITOR;
- if (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6){
- struct ieee80211_sub_if_data *sdata;
+ if (!is_multicast_ether_addr(hdr->addr1) &&
+ (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6)) {
struct mesh_path *mppath;
- sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
rcu_read_lock();
mppath = mpp_path_lookup(mesh_hdr->eaddr2, sdata);
if (!mppath) {
@@ -1518,7 +1522,9 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
rcu_read_unlock();
}
- if (compare_ether_addr(rx->dev->dev_addr, hdr->addr3) == 0)
+ /* Frame has reached destination. Don't forward */
+ if (!is_multicast_ether_addr(hdr->addr1) &&
+ compare_ether_addr(rx->dev->dev_addr, hdr->addr3) == 0)
return RX_CONTINUE;
mesh_hdr->ttl--;
@@ -1529,6 +1535,8 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
dropped_frames_ttl);
else {
struct ieee80211_hdr *fwd_hdr;
+ struct ieee80211_tx_info *info;
+
fwd_skb = skb_copy(skb, GFP_ATOMIC);
if (!fwd_skb && net_ratelimit())
@@ -1536,19 +1544,40 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
rx->dev->name);
fwd_hdr = (struct ieee80211_hdr *) fwd_skb->data;
- /*
- * Save TA to addr1 to send TA a path error if a
- * suitable next hop is not found
- */
- memcpy(fwd_hdr->addr1, fwd_hdr->addr2, ETH_ALEN);
memcpy(fwd_hdr->addr2, rx->dev->dev_addr, ETH_ALEN);
- fwd_skb->dev = rx->local->mdev;
- fwd_skb->iif = rx->dev->ifindex;
- dev_queue_xmit(fwd_skb);
+ info = IEEE80211_SKB_CB(fwd_skb);
+ memset(info, 0, sizeof(*info));
+ info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
+ info->control.vif = &rx->sdata->vif;
+ ieee80211_select_queue(local, fwd_skb);
+ if (is_multicast_ether_addr(fwd_hdr->addr1))
+ IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh,
+ fwded_mcast);
+ else {
+ int err;
+ /*
+ * Save TA to addr1 to send TA a path error if a
+ * suitable next hop is not found
+ */
+ memcpy(fwd_hdr->addr1, fwd_hdr->addr2,
+ ETH_ALEN);
+ err = mesh_nexthop_lookup(fwd_skb, sdata);
+ /* Failed to immediately resolve next hop:
+ * fwded frame was dropped or will be added
+ * later to the pending skb queue. */
+ if (err)
+ return RX_DROP_MONITOR;
+
+ IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh,
+ fwded_unicast);
+ }
+ IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh,
+ fwded_frames);
+ ieee80211_add_pending_skb(local, fwd_skb);
}
}
- if (is_multicast_ether_addr(hdr->addr3) ||
+ if (is_multicast_ether_addr(hdr->addr1) ||
rx->dev->flags & IFF_PROMISC)
return RX_CONTINUE;
else
@@ -1620,7 +1649,7 @@ ieee80211_rx_h_ctrl(struct ieee80211_rx_data *rx)
/* manage reordering buffer according to requested */
/* sequence number */
rcu_read_lock();
- ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, NULL, NULL,
+ ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, NULL,
start_seq_num, 1);
rcu_read_unlock();
return RX_DROP_UNUSABLE;
@@ -1644,12 +1673,7 @@ static void ieee80211_process_sa_query_req(struct ieee80211_sub_if_data *sdata,
if (compare_ether_addr(mgmt->sa, sdata->u.mgd.bssid) != 0 ||
compare_ether_addr(mgmt->bssid, sdata->u.mgd.bssid) != 0) {
- /* Not from the current AP. */
- return;
- }
-
- if (sdata->u.mgd.state == IEEE80211_STA_MLME_ASSOCIATE) {
- /* Association in progress; ignore SA Query */
+ /* Not from the current AP or not associated yet. */
return;
}
@@ -1686,7 +1710,6 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
struct ieee80211_local *local = rx->local;
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data;
- struct ieee80211_bss *bss;
int len = rx->skb->len;
if (!ieee80211_is_action(mgmt->frame_control))
@@ -1764,17 +1787,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
if (memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN))
return RX_DROP_MONITOR;
- bss = ieee80211_rx_bss_get(local, sdata->u.mgd.bssid,
- local->hw.conf.channel->center_freq,
- sdata->u.mgd.ssid,
- sdata->u.mgd.ssid_len);
- if (!bss)
- return RX_DROP_MONITOR;
-
- ieee80211_sta_process_chanswitch(sdata,
- &mgmt->u.action.u.chan_switch.sw_elem, bss);
- ieee80211_rx_bss_put(local, bss);
- break;
+ return ieee80211_sta_rx_mgmt(sdata, rx->skb);
}
break;
case WLAN_CATEGORY_SA_QUERY:
@@ -1817,19 +1830,18 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
return RX_DROP_MONITOR;
if (ieee80211_vif_is_mesh(&sdata->vif))
- return ieee80211_mesh_rx_mgmt(sdata, rx->skb, rx->status);
+ return ieee80211_mesh_rx_mgmt(sdata, rx->skb);
if (sdata->vif.type == NL80211_IFTYPE_ADHOC)
- return ieee80211_ibss_rx_mgmt(sdata, rx->skb, rx->status);
+ return ieee80211_ibss_rx_mgmt(sdata, rx->skb);
if (sdata->vif.type == NL80211_IFTYPE_STATION)
- return ieee80211_sta_rx_mgmt(sdata, rx->skb, rx->status);
+ return ieee80211_sta_rx_mgmt(sdata, rx->skb);
return RX_DROP_MONITOR;
}
-static void ieee80211_rx_michael_mic_report(struct net_device *dev,
- struct ieee80211_hdr *hdr,
+static void ieee80211_rx_michael_mic_report(struct ieee80211_hdr *hdr,
struct ieee80211_rx_data *rx)
{
int keyidx;
@@ -1866,7 +1878,8 @@ static void ieee80211_rx_michael_mic_report(struct net_device *dev,
!ieee80211_is_auth(hdr->frame_control))
goto ignore;
- mac80211_ev_michael_mic_failure(rx->sdata, keyidx, hdr, NULL);
+ mac80211_ev_michael_mic_failure(rx->sdata, keyidx, hdr, NULL,
+ GFP_ATOMIC);
ignore:
dev_kfree_skb(rx->skb);
rx->skb = NULL;
@@ -2028,13 +2041,8 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
case NL80211_IFTYPE_STATION:
if (!bssid)
return 0;
- if (!ieee80211_bssid_match(bssid, sdata->u.mgd.bssid)) {
- if (!(rx->flags & IEEE80211_RX_IN_SCAN))
- return 0;
- rx->flags &= ~IEEE80211_RX_RA_MATCH;
- } else if (!multicast &&
- compare_ether_addr(sdata->dev->dev_addr,
- hdr->addr1) != 0) {
+ if (!multicast &&
+ compare_ether_addr(sdata->dev->dev_addr, hdr->addr1) != 0) {
if (!(sdata->dev->flags & IFF_PROMISC))
return 0;
rx->flags &= ~IEEE80211_RX_RA_MATCH;
@@ -2114,9 +2122,9 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
*/
static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
struct sk_buff *skb,
- struct ieee80211_rx_status *status,
struct ieee80211_rate *rate)
{
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata;
struct ieee80211_hdr *hdr;
@@ -2143,11 +2151,12 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
}
if ((status->flag & RX_FLAG_MMIC_ERROR)) {
- ieee80211_rx_michael_mic_report(local->mdev, hdr, &rx);
+ ieee80211_rx_michael_mic_report(hdr, &rx);
return;
}
- if (unlikely(local->sw_scanning || local->hw_scanning))
+ if (unlikely(test_bit(SCAN_HW_SCANNING, &local->scanning) ||
+ test_bit(SCAN_OFF_CHANNEL, &local->scanning)))
rx.flags |= IEEE80211_RX_IN_SCAN;
ieee80211_parse_qos(&rx);
@@ -2227,20 +2236,21 @@ static void ieee80211_release_reorder_frame(struct ieee80211_hw *hw,
{
struct ieee80211_supported_band *sband;
struct ieee80211_rate *rate;
- struct ieee80211_rx_status status;
+ struct sk_buff *skb = tid_agg_rx->reorder_buf[index];
+ struct ieee80211_rx_status *status;
- if (!tid_agg_rx->reorder_buf[index])
+ if (!skb)
goto no_frame;
+ status = IEEE80211_SKB_RXCB(skb);
+
/* release the reordered frames to stack */
- memcpy(&status, tid_agg_rx->reorder_buf[index]->cb, sizeof(status));
- sband = hw->wiphy->bands[status.band];
- if (status.flag & RX_FLAG_HT)
+ sband = hw->wiphy->bands[status->band];
+ if (status->flag & RX_FLAG_HT)
rate = sband->bitrates; /* TODO: HT rates */
else
- rate = &sband->bitrates[status.rate_idx];
- __ieee80211_rx_handle_packet(hw, tid_agg_rx->reorder_buf[index],
- &status, rate);
+ rate = &sband->bitrates[status->rate_idx];
+ __ieee80211_rx_handle_packet(hw, skb, rate);
tid_agg_rx->stored_mpdu_num--;
tid_agg_rx->reorder_buf[index] = NULL;
@@ -2265,7 +2275,6 @@ no_frame:
static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
struct tid_ampdu_rx *tid_agg_rx,
struct sk_buff *skb,
- struct ieee80211_rx_status *rxstatus,
u16 mpdu_seq_num,
int bar_req)
{
@@ -2324,8 +2333,6 @@ static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
/* put the frame in the reordering buffer */
tid_agg_rx->reorder_buf[index] = skb;
tid_agg_rx->reorder_time[index] = jiffies;
- memcpy(tid_agg_rx->reorder_buf[index]->cb, rxstatus,
- sizeof(*rxstatus));
tid_agg_rx->stored_mpdu_num++;
/* release the buffer until next missing frame */
index = seq_sub(tid_agg_rx->head_seq_num, tid_agg_rx->ssn)
@@ -2374,8 +2381,7 @@ static u8 ieee80211_sta_manage_reorder_buf(struct ieee80211_hw *hw,
}
static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local,
- struct sk_buff *skb,
- struct ieee80211_rx_status *status)
+ struct sk_buff *skb)
{
struct ieee80211_hw *hw = &local->hw;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
@@ -2424,7 +2430,7 @@ static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local,
/* according to mpdu sequence number deal with reordering buffer */
mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4;
- ret = ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb, status,
+ ret = ieee80211_sta_manage_reorder_buf(hw, tid_agg_rx, skb,
mpdu_seq_num, 0);
end_reorder:
return ret;
@@ -2434,24 +2440,20 @@ static u8 ieee80211_rx_reorder_ampdu(struct ieee80211_local *local,
* This is the receive path handler. It is called by a low level driver when an
* 802.11 MPDU is received from the hardware.
*/
-void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
- struct ieee80211_rx_status *status)
+void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb)
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_rate *rate = NULL;
struct ieee80211_supported_band *sband;
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
- if (status->band < 0 ||
- status->band >= IEEE80211_NUM_BANDS) {
- WARN_ON(1);
- return;
- }
+ if (WARN_ON(status->band < 0 ||
+ status->band >= IEEE80211_NUM_BANDS))
+ goto drop;
sband = local->hw.wiphy->bands[status->band];
- if (!sband) {
- WARN_ON(1);
- return;
- }
+ if (WARN_ON(!sband))
+ goto drop;
/*
* If we're suspending, it is possible although not too likely
@@ -2460,16 +2462,21 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
* that might, for example, cause stations to be added or other
* driver callbacks be invoked.
*/
- if (unlikely(local->quiescing || local->suspended)) {
- kfree_skb(skb);
- return;
- }
+ if (unlikely(local->quiescing || local->suspended))
+ goto drop;
+
+ /*
+ * The same happens when we're not even started,
+ * but that's worth a warning.
+ */
+ if (WARN_ON(!local->started))
+ goto drop;
if (status->flag & RX_FLAG_HT) {
/* rate_idx is MCS index */
if (WARN_ON(status->rate_idx < 0 ||
status->rate_idx >= 76))
- return;
+ goto drop;
/* HT rates are not in the table - use the highest legacy rate
* for now since other parts of mac80211 may not yet be fully
* MCS aware. */
@@ -2477,7 +2484,7 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
} else {
if (WARN_ON(status->rate_idx < 0 ||
status->rate_idx >= sband->n_bitrates))
- return;
+ goto drop;
rate = &sband->bitrates[status->rate_idx];
}
@@ -2494,7 +2501,7 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
* if it was previously present.
* Also, frames with less than 16 bytes are dropped.
*/
- skb = ieee80211_rx_monitor(local, skb, status, rate);
+ skb = ieee80211_rx_monitor(local, skb, rate);
if (!skb) {
rcu_read_unlock();
return;
@@ -2512,25 +2519,25 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
* frames from other than operational channel), but that should not
* happen in normal networks.
*/
- if (!ieee80211_rx_reorder_ampdu(local, skb, status))
- __ieee80211_rx_handle_packet(hw, skb, status, rate);
+ if (!ieee80211_rx_reorder_ampdu(local, skb))
+ __ieee80211_rx_handle_packet(hw, skb, rate);
rcu_read_unlock();
+
+ return;
+ drop:
+ kfree_skb(skb);
}
-EXPORT_SYMBOL(__ieee80211_rx);
+EXPORT_SYMBOL(ieee80211_rx);
/* This is a version of the rx handler that can be called from hard irq
* context. Post the skb on the queue and schedule the tasklet */
-void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb,
- struct ieee80211_rx_status *status)
+void ieee80211_rx_irqsafe(struct ieee80211_hw *hw, struct sk_buff *skb)
{
struct ieee80211_local *local = hw_to_local(hw);
BUILD_BUG_ON(sizeof(struct ieee80211_rx_status) > sizeof(skb->cb));
- skb->dev = local->mdev;
- /* copy status into skb->cb for use by tasklet */
- memcpy(skb->cb, status, sizeof(*status));
skb->pkt_type = IEEE80211_RX_MSG;
skb_queue_tail(&local->skb_queue, skb);
tasklet_schedule(&local->tasklet);
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 2a8d09ad17ff..039901109fa1 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -18,7 +18,6 @@
#include <linux/if_arp.h>
#include <linux/rtnetlink.h>
#include <net/mac80211.h>
-#include <net/iw_handler.h>
#include "ieee80211_i.h"
#include "driver-ops.h"
@@ -26,7 +25,7 @@
#define IEEE80211_PROBE_DELAY (HZ / 33)
#define IEEE80211_CHANNEL_TIME (HZ / 33)
-#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 5)
+#define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 8)
struct ieee80211_bss *
ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq,
@@ -121,23 +120,10 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
return bss;
}
-void ieee80211_rx_bss_remove(struct ieee80211_sub_if_data *sdata, u8 *bssid,
- int freq, u8 *ssid, u8 ssid_len)
-{
- struct ieee80211_bss *bss;
- struct ieee80211_local *local = sdata->local;
-
- bss = ieee80211_rx_bss_get(local, bssid, freq, ssid, ssid_len);
- if (bss) {
- cfg80211_unlink_bss(local->hw.wiphy, (void *)bss);
- ieee80211_rx_bss_put(local, bss);
- }
-}
-
ieee80211_rx_result
-ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
- struct ieee80211_rx_status *rx_status)
+ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
{
+ struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_mgmt *mgmt;
struct ieee80211_bss *bss;
u8 *elements;
@@ -278,7 +264,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
mutex_lock(&local->scan_mtx);
- if (WARN_ON(!local->hw_scanning && !local->sw_scanning)) {
+ if (WARN_ON(!local->scanning)) {
mutex_unlock(&local->scan_mtx);
return;
}
@@ -288,16 +274,16 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
return;
}
- if (local->hw_scanning)
+ if (test_bit(SCAN_HW_SCANNING, &local->scanning))
ieee80211_restore_scan_ies(local);
- if (local->scan_req != &local->int_scan_req)
+ if (local->scan_req != local->int_scan_req)
cfg80211_scan_done(local->scan_req, aborted);
local->scan_req = NULL;
+ local->scan_sdata = NULL;
- was_hw_scan = local->hw_scanning;
- local->hw_scanning = false;
- local->sw_scanning = false;
+ was_hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning);
+ local->scanning = 0;
local->scan_channel = NULL;
/* we only have to protect scan_req and hw/sw scan */
@@ -307,16 +293,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
if (was_hw_scan)
goto done;
- netif_tx_lock_bh(local->mdev);
- netif_addr_lock(local->mdev);
- local->filter_flags &= ~FIF_BCN_PRBRESP_PROMISC;
- drv_configure_filter(local, FIF_BCN_PRBRESP_PROMISC,
- &local->filter_flags,
- local->mdev->mc_count,
- local->mdev->mc_list);
-
- netif_addr_unlock(local->mdev);
- netif_tx_unlock_bh(local->mdev);
+ ieee80211_configure_filter(local);
drv_sw_scan_complete(local);
@@ -327,7 +304,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
/* Tell AP we're back */
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
- if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED) {
+ if (sdata->u.mgd.associated) {
ieee80211_scan_ps_disable(sdata);
netif_tx_wake_all_queues(sdata->dev);
}
@@ -382,30 +359,24 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
ieee80211_bss_info_change_notify(
sdata, BSS_CHANGED_BEACON_ENABLED);
- if (sdata->vif.type == NL80211_IFTYPE_STATION) {
- if (sdata->u.mgd.flags & IEEE80211_STA_ASSOCIATED) {
- netif_tx_stop_all_queues(sdata->dev);
- ieee80211_scan_ps_enable(sdata);
- }
- } else
+ /*
+ * only handle non-STA interfaces here, STA interfaces
+ * are handled in the scan state machine
+ */
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
netif_tx_stop_all_queues(sdata->dev);
}
mutex_unlock(&local->iflist_mtx);
- local->scan_state = SCAN_SET_CHANNEL;
+ local->next_scan_state = SCAN_DECISION;
local->scan_channel_idx = 0;
- netif_addr_lock_bh(local->mdev);
- local->filter_flags |= FIF_BCN_PRBRESP_PROMISC;
- drv_configure_filter(local, FIF_BCN_PRBRESP_PROMISC,
- &local->filter_flags,
- local->mdev->mc_count,
- local->mdev->mc_list);
- netif_addr_unlock_bh(local->mdev);
+ ieee80211_configure_filter(local);
/* TODO: start scan as soon as all nullfunc frames are ACKed */
- queue_delayed_work(local->hw.workqueue, &local->scan_work,
- IEEE80211_CHANNEL_TIME);
+ ieee80211_queue_delayed_work(&local->hw,
+ &local->scan_work,
+ IEEE80211_CHANNEL_TIME);
return 0;
}
@@ -441,20 +412,18 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
local->scan_req = req;
local->scan_sdata = sdata;
- if (req != &local->int_scan_req &&
+ if (req != local->int_scan_req &&
sdata->vif.type == NL80211_IFTYPE_STATION &&
- (ifmgd->state == IEEE80211_STA_MLME_DIRECT_PROBE ||
- ifmgd->state == IEEE80211_STA_MLME_AUTHENTICATE ||
- ifmgd->state == IEEE80211_STA_MLME_ASSOCIATE)) {
- /* actually wait for the assoc to finish/time out */
+ !list_empty(&ifmgd->work_list)) {
+ /* actually wait for the work it's doing to finish/time out */
set_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request);
return 0;
}
if (local->ops->hw_scan)
- local->hw_scanning = true;
+ __set_bit(SCAN_HW_SCANNING, &local->scanning);
else
- local->sw_scanning = true;
+ __set_bit(SCAN_SW_SCANNING, &local->scanning);
/*
* Kicking off the scan need not be protected,
* only the scan variable stuff, since now
@@ -477,11 +446,9 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
mutex_lock(&local->scan_mtx);
if (rc) {
- if (local->ops->hw_scan) {
- local->hw_scanning = false;
+ if (local->ops->hw_scan)
ieee80211_restore_scan_ies(local);
- } else
- local->sw_scanning = false;
+ local->scanning = 0;
ieee80211_recalc_idle(local);
@@ -492,13 +459,195 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
return rc;
}
+static int ieee80211_scan_state_decision(struct ieee80211_local *local,
+ unsigned long *next_delay)
+{
+ bool associated = false;
+ struct ieee80211_sub_if_data *sdata;
+
+ /* if no more bands/channels left, complete scan and advance to the idle state */
+ if (local->scan_channel_idx >= local->scan_req->n_channels) {
+ ieee80211_scan_completed(&local->hw, false);
+ return 1;
+ }
+
+ /* check if at least one STA interface is associated */
+ mutex_lock(&local->iflist_mtx);
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (!netif_running(sdata->dev))
+ continue;
+
+ if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+ if (sdata->u.mgd.associated) {
+ associated = true;
+ break;
+ }
+ }
+ }
+ mutex_unlock(&local->iflist_mtx);
+
+ if (local->scan_channel) {
+ /*
+ * we're currently scanning a different channel, let's
+ * switch back to the operating channel now if at least
+ * one interface is associated. Otherwise just scan the
+ * next channel
+ */
+ if (associated)
+ local->next_scan_state = SCAN_ENTER_OPER_CHANNEL;
+ else
+ local->next_scan_state = SCAN_SET_CHANNEL;
+ } else {
+ /*
+ * we're on the operating channel currently, let's
+ * leave that channel now to scan another one
+ */
+ local->next_scan_state = SCAN_LEAVE_OPER_CHANNEL;
+ }
+
+ *next_delay = 0;
+ return 0;
+}
+
+static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local,
+ unsigned long *next_delay)
+{
+ struct ieee80211_sub_if_data *sdata;
+
+ /*
+ * notify the AP about us leaving the channel and stop all STA interfaces
+ */
+ mutex_lock(&local->iflist_mtx);
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (!netif_running(sdata->dev))
+ continue;
+
+ if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+ netif_tx_stop_all_queues(sdata->dev);
+ if (sdata->u.mgd.associated)
+ ieee80211_scan_ps_enable(sdata);
+ }
+ }
+ mutex_unlock(&local->iflist_mtx);
+
+ __set_bit(SCAN_OFF_CHANNEL, &local->scanning);
+
+ /* advance to the next channel to be scanned */
+ *next_delay = HZ / 10;
+ local->next_scan_state = SCAN_SET_CHANNEL;
+}
+
+static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *local,
+ unsigned long *next_delay)
+{
+ struct ieee80211_sub_if_data *sdata = local->scan_sdata;
+
+ /* switch back to the operating channel */
+ local->scan_channel = NULL;
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
+
+ /*
+ * notify the AP about us being back and restart all STA interfaces
+ */
+ mutex_lock(&local->iflist_mtx);
+ list_for_each_entry(sdata, &local->interfaces, list) {
+ if (!netif_running(sdata->dev))
+ continue;
+
+ /* Tell AP we're back */
+ if (sdata->vif.type == NL80211_IFTYPE_STATION) {
+ if (sdata->u.mgd.associated)
+ ieee80211_scan_ps_disable(sdata);
+ netif_tx_wake_all_queues(sdata->dev);
+ }
+ }
+ mutex_unlock(&local->iflist_mtx);
+
+ __clear_bit(SCAN_OFF_CHANNEL, &local->scanning);
+
+ *next_delay = HZ / 5;
+ local->next_scan_state = SCAN_DECISION;
+}
+
+static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
+ unsigned long *next_delay)
+{
+ int skip;
+ struct ieee80211_channel *chan;
+ struct ieee80211_sub_if_data *sdata = local->scan_sdata;
+
+ skip = 0;
+ chan = local->scan_req->channels[local->scan_channel_idx];
+
+ if (chan->flags & IEEE80211_CHAN_DISABLED ||
+ (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
+ chan->flags & IEEE80211_CHAN_NO_IBSS))
+ skip = 1;
+
+ if (!skip) {
+ local->scan_channel = chan;
+ if (ieee80211_hw_config(local,
+ IEEE80211_CONF_CHANGE_CHANNEL))
+ skip = 1;
+ }
+
+ /* advance state machine to next channel/band */
+ local->scan_channel_idx++;
+
+ if (skip) {
+ /* if we skip this channel return to the decision state */
+ local->next_scan_state = SCAN_DECISION;
+ return;
+ }
+
+ /*
+ * Probe delay is used to update the NAV, cf. 11.1.3.2.2
+ * (which unfortunately doesn't say _why_ step a) is done,
+ * but it waits for the probe delay or until a frame is
+ * received - and the received frame would update the NAV).
+ * For now, we do not support waiting until a frame is
+ * received.
+ *
+ * In any case, it is not necessary for a passive scan.
+ */
+ if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN ||
+ !local->scan_req->n_ssids) {
+ *next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
+ local->next_scan_state = SCAN_DECISION;
+ return;
+ }
+
+ /* active scan, send probes */
+ *next_delay = IEEE80211_PROBE_DELAY;
+ local->next_scan_state = SCAN_SEND_PROBE;
+}
+
+static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
+ unsigned long *next_delay)
+{
+ int i;
+ struct ieee80211_sub_if_data *sdata = local->scan_sdata;
+
+ for (i = 0; i < local->scan_req->n_ssids; i++)
+ ieee80211_send_probe_req(
+ sdata, NULL,
+ local->scan_req->ssids[i].ssid,
+ local->scan_req->ssids[i].ssid_len,
+ local->scan_req->ie, local->scan_req->ie_len);
+
+ /*
+ * After sending probe requests, wait for probe responses
+ * on the channel.
+ */
+ *next_delay = IEEE80211_CHANNEL_TIME;
+ local->next_scan_state = SCAN_DECISION;
+}
+
void ieee80211_scan_work(struct work_struct *work)
{
struct ieee80211_local *local =
container_of(work, struct ieee80211_local, scan_work.work);
struct ieee80211_sub_if_data *sdata = local->scan_sdata;
- struct ieee80211_channel *chan;
- int skip, i;
unsigned long next_delay = 0;
mutex_lock(&local->scan_mtx);
@@ -507,11 +656,12 @@ void ieee80211_scan_work(struct work_struct *work)
return;
}
- if (local->scan_req && !(local->sw_scanning || local->hw_scanning)) {
+ if (local->scan_req && !local->scanning) {
struct cfg80211_scan_request *req = local->scan_req;
int rc;
local->scan_req = NULL;
+ local->scan_sdata = NULL;
rc = __ieee80211_start_scan(sdata, req);
mutex_unlock(&local->scan_mtx);
@@ -531,72 +681,32 @@ void ieee80211_scan_work(struct work_struct *work)
return;
}
- switch (local->scan_state) {
- case SCAN_SET_CHANNEL:
- /* if no more bands/channels left, complete scan */
- if (local->scan_channel_idx >= local->scan_req->n_channels) {
- ieee80211_scan_completed(&local->hw, false);
- return;
- }
- skip = 0;
- chan = local->scan_req->channels[local->scan_channel_idx];
-
- if (chan->flags & IEEE80211_CHAN_DISABLED ||
- (sdata->vif.type == NL80211_IFTYPE_ADHOC &&
- chan->flags & IEEE80211_CHAN_NO_IBSS))
- skip = 1;
-
- if (!skip) {
- local->scan_channel = chan;
- if (ieee80211_hw_config(local,
- IEEE80211_CONF_CHANGE_CHANNEL))
- skip = 1;
- }
-
- /* advance state machine to next channel/band */
- local->scan_channel_idx++;
-
- if (skip)
+ /*
+ * as long as no delay is required advance immediately
+ * without scheduling a new work
+ */
+ do {
+ switch (local->next_scan_state) {
+ case SCAN_DECISION:
+ if (ieee80211_scan_state_decision(local, &next_delay))
+ return;
break;
-
- /*
- * Probe delay is used to update the NAV, cf. 11.1.3.2.2
- * (which unfortunately doesn't say _why_ step a) is done,
- * but it waits for the probe delay or until a frame is
- * received - and the received frame would update the NAV).
- * For now, we do not support waiting until a frame is
- * received.
- *
- * In any case, it is not necessary for a passive scan.
- */
- if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN ||
- !local->scan_req->n_ssids) {
- next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
+ case SCAN_SET_CHANNEL:
+ ieee80211_scan_state_set_channel(local, &next_delay);
+ break;
+ case SCAN_SEND_PROBE:
+ ieee80211_scan_state_send_probe(local, &next_delay);
+ break;
+ case SCAN_LEAVE_OPER_CHANNEL:
+ ieee80211_scan_state_leave_oper_channel(local, &next_delay);
+ break;
+ case SCAN_ENTER_OPER_CHANNEL:
+ ieee80211_scan_state_enter_oper_channel(local, &next_delay);
break;
}
+ } while (next_delay == 0);
- next_delay = IEEE80211_PROBE_DELAY;
- local->scan_state = SCAN_SEND_PROBE;
- break;
- case SCAN_SEND_PROBE:
- for (i = 0; i < local->scan_req->n_ssids; i++)
- ieee80211_send_probe_req(
- sdata, NULL,
- local->scan_req->ssids[i].ssid,
- local->scan_req->ssids[i].ssid_len,
- local->scan_req->ie, local->scan_req->ie_len);
-
- /*
- * After sending probe requests, wait for probe responses
- * on the channel.
- */
- next_delay = IEEE80211_CHANNEL_TIME;
- local->scan_state = SCAN_SET_CHANNEL;
- break;
- }
-
- queue_delayed_work(local->hw.workqueue, &local->scan_work,
- next_delay);
+ ieee80211_queue_delayed_work(&local->hw, &local->scan_work, next_delay);
}
int ieee80211_request_scan(struct ieee80211_sub_if_data *sdata,
@@ -623,10 +733,10 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
if (local->scan_req)
goto unlock;
- memcpy(local->int_scan_req.ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN);
- local->int_scan_req.ssids[0].ssid_len = ssid_len;
+ memcpy(local->int_scan_req->ssids[0].ssid, ssid, IEEE80211_MAX_SSID_LEN);
+ local->int_scan_req->ssids[0].ssid_len = ssid_len;
- ret = __ieee80211_start_scan(sdata, &sdata->local->int_scan_req);
+ ret = __ieee80211_start_scan(sdata, sdata->local->int_scan_req);
unlock:
mutex_unlock(&local->scan_mtx);
return ret;
@@ -634,7 +744,7 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata,
void ieee80211_scan_cancel(struct ieee80211_local *local)
{
- bool swscan;
+ bool abortscan;
cancel_delayed_work_sync(&local->scan_work);
@@ -643,9 +753,10 @@ void ieee80211_scan_cancel(struct ieee80211_local *local)
* queued -- mostly at suspend under RTNL.
*/
mutex_lock(&local->scan_mtx);
- swscan = local->sw_scanning;
+ abortscan = test_bit(SCAN_SW_SCANNING, &local->scanning) ||
+ (!local->scanning && local->scan_req);
mutex_unlock(&local->scan_mtx);
- if (swscan)
+ if (abortscan)
ieee80211_scan_completed(&local->hw, true);
}
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index a360bceeba59..eec001491e66 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -349,6 +349,7 @@ int sta_info_insert(struct sta_info *sta)
goto out_free;
}
list_add(&sta->list, &local->sta_list);
+ local->sta_generation++;
local->num_sta++;
sta_info_hash_add(local, sta);
@@ -485,6 +486,7 @@ static void __sta_info_unlink(struct sta_info **sta)
}
local->num_sta--;
+ local->sta_generation++;
if (local->ops->sta_notify) {
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 49a1a1f76511..ccc3adf962c7 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -30,7 +30,6 @@
* @WLAN_STA_ASSOC_AP: We're associated to that station, it is an AP.
* @WLAN_STA_WME: Station is a QoS-STA.
* @WLAN_STA_WDS: Station is one of our WDS peers.
- * @WLAN_STA_PSPOLL: Station has just PS-polled us.
* @WLAN_STA_CLEAR_PS_FILT: Clear PS filter in hardware (using the
* IEEE80211_TX_CTL_CLEAR_PS_FILT control flag) when the next
* frame to this station is transmitted.
@@ -47,7 +46,6 @@ enum ieee80211_sta_info_flags {
WLAN_STA_ASSOC_AP = 1<<5,
WLAN_STA_WME = 1<<6,
WLAN_STA_WDS = 1<<7,
- WLAN_STA_PSPOLL = 1<<8,
WLAN_STA_CLEAR_PS_FILT = 1<<9,
WLAN_STA_MFP = 1<<10,
WLAN_STA_SUSPEND = 1<<11
@@ -308,6 +306,23 @@ struct sta_info {
struct dentry *inactive_ms;
struct dentry *last_seq_ctrl;
struct dentry *agg_status;
+ struct dentry *aid;
+ struct dentry *dev;
+ struct dentry *rx_packets;
+ struct dentry *tx_packets;
+ struct dentry *rx_bytes;
+ struct dentry *tx_bytes;
+ struct dentry *rx_duplicates;
+ struct dentry *rx_fragments;
+ struct dentry *rx_dropped;
+ struct dentry *tx_fragments;
+ struct dentry *tx_filtered;
+ struct dentry *tx_retry_failed;
+ struct dentry *tx_retry_count;
+ struct dentry *last_signal;
+ struct dentry *last_qual;
+ struct dentry *last_noise;
+ struct dentry *wep_weak_iv_count;
bool add_has_run;
} debugfs;
#endif
@@ -342,17 +357,6 @@ static inline void clear_sta_flags(struct sta_info *sta, const u32 flags)
spin_unlock_irqrestore(&sta->flaglock, irqfl);
}
-static inline void set_and_clear_sta_flags(struct sta_info *sta,
- const u32 set, const u32 clear)
-{
- unsigned long irqfl;
-
- spin_lock_irqsave(&sta->flaglock, irqfl);
- sta->flags |= set;
- sta->flags &= ~clear;
- spin_unlock_irqrestore(&sta->flaglock, irqfl);
-}
-
static inline u32 test_sta_flags(struct sta_info *sta, const u32 flags)
{
u32 ret;
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 3a8922cd1038..5143d203256b 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -192,7 +192,7 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED))
return TX_CONTINUE;
- if (unlikely(tx->local->sw_scanning) &&
+ if (unlikely(test_bit(SCAN_OFF_CHANNEL, &tx->local->scanning)) &&
!ieee80211_is_probe_req(hdr->frame_control) &&
!ieee80211_is_nullfunc(hdr->frame_control))
/*
@@ -317,30 +317,30 @@ ieee80211_tx_h_multicast_ps_buf(struct ieee80211_tx_data *tx)
if (!atomic_read(&tx->sdata->bss->num_sta_ps))
return TX_CONTINUE;
+ /* buffered in hardware */
+ if (!(tx->local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING)) {
+ info->flags |= IEEE80211_TX_CTL_SEND_AFTER_DTIM;
+
+ return TX_CONTINUE;
+ }
+
/* buffered in mac80211 */
- if (tx->local->hw.flags & IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING) {
- if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)
- purge_old_ps_buffers(tx->local);
- if (skb_queue_len(&tx->sdata->bss->ps_bc_buf) >=
- AP_MAX_BC_BUFFER) {
+ if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)
+ purge_old_ps_buffers(tx->local);
+
+ if (skb_queue_len(&tx->sdata->bss->ps_bc_buf) >= AP_MAX_BC_BUFFER) {
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: BC TX buffer full - "
- "dropping the oldest frame\n",
- tx->dev->name);
- }
+ if (net_ratelimit())
+ printk(KERN_DEBUG "%s: BC TX buffer full - dropping the oldest frame\n",
+ tx->dev->name);
#endif
- dev_kfree_skb(skb_dequeue(&tx->sdata->bss->ps_bc_buf));
- } else
- tx->local->total_ps_buffered++;
- skb_queue_tail(&tx->sdata->bss->ps_bc_buf, tx->skb);
- return TX_QUEUED;
- }
+ dev_kfree_skb(skb_dequeue(&tx->sdata->bss->ps_bc_buf));
+ } else
+ tx->local->total_ps_buffered++;
- /* buffered in hardware */
- info->flags |= IEEE80211_TX_CTL_SEND_AFTER_DTIM;
+ skb_queue_tail(&tx->sdata->bss->ps_bc_buf, tx->skb);
- return TX_CONTINUE;
+ return TX_QUEUED;
}
static int ieee80211_use_mfp(__le16 fc, struct sta_info *sta,
@@ -373,7 +373,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
staflags = get_sta_flags(sta);
if (unlikely((staflags & WLAN_STA_PS) &&
- !(staflags & WLAN_STA_PSPOLL))) {
+ !(info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE))) {
#ifdef CONFIG_MAC80211_VERBOSE_PS_DEBUG
printk(KERN_DEBUG "STA %pM aid %d: PS buffer (entries "
"before %d)\n",
@@ -400,6 +400,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
sta_info_set_tim_bit(sta);
info->control.jiffies = jiffies;
+ info->control.vif = &tx->sdata->vif;
info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
skb_queue_tail(&sta->ps_tx_buf, tx->skb);
return TX_QUEUED;
@@ -411,24 +412,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
sta->sta.addr);
}
#endif /* CONFIG_MAC80211_VERBOSE_PS_DEBUG */
- if (test_and_clear_sta_flags(sta, WLAN_STA_PSPOLL)) {
- /*
- * The sleeping station with pending data is now snoozing.
- * It queried us for its buffered frames and will go back
- * to deep sleep once it got everything.
- *
- * inform the driver, in case the hardware does powersave
- * frame filtering and keeps a station blacklist on its own
- * (e.g: p54), so that frames can be delivered unimpeded.
- *
- * Note: It should be safe to disable the filter now.
- * As, it is really unlikely that we still have any pending
- * frame for this station in the hw's buffers/fifos left,
- * that is not rejected with a unsuccessful tx_status yet.
- */
- info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT;
- }
return TX_CONTINUE;
}
@@ -451,7 +435,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx->skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx->skb->data;
- if (unlikely(tx->skb->do_not_encrypt))
+ if (unlikely(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT))
tx->key = NULL;
else if (tx->sta && (key = rcu_dereference(tx->sta->key)))
tx->key = key;
@@ -497,7 +481,7 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
}
if (!tx->key || !(tx->key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE))
- tx->skb->do_not_encrypt = 1;
+ info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
return TX_CONTINUE;
}
@@ -512,6 +496,7 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
int i, len;
bool inval = false, rts = false, short_preamble = false;
struct ieee80211_tx_rate_control txrc;
+ u32 sta_flags;
memset(&txrc, 0, sizeof(txrc));
@@ -544,7 +529,26 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx)
(tx->sta && test_sta_flags(tx->sta, WLAN_STA_SHORT_PREAMBLE))))
txrc.short_preamble = short_preamble = true;
+ sta_flags = tx->sta ? get_sta_flags(tx->sta) : 0;
+
+ /*
+ * Lets not bother rate control if we're associated and cannot
+ * talk to the sta. This should not happen.
+ */
+ if (WARN(test_bit(SCAN_SW_SCANNING, &tx->local->scanning) &&
+ (sta_flags & WLAN_STA_ASSOC) &&
+ !rate_usable_index_exists(sband, &tx->sta->sta),
+ "%s: Dropped data frame as no usable bitrate found while "
+ "scanning and associated. Target station: "
+ "%pM on %d GHz band\n",
+ tx->dev->name, hdr->addr1,
+ tx->channel->band ? 5 : 2))
+ return TX_DROP;
+ /*
+ * If we're associated with the sta at this point we know we can at
+ * least send the frame at the lowest bit rate.
+ */
rate_control_get_rate(tx->sdata, tx->sta, &txrc);
if (unlikely(info->control.rates[0].idx < 0))
@@ -676,7 +680,7 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
* number, if we have no matching interface then we
* neither assign one ourselves nor ask the driver to.
*/
- if (unlikely(!info->control.vif))
+ if (unlikely(info->control.vif->type == NL80211_IFTYPE_MONITOR))
return TX_CONTINUE;
if (unlikely(ieee80211_is_ctl(hdr->frame_control)))
@@ -696,7 +700,6 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
/* for pure STA mode without beacons, we can do it */
hdr->seq_ctrl = cpu_to_le16(tx->sdata->sequence_number);
tx->sdata->sequence_number += 0x10;
- tx->sdata->sequence_number &= IEEE80211_SCTL_SEQ;
return TX_CONTINUE;
}
@@ -754,9 +757,7 @@ static int ieee80211_fragment(struct ieee80211_local *local,
memcpy(tmp->cb, skb->cb, sizeof(tmp->cb));
skb_copy_queue_mapping(tmp, skb);
tmp->priority = skb->priority;
- tmp->do_not_encrypt = skb->do_not_encrypt;
tmp->dev = skb->dev;
- tmp->iif = skb->iif;
/* copy header and data */
memcpy(skb_put(tmp, hdrlen), skb->data, hdrlen);
@@ -784,7 +785,7 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
/*
* Warn when submitting a fragmented A-MPDU frame and drop it.
- * This scenario is handled in __ieee80211_tx_prepare but extra
+ * This scenario is handled in ieee80211_tx_prepare but extra
* caution taken here as fragmented ampdu may cause Tx stop.
*/
if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU))
@@ -842,6 +843,23 @@ ieee80211_tx_h_fragment(struct ieee80211_tx_data *tx)
}
static ieee80211_tx_result debug_noinline
+ieee80211_tx_h_stats(struct ieee80211_tx_data *tx)
+{
+ struct sk_buff *skb = tx->skb;
+
+ if (!tx->sta)
+ return TX_CONTINUE;
+
+ tx->sta->tx_packets++;
+ do {
+ tx->sta->tx_fragments++;
+ tx->sta->tx_bytes += skb->len;
+ } while ((skb = skb->next));
+
+ return TX_CONTINUE;
+}
+
+static ieee80211_tx_result debug_noinline
ieee80211_tx_h_encrypt(struct ieee80211_tx_data *tx)
{
if (!tx->key)
@@ -885,23 +903,6 @@ ieee80211_tx_h_calculate_duration(struct ieee80211_tx_data *tx)
return TX_CONTINUE;
}
-static ieee80211_tx_result debug_noinline
-ieee80211_tx_h_stats(struct ieee80211_tx_data *tx)
-{
- struct sk_buff *skb = tx->skb;
-
- if (!tx->sta)
- return TX_CONTINUE;
-
- tx->sta->tx_packets++;
- do {
- tx->sta->tx_fragments++;
- tx->sta->tx_bytes += skb->len;
- } while ((skb = skb->next));
-
- return TX_CONTINUE;
-}
-
/* actual transmit path */
/*
@@ -923,11 +924,12 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
struct ieee80211_radiotap_header *rthdr =
(struct ieee80211_radiotap_header *) skb->data;
struct ieee80211_supported_band *sband;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
int ret = ieee80211_radiotap_iterator_init(&iterator, rthdr, skb->len);
sband = tx->local->hw.wiphy->bands[tx->channel->band];
- skb->do_not_encrypt = 1;
+ info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
tx->flags &= ~IEEE80211_TX_FRAGMENTED;
/*
@@ -965,7 +967,7 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
skb_trim(skb, skb->len - FCS_LEN);
}
if (*iterator.this_arg & IEEE80211_RADIOTAP_F_WEP)
- tx->skb->do_not_encrypt = 0;
+ info->flags &= ~IEEE80211_TX_INTFL_DONT_ENCRYPT;
if (*iterator.this_arg & IEEE80211_RADIOTAP_F_FRAG)
tx->flags |= IEEE80211_TX_FRAGMENTED;
break;
@@ -998,13 +1000,12 @@ static bool __ieee80211_parse_tx_radiotap(struct ieee80211_tx_data *tx,
* initialises @tx
*/
static ieee80211_tx_result
-__ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
- struct sk_buff *skb,
- struct net_device *dev)
+ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_tx_data *tx,
+ struct sk_buff *skb)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_local *local = sdata->local;
struct ieee80211_hdr *hdr;
- struct ieee80211_sub_if_data *sdata;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
int hdrlen, tid;
u8 *qc, *state;
@@ -1012,9 +1013,9 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
memset(tx, 0, sizeof(*tx));
tx->skb = skb;
- tx->dev = dev; /* use original interface */
+ tx->dev = sdata->dev; /* use original interface */
tx->local = local;
- tx->sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ tx->sdata = sdata;
tx->channel = local->hw.conf.channel;
/*
* Set this flag (used below to indicate "automatic fragmentation"),
@@ -1023,7 +1024,6 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
tx->flags |= IEEE80211_TX_FRAGMENTED;
/* process and remove the injection radiotap header */
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED)) {
if (!__ieee80211_parse_tx_radiotap(tx, skb))
return TX_DROP;
@@ -1075,6 +1075,7 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
} else if (*state != HT_AGG_STATE_IDLE) {
/* in progress */
queued = true;
+ info->control.vif = &sdata->vif;
info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
__skb_queue_tail(&tid_tx->pending, skb);
}
@@ -1119,50 +1120,29 @@ __ieee80211_tx_prepare(struct ieee80211_tx_data *tx,
return TX_CONTINUE;
}
-/*
- * NB: @tx is uninitialised when passed in here
- */
-static int ieee80211_tx_prepare(struct ieee80211_local *local,
- struct ieee80211_tx_data *tx,
- struct sk_buff *skb)
-{
- struct net_device *dev;
-
- dev = dev_get_by_index(&init_net, skb->iif);
- if (unlikely(dev && !is_ieee80211_device(local, dev))) {
- dev_put(dev);
- dev = NULL;
- }
- if (unlikely(!dev))
- return -ENODEV;
- /*
- * initialises tx with control
- *
- * return value is safe to ignore here because this function
- * can only be invoked for multicast frames
- *
- * XXX: clean up
- */
- __ieee80211_tx_prepare(tx, skb, dev);
- dev_put(dev);
- return 0;
-}
-
static int __ieee80211_tx(struct ieee80211_local *local,
struct sk_buff **skbp,
- struct sta_info *sta)
+ struct sta_info *sta,
+ bool txpending)
{
struct sk_buff *skb = *skbp, *next;
struct ieee80211_tx_info *info;
+ struct ieee80211_sub_if_data *sdata;
+ unsigned long flags;
int ret, len;
bool fragm = false;
- local->mdev->trans_start = jiffies;
-
while (skb) {
- if (ieee80211_queue_stopped(&local->hw,
- skb_get_queue_mapping(skb)))
- return IEEE80211_TX_PENDING;
+ int q = skb_get_queue_mapping(skb);
+
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+ ret = IEEE80211_TX_OK;
+ if (local->queue_stop_reasons[q] ||
+ (!txpending && !skb_queue_empty(&local->pending[q])))
+ ret = IEEE80211_TX_PENDING;
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+ if (ret != IEEE80211_TX_OK)
+ return ret;
info = IEEE80211_SKB_CB(skb);
@@ -1172,13 +1152,35 @@ static int __ieee80211_tx(struct ieee80211_local *local,
next = skb->next;
len = skb->len;
+
+ if (next)
+ info->flags |= IEEE80211_TX_CTL_MORE_FRAMES;
+
+ sdata = vif_to_sdata(info->control.vif);
+
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_MONITOR:
+ info->control.vif = NULL;
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+ info->control.vif = &container_of(sdata->bss,
+ struct ieee80211_sub_if_data, u.ap)->vif;
+ break;
+ default:
+ /* keep */
+ break;
+ }
+
ret = drv_tx(local, skb);
if (WARN_ON(ret != NETDEV_TX_OK && skb->len != len)) {
dev_kfree_skb(skb);
ret = NETDEV_TX_OK;
}
- if (ret != NETDEV_TX_OK)
+ if (ret != NETDEV_TX_OK) {
+ info->control.vif = &sdata->vif;
return IEEE80211_TX_AGAIN;
+ }
+
*skbp = skb = next;
ieee80211_led_tx(local, 1);
fragm = true;
@@ -1210,9 +1212,9 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
CALL_TXH(ieee80211_tx_h_sequence)
CALL_TXH(ieee80211_tx_h_fragment)
/* handlers after fragment must be aware of tx info fragmentation! */
+ CALL_TXH(ieee80211_tx_h_stats)
CALL_TXH(ieee80211_tx_h_encrypt)
CALL_TXH(ieee80211_tx_h_calculate_duration)
- CALL_TXH(ieee80211_tx_h_stats)
#undef CALL_TXH
txh_done:
@@ -1234,10 +1236,10 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
return 0;
}
-static void ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
- bool txpending)
+static void ieee80211_tx(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, bool txpending)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_data tx;
ieee80211_tx_result res_prepare;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -1248,8 +1250,6 @@ static void ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
queue = skb_get_queue_mapping(skb);
- WARN_ON(!txpending && !skb_queue_empty(&local->pending[queue]));
-
if (unlikely(skb->len < 10)) {
dev_kfree_skb(skb);
return;
@@ -1258,7 +1258,7 @@ static void ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
rcu_read_lock();
/* initialises tx */
- res_prepare = __ieee80211_tx_prepare(&tx, skb, dev);
+ res_prepare = ieee80211_tx_prepare(sdata, &tx, skb);
if (unlikely(res_prepare == TX_DROP)) {
dev_kfree_skb(skb);
@@ -1277,7 +1277,7 @@ static void ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
retries = 0;
retry:
- ret = __ieee80211_tx(local, &tx.skb, tx.sta);
+ ret = __ieee80211_tx(local, &tx.skb, tx.sta, txpending);
switch (ret) {
case IEEE80211_TX_OK:
break;
@@ -1295,34 +1295,35 @@ static void ieee80211_tx(struct net_device *dev, struct sk_buff *skb,
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
- if (__netif_subqueue_stopped(local->mdev, queue)) {
+ if (local->queue_stop_reasons[queue] ||
+ !skb_queue_empty(&local->pending[queue])) {
+ /*
+ * if queue is stopped, queue up frames for later
+ * transmission from the tasklet
+ */
do {
next = skb->next;
skb->next = NULL;
if (unlikely(txpending))
- skb_queue_head(&local->pending[queue],
- skb);
+ __skb_queue_head(&local->pending[queue],
+ skb);
else
- skb_queue_tail(&local->pending[queue],
- skb);
+ __skb_queue_tail(&local->pending[queue],
+ skb);
} while ((skb = next));
- /*
- * Make sure nobody will enable the queue on us
- * (without going through the tasklet) nor disable the
- * netdev queue underneath the pending handling code.
- */
- __set_bit(IEEE80211_QUEUE_STOP_REASON_PENDING,
- &local->queue_stop_reasons[queue]);
-
spin_unlock_irqrestore(&local->queue_stop_reason_lock,
flags);
} else {
+ /*
+ * otherwise retry, but this is a race condition or
+ * a driver bug (which we warn about if it persists)
+ */
spin_unlock_irqrestore(&local->queue_stop_reason_lock,
flags);
retries++;
- if (WARN(retries > 10, "tx refused but queue active"))
+ if (WARN(retries > 10, "tx refused but queue active\n"))
goto drop;
goto retry;
}
@@ -1383,44 +1384,25 @@ static int ieee80211_skb_resize(struct ieee80211_local *local,
return 0;
}
-int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb)
{
- struct ieee80211_master_priv *mpriv = netdev_priv(dev);
- struct ieee80211_local *local = mpriv->local;
+ struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- struct net_device *odev = NULL;
- struct ieee80211_sub_if_data *osdata;
+ struct ieee80211_sub_if_data *tmp_sdata;
int headroom;
bool may_encrypt;
- enum {
- NOT_MONITOR,
- FOUND_SDATA,
- UNKNOWN_ADDRESS,
- } monitor_iface = NOT_MONITOR;
-
- if (skb->iif)
- odev = dev_get_by_index(&init_net, skb->iif);
- if (unlikely(odev && !is_ieee80211_device(local, odev))) {
- dev_put(odev);
- odev = NULL;
- }
- if (unlikely(!odev)) {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- printk(KERN_DEBUG "%s: Discarded packet with nonexistent "
- "originating device\n", dev->name);
-#endif
- dev_kfree_skb(skb);
- return NETDEV_TX_OK;
- }
+
+ dev_hold(sdata->dev);
if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) &&
local->hw.conf.dynamic_ps_timeout > 0 &&
- !local->sw_scanning && !local->hw_scanning && local->ps_sdata) {
+ !(local->scanning) && local->ps_sdata) {
if (local->hw.conf.flags & IEEE80211_CONF_PS) {
ieee80211_stop_queues_by_reason(&local->hw,
IEEE80211_QUEUE_STOP_REASON_PS);
- queue_work(local->hw.workqueue,
+ ieee80211_queue_work(&local->hw,
&local->dynamic_ps_disable_work);
}
@@ -1428,31 +1410,13 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
}
- memset(info, 0, sizeof(*info));
-
info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
- osdata = IEEE80211_DEV_TO_SUB_IF(odev);
-
- if (ieee80211_vif_is_mesh(&osdata->vif) &&
- ieee80211_is_data(hdr->frame_control)) {
- if (is_multicast_ether_addr(hdr->addr3))
- memcpy(hdr->addr1, hdr->addr3, ETH_ALEN);
- else
- if (mesh_nexthop_lookup(skb, osdata)) {
- dev_put(odev);
- return NETDEV_TX_OK;
- }
- if (memcmp(odev->dev_addr, hdr->addr4, ETH_ALEN) != 0)
- IEEE80211_IFSTA_MESH_CTR_INC(&osdata->u.mesh,
- fwded_frames);
- } else if (unlikely(osdata->vif.type == NL80211_IFTYPE_MONITOR)) {
- struct ieee80211_sub_if_data *sdata;
+ if (unlikely(sdata->vif.type == NL80211_IFTYPE_MONITOR)) {
int hdrlen;
u16 len_rthdr;
info->flags |= IEEE80211_TX_CTL_INJECTED;
- monitor_iface = UNKNOWN_ADDRESS;
len_rthdr = ieee80211_get_radiotap_len(skb->data);
hdr = (struct ieee80211_hdr *)(skb->data + len_rthdr);
@@ -1471,20 +1435,17 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
*/
rcu_read_lock();
- list_for_each_entry_rcu(sdata, &local->interfaces,
+ list_for_each_entry_rcu(tmp_sdata, &local->interfaces,
list) {
- if (!netif_running(sdata->dev))
+ if (!netif_running(tmp_sdata->dev))
continue;
- if (sdata->vif.type != NL80211_IFTYPE_AP)
+ if (tmp_sdata->vif.type != NL80211_IFTYPE_AP)
continue;
- if (compare_ether_addr(sdata->dev->dev_addr,
+ if (compare_ether_addr(tmp_sdata->dev->dev_addr,
hdr->addr2)) {
- dev_hold(sdata->dev);
- dev_put(odev);
- osdata = sdata;
- odev = osdata->dev;
- skb->iif = sdata->dev->ifindex;
- monitor_iface = FOUND_SDATA;
+ dev_hold(tmp_sdata->dev);
+ dev_put(sdata->dev);
+ sdata = tmp_sdata;
break;
}
}
@@ -1492,40 +1453,44 @@ int ieee80211_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
}
}
- may_encrypt = !skb->do_not_encrypt;
+ may_encrypt = !(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT);
- headroom = osdata->local->tx_headroom;
+ headroom = local->tx_headroom;
if (may_encrypt)
headroom += IEEE80211_ENCRYPT_HEADROOM;
headroom -= skb_headroom(skb);
headroom = max_t(int, 0, headroom);
- if (ieee80211_skb_resize(osdata->local, skb, headroom, may_encrypt)) {
+ if (ieee80211_skb_resize(local, skb, headroom, may_encrypt)) {
dev_kfree_skb(skb);
- dev_put(odev);
- return NETDEV_TX_OK;
+ dev_put(sdata->dev);
+ return;
}
- if (osdata->vif.type == NL80211_IFTYPE_AP_VLAN)
- osdata = container_of(osdata->bss,
- struct ieee80211_sub_if_data,
- u.ap);
- if (likely(monitor_iface != UNKNOWN_ADDRESS))
- info->control.vif = &osdata->vif;
+ info->control.vif = &sdata->vif;
- ieee80211_tx(odev, skb, false);
- dev_put(odev);
+ if (ieee80211_vif_is_mesh(&sdata->vif) &&
+ ieee80211_is_data(hdr->frame_control) &&
+ !is_multicast_ether_addr(hdr->addr1))
+ if (mesh_nexthop_lookup(skb, sdata)) {
+ /* skb queued: don't free */
+ dev_put(sdata->dev);
+ return;
+ }
- return NETDEV_TX_OK;
+ ieee80211_select_queue(local, skb);
+ ieee80211_tx(sdata, skb, false);
+ dev_put(sdata->dev);
}
-int ieee80211_monitor_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
+netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_channel *chan = local->hw.conf.channel;
struct ieee80211_radiotap_header *prthdr =
(struct ieee80211_radiotap_header *)skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
u16 len_rthdr;
/*
@@ -1563,15 +1528,6 @@ int ieee80211_monitor_start_xmit(struct sk_buff *skb,
if (unlikely(skb->len < len_rthdr))
goto fail; /* skb too short for claimed rt header extent */
- skb->dev = local->mdev;
-
- /* needed because we set skb device to master */
- skb->iif = dev->ifindex;
-
- /* sometimes we do encrypt injected frames, will be fixed
- * up in radiotap parser if not wanted */
- skb->do_not_encrypt = 0;
-
/*
* fix up the pointers accounting for the radiotap
* header still being in there. We are being given
@@ -1586,8 +1542,10 @@ int ieee80211_monitor_start_xmit(struct sk_buff *skb,
skb_set_network_header(skb, len_rthdr);
skb_set_transport_header(skb, len_rthdr);
- /* pass the radiotap header up to the next stage intact */
- dev_queue_xmit(skb);
+ memset(info, 0, sizeof(*info));
+
+ /* pass the radiotap header up to xmit */
+ ieee80211_xmit(IEEE80211_DEV_TO_SUB_IF(dev), skb);
return NETDEV_TX_OK;
fail:
@@ -1610,11 +1568,12 @@ fail:
* encapsulated packet will then be passed to master interface, wlan#.11, for
* transmission (through low-level driver).
*/
-int ieee80211_subif_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
+netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
int ret = NETDEV_TX_BUSY, head_need;
u16 ethertype, hdrlen, meshhdrlen = 0;
__le16 fc;
@@ -1627,7 +1586,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
u32 sta_flags = 0;
if (unlikely(skb->len < ETH_HLEN)) {
- ret = 0;
+ ret = NETDEV_TX_OK;
goto fail;
}
@@ -1660,52 +1619,58 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
break;
#ifdef CONFIG_MAC80211_MESH
case NL80211_IFTYPE_MESH_POINT:
- fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
if (!sdata->u.mesh.mshcfg.dot11MeshTTL) {
/* Do not send frames with mesh_ttl == 0 */
sdata->u.mesh.mshstats.dropped_frames_ttl++;
- ret = 0;
+ ret = NETDEV_TX_OK;
goto fail;
}
- memset(&mesh_hdr, 0, sizeof(mesh_hdr));
if (compare_ether_addr(dev->dev_addr,
skb->data + ETH_ALEN) == 0) {
- /* RA TA DA SA */
- memset(hdr.addr1, 0, ETH_ALEN);
- memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
- memcpy(hdr.addr3, skb->data, ETH_ALEN);
- memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
- meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr, sdata);
+ hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc,
+ skb->data, skb->data + ETH_ALEN);
+ meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr,
+ sdata, NULL, NULL, NULL);
} else {
/* packet from other interface */
struct mesh_path *mppath;
+ int is_mesh_mcast = 1;
+ char *mesh_da;
- memset(hdr.addr1, 0, ETH_ALEN);
- memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
- memcpy(hdr.addr4, dev->dev_addr, ETH_ALEN);
-
+ rcu_read_lock();
if (is_multicast_ether_addr(skb->data))
- memcpy(hdr.addr3, skb->data, ETH_ALEN);
+ /* DA TA mSA AE:SA */
+ mesh_da = skb->data;
else {
- rcu_read_lock();
mppath = mpp_path_lookup(skb->data, sdata);
- if (mppath)
- memcpy(hdr.addr3, mppath->mpp, ETH_ALEN);
- else
- memset(hdr.addr3, 0xff, ETH_ALEN);
- rcu_read_unlock();
+ if (mppath) {
+ /* RA TA mDA mSA AE:DA SA */
+ mesh_da = mppath->mpp;
+ is_mesh_mcast = 0;
+ } else
+ /* DA TA mSA AE:SA */
+ mesh_da = dev->broadcast;
}
+ hdrlen = ieee80211_fill_mesh_addresses(&hdr, &fc,
+ mesh_da, dev->dev_addr);
+ rcu_read_unlock();
+ if (is_mesh_mcast)
+ meshhdrlen =
+ ieee80211_new_mesh_header(&mesh_hdr,
+ sdata,
+ skb->data + ETH_ALEN,
+ NULL,
+ NULL);
+ else
+ meshhdrlen =
+ ieee80211_new_mesh_header(&mesh_hdr,
+ sdata,
+ NULL,
+ skb->data,
+ skb->data + ETH_ALEN);
- mesh_hdr.flags |= MESH_FLAGS_AE_A5_A6;
- mesh_hdr.ttl = sdata->u.mesh.mshcfg.dot11MeshTTL;
- put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &mesh_hdr.seqnum);
- memcpy(mesh_hdr.eaddr1, skb->data, ETH_ALEN);
- memcpy(mesh_hdr.eaddr2, skb->data + ETH_ALEN, ETH_ALEN);
- sdata->u.mesh.mesh_seqnum++;
- meshhdrlen = 18;
}
- hdrlen = 30;
break;
#endif
case NL80211_IFTYPE_STATION:
@@ -1724,7 +1689,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
hdrlen = 24;
break;
default:
- ret = 0;
+ ret = NETDEV_TX_OK;
goto fail;
}
@@ -1766,7 +1731,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
I802_DEBUG_INC(local->tx_handlers_drop_unauth_port);
- ret = 0;
+ ret = NETDEV_TX_OK;
goto fail;
}
@@ -1842,9 +1807,6 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
nh_pos += hdrlen;
h_pos += hdrlen;
- skb->iif = dev->ifindex;
-
- skb->dev = local->mdev;
dev->stats.tx_packets++;
dev->stats.tx_bytes += skb->len;
@@ -1855,13 +1817,15 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
skb_set_network_header(skb, nh_pos);
skb_set_transport_header(skb, h_pos);
+ memset(info, 0, sizeof(*info));
+
dev->trans_start = jiffies;
- dev_queue_xmit(skb);
+ ieee80211_xmit(sdata, skb);
- return 0;
+ return NETDEV_TX_OK;
fail:
- if (!ret)
+ if (ret == NETDEV_TX_OK)
dev_kfree_skb(skb);
return ret;
@@ -1887,101 +1851,74 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
struct ieee80211_hdr *hdr;
- struct net_device *dev;
int ret;
bool result = true;
- /* does interface still exist? */
- dev = dev_get_by_index(&init_net, skb->iif);
- if (!dev) {
- dev_kfree_skb(skb);
- return true;
- }
-
- /* validate info->control.vif against skb->iif */
- sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
- sdata = container_of(sdata->bss,
- struct ieee80211_sub_if_data,
- u.ap);
-
- if (unlikely(info->control.vif && info->control.vif != &sdata->vif)) {
- dev_kfree_skb(skb);
- result = true;
- goto out;
- }
+ sdata = vif_to_sdata(info->control.vif);
if (info->flags & IEEE80211_TX_INTFL_NEED_TXPROCESSING) {
- ieee80211_tx(dev, skb, true);
+ ieee80211_tx(sdata, skb, true);
} else {
hdr = (struct ieee80211_hdr *)skb->data;
sta = sta_info_get(local, hdr->addr1);
- ret = __ieee80211_tx(local, &skb, sta);
+ ret = __ieee80211_tx(local, &skb, sta, true);
if (ret != IEEE80211_TX_OK)
result = false;
}
- out:
- dev_put(dev);
-
return result;
}
/*
- * Transmit all pending packets. Called from tasklet, locks master device
- * TX lock so that no new packets can come in.
+ * Transmit all pending packets. Called from tasklet.
*/
void ieee80211_tx_pending(unsigned long data)
{
struct ieee80211_local *local = (struct ieee80211_local *)data;
- struct net_device *dev = local->mdev;
unsigned long flags;
int i;
- bool next;
+ bool txok;
rcu_read_lock();
- netif_tx_lock_bh(dev);
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
for (i = 0; i < local->hw.queues; i++) {
/*
* If queue is stopped by something other than due to pending
* frames, or we have no pending frames, proceed to next queue.
*/
- spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
- next = false;
- if (local->queue_stop_reasons[i] !=
- BIT(IEEE80211_QUEUE_STOP_REASON_PENDING) ||
+ if (local->queue_stop_reasons[i] ||
skb_queue_empty(&local->pending[i]))
- next = true;
- spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
-
- if (next)
continue;
- /*
- * start the queue now to allow processing our packets,
- * we're under the tx lock here anyway so nothing will
- * happen as a result of this
- */
- netif_start_subqueue(local->mdev, i);
-
while (!skb_queue_empty(&local->pending[i])) {
- struct sk_buff *skb = skb_dequeue(&local->pending[i]);
+ struct sk_buff *skb = __skb_dequeue(&local->pending[i]);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_sub_if_data *sdata;
- if (!ieee80211_tx_pending_skb(local, skb)) {
- skb_queue_head(&local->pending[i], skb);
- break;
+ if (WARN_ON(!info->control.vif)) {
+ kfree_skb(skb);
+ continue;
}
- }
- /* Start regular packet processing again. */
- if (skb_queue_empty(&local->pending[i]))
- ieee80211_wake_queue_by_reason(&local->hw, i,
- IEEE80211_QUEUE_STOP_REASON_PENDING);
+ sdata = vif_to_sdata(info->control.vif);
+ dev_hold(sdata->dev);
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock,
+ flags);
+
+ txok = ieee80211_tx_pending_skb(local, skb);
+ dev_put(sdata->dev);
+ if (!txok)
+ __skb_queue_head(&local->pending[i], skb);
+ spin_lock_irqsave(&local->queue_stop_reason_lock,
+ flags);
+ if (!txok)
+ break;
+ }
}
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
- netif_tx_unlock_bh(dev);
rcu_read_unlock();
}
@@ -2156,8 +2093,7 @@ struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw,
info = IEEE80211_SKB_CB(skb);
- skb->do_not_encrypt = 1;
-
+ info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
info->band = band;
/*
* XXX: For now, always use the lowest rate
@@ -2228,9 +2164,6 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
sdata = vif_to_sdata(vif);
bss = &sdata->u.ap;
- if (!bss)
- return NULL;
-
rcu_read_lock();
beacon = rcu_dereference(bss->beacon);
@@ -2256,7 +2189,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
cpu_to_le16(IEEE80211_FCTL_MOREDATA);
}
- if (!ieee80211_tx_prepare(local, &tx, skb))
+ if (!ieee80211_tx_prepare(sdata, &tx, skb))
break;
dev_kfree_skb_any(skb);
}
@@ -2276,3 +2209,24 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
return skb;
}
EXPORT_SYMBOL(ieee80211_get_buffered_bc);
+
+void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
+ int encrypt)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ skb_set_mac_header(skb, 0);
+ skb_set_network_header(skb, 0);
+ skb_set_transport_header(skb, 0);
+
+ if (!encrypt)
+ info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+
+ /*
+ * The other path calling ieee80211_xmit is from the tasklet,
+ * and while we can handle concurrent transmissions locking
+ * requirements are that we do not come into tx with bhs on.
+ */
+ local_bh_disable();
+ ieee80211_xmit(sdata, skb);
+ local_bh_enable();
+}
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 915e77769312..dd6564321369 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -31,6 +31,7 @@
#include "mesh.h"
#include "wme.h"
#include "led.h"
+#include "wep.h"
/* privid for wiphys to determine whether they belong to us or not */
void *mac80211_wiphy_privid = &mac80211_wiphy_privid;
@@ -274,16 +275,12 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue,
__clear_bit(reason, &local->queue_stop_reasons[queue]);
- if (!skb_queue_empty(&local->pending[queue]) &&
- local->queue_stop_reasons[queue] ==
- BIT(IEEE80211_QUEUE_STOP_REASON_PENDING))
- tasklet_schedule(&local->tx_pending_tasklet);
-
if (local->queue_stop_reasons[queue] != 0)
/* someone still has this queue stopped */
return;
- netif_wake_subqueue(local->mdev, queue);
+ if (!skb_queue_empty(&local->pending[queue]))
+ tasklet_schedule(&local->tx_pending_tasklet);
}
void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue,
@@ -312,14 +309,6 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue,
if (WARN_ON(queue >= hw->queues))
return;
- /*
- * Only stop if it was previously running, this is necessary
- * for correct pending packets handling because there we may
- * start (but not wake) the queue and rely on that.
- */
- if (!local->queue_stop_reasons[queue])
- netif_stop_subqueue(local->mdev, queue);
-
__set_bit(reason, &local->queue_stop_reasons[queue]);
}
@@ -347,11 +336,16 @@ void ieee80211_add_pending_skb(struct ieee80211_local *local,
struct ieee80211_hw *hw = &local->hw;
unsigned long flags;
int queue = skb_get_queue_mapping(skb);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ if (WARN_ON(!info->control.vif)) {
+ kfree(skb);
+ return;
+ }
spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
__ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
- __ieee80211_stop_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_PENDING);
- skb_queue_tail(&local->pending[queue], skb);
+ __skb_queue_tail(&local->pending[queue], skb);
__ieee80211_wake_queue(hw, queue, IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
}
@@ -370,18 +364,21 @@ int ieee80211_add_pending_skbs(struct ieee80211_local *local,
IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
while ((skb = skb_dequeue(skbs))) {
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ if (WARN_ON(!info->control.vif)) {
+ kfree(skb);
+ continue;
+ }
+
ret++;
queue = skb_get_queue_mapping(skb);
- skb_queue_tail(&local->pending[queue], skb);
+ __skb_queue_tail(&local->pending[queue], skb);
}
- for (i = 0; i < hw->queues; i++) {
- if (ret)
- __ieee80211_stop_queue(hw, i,
- IEEE80211_QUEUE_STOP_REASON_PENDING);
+ for (i = 0; i < hw->queues; i++)
__ieee80211_wake_queue(hw, i,
IEEE80211_QUEUE_STOP_REASON_SKB_ADD);
- }
spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
return ret;
@@ -412,11 +409,16 @@ EXPORT_SYMBOL(ieee80211_stop_queues);
int ieee80211_queue_stopped(struct ieee80211_hw *hw, int queue)
{
struct ieee80211_local *local = hw_to_local(hw);
+ unsigned long flags;
+ int ret;
if (WARN_ON(queue >= hw->queues))
return true;
- return __netif_subqueue_stopped(local->mdev, queue);
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
+ ret = !!local->queue_stop_reasons[queue];
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
+ return ret;
}
EXPORT_SYMBOL(ieee80211_queue_stopped);
@@ -509,6 +511,46 @@ void ieee80211_iterate_active_interfaces_atomic(
}
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_atomic);
+/*
+ * Nothing should have been stuffed into the workqueue during
+ * the suspend->resume cycle. If this WARN is seen then there
+ * is a bug with either the driver suspend or something in
+ * mac80211 stuffing into the workqueue which we haven't yet
+ * cleared during mac80211's suspend cycle.
+ */
+static bool ieee80211_can_queue_work(struct ieee80211_local *local)
+{
+ if (WARN(local->suspended, "queueing ieee80211 work while "
+ "going to suspend\n"))
+ return false;
+
+ return true;
+}
+
+void ieee80211_queue_work(struct ieee80211_hw *hw, struct work_struct *work)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+
+ if (!ieee80211_can_queue_work(local))
+ return;
+
+ queue_work(local->workqueue, work);
+}
+EXPORT_SYMBOL(ieee80211_queue_work);
+
+void ieee80211_queue_delayed_work(struct ieee80211_hw *hw,
+ struct delayed_work *dwork,
+ unsigned long delay)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+
+ if (!ieee80211_can_queue_work(local))
+ return;
+
+ queue_delayed_work(local->workqueue, dwork, delay);
+}
+EXPORT_SYMBOL(ieee80211_queue_delayed_work);
+
void ieee802_11_parse_elems(u8 *start, size_t len,
struct ieee802_11_elems *elems)
{
@@ -760,20 +802,6 @@ void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
ieee80211_set_wmm_default(sdata);
}
-void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
- int encrypt)
-{
- skb->dev = sdata->local->mdev;
- skb_set_mac_header(skb, 0);
- skb_set_network_header(skb, 0);
- skb_set_transport_header(skb, 0);
-
- skb->iif = sdata->dev->ifindex;
- skb->do_not_encrypt = !encrypt;
-
- dev_queue_xmit(skb);
-}
-
u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
enum ieee80211_band band)
{
@@ -804,12 +832,13 @@ u32 ieee80211_mandatory_rates(struct ieee80211_local *local,
void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
u16 transaction, u16 auth_alg,
- u8 *extra, size_t extra_len,
- const u8 *bssid, int encrypt)
+ u8 *extra, size_t extra_len, const u8 *bssid,
+ const u8 *key, u8 key_len, u8 key_idx)
{
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
+ int err;
skb = dev_alloc_skb(local->hw.extra_tx_headroom +
sizeof(*mgmt) + 6 + extra_len);
@@ -824,8 +853,6 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
memset(mgmt, 0, 24 + 6);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_AUTH);
- if (encrypt)
- mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
memcpy(mgmt->da, bssid, ETH_ALEN);
memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
memcpy(mgmt->bssid, bssid, ETH_ALEN);
@@ -835,7 +862,13 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
if (extra)
memcpy(skb_put(skb, extra_len), extra, extra_len);
- ieee80211_tx_skb(sdata, skb, encrypt);
+ if (auth_alg == WLAN_AUTH_SHARED_KEY && transaction == 3) {
+ mgmt->frame_control |= cpu_to_le16(IEEE80211_FCTL_PROTECTED);
+ err = ieee80211_wep_encrypt(local, skb, key, key_len, key_idx);
+ WARN_ON(err);
+ }
+
+ ieee80211_tx_skb(sdata, skb, 0);
}
int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
@@ -974,6 +1007,16 @@ u32 ieee80211_sta_get_rates(struct ieee80211_local *local,
return supp_rates;
}
+void ieee80211_stop_device(struct ieee80211_local *local)
+{
+ ieee80211_led_radio(local, false);
+
+ cancel_work_sync(&local->reconfig_filter);
+ drv_stop(local);
+
+ flush_workqueue(local->workqueue);
+}
+
int ieee80211_reconfig(struct ieee80211_local *local)
{
struct ieee80211_hw *hw = &local->hw;
@@ -1043,9 +1086,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
/* reconfigure hardware */
ieee80211_hw_config(local, ~0);
- netif_addr_lock_bh(local->mdev);
ieee80211_configure_filter(local);