summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/6lowpan/iphc.c103
-rw-r--r--net/8021q/vlan_dev.c28
-rw-r--r--net/Kconfig1
-rw-r--r--net/Makefile3
-rw-r--r--net/appletalk/ddp.c4
-rw-r--r--net/atm/common.c16
-rw-r--r--net/ax25/af_ax25.c4
-rw-r--r--net/bluetooth/6lowpan.c307
-rw-r--r--net/bluetooth/Kconfig21
-rw-r--r--net/bluetooth/Makefile2
-rw-r--r--net/bluetooth/a2mp.c4
-rw-r--r--net/bluetooth/af_bluetooth.c6
-rw-r--r--net/bluetooth/amp.c25
-rw-r--r--net/bluetooth/bnep/Kconfig2
-rw-r--r--net/bluetooth/cmtp/Kconfig2
-rw-r--r--net/bluetooth/ecc.c816
-rw-r--r--net/bluetooth/ecc.h54
-rw-r--r--net/bluetooth/hci_conn.c15
-rw-r--r--net/bluetooth/hci_core.c415
-rw-r--r--net/bluetooth/hci_event.c296
-rw-r--r--net/bluetooth/hci_sock.c6
-rw-r--r--net/bluetooth/hidp/Kconfig2
-rw-r--r--net/bluetooth/hidp/core.c10
-rw-r--r--net/bluetooth/l2cap_core.c104
-rw-r--r--net/bluetooth/l2cap_sock.c37
-rw-r--r--net/bluetooth/mgmt.c1002
-rw-r--r--net/bluetooth/rfcomm/Kconfig2
-rw-r--r--net/bluetooth/rfcomm/core.c30
-rw-r--r--net/bluetooth/rfcomm/sock.c2
-rw-r--r--net/bluetooth/sco.c2
-rw-r--r--net/bluetooth/smp.c1631
-rw-r--r--net/bluetooth/smp.h60
-rw-r--r--net/bridge/br_fdb.c144
-rw-r--r--net/bridge/br_forward.c5
-rw-r--r--net/bridge/br_input.c60
-rw-r--r--net/bridge/br_multicast.c3
-rw-r--r--net/bridge/br_netfilter.c1
-rw-r--r--net/bridge/br_netlink.c5
-rw-r--r--net/bridge/br_private.h20
-rw-r--r--net/bridge/br_stp.c7
-rw-r--r--net/bridge/br_sysfs_if.c2
-rw-r--r--net/bridge/br_vlan.c4
-rw-r--r--net/bridge/netfilter/nf_tables_bridge.c88
-rw-r--r--net/bridge/netfilter/nft_reject_bridge.c53
-rw-r--r--net/caif/caif_socket.c10
-rw-r--r--net/can/af_can.c7
-rw-r--r--net/can/bcm.c37
-rw-r--r--net/can/gw.c2
-rw-r--r--net/can/raw.c8
-rw-r--r--net/ceph/crypto.c169
-rw-r--r--net/ceph/osd_client.c7
-rw-r--r--net/compat.c87
-rw-r--r--net/core/datagram.c301
-rw-r--r--net/core/dev.c182
-rw-r--r--net/core/dev_addr_lists.c4
-rw-r--r--net/core/dev_ioctl.c9
-rw-r--r--net/core/dst.c24
-rw-r--r--net/core/ethtool.c81
-rw-r--r--net/core/filter.c97
-rw-r--r--net/core/iovec.c47
-rw-r--r--net/core/link_watch.c2
-rw-r--r--net/core/neighbour.c279
-rw-r--r--net/core/net-sysfs.c44
-rw-r--r--net/core/netpoll.c4
-rw-r--r--net/core/pktgen.c3
-rw-r--r--net/core/rtnetlink.c188
-rw-r--r--net/core/scm.c3
-rw-r--r--net/core/skbuff.c356
-rw-r--r--net/core/sock.c20
-rw-r--r--net/core/sysctl_net_core.c21
-rw-r--r--net/core/utils.c3
-rw-r--r--net/dcb/dcbnl.c36
-rw-r--r--net/dccp/ackvec.c2
-rw-r--r--net/dccp/dccp.h4
-rw-r--r--net/dccp/feat.c4
-rw-r--r--net/dccp/input.c2
-rw-r--r--net/dccp/ipv4.c6
-rw-r--r--net/dccp/proto.c9
-rw-r--r--net/decnet/af_decnet.c4
-rw-r--r--net/decnet/dn_neigh.c4
-rw-r--r--net/dsa/Kconfig11
-rw-r--r--net/dsa/dsa.c170
-rw-r--r--net/dsa/slave.c97
-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/ieee802154/6lowpan_rtnl.c149
-rw-r--r--net/ieee802154/Makefile4
-rw-r--r--net/ieee802154/af802154.h4
-rw-r--r--net/ieee802154/af_ieee802154.c8
-rw-r--r--net/ieee802154/core.c321
-rw-r--r--net/ieee802154/core.h46
-rw-r--r--net/ieee802154/dgram.c11
-rw-r--r--net/ieee802154/header_ops.c3
-rw-r--r--net/ieee802154/ieee802154.h6
-rw-r--r--net/ieee802154/netlink.c11
-rw-r--r--net/ieee802154/nl-mac.c269
-rw-r--r--net/ieee802154/nl-phy.c32
-rw-r--r--net/ieee802154/nl802154.c957
-rw-r--r--net/ieee802154/nl802154.h7
-rw-r--r--net/ieee802154/nl_policy.c4
-rw-r--r--net/ieee802154/raw.c9
-rw-r--r--net/ieee802154/rdev-ops.h89
-rw-r--r--net/ieee802154/reassembly.c8
-rw-r--r--net/ieee802154/reassembly.h4
-rw-r--r--net/ieee802154/sysfs.c128
-rw-r--r--net/ieee802154/sysfs.h9
-rw-r--r--net/ieee802154/wpan-class.c230
-rw-r--r--net/ipv4/Kconfig9
-rw-r--r--net/ipv4/af_inet.c13
-rw-r--r--net/ipv4/arp.c2
-rw-r--r--net/ipv4/cipso_ipv4.c8
-rw-r--r--net/ipv4/esp4.c14
-rw-r--r--net/ipv4/fib_rules.c4
-rw-r--r--net/ipv4/fib_trie.c3
-rw-r--r--net/ipv4/fou.c425
-rw-r--r--net/ipv4/geneve.c17
-rw-r--r--net/ipv4/gre_offload.c3
-rw-r--r--net/ipv4/icmp.c51
-rw-r--r--net/ipv4/igmp.c45
-rw-r--r--net/ipv4/inet_fragment.c2
-rw-r--r--net/ipv4/ip_fragment.c19
-rw-r--r--net/ipv4/ip_gre.c2
-rw-r--r--net/ipv4/ip_output.c16
-rw-r--r--net/ipv4/ip_sockglue.c30
-rw-r--r--net/ipv4/ip_tunnel.c115
-rw-r--r--net/ipv4/ip_vti.c1
-rw-r--r--net/ipv4/ipconfig.c19
-rw-r--r--net/ipv4/ipip.c2
-rw-r--r--net/ipv4/netfilter/Kconfig9
-rw-r--r--net/ipv4/netfilter/Makefile1
-rw-r--r--net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c6
-rw-r--r--net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c53
-rw-r--r--net/ipv4/netfilter/nf_conntrack_proto_icmp.c10
-rw-r--r--net/ipv4/netfilter/nf_log_arp.c24
-rw-r--r--net/ipv4/netfilter/nf_log_ipv4.c12
-rw-r--r--net/ipv4/netfilter/nf_reject_ipv4.c1
-rw-r--r--net/ipv4/netfilter/nft_masq_ipv4.c1
-rw-r--r--net/ipv4/netfilter/nft_redir_ipv4.c77
-rw-r--r--net/ipv4/netfilter/nft_reject_ipv4.c7
-rw-r--r--net/ipv4/ping.c27
-rw-r--r--net/ipv4/proc.c11
-rw-r--r--net/ipv4/raw.c109
-rw-r--r--net/ipv4/syncookies.c86
-rw-r--r--net/ipv4/sysctl_net_ipv4.c7
-rw-r--r--net/ipv4/tcp.c77
-rw-r--r--net/ipv4/tcp_cong.c2
-rw-r--r--net/ipv4/tcp_cubic.c31
-rw-r--r--net/ipv4/tcp_input.c113
-rw-r--r--net/ipv4/tcp_ipv4.c13
-rw-r--r--net/ipv4/tcp_memcontrol.c87
-rw-r--r--net/ipv4/tcp_offload.c2
-rw-r--r--net/ipv4/tcp_output.c152
-rw-r--r--net/ipv4/tcp_timer.c18
-rw-r--r--net/ipv4/udp.c210
-rw-r--r--net/ipv4/udp_offload.c69
-rw-r--r--net/ipv6/addrconf.c74
-rw-r--r--net/ipv6/ah6.c14
-rw-r--r--net/ipv6/datagram.c31
-rw-r--r--net/ipv6/esp6.c17
-rw-r--r--net/ipv6/exthdrs.c20
-rw-r--r--net/ipv6/icmp.c31
-rw-r--r--net/ipv6/ip6_flowlabel.c13
-rw-r--r--net/ipv6/ip6_gre.c11
-rw-r--r--net/ipv6/ip6_input.c3
-rw-r--r--net/ipv6/ip6_offload.c5
-rw-r--r--net/ipv6/ip6_output.c22
-rw-r--r--net/ipv6/ip6_tunnel.c119
-rw-r--r--net/ipv6/ip6_udp_tunnel.c4
-rw-r--r--net/ipv6/ip6_vti.c53
-rw-r--r--net/ipv6/ip6mr.c6
-rw-r--r--net/ipv6/ipv6_sockglue.c6
-rw-r--r--net/ipv6/mcast.c15
-rw-r--r--net/ipv6/mip6.c11
-rw-r--r--net/ipv6/ndisc.c11
-rw-r--r--net/ipv6/netfilter.c2
-rw-r--r--net/ipv6/netfilter/Kconfig9
-rw-r--r--net/ipv6/netfilter/Makefile1
-rw-r--r--net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c6
-rw-r--r--net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c10
-rw-r--r--net/ipv6/netfilter/nf_log_ipv6.c14
-rw-r--r--net/ipv6/netfilter/nf_reject_ipv6.c1
-rw-r--r--net/ipv6/netfilter/nft_masq_ipv6.c1
-rw-r--r--net/ipv6/netfilter/nft_redir_ipv6.c77
-rw-r--r--net/ipv6/netfilter/nft_reject_ipv6.c7
-rw-r--r--net/ipv6/ping.c3
-rw-r--r--net/ipv6/proc.c1
-rw-r--r--net/ipv6/raw.c124
-rw-r--r--net/ipv6/reassembly.c15
-rw-r--r--net/ipv6/route.c94
-rw-r--r--net/ipv6/sit.c24
-rw-r--r--net/ipv6/syncookies.c13
-rw-r--r--net/ipv6/tcp_ipv6.c37
-rw-r--r--net/ipv6/udp.c170
-rw-r--r--net/ipv6/udp_offload.c4
-rw-r--r--net/ipx/af_ipx.c14
-rw-r--r--net/ipx/ipx_proc.c18
-rw-r--r--net/ipx/ipx_route.c4
-rw-r--r--net/ipx/sysctl_net_ipx.c4
-rw-r--r--net/irda/af_irda.c253
-rw-r--r--net/irda/discovery.c12
-rw-r--r--net/irda/ircomm/ircomm_core.c55
-rw-r--r--net/irda/ircomm/ircomm_event.c28
-rw-r--r--net/irda/ircomm/ircomm_lmp.c30
-rw-r--r--net/irda/ircomm/ircomm_param.c52
-rw-r--r--net/irda/ircomm/ircomm_ttp.c30
-rw-r--r--net/irda/ircomm/ircomm_tty.c116
-rw-r--r--net/irda/ircomm/ircomm_tty_attach.c122
-rw-r--r--net/irda/ircomm/ircomm_tty_ioctl.c21
-rw-r--r--net/irda/irda_device.c34
-rw-r--r--net/irda/iriap.c119
-rw-r--r--net/irda/iriap_event.c42
-rw-r--r--net/irda/irias_object.c78
-rw-r--r--net/irda/irlan/irlan_client.c86
-rw-r--r--net/irda/irlan/irlan_client_event.c66
-rw-r--r--net/irda/irlan/irlan_common.c80
-rw-r--r--net/irda/irlan/irlan_eth.c26
-rw-r--r--net/irda/irlan/irlan_event.c4
-rw-r--r--net/irda/irlan/irlan_filter.c23
-rw-r--r--net/irda/irlan/irlan_provider.c42
-rw-r--r--net/irda/irlan/irlan_provider_event.c16
-rw-r--r--net/irda/irlap.c74
-rw-r--r--net/irda/irlap_event.c234
-rw-r--r--net/irda/irlap_frame.c117
-rw-r--r--net/irda/irlmp.c186
-rw-r--r--net/irda/irlmp_event.c133
-rw-r--r--net/irda/irlmp_frame.c56
-rw-r--r--net/irda/irmod.c12
-rw-r--r--net/irda/irnetlink.c4
-rw-r--r--net/irda/irqueue.c14
-rw-r--r--net/irda/irsysctl.c9
-rw-r--r--net/irda/irttp.c164
-rw-r--r--net/irda/parameters.c85
-rw-r--r--net/irda/qos.c89
-rw-r--r--net/irda/wrapper.c28
-rw-r--r--net/iucv/af_iucv.c8
-rw-r--r--net/key/af_key.c4
-rw-r--r--net/l2tp/l2tp_eth.c1
-rw-r--r--net/l2tp/l2tp_ip.c4
-rw-r--r--net/l2tp/l2tp_ip6.c4
-rw-r--r--net/l2tp/l2tp_ppp.c5
-rw-r--r--net/lapb/lapb_iface.c17
-rw-r--r--net/llc/af_llc.c5
-rw-r--r--net/llc/llc_c_st.c708
-rw-r--r--net/llc/llc_conn.c4
-rw-r--r--net/llc/llc_if.c2
-rw-r--r--net/llc/llc_s_st.c20
-rw-r--r--net/llc/llc_sap.c2
-rw-r--r--net/mac80211/Kconfig18
-rw-r--r--net/mac80211/Makefile3
-rw-r--r--net/mac80211/aes_ccm.c3
-rw-r--r--net/mac80211/agg-tx.c12
-rw-r--r--net/mac80211/cfg.c193
-rw-r--r--net/mac80211/chan.c28
-rw-r--r--net/mac80211/debug.h10
-rw-r--r--net/mac80211/debugfs_key.c12
-rw-r--r--net/mac80211/debugfs_sta.c11
-rw-r--r--net/mac80211/driver-ops.h157
-rw-r--r--net/mac80211/ibss.c2
-rw-r--r--net/mac80211/ieee80211_i.h125
-rw-r--r--net/mac80211/iface.c51
-rw-r--r--net/mac80211/key.c11
-rw-r--r--net/mac80211/main.c44
-rw-r--r--net/mac80211/mesh.c2
-rw-r--r--net/mac80211/mesh.h3
-rw-r--r--net/mac80211/mesh_pathtbl.c31
-rw-r--r--net/mac80211/mlme.c338
-rw-r--r--net/mac80211/ocb.c250
-rw-r--r--net/mac80211/rate.c8
-rw-r--r--net/mac80211/rate.h24
-rw-r--r--net/mac80211/rc80211_minstrel.c7
-rw-r--r--net/mac80211/rc80211_minstrel_ht.c349
-rw-r--r--net/mac80211/rc80211_minstrel_ht.h40
-rw-r--r--net/mac80211/rc80211_minstrel_ht_debugfs.c43
-rw-r--r--net/mac80211/rx.c195
-rw-r--r--net/mac80211/scan.c129
-rw-r--r--net/mac80211/spectmgmt.c18
-rw-r--r--net/mac80211/sta_info.c19
-rw-r--r--net/mac80211/sta_info.h11
-rw-r--r--net/mac80211/status.c171
-rw-r--r--net/mac80211/tdls.c800
-rw-r--r--net/mac80211/trace.h248
-rw-r--r--net/mac80211/tx.c316
-rw-r--r--net/mac80211/util.c203
-rw-r--r--net/mac80211/vht.c2
-rw-r--r--net/mac80211/wep.c2
-rw-r--r--net/mac80211/wme.c72
-rw-r--r--net/mac80211/wme.h2
-rw-r--r--net/mac80211/wpa.c5
-rw-r--r--net/mac802154/Kconfig2
-rw-r--r--net/mac802154/Makefile4
-rw-r--r--net/mac802154/cfg.c210
-rw-r--r--net/mac802154/cfg.h9
-rw-r--r--net/mac802154/driver-ops.h222
-rw-r--r--net/mac802154/ieee802154_dev.c415
-rw-r--r--net/mac802154/ieee802154_i.h (renamed from net/mac802154/mac802154.h)132
-rw-r--r--net/mac802154/iface.c586
-rw-r--r--net/mac802154/llsec.c23
-rw-r--r--net/mac802154/mac_cmd.c88
-rw-r--r--net/mac802154/main.c217
-rw-r--r--net/mac802154/mib.c271
-rw-r--r--net/mac802154/monitor.c117
-rw-r--r--net/mac802154/rx.c310
-rw-r--r--net/mac802154/tx.c154
-rw-r--r--net/mac802154/util.c84
-rw-r--r--net/mac802154/wpan.c599
-rw-r--r--net/mpls/mpls_gso.c3
-rw-r--r--net/netfilter/Kconfig17
-rw-r--r--net/netfilter/Makefile2
-rw-r--r--net/netfilter/core.c1
-rw-r--r--net/netfilter/ipset/ip_set_core.c6
-rw-r--r--net/netfilter/ipset/ip_set_hash_gen.h101
-rw-r--r--net/netfilter/ipset/ip_set_hash_netiface.c1
-rw-r--r--net/netfilter/ipset/ip_set_hash_netnet.c2
-rw-r--r--net/netfilter/ipset/ip_set_hash_netportnet.c2
-rw-r--r--net/netfilter/ipvs/ip_vs_ctl.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_pe.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_sched.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_sync.c3
-rw-r--r--net/netfilter/ipvs/ip_vs_xmit.c7
-rw-r--r--net/netfilter/nf_conntrack_core.c11
-rw-r--r--net/netfilter/nf_conntrack_h323_main.c14
-rw-r--r--net/netfilter/nf_conntrack_helper.c2
-rw-r--r--net/netfilter/nf_conntrack_l3proto_generic.c5
-rw-r--r--net/netfilter/nf_conntrack_proto_dccp.c14
-rw-r--r--net/netfilter/nf_conntrack_proto_generic.c5
-rw-r--r--net/netfilter/nf_conntrack_proto_gre.c18
-rw-r--r--net/netfilter/nf_conntrack_proto_sctp.c14
-rw-r--r--net/netfilter/nf_conntrack_proto_tcp.c14
-rw-r--r--net/netfilter/nf_conntrack_proto_udp.c10
-rw-r--r--net/netfilter/nf_conntrack_proto_udplite.c10
-rw-r--r--net/netfilter/nf_conntrack_standalone.c77
-rw-r--r--net/netfilter/nf_log.c70
-rw-r--r--net/netfilter/nf_nat_redirect.c127
-rw-r--r--net/netfilter/nf_tables_api.c29
-rw-r--r--net/netfilter/nfnetlink.c12
-rw-r--r--net/netfilter/nfnetlink_log.c18
-rw-r--r--net/netfilter/nfnetlink_queue_core.c13
-rw-r--r--net/netfilter/nft_compat.c40
-rw-r--r--net/netfilter/nft_hash.c10
-rw-r--r--net/netfilter/nft_meta.c7
-rw-r--r--net/netfilter/nft_redir.c99
-rw-r--r--net/netfilter/x_tables.c19
-rw-r--r--net/netfilter/xt_DSCP.c6
-rw-r--r--net/netfilter/xt_REDIRECT.c83
-rw-r--r--net/netfilter/xt_connlimit.c25
-rw-r--r--net/netfilter/xt_hashlimit.c36
-rw-r--r--net/netfilter/xt_recent.c64
-rw-r--r--net/netfilter/xt_set.c73
-rw-r--r--net/netlink/af_netlink.c28
-rw-r--r--net/netrom/af_netrom.c4
-rw-r--r--net/nfc/digital_dep.c834
-rw-r--r--net/nfc/hci/command.c3
-rw-r--r--net/nfc/hci/core.c56
-rw-r--r--net/nfc/llcp_commands.c10
-rw-r--r--net/nfc/llcp_core.c5
-rw-r--r--net/nfc/llcp_sock.c8
-rw-r--r--net/nfc/nci/core.c150
-rw-r--r--net/nfc/nci/data.c24
-rw-r--r--net/nfc/nci/ntf.c152
-rw-r--r--net/nfc/netlink.c77
-rw-r--r--net/nfc/rawsock.c4
-rw-r--r--net/openvswitch/Kconfig23
-rw-r--r--net/openvswitch/Makefile14
-rw-r--r--net/openvswitch/actions.c365
-rw-r--r--net/openvswitch/datapath.c360
-rw-r--r--net/openvswitch/datapath.h24
-rw-r--r--net/openvswitch/flow.c38
-rw-r--r--net/openvswitch/flow.h88
-rw-r--r--net/openvswitch/flow_netlink.c636
-rw-r--r--net/openvswitch/flow_netlink.h18
-rw-r--r--net/openvswitch/flow_table.c27
-rw-r--r--net/openvswitch/flow_table.h10
-rw-r--r--net/openvswitch/vport-geneve.c46
-rw-r--r--net/openvswitch/vport-gre.c47
-rw-r--r--net/openvswitch/vport-internal_dev.c22
-rw-r--r--net/openvswitch/vport-netdev.c16
-rw-r--r--net/openvswitch/vport-netdev.h3
-rw-r--r--net/openvswitch/vport-vxlan.c47
-rw-r--r--net/openvswitch/vport.c171
-rw-r--r--net/openvswitch/vport.h34
-rw-r--r--net/packet/af_packet.c82
-rw-r--r--net/phonet/af_phonet.c9
-rw-r--r--net/phonet/datagram.c4
-rw-r--r--net/phonet/pep-gprs.c3
-rw-r--r--net/phonet/pep.c16
-rw-r--r--net/rds/ib.h3
-rw-r--r--net/rds/ib_recv.c37
-rw-r--r--net/rds/iw.h3
-rw-r--r--net/rds/iw_recv.c37
-rw-r--r--net/rds/message.c77
-rw-r--r--net/rds/rds.h9
-rw-r--r--net/rds/recv.c6
-rw-r--r--net/rds/send.c6
-rw-r--r--net/rds/tcp.h3
-rw-r--r--net/rds/tcp_recv.c38
-rw-r--r--net/rfkill/rfkill-gpio.c18
-rw-r--r--net/rose/af_rose.c4
-rw-r--r--net/rxrpc/ar-output.c10
-rw-r--r--net/rxrpc/ar-recvmsg.c2
-rw-r--r--net/sched/Kconfig18
-rw-r--r--net/sched/Makefile1
-rw-r--r--net/sched/act_gact.c2
-rw-r--r--net/sched/act_ipt.c2
-rw-r--r--net/sched/act_mirred.c2
-rw-r--r--net/sched/act_pedit.c2
-rw-r--r--net/sched/act_police.c2
-rw-r--r--net/sched/act_simple.c2
-rw-r--r--net/sched/act_vlan.c207
-rw-r--r--net/sched/cls_basic.c26
-rw-r--r--net/sched/cls_bpf.c26
-rw-r--r--net/sched/cls_cgroup.c14
-rw-r--r--net/sched/cls_flow.c14
-rw-r--r--net/sched/cls_fw.c8
-rw-r--r--net/sched/cls_route.c8
-rw-r--r--net/sched/cls_rsvp.h8
-rw-r--r--net/sched/cls_tcindex.c15
-rw-r--r--net/sched/cls_u32.c5
-rw-r--r--net/sched/sch_fq.c9
-rw-r--r--net/sched/sch_fq_codel.c2
-rw-r--r--net/sched/sch_netem.c27
-rw-r--r--net/sctp/auth.c2
-rw-r--r--net/sctp/chunk.c9
-rw-r--r--net/sctp/output.c4
-rw-r--r--net/sctp/proc.c12
-rw-r--r--net/sctp/sm_make_chunk.c23
-rw-r--r--net/sctp/socket.c19
-rw-r--r--net/sctp/ulpqueue.c5
-rw-r--r--net/socket.c181
-rw-r--r--net/sunrpc/Kconfig1
-rw-r--r--net/sunrpc/Makefile1
-rw-r--r--net/sunrpc/auth.c4
-rw-r--r--net/sunrpc/auth_generic.c2
-rw-r--r--net/sunrpc/auth_gss/auth_gss.c37
-rw-r--r--net/sunrpc/auth_gss/gss_generic_token.c2
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_crypto.c2
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_keys.c2
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_mech.c2
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_seal.c2
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_seqnum.c2
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_unseal.c2
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_wrap.c2
-rw-r--r--net/sunrpc/auth_gss/gss_mech_switch.c2
-rw-r--r--net/sunrpc/auth_gss/gss_rpc_xdr.h2
-rw-r--r--net/sunrpc/auth_gss/svcauth_gss.c2
-rw-r--r--net/sunrpc/auth_null.c4
-rw-r--r--net/sunrpc/auth_unix.c2
-rw-r--r--net/sunrpc/backchannel_rqst.c2
-rw-r--r--net/sunrpc/clnt.c16
-rw-r--r--net/sunrpc/debugfs.c292
-rw-r--r--net/sunrpc/rpcb_clnt.c2
-rw-r--r--net/sunrpc/sched.c4
-rw-r--r--net/sunrpc/stats.c21
-rw-r--r--net/sunrpc/sunrpc_syms.c12
-rw-r--r--net/sunrpc/svc.c23
-rw-r--r--net/sunrpc/svc_xprt.c31
-rw-r--r--net/sunrpc/svcsock.c27
-rw-r--r--net/sunrpc/sysctl.c2
-rw-r--r--net/sunrpc/xprt.c19
-rw-r--r--net/sunrpc/xprtrdma/rpc_rdma.c4
-rw-r--r--net/sunrpc/xprtrdma/transport.c12
-rw-r--r--net/sunrpc/xprtrdma/verbs.c122
-rw-r--r--net/sunrpc/xprtrdma/xprt_rdma.h6
-rw-r--r--net/sunrpc/xprtsock.c77
-rw-r--r--net/switchdev/Kconfig13
-rw-r--r--net/switchdev/Makefile5
-rw-r--r--net/switchdev/switchdev.c52
-rw-r--r--net/tipc/Makefile4
-rw-r--r--net/tipc/bcast.c230
-rw-r--r--net/tipc/bcast.h6
-rw-r--r--net/tipc/bearer.c447
-rw-r--r--net/tipc/bearer.h16
-rw-r--r--net/tipc/core.h2
-rw-r--r--net/tipc/link.c981
-rw-r--r--net/tipc/link.h55
-rw-r--r--net/tipc/msg.c133
-rw-r--r--net/tipc/msg.h16
-rw-r--r--net/tipc/name_distr.c181
-rw-r--r--net/tipc/name_distr.h1
-rw-r--r--net/tipc/name_table.c373
-rw-r--r--net/tipc/name_table.h30
-rw-r--r--net/tipc/net.c106
-rw-r--r--net/tipc/net.h8
-rw-r--r--net/tipc/netlink.c133
-rw-r--r--net/tipc/netlink.h (renamed from net/tipc/node_subscr.h)35
-rw-r--r--net/tipc/node.c108
-rw-r--r--net/tipc/node.h16
-rw-r--r--net/tipc/node_subscr.c96
-rw-r--r--net/tipc/socket.c419
-rw-r--r--net/tipc/socket.h3
-rw-r--r--net/tipc/subscr.c1
-rw-r--r--net/unix/af_unix.c11
-rw-r--r--net/vmw_vsock/af_vsock.c6
-rw-r--r--net/vmw_vsock/vmci_transport.c18
-rw-r--r--net/wireless/Kconfig2
-rw-r--r--net/wireless/Makefile2
-rw-r--r--net/wireless/chan.c10
-rw-r--r--net/wireless/core.c95
-rw-r--r--net/wireless/core.h13
-rw-r--r--net/wireless/nl80211.c962
-rw-r--r--net/wireless/ocb.c88
-rw-r--r--net/wireless/rdev-ops.h79
-rw-r--r--net/wireless/reg.c156
-rw-r--r--net/wireless/sme.c13
-rw-r--r--net/wireless/trace.h155
-rw-r--r--net/wireless/util.c5
-rw-r--r--net/x25/af_x25.c4
-rw-r--r--net/xfrm/xfrm_policy.c52
-rw-r--r--net/xfrm/xfrm_user.c12
509 files changed, 24417 insertions, 10858 deletions
diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c
index 142eef55c9e2..32ffec6ef164 100644
--- a/net/6lowpan/iphc.c
+++ b/net/6lowpan/iphc.c
@@ -15,9 +15,6 @@
* 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.
*/
/* Jon's code is based on 6lowpan implementation for Contiki which is:
@@ -171,37 +168,6 @@ static int uncompress_context_based_src_addr(struct sk_buff *skb,
return 0;
}
-static int skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr,
- struct net_device *dev, skb_delivery_cb deliver_skb)
-{
- struct sk_buff *new;
- int stat;
-
- new = skb_copy_expand(skb, sizeof(struct ipv6hdr), skb_tailroom(skb),
- GFP_ATOMIC);
- kfree_skb(skb);
-
- if (!new)
- return -ENOMEM;
-
- skb_push(new, sizeof(struct ipv6hdr));
- skb_reset_network_header(new);
- skb_copy_to_linear_data(new, hdr, sizeof(struct ipv6hdr));
-
- new->protocol = htons(ETH_P_IPV6);
- new->pkt_type = PACKET_HOST;
- new->dev = dev;
-
- raw_dump_table(__func__, "raw skb data dump before receiving",
- new->data, new->len);
-
- stat = deliver_skb(new, dev);
-
- kfree_skb(new);
-
- return stat;
-}
-
/* Uncompress function for multicast destination address,
* when M bit is set.
*/
@@ -332,10 +298,12 @@ err:
/* TTL uncompression values */
static const u8 lowpan_ttl_values[] = { 0, 1, 64, 255 };
-int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
- const u8 *saddr, const u8 saddr_type, const u8 saddr_len,
- const u8 *daddr, const u8 daddr_type, const u8 daddr_len,
- u8 iphc0, u8 iphc1, skb_delivery_cb deliver_skb)
+int
+lowpan_header_decompress(struct sk_buff *skb, struct net_device *dev,
+ const u8 *saddr, const u8 saddr_type,
+ const u8 saddr_len, const u8 *daddr,
+ const u8 daddr_type, const u8 daddr_len,
+ u8 iphc0, u8 iphc1)
{
struct ipv6hdr hdr = {};
u8 tmp, num_context = 0;
@@ -348,7 +316,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
if (iphc1 & LOWPAN_IPHC_CID) {
pr_debug("CID flag is set, increase header with one\n");
if (lowpan_fetch_skb(skb, &num_context, sizeof(num_context)))
- goto drop;
+ return -EINVAL;
}
hdr.version = 6;
@@ -360,7 +328,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
*/
case 0: /* 00b */
if (lowpan_fetch_skb(skb, &tmp, sizeof(tmp)))
- goto drop;
+ return -EINVAL;
memcpy(&hdr.flow_lbl, &skb->data[0], 3);
skb_pull(skb, 3);
@@ -373,7 +341,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
*/
case 2: /* 10b */
if (lowpan_fetch_skb(skb, &tmp, sizeof(tmp)))
- goto drop;
+ return -EINVAL;
hdr.priority = ((tmp >> 2) & 0x0f);
hdr.flow_lbl[0] = ((tmp << 6) & 0xC0) | ((tmp >> 2) & 0x30);
@@ -383,7 +351,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
*/
case 1: /* 01b */
if (lowpan_fetch_skb(skb, &tmp, sizeof(tmp)))
- goto drop;
+ return -EINVAL;
hdr.flow_lbl[0] = (skb->data[0] & 0x0F) | ((tmp >> 2) & 0x30);
memcpy(&hdr.flow_lbl[1], &skb->data[0], 2);
@@ -400,7 +368,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) {
/* Next header is carried inline */
if (lowpan_fetch_skb(skb, &hdr.nexthdr, sizeof(hdr.nexthdr)))
- goto drop;
+ return -EINVAL;
pr_debug("NH flag is set, next header carried inline: %02x\n",
hdr.nexthdr);
@@ -412,7 +380,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
} else {
if (lowpan_fetch_skb(skb, &hdr.hop_limit,
sizeof(hdr.hop_limit)))
- goto drop;
+ return -EINVAL;
}
/* Extract SAM to the tmp variable */
@@ -431,7 +399,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
/* Check on error of previous branch */
if (err)
- goto drop;
+ return -EINVAL;
/* Extract DAM to the tmp variable */
tmp = ((iphc1 & LOWPAN_IPHC_DAM_11) >> LOWPAN_IPHC_DAM_BIT) & 0x03;
@@ -446,7 +414,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
tmp);
if (err)
- goto drop;
+ return -EINVAL;
}
} else {
err = uncompress_addr(skb, &hdr.daddr, tmp, daddr,
@@ -454,28 +422,23 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
pr_debug("dest: stateless compression mode %d dest %pI6c\n",
tmp, &hdr.daddr);
if (err)
- goto drop;
+ return -EINVAL;
}
/* UDP data uncompression */
if (iphc0 & LOWPAN_IPHC_NH_C) {
struct udphdr uh;
- struct sk_buff *new;
+ const int needed = sizeof(struct udphdr) + sizeof(hdr);
if (uncompress_udp_header(skb, &uh))
- goto drop;
+ return -EINVAL;
/* replace the compressed UDP head by the uncompressed UDP
* header
*/
- new = skb_copy_expand(skb, sizeof(struct udphdr),
- skb_tailroom(skb), GFP_ATOMIC);
- kfree_skb(skb);
-
- if (!new)
- return -ENOMEM;
-
- skb = new;
+ err = skb_cow(skb, needed);
+ if (unlikely(err))
+ return err;
skb_push(skb, sizeof(struct udphdr));
skb_reset_transport_header(skb);
@@ -485,6 +448,10 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
(u8 *)&uh, sizeof(uh));
hdr.nexthdr = UIP_PROTO_UDP;
+ } else {
+ err = skb_cow(skb, sizeof(hdr));
+ if (unlikely(err))
+ return err;
}
hdr.payload_len = htons(skb->len);
@@ -497,15 +464,15 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
hdr.version, ntohs(hdr.payload_len), hdr.nexthdr,
hdr.hop_limit, &hdr.daddr);
- raw_dump_table(__func__, "raw header dump", (u8 *)&hdr, sizeof(hdr));
+ skb_push(skb, sizeof(hdr));
+ skb_reset_network_header(skb);
+ skb_copy_to_linear_data(skb, &hdr, sizeof(hdr));
- return skb_deliver(skb, &hdr, dev, deliver_skb);
+ raw_dump_table(__func__, "raw header dump", (u8 *)&hdr, sizeof(hdr));
-drop:
- kfree_skb(skb);
- return -EINVAL;
+ return 0;
}
-EXPORT_SYMBOL_GPL(lowpan_process_data);
+EXPORT_SYMBOL_GPL(lowpan_header_decompress);
static u8 lowpan_compress_addr_64(u8 **hc_ptr, u8 shift,
const struct in6_addr *ipaddr,
@@ -535,9 +502,17 @@ static u8 lowpan_compress_addr_64(u8 **hc_ptr, u8 shift,
static void compress_udp_header(u8 **hc_ptr, struct sk_buff *skb)
{
- struct udphdr *uh = udp_hdr(skb);
+ struct udphdr *uh;
u8 tmp;
+ /* In the case of RAW sockets the transport header is not set by
+ * the ip6 stack so we must set it ourselves
+ */
+ if (skb->transport_header == skb->network_header)
+ skb_set_transport_header(skb, sizeof(struct ipv6hdr));
+
+ uh = udp_hdr(skb);
+
if (((ntohs(uh->source) & LOWPAN_NHC_UDP_4BIT_MASK) ==
LOWPAN_NHC_UDP_4BIT_PORT) &&
((ntohs(uh->dest) & LOWPAN_NHC_UDP_4BIT_MASK) ==
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index 0d441ec8763e..118956448cf6 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -26,6 +26,7 @@
#include <linux/slab.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
+#include <linux/net_tstamp.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <net/arp.h>
@@ -150,7 +151,7 @@ static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb,
u16 vlan_tci;
vlan_tci = vlan->vlan_id;
vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb->priority);
- skb = __vlan_hwaccel_put_tag(skb, vlan->vlan_proto, vlan_tci);
+ __vlan_hwaccel_put_tag(skb, vlan->vlan_proto, vlan_tci);
}
skb->dev = vlan->real_dev;
@@ -578,11 +579,12 @@ static int vlan_dev_init(struct net_device *dev)
(1<<__LINK_STATE_PRESENT);
dev->hw_features = NETIF_F_ALL_CSUM | NETIF_F_SG |
- NETIF_F_FRAGLIST | NETIF_F_ALL_TSO |
+ NETIF_F_FRAGLIST | NETIF_F_GSO_SOFTWARE |
NETIF_F_HIGHDMA | NETIF_F_SCTP_CSUM |
NETIF_F_ALL_FCOE;
- dev->features |= real_dev->vlan_features | NETIF_F_LLTX;
+ dev->features |= real_dev->vlan_features | NETIF_F_LLTX |
+ NETIF_F_GSO_SOFTWARE;
dev->gso_max_size = real_dev->gso_max_size;
if (dev->features & NETIF_F_VLAN_FEATURES)
netdev_warn(real_dev, "VLAN features are set incorrectly. Q-in-Q configurations may not work correctly.\n");
@@ -647,7 +649,7 @@ static netdev_features_t vlan_dev_fix_features(struct net_device *dev,
features |= NETIF_F_RXCSUM;
features = netdev_intersect_features(features, real_dev->features);
- features |= old_features & NETIF_F_SOFT_FEATURES;
+ features |= old_features & (NETIF_F_SOFT_FEATURES | NETIF_F_GSO_SOFTWARE);
features |= NETIF_F_LLTX;
return features;
@@ -669,6 +671,23 @@ static void vlan_ethtool_get_drvinfo(struct net_device *dev,
strlcpy(info->fw_version, "N/A", sizeof(info->fw_version));
}
+static int vlan_ethtool_get_ts_info(struct net_device *dev,
+ struct ethtool_ts_info *info)
+{
+ const struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
+ const struct ethtool_ops *ops = vlan->real_dev->ethtool_ops;
+
+ if (ops->get_ts_info) {
+ return ops->get_ts_info(vlan->real_dev, info);
+ } else {
+ info->so_timestamping = SOF_TIMESTAMPING_RX_SOFTWARE |
+ SOF_TIMESTAMPING_SOFTWARE;
+ info->phc_index = -1;
+ }
+
+ return 0;
+}
+
static struct rtnl_link_stats64 *vlan_dev_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
{
struct vlan_pcpu_stats *p;
@@ -752,6 +771,7 @@ static const struct ethtool_ops vlan_ethtool_ops = {
.get_settings = vlan_ethtool_get_settings,
.get_drvinfo = vlan_ethtool_get_drvinfo,
.get_link = ethtool_op_get_link,
+ .get_ts_info = vlan_ethtool_get_ts_info,
};
static const struct net_device_ops vlan_netdev_ops = {
diff --git a/net/Kconfig b/net/Kconfig
index 99815b5454bf..ff9ffc17fa0e 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -228,6 +228,7 @@ source "net/vmw_vsock/Kconfig"
source "net/netlink/Kconfig"
source "net/mpls/Kconfig"
source "net/hsr/Kconfig"
+source "net/switchdev/Kconfig"
config RPS
boolean
diff --git a/net/Makefile b/net/Makefile
index 7ed1970074b0..95fc694e4ddc 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -73,3 +73,6 @@ obj-$(CONFIG_OPENVSWITCH) += openvswitch/
obj-$(CONFIG_VSOCKETS) += vmw_vsock/
obj-$(CONFIG_NET_MPLS_GSO) += mpls/
obj-$(CONFIG_HSR) += hsr/
+ifneq ($(CONFIG_NET_SWITCHDEV),)
+obj-y += switchdev/
+endif
diff --git a/net/appletalk/ddp.c b/net/appletalk/ddp.c
index c00897f65a31..0d0766ea5ab1 100644
--- a/net/appletalk/ddp.c
+++ b/net/appletalk/ddp.c
@@ -1659,7 +1659,7 @@ static int atalk_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
SOCK_DEBUG(sk, "SK %p: Copy user data (%Zd bytes).\n", sk, len);
- err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
+ err = memcpy_from_msg(skb_put(skb, len), msg, len);
if (err) {
kfree_skb(skb);
err = -EFAULT;
@@ -1758,7 +1758,7 @@ static int atalk_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr
copied = size;
msg->msg_flags |= MSG_TRUNC;
}
- err = skb_copy_datagram_iovec(skb, offset, msg->msg_iov, copied);
+ err = skb_copy_datagram_msg(skb, offset, msg, copied);
if (!err && msg->msg_name) {
DECLARE_SOCKADDR(struct sockaddr_at *, sat, msg->msg_name);
diff --git a/net/atm/common.c b/net/atm/common.c
index 6a765156a3f6..b84057e41bd6 100644
--- a/net/atm/common.c
+++ b/net/atm/common.c
@@ -554,7 +554,7 @@ int vcc_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
msg->msg_flags |= MSG_TRUNC;
}
- error = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ error = skb_copy_datagram_msg(skb, 0, msg, copied);
if (error)
return error;
sock_recv_ts_and_drops(msg, sk, skb);
@@ -570,15 +570,13 @@ int vcc_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
}
int vcc_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m,
- size_t total_len)
+ size_t size)
{
struct sock *sk = sock->sk;
DEFINE_WAIT(wait);
struct atm_vcc *vcc;
struct sk_buff *skb;
int eff, error;
- const void __user *buff;
- int size;
lock_sock(sk);
if (sock->state != SS_CONNECTED) {
@@ -589,12 +587,6 @@ int vcc_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m,
error = -EISCONN;
goto out;
}
- if (m->msg_iovlen != 1) {
- error = -ENOSYS; /* fix this later @@@ */
- goto out;
- }
- buff = m->msg_iov->iov_base;
- size = m->msg_iov->iov_len;
vcc = ATM_SD(sock);
if (test_bit(ATM_VF_RELEASED, &vcc->flags) ||
test_bit(ATM_VF_CLOSE, &vcc->flags) ||
@@ -607,7 +599,7 @@ int vcc_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m,
error = 0;
goto out;
}
- if (size < 0 || size > vcc->qos.txtp.max_sdu) {
+ if (size > vcc->qos.txtp.max_sdu) {
error = -EMSGSIZE;
goto out;
}
@@ -639,7 +631,7 @@ int vcc_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m,
goto out;
skb->dev = NULL; /* for paths shared with net_device interfaces */
ATM_SKB(skb)->atm_options = vcc->atm_options;
- if (copy_from_user(skb_put(skb, size), buff, size)) {
+ if (copy_from_iter(skb_put(skb, size), size, &m->msg_iter) != size) {
kfree_skb(skb);
error = -EFAULT;
goto out;
diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c
index c35c3f48fc0f..ca049a7c9287 100644
--- a/net/ax25/af_ax25.c
+++ b/net/ax25/af_ax25.c
@@ -1549,7 +1549,7 @@ static int ax25_sendmsg(struct kiocb *iocb, struct socket *sock,
skb_reserve(skb, size - len);
/* User data follows immediately after the AX.25 data */
- if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
+ if (memcpy_from_msg(skb_put(skb, len), msg, len)) {
err = -EFAULT;
kfree_skb(skb);
goto out;
@@ -1634,7 +1634,7 @@ static int ax25_recvmsg(struct kiocb *iocb, struct socket *sock,
msg->msg_flags |= MSG_TRUNC;
}
- skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ skb_copy_datagram_msg(skb, 0, msg, copied);
if (msg->msg_name) {
ax25_digi digi;
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index c2e0d14433df..76617be1e797 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -53,7 +53,7 @@ struct skb_cb {
* The list contains struct lowpan_dev elements.
*/
static LIST_HEAD(bt_6lowpan_devices);
-static DEFINE_RWLOCK(devices_lock);
+static DEFINE_SPINLOCK(devices_lock);
/* If psm is set to 0 (default value), then 6lowpan is disabled.
* Other values are used to indicate a Protocol Service Multiplexer
@@ -67,6 +67,7 @@ static struct l2cap_chan *listen_chan;
struct lowpan_peer {
struct list_head list;
+ struct rcu_head rcu;
struct l2cap_chan *chan;
/* peer addresses in various formats */
@@ -93,13 +94,14 @@ static inline struct lowpan_dev *lowpan_dev(const struct net_device *netdev)
static inline void peer_add(struct lowpan_dev *dev, struct lowpan_peer *peer)
{
- list_add(&peer->list, &dev->peers);
+ list_add_rcu(&peer->list, &dev->peers);
atomic_inc(&dev->peer_count);
}
static inline bool peer_del(struct lowpan_dev *dev, struct lowpan_peer *peer)
{
- list_del(&peer->list);
+ list_del_rcu(&peer->list);
+ kfree_rcu(peer, rcu);
module_put(THIS_MODULE);
@@ -114,31 +116,37 @@ static inline bool peer_del(struct lowpan_dev *dev, struct lowpan_peer *peer)
static inline struct lowpan_peer *peer_lookup_ba(struct lowpan_dev *dev,
bdaddr_t *ba, __u8 type)
{
- struct lowpan_peer *peer, *tmp;
+ struct lowpan_peer *peer;
BT_DBG("peers %d addr %pMR type %d", atomic_read(&dev->peer_count),
ba, type);
- list_for_each_entry_safe(peer, tmp, &dev->peers, list) {
+ rcu_read_lock();
+
+ list_for_each_entry_rcu(peer, &dev->peers, list) {
BT_DBG("dst addr %pMR dst type %d",
&peer->chan->dst, peer->chan->dst_type);
if (bacmp(&peer->chan->dst, ba))
continue;
- if (type == peer->chan->dst_type)
+ if (type == peer->chan->dst_type) {
+ rcu_read_unlock();
return peer;
+ }
}
+ rcu_read_unlock();
+
return NULL;
}
-static inline struct lowpan_peer *peer_lookup_chan(struct lowpan_dev *dev,
- struct l2cap_chan *chan)
+static inline struct lowpan_peer *__peer_lookup_chan(struct lowpan_dev *dev,
+ struct l2cap_chan *chan)
{
- struct lowpan_peer *peer, *tmp;
+ struct lowpan_peer *peer;
- list_for_each_entry_safe(peer, tmp, &dev->peers, list) {
+ list_for_each_entry_rcu(peer, &dev->peers, list) {
if (peer->chan == chan)
return peer;
}
@@ -146,12 +154,12 @@ static inline struct lowpan_peer *peer_lookup_chan(struct lowpan_dev *dev,
return NULL;
}
-static inline struct lowpan_peer *peer_lookup_conn(struct lowpan_dev *dev,
- struct l2cap_conn *conn)
+static inline struct lowpan_peer *__peer_lookup_conn(struct lowpan_dev *dev,
+ struct l2cap_conn *conn)
{
- struct lowpan_peer *peer, *tmp;
+ struct lowpan_peer *peer;
- list_for_each_entry_safe(peer, tmp, &dev->peers, list) {
+ list_for_each_entry_rcu(peer, &dev->peers, list) {
if (peer->chan->conn == conn)
return peer;
}
@@ -163,7 +171,7 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_dev *dev,
struct in6_addr *daddr,
struct sk_buff *skb)
{
- struct lowpan_peer *peer, *tmp;
+ struct lowpan_peer *peer;
struct in6_addr *nexthop;
struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
int count = atomic_read(&dev->peer_count);
@@ -174,9 +182,13 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_dev *dev,
* send the packet. If only one peer exists, then we can send the
* packet right away.
*/
- if (count == 1)
- return list_first_entry(&dev->peers, struct lowpan_peer,
- list);
+ if (count == 1) {
+ rcu_read_lock();
+ peer = list_first_or_null_rcu(&dev->peers, struct lowpan_peer,
+ list);
+ rcu_read_unlock();
+ return peer;
+ }
if (!rt) {
nexthop = &lowpan_cb(skb)->gw;
@@ -195,53 +207,57 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_dev *dev,
BT_DBG("gw %pI6c", nexthop);
- list_for_each_entry_safe(peer, tmp, &dev->peers, list) {
+ rcu_read_lock();
+
+ list_for_each_entry_rcu(peer, &dev->peers, list) {
BT_DBG("dst addr %pMR dst type %d ip %pI6c",
&peer->chan->dst, peer->chan->dst_type,
&peer->peer_addr);
- if (!ipv6_addr_cmp(&peer->peer_addr, nexthop))
+ if (!ipv6_addr_cmp(&peer->peer_addr, nexthop)) {
+ rcu_read_unlock();
return peer;
+ }
}
+ rcu_read_unlock();
+
return NULL;
}
static struct lowpan_peer *lookup_peer(struct l2cap_conn *conn)
{
- struct lowpan_dev *entry, *tmp;
+ struct lowpan_dev *entry;
struct lowpan_peer *peer = NULL;
- unsigned long flags;
- read_lock_irqsave(&devices_lock, flags);
+ rcu_read_lock();
- list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
- peer = peer_lookup_conn(entry, conn);
+ list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
+ peer = __peer_lookup_conn(entry, conn);
if (peer)
break;
}
- read_unlock_irqrestore(&devices_lock, flags);
+ rcu_read_unlock();
return peer;
}
static struct lowpan_dev *lookup_dev(struct l2cap_conn *conn)
{
- struct lowpan_dev *entry, *tmp;
+ struct lowpan_dev *entry;
struct lowpan_dev *dev = NULL;
- unsigned long flags;
- read_lock_irqsave(&devices_lock, flags);
+ rcu_read_lock();
- list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+ list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
if (conn->hcon->hdev == entry->hdev) {
dev = entry;
break;
}
}
- read_unlock_irqrestore(&devices_lock, flags);
+ rcu_read_unlock();
return dev;
}
@@ -249,59 +265,49 @@ static struct lowpan_dev *lookup_dev(struct l2cap_conn *conn)
static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev)
{
struct sk_buff *skb_cp;
- int ret;
skb_cp = skb_copy(skb, GFP_ATOMIC);
if (!skb_cp)
- return -ENOMEM;
-
- ret = netif_rx(skb_cp);
- if (ret < 0) {
- BT_DBG("receive skb %d", ret);
return NET_RX_DROP;
- }
- return ret;
+ return netif_rx(skb_cp);
}
-static int process_data(struct sk_buff *skb, struct net_device *netdev,
- struct l2cap_chan *chan)
+static int iphc_decompress(struct sk_buff *skb, struct net_device *netdev,
+ struct l2cap_chan *chan)
{
const u8 *saddr, *daddr;
u8 iphc0, iphc1;
struct lowpan_dev *dev;
struct lowpan_peer *peer;
- unsigned long flags;
dev = lowpan_dev(netdev);
- read_lock_irqsave(&devices_lock, flags);
- peer = peer_lookup_chan(dev, chan);
- read_unlock_irqrestore(&devices_lock, flags);
+ rcu_read_lock();
+ peer = __peer_lookup_chan(dev, chan);
+ rcu_read_unlock();
if (!peer)
- goto drop;
+ return -EINVAL;
saddr = peer->eui64_addr;
daddr = dev->netdev->dev_addr;
/* at least two bytes will be used for the encoding */
if (skb->len < 2)
- goto drop;
+ return -EINVAL;
if (lowpan_fetch_skb_u8(skb, &iphc0))
- goto drop;
+ return -EINVAL;
if (lowpan_fetch_skb_u8(skb, &iphc1))
- goto drop;
+ return -EINVAL;
- return lowpan_process_data(skb, netdev,
- saddr, IEEE802154_ADDR_LONG, EUI64_ADDR_LEN,
- daddr, IEEE802154_ADDR_LONG, EUI64_ADDR_LEN,
- iphc0, iphc1, give_skb_to_upper);
+ return lowpan_header_decompress(skb, netdev,
+ saddr, IEEE802154_ADDR_LONG,
+ EUI64_ADDR_LEN, daddr,
+ IEEE802154_ADDR_LONG, EUI64_ADDR_LEN,
+ iphc0, iphc1);
-drop:
- kfree_skb(skb);
- return -EINVAL;
}
static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
@@ -316,6 +322,10 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
if (dev->type != ARPHRD_6LOWPAN)
goto drop;
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb)
+ goto drop;
+
/* check that it's our buffer */
if (skb->data[0] == LOWPAN_DISPATCH_IPV6) {
/* Copy the packet so that the IPv6 header is
@@ -340,8 +350,8 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
dev->stats.rx_bytes += skb->len;
dev->stats.rx_packets++;
- kfree_skb(local_skb);
- kfree_skb(skb);
+ consume_skb(local_skb);
+ consume_skb(skb);
} else {
switch (skb->data[0] & 0xe0) {
case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */
@@ -349,14 +359,27 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
if (!local_skb)
goto drop;
- ret = process_data(local_skb, dev, chan);
- if (ret != NET_RX_SUCCESS)
+ ret = iphc_decompress(local_skb, dev, chan);
+ if (ret < 0) {
+ kfree_skb(local_skb);
+ goto drop;
+ }
+
+ local_skb->protocol = htons(ETH_P_IPV6);
+ local_skb->pkt_type = PACKET_HOST;
+ local_skb->dev = dev;
+
+ if (give_skb_to_upper(local_skb, dev)
+ != NET_RX_SUCCESS) {
+ kfree_skb(local_skb);
goto drop;
+ }
dev->stats.rx_bytes += skb->len;
dev->stats.rx_packets++;
- kfree_skb(skb);
+ consume_skb(local_skb);
+ consume_skb(skb);
break;
default:
break;
@@ -443,7 +466,6 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev,
if (ipv6_addr_is_multicast(&ipv6_daddr)) {
lowpan_cb(skb)->chan = NULL;
} else {
- unsigned long flags;
u8 addr_type;
/* Get destination BT device from skb.
@@ -454,19 +476,14 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev,
BT_DBG("dest addr %pMR type %d IP %pI6c", &addr,
addr_type, &ipv6_daddr);
- read_lock_irqsave(&devices_lock, flags);
peer = peer_lookup_ba(dev, &addr, addr_type);
- read_unlock_irqrestore(&devices_lock, flags);
-
if (!peer) {
/* The packet might be sent to 6lowpan interface
* because of routing (either via default route
* or user set route) so get peer according to
* the destination address.
*/
- read_lock_irqsave(&devices_lock, flags);
peer = peer_lookup_dst(dev, &ipv6_daddr, skb);
- read_unlock_irqrestore(&devices_lock, flags);
if (!peer) {
BT_DBG("no such peer %pMR found", &addr);
return -ENOENT;
@@ -520,12 +537,12 @@ static int send_pkt(struct l2cap_chan *chan, struct sk_buff *skb,
*/
chan->data = skb;
- memset(&msg, 0, sizeof(msg));
- msg.msg_iov = (struct iovec *) &iv;
- msg.msg_iovlen = 1;
iv.iov_base = skb->data;
iv.iov_len = skb->len;
+ memset(&msg, 0, sizeof(msg));
+ iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, &iv, 1, skb->len);
+
err = l2cap_chan_send(chan, &msg, skb->len);
if (err > 0) {
netdev->stats.tx_bytes += err;
@@ -549,14 +566,13 @@ static int send_pkt(struct l2cap_chan *chan, struct sk_buff *skb,
static int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
{
struct sk_buff *local_skb;
- struct lowpan_dev *entry, *tmp;
- unsigned long flags;
+ struct lowpan_dev *entry;
int err = 0;
- read_lock_irqsave(&devices_lock, flags);
+ rcu_read_lock();
- list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
- struct lowpan_peer *pentry, *ptmp;
+ list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
+ struct lowpan_peer *pentry;
struct lowpan_dev *dev;
if (entry->netdev != netdev)
@@ -564,7 +580,7 @@ static int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
dev = lowpan_dev(entry->netdev);
- list_for_each_entry_safe(pentry, ptmp, &dev->peers, list) {
+ list_for_each_entry_rcu(pentry, &dev->peers, list) {
int ret;
local_skb = skb_clone(skb, GFP_ATOMIC);
@@ -581,7 +597,7 @@ static int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
}
}
- read_unlock_irqrestore(&devices_lock, flags);
+ rcu_read_unlock();
return err;
}
@@ -591,17 +607,13 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
int err = 0;
bdaddr_t addr;
u8 addr_type;
- struct sk_buff *tmpskb;
/* We must take a copy of the skb before we modify/replace the ipv6
* header as the header could be used elsewhere
*/
- tmpskb = skb_unshare(skb, GFP_ATOMIC);
- if (!tmpskb) {
- kfree_skb(skb);
+ skb = skb_unshare(skb, GFP_ATOMIC);
+ if (!skb)
return NET_XMIT_DROP;
- }
- skb = tmpskb;
/* Return values from setup_header()
* <0 - error, packet is dropped
@@ -638,7 +650,26 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
return err < 0 ? NET_XMIT_DROP : err;
}
+static struct lock_class_key bt_tx_busylock;
+static struct lock_class_key bt_netdev_xmit_lock_key;
+
+static void bt_set_lockdep_class_one(struct net_device *dev,
+ struct netdev_queue *txq,
+ void *_unused)
+{
+ lockdep_set_class(&txq->_xmit_lock, &bt_netdev_xmit_lock_key);
+}
+
+static int bt_dev_init(struct net_device *dev)
+{
+ netdev_for_each_tx_queue(dev, bt_set_lockdep_class_one, NULL);
+ dev->qdisc_tx_busylock = &bt_tx_busylock;
+
+ return 0;
+}
+
static const struct net_device_ops netdev_ops = {
+ .ndo_init = bt_dev_init,
.ndo_start_xmit = bt_xmit,
};
@@ -783,7 +814,6 @@ static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan,
struct lowpan_dev *dev)
{
struct lowpan_peer *peer;
- unsigned long flags;
peer = kzalloc(sizeof(*peer), GFP_ATOMIC);
if (!peer)
@@ -806,10 +836,10 @@ static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan,
*/
set_ip_addr_bits(chan->dst_type, (u8 *)&peer->peer_addr.s6_addr + 8);
- write_lock_irqsave(&devices_lock, flags);
+ spin_lock(&devices_lock);
INIT_LIST_HEAD(&peer->list);
peer_add(dev, peer);
- write_unlock_irqrestore(&devices_lock, flags);
+ spin_unlock(&devices_lock);
/* Notifying peers about us needs to be done without locks held */
INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers);
@@ -822,7 +852,6 @@ static int setup_netdev(struct l2cap_chan *chan, struct lowpan_dev **dev)
{
struct net_device *netdev;
int err = 0;
- unsigned long flags;
netdev = alloc_netdev(sizeof(struct lowpan_dev), IFACE_NAME_TEMPLATE,
NET_NAME_UNKNOWN, netdev_setup);
@@ -852,10 +881,10 @@ static int setup_netdev(struct l2cap_chan *chan, struct lowpan_dev **dev)
(*dev)->hdev = chan->conn->hcon->hdev;
INIT_LIST_HEAD(&(*dev)->peers);
- write_lock_irqsave(&devices_lock, flags);
+ spin_lock(&devices_lock);
INIT_LIST_HEAD(&(*dev)->list);
- list_add(&(*dev)->list, &bt_6lowpan_devices);
- write_unlock_irqrestore(&devices_lock, flags);
+ list_add_rcu(&(*dev)->list, &bt_6lowpan_devices);
+ spin_unlock(&devices_lock);
return 0;
@@ -909,11 +938,10 @@ static void delete_netdev(struct work_struct *work)
static void chan_close_cb(struct l2cap_chan *chan)
{
- struct lowpan_dev *entry, *tmp;
+ struct lowpan_dev *entry;
struct lowpan_dev *dev = NULL;
struct lowpan_peer *peer;
int err = -ENOENT;
- unsigned long flags;
bool last = false, removed = true;
BT_DBG("chan %p conn %p", chan, chan->conn);
@@ -928,11 +956,11 @@ static void chan_close_cb(struct l2cap_chan *chan)
removed = false;
}
- write_lock_irqsave(&devices_lock, flags);
+ spin_lock(&devices_lock);
- list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+ list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
dev = lowpan_dev(entry->netdev);
- peer = peer_lookup_chan(dev, chan);
+ peer = __peer_lookup_chan(dev, chan);
if (peer) {
last = peer_del(dev, peer);
err = 0;
@@ -943,13 +971,12 @@ static void chan_close_cb(struct l2cap_chan *chan)
atomic_read(&chan->kref.refcount));
l2cap_chan_put(chan);
- kfree(peer);
break;
}
}
if (!err && last && dev && !atomic_read(&dev->peer_count)) {
- write_unlock_irqrestore(&devices_lock, flags);
+ spin_unlock(&devices_lock);
cancel_delayed_work_sync(&dev->notify_peers);
@@ -960,7 +987,7 @@ static void chan_close_cb(struct l2cap_chan *chan)
schedule_work(&entry->delete_netdev);
}
} else {
- write_unlock_irqrestore(&devices_lock, flags);
+ spin_unlock(&devices_lock);
}
return;
@@ -1023,7 +1050,6 @@ static const struct l2cap_ops bt_6lowpan_chan_ops = {
.suspend = chan_suspend_cb,
.get_sndtimeo = chan_get_sndtimeo_cb,
.alloc_skb = chan_alloc_skb_cb,
- .memcpy_fromiovec = l2cap_chan_no_memcpy_fromiovec,
.teardown = l2cap_chan_no_teardown,
.defer = l2cap_chan_no_defer,
@@ -1103,6 +1129,8 @@ static struct l2cap_chan *bt_6lowpan_listen(void)
pchan->state = BT_LISTEN;
pchan->src_type = BDADDR_LE_PUBLIC;
+ atomic_set(&pchan->nesting, L2CAP_NESTING_PARENT);
+
BT_DBG("psm 0x%04x chan %p src type %d", psm_6lowpan, pchan,
pchan->src_type);
@@ -1152,10 +1180,9 @@ static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
static void disconnect_all_peers(void)
{
- struct lowpan_dev *entry, *tmp_dev;
+ struct lowpan_dev *entry;
struct lowpan_peer *peer, *tmp_peer, *new_peer;
struct list_head peers;
- unsigned long flags;
INIT_LIST_HEAD(&peers);
@@ -1164,10 +1191,10 @@ static void disconnect_all_peers(void)
* with the same list at the same time.
*/
- read_lock_irqsave(&devices_lock, flags);
+ rcu_read_lock();
- list_for_each_entry_safe(entry, tmp_dev, &bt_6lowpan_devices, list) {
- list_for_each_entry_safe(peer, tmp_peer, &entry->peers, list) {
+ list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
+ list_for_each_entry_rcu(peer, &entry->peers, list) {
new_peer = kmalloc(sizeof(*new_peer), GFP_ATOMIC);
if (!new_peer)
break;
@@ -1179,26 +1206,36 @@ static void disconnect_all_peers(void)
}
}
- read_unlock_irqrestore(&devices_lock, flags);
+ rcu_read_unlock();
+ spin_lock(&devices_lock);
list_for_each_entry_safe(peer, tmp_peer, &peers, list) {
l2cap_chan_close(peer->chan, ENOENT);
- kfree(peer);
+
+ list_del_rcu(&peer->list);
+ kfree_rcu(peer, rcu);
+
+ module_put(THIS_MODULE);
}
+ spin_unlock(&devices_lock);
}
-static int lowpan_psm_set(void *data, u64 val)
-{
+struct set_psm {
+ struct work_struct work;
u16 psm;
+};
- psm = val;
- if (psm == 0 || psm_6lowpan != psm)
+static void do_psm_set(struct work_struct *work)
+{
+ struct set_psm *set_psm = container_of(work, struct set_psm, work);
+
+ if (set_psm->psm == 0 || psm_6lowpan != set_psm->psm)
/* Disconnect existing connections if 6lowpan is
* disabled (psm = 0), or if psm changes.
*/
disconnect_all_peers();
- psm_6lowpan = psm;
+ psm_6lowpan = set_psm->psm;
if (listen_chan) {
l2cap_chan_close(listen_chan, 0);
@@ -1207,6 +1244,22 @@ static int lowpan_psm_set(void *data, u64 val)
listen_chan = bt_6lowpan_listen();
+ kfree(set_psm);
+}
+
+static int lowpan_psm_set(void *data, u64 val)
+{
+ struct set_psm *set_psm;
+
+ set_psm = kzalloc(sizeof(*set_psm), GFP_KERNEL);
+ if (!set_psm)
+ return -ENOMEM;
+
+ set_psm->psm = val;
+ INIT_WORK(&set_psm->work, do_psm_set);
+
+ schedule_work(&set_psm->work);
+
return 0;
}
@@ -1288,19 +1341,18 @@ static ssize_t lowpan_control_write(struct file *fp,
static int lowpan_control_show(struct seq_file *f, void *ptr)
{
- struct lowpan_dev *entry, *tmp_dev;
- struct lowpan_peer *peer, *tmp_peer;
- unsigned long flags;
+ struct lowpan_dev *entry;
+ struct lowpan_peer *peer;
- read_lock_irqsave(&devices_lock, flags);
+ spin_lock(&devices_lock);
- list_for_each_entry_safe(entry, tmp_dev, &bt_6lowpan_devices, list) {
- list_for_each_entry_safe(peer, tmp_peer, &entry->peers, list)
+ list_for_each_entry(entry, &bt_6lowpan_devices, list) {
+ list_for_each_entry(peer, &entry->peers, list)
seq_printf(f, "%pMR (type %u)\n",
&peer->chan->dst, peer->chan->dst_type);
}
- read_unlock_irqrestore(&devices_lock, flags);
+ spin_unlock(&devices_lock);
return 0;
}
@@ -1322,7 +1374,6 @@ static void disconnect_devices(void)
{
struct lowpan_dev *entry, *tmp, *new_dev;
struct list_head devices;
- unsigned long flags;
INIT_LIST_HEAD(&devices);
@@ -1331,9 +1382,9 @@ static void disconnect_devices(void)
* devices list.
*/
- read_lock_irqsave(&devices_lock, flags);
+ rcu_read_lock();
- list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+ list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
new_dev = kmalloc(sizeof(*new_dev), GFP_ATOMIC);
if (!new_dev)
break;
@@ -1341,10 +1392,10 @@ static void disconnect_devices(void)
new_dev->netdev = entry->netdev;
INIT_LIST_HEAD(&new_dev->list);
- list_add(&new_dev->list, &devices);
+ list_add_rcu(&new_dev->list, &devices);
}
- read_unlock_irqrestore(&devices_lock, flags);
+ rcu_read_unlock();
list_for_each_entry_safe(entry, tmp, &devices, list) {
ifdown(entry->netdev);
@@ -1359,17 +1410,15 @@ static int device_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
- struct lowpan_dev *entry, *tmp;
- unsigned long flags;
+ struct lowpan_dev *entry;
if (netdev->type != ARPHRD_6LOWPAN)
return NOTIFY_DONE;
switch (event) {
case NETDEV_UNREGISTER:
- write_lock_irqsave(&devices_lock, flags);
- list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices,
- list) {
+ spin_lock(&devices_lock);
+ list_for_each_entry(entry, &bt_6lowpan_devices, list) {
if (entry->netdev == netdev) {
BT_DBG("Unregistered netdev %s %p",
netdev->name, netdev);
@@ -1378,7 +1427,7 @@ static int device_event(struct notifier_block *unused,
break;
}
}
- write_unlock_irqrestore(&devices_lock, flags);
+ spin_unlock(&devices_lock);
break;
}
diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig
index 600fb29288f4..29bcafc41adf 100644
--- a/net/bluetooth/Kconfig
+++ b/net/bluetooth/Kconfig
@@ -10,6 +10,7 @@ menuconfig BT
select CRYPTO
select CRYPTO_BLKCIPHER
select CRYPTO_AES
+ select CRYPTO_CMAC
select CRYPTO_ECB
select CRYPTO_SHA256
help
@@ -39,11 +40,10 @@ menuconfig BT
to Bluetooth kernel modules are provided in the BlueZ packages. For
more information, see <http://www.bluez.org/>.
-config BT_6LOWPAN
- tristate "Bluetooth 6LoWPAN support"
- depends on BT && 6LOWPAN
- help
- IPv6 compression over Bluetooth Low Energy.
+config BT_BREDR
+ bool "Bluetooth Classic (BR/EDR) features"
+ depends on BT
+ default y
source "net/bluetooth/rfcomm/Kconfig"
@@ -53,4 +53,15 @@ source "net/bluetooth/cmtp/Kconfig"
source "net/bluetooth/hidp/Kconfig"
+config BT_LE
+ bool "Bluetooth Low Energy (LE) features"
+ depends on BT
+ default y
+
+config BT_6LOWPAN
+ tristate "Bluetooth 6LoWPAN support"
+ depends on BT_LE && 6LOWPAN
+ help
+ IPv6 compression over Bluetooth Low Energy.
+
source "drivers/bluetooth/Kconfig"
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index 886e9aa3ecf1..a5432a6a0ae6 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -13,6 +13,6 @@ bluetooth_6lowpan-y := 6lowpan.o
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \
- a2mp.o amp.o
+ a2mp.o amp.o ecc.o
subdir-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c
index 5dcade511fdb..cedfbda15dad 100644
--- a/net/bluetooth/a2mp.c
+++ b/net/bluetooth/a2mp.c
@@ -60,8 +60,7 @@ void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data)
memset(&msg, 0, sizeof(msg));
- msg.msg_iov = (struct iovec *) &iv;
- msg.msg_iovlen = 1;
+ iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, &iv, 1, total_len);
l2cap_chan_send(chan, &msg, total_len);
@@ -720,7 +719,6 @@ static const struct l2cap_ops a2mp_chan_ops = {
.resume = l2cap_chan_no_resume,
.set_shutdown = l2cap_chan_no_set_shutdown,
.get_sndtimeo = l2cap_chan_no_get_sndtimeo,
- .memcpy_fromiovec = l2cap_chan_no_memcpy_fromiovec,
};
static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked)
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index 339c74ad4553..012e3b03589d 100644
--- a/net/bluetooth/af_bluetooth.c
+++ b/net/bluetooth/af_bluetooth.c
@@ -31,7 +31,7 @@
#include <net/bluetooth/bluetooth.h>
#include <linux/proc_fs.h>
-#define VERSION "2.19"
+#define VERSION "2.20"
/* Bluetooth sockets */
#define BT_MAX_PROTO 8
@@ -237,7 +237,7 @@ int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
}
skb_reset_transport_header(skb);
- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
if (err == 0) {
sock_recv_ts_and_drops(msg, sk, skb);
@@ -328,7 +328,7 @@ int bt_sock_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
}
chunk = min_t(unsigned int, skb->len, size);
- if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, chunk)) {
+ if (skb_copy_datagram_msg(skb, 0, msg, chunk)) {
skb_queue_head(&sk->sk_receive_queue, skb);
if (!copied)
copied = -EFAULT;
diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c
index 2640d78f30b8..ee016f039100 100644
--- a/net/bluetooth/amp.c
+++ b/net/bluetooth/amp.c
@@ -134,6 +134,7 @@ struct hci_conn *phylink_add(struct hci_dev *hdev, struct amp_mgr *mgr,
static int hmac_sha256(u8 *key, u8 ksize, char *plaintext, u8 psize, u8 *output)
{
struct crypto_shash *tfm;
+ struct shash_desc *shash;
int ret;
if (!ksize)
@@ -148,18 +149,24 @@ static int hmac_sha256(u8 *key, u8 ksize, char *plaintext, u8 psize, u8 *output)
ret = crypto_shash_setkey(tfm, key, ksize);
if (ret) {
BT_DBG("crypto_ahash_setkey failed: err %d", ret);
- } else {
- char desc[sizeof(struct shash_desc) +
- crypto_shash_descsize(tfm)] CRYPTO_MINALIGN_ATTR;
- struct shash_desc *shash = (struct shash_desc *)desc;
-
- shash->tfm = tfm;
- shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+ goto failed;
+ }
- ret = crypto_shash_digest(shash, plaintext, psize,
- output);
+ shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm),
+ GFP_KERNEL);
+ if (!shash) {
+ ret = -ENOMEM;
+ goto failed;
}
+ shash->tfm = tfm;
+ shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+ ret = crypto_shash_digest(shash, plaintext, psize, output);
+
+ kfree(shash);
+
+failed:
crypto_free_shash(tfm);
return ret;
}
diff --git a/net/bluetooth/bnep/Kconfig b/net/bluetooth/bnep/Kconfig
index 71791fc9f6b1..9b70317c49dc 100644
--- a/net/bluetooth/bnep/Kconfig
+++ b/net/bluetooth/bnep/Kconfig
@@ -1,6 +1,6 @@
config BT_BNEP
tristate "BNEP protocol support"
- depends on BT
+ depends on BT_BREDR
select CRC32
help
BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet
diff --git a/net/bluetooth/cmtp/Kconfig b/net/bluetooth/cmtp/Kconfig
index 94cbf42ce155..939da0fbdd88 100644
--- a/net/bluetooth/cmtp/Kconfig
+++ b/net/bluetooth/cmtp/Kconfig
@@ -1,6 +1,6 @@
config BT_CMTP
tristate "CMTP protocol support"
- depends on BT && ISDN_CAPI
+ depends on BT_BREDR && ISDN_CAPI
help
CMTP (CAPI Message Transport Protocol) is a transport layer
for CAPI messages. CMTP is required for the Bluetooth Common
diff --git a/net/bluetooth/ecc.c b/net/bluetooth/ecc.c
new file mode 100644
index 000000000000..e1709f8467ac
--- /dev/null
+++ b/net/bluetooth/ecc.c
@@ -0,0 +1,816 @@
+/*
+ * Copyright (c) 2013, Kenneth MacKay
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/random.h>
+
+#include "ecc.h"
+
+/* 256-bit curve */
+#define ECC_BYTES 32
+
+#define MAX_TRIES 16
+
+/* Number of u64's needed */
+#define NUM_ECC_DIGITS (ECC_BYTES / 8)
+
+struct ecc_point {
+ u64 x[NUM_ECC_DIGITS];
+ u64 y[NUM_ECC_DIGITS];
+};
+
+typedef struct {
+ u64 m_low;
+ u64 m_high;
+} uint128_t;
+
+#define CURVE_P_32 { 0xFFFFFFFFFFFFFFFFull, 0x00000000FFFFFFFFull, \
+ 0x0000000000000000ull, 0xFFFFFFFF00000001ull }
+
+#define CURVE_G_32 { \
+ { 0xF4A13945D898C296ull, 0x77037D812DEB33A0ull, \
+ 0xF8BCE6E563A440F2ull, 0x6B17D1F2E12C4247ull }, \
+ { 0xCBB6406837BF51F5ull, 0x2BCE33576B315ECEull, \
+ 0x8EE7EB4A7C0F9E16ull, 0x4FE342E2FE1A7F9Bull } \
+}
+
+#define CURVE_N_32 { 0xF3B9CAC2FC632551ull, 0xBCE6FAADA7179E84ull, \
+ 0xFFFFFFFFFFFFFFFFull, 0xFFFFFFFF00000000ull }
+
+static u64 curve_p[NUM_ECC_DIGITS] = CURVE_P_32;
+static struct ecc_point curve_g = CURVE_G_32;
+static u64 curve_n[NUM_ECC_DIGITS] = CURVE_N_32;
+
+static void vli_clear(u64 *vli)
+{
+ int i;
+
+ for (i = 0; i < NUM_ECC_DIGITS; i++)
+ vli[i] = 0;
+}
+
+/* Returns true if vli == 0, false otherwise. */
+static bool vli_is_zero(const u64 *vli)
+{
+ int i;
+
+ for (i = 0; i < NUM_ECC_DIGITS; i++) {
+ if (vli[i])
+ return false;
+ }
+
+ return true;
+}
+
+/* Returns nonzero if bit bit of vli is set. */
+static u64 vli_test_bit(const u64 *vli, unsigned int bit)
+{
+ return (vli[bit / 64] & ((u64) 1 << (bit % 64)));
+}
+
+/* Counts the number of 64-bit "digits" in vli. */
+static unsigned int vli_num_digits(const u64 *vli)
+{
+ int i;
+
+ /* Search from the end until we find a non-zero digit.
+ * We do it in reverse because we expect that most digits will
+ * be nonzero.
+ */
+ for (i = NUM_ECC_DIGITS - 1; i >= 0 && vli[i] == 0; i--);
+
+ return (i + 1);
+}
+
+/* Counts the number of bits required for vli. */
+static unsigned int vli_num_bits(const u64 *vli)
+{
+ unsigned int i, num_digits;
+ u64 digit;
+
+ num_digits = vli_num_digits(vli);
+ if (num_digits == 0)
+ return 0;
+
+ digit = vli[num_digits - 1];
+ for (i = 0; digit; i++)
+ digit >>= 1;
+
+ return ((num_digits - 1) * 64 + i);
+}
+
+/* Sets dest = src. */
+static void vli_set(u64 *dest, const u64 *src)
+{
+ int i;
+
+ for (i = 0; i < NUM_ECC_DIGITS; i++)
+ dest[i] = src[i];
+}
+
+/* Returns sign of left - right. */
+static int vli_cmp(const u64 *left, const u64 *right)
+{
+ int i;
+
+ for (i = NUM_ECC_DIGITS - 1; i >= 0; i--) {
+ if (left[i] > right[i])
+ return 1;
+ else if (left[i] < right[i])
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Computes result = in << c, returning carry. Can modify in place
+ * (if result == in). 0 < shift < 64.
+ */
+static u64 vli_lshift(u64 *result, const u64 *in,
+ unsigned int shift)
+{
+ u64 carry = 0;
+ int i;
+
+ for (i = 0; i < NUM_ECC_DIGITS; i++) {
+ u64 temp = in[i];
+
+ result[i] = (temp << shift) | carry;
+ carry = temp >> (64 - shift);
+ }
+
+ return carry;
+}
+
+/* Computes vli = vli >> 1. */
+static void vli_rshift1(u64 *vli)
+{
+ u64 *end = vli;
+ u64 carry = 0;
+
+ vli += NUM_ECC_DIGITS;
+
+ while (vli-- > end) {
+ u64 temp = *vli;
+ *vli = (temp >> 1) | carry;
+ carry = temp << 63;
+ }
+}
+
+/* Computes result = left + right, returning carry. Can modify in place. */
+static u64 vli_add(u64 *result, const u64 *left,
+ const u64 *right)
+{
+ u64 carry = 0;
+ int i;
+
+ for (i = 0; i < NUM_ECC_DIGITS; i++) {
+ u64 sum;
+
+ sum = left[i] + right[i] + carry;
+ if (sum != left[i])
+ carry = (sum < left[i]);
+
+ result[i] = sum;
+ }
+
+ return carry;
+}
+
+/* Computes result = left - right, returning borrow. Can modify in place. */
+static u64 vli_sub(u64 *result, const u64 *left, const u64 *right)
+{
+ u64 borrow = 0;
+ int i;
+
+ for (i = 0; i < NUM_ECC_DIGITS; i++) {
+ u64 diff;
+
+ diff = left[i] - right[i] - borrow;
+ if (diff != left[i])
+ borrow = (diff > left[i]);
+
+ result[i] = diff;
+ }
+
+ return borrow;
+}
+
+static uint128_t mul_64_64(u64 left, u64 right)
+{
+ u64 a0 = left & 0xffffffffull;
+ u64 a1 = left >> 32;
+ u64 b0 = right & 0xffffffffull;
+ u64 b1 = right >> 32;
+ u64 m0 = a0 * b0;
+ u64 m1 = a0 * b1;
+ u64 m2 = a1 * b0;
+ u64 m3 = a1 * b1;
+ uint128_t result;
+
+ m2 += (m0 >> 32);
+ m2 += m1;
+
+ /* Overflow */
+ if (m2 < m1)
+ m3 += 0x100000000ull;
+
+ result.m_low = (m0 & 0xffffffffull) | (m2 << 32);
+ result.m_high = m3 + (m2 >> 32);
+
+ return result;
+}
+
+static uint128_t add_128_128(uint128_t a, uint128_t b)
+{
+ uint128_t result;
+
+ result.m_low = a.m_low + b.m_low;
+ result.m_high = a.m_high + b.m_high + (result.m_low < a.m_low);
+
+ return result;
+}
+
+static void vli_mult(u64 *result, const u64 *left, const u64 *right)
+{
+ uint128_t r01 = { 0, 0 };
+ u64 r2 = 0;
+ unsigned int i, k;
+
+ /* Compute each digit of result in sequence, maintaining the
+ * carries.
+ */
+ for (k = 0; k < NUM_ECC_DIGITS * 2 - 1; k++) {
+ unsigned int min;
+
+ if (k < NUM_ECC_DIGITS)
+ min = 0;
+ else
+ min = (k + 1) - NUM_ECC_DIGITS;
+
+ for (i = min; i <= k && i < NUM_ECC_DIGITS; i++) {
+ uint128_t product;
+
+ product = mul_64_64(left[i], right[k - i]);
+
+ r01 = add_128_128(r01, product);
+ r2 += (r01.m_high < product.m_high);
+ }
+
+ result[k] = r01.m_low;
+ r01.m_low = r01.m_high;
+ r01.m_high = r2;
+ r2 = 0;
+ }
+
+ result[NUM_ECC_DIGITS * 2 - 1] = r01.m_low;
+}
+
+static void vli_square(u64 *result, const u64 *left)
+{
+ uint128_t r01 = { 0, 0 };
+ u64 r2 = 0;
+ int i, k;
+
+ for (k = 0; k < NUM_ECC_DIGITS * 2 - 1; k++) {
+ unsigned int min;
+
+ if (k < NUM_ECC_DIGITS)
+ min = 0;
+ else
+ min = (k + 1) - NUM_ECC_DIGITS;
+
+ for (i = min; i <= k && i <= k - i; i++) {
+ uint128_t product;
+
+ product = mul_64_64(left[i], left[k - i]);
+
+ if (i < k - i) {
+ r2 += product.m_high >> 63;
+ product.m_high = (product.m_high << 1) |
+ (product.m_low >> 63);
+ product.m_low <<= 1;
+ }
+
+ r01 = add_128_128(r01, product);
+ r2 += (r01.m_high < product.m_high);
+ }
+
+ result[k] = r01.m_low;
+ r01.m_low = r01.m_high;
+ r01.m_high = r2;
+ r2 = 0;
+ }
+
+ result[NUM_ECC_DIGITS * 2 - 1] = r01.m_low;
+}
+
+/* Computes result = (left + right) % mod.
+ * Assumes that left < mod and right < mod, result != mod.
+ */
+static void vli_mod_add(u64 *result, const u64 *left, const u64 *right,
+ const u64 *mod)
+{
+ u64 carry;
+
+ carry = vli_add(result, left, right);
+
+ /* result > mod (result = mod + remainder), so subtract mod to
+ * get remainder.
+ */
+ if (carry || vli_cmp(result, mod) >= 0)
+ vli_sub(result, result, mod);
+}
+
+/* Computes result = (left - right) % mod.
+ * Assumes that left < mod and right < mod, result != mod.
+ */
+static void vli_mod_sub(u64 *result, const u64 *left, const u64 *right,
+ const u64 *mod)
+{
+ u64 borrow = vli_sub(result, left, right);
+
+ /* In this case, p_result == -diff == (max int) - diff.
+ * Since -x % d == d - x, we can get the correct result from
+ * result + mod (with overflow).
+ */
+ if (borrow)
+ vli_add(result, result, mod);
+}
+
+/* Computes result = product % curve_p
+ from http://www.nsa.gov/ia/_files/nist-routines.pdf */
+static void vli_mmod_fast(u64 *result, const u64 *product)
+{
+ u64 tmp[NUM_ECC_DIGITS];
+ int carry;
+
+ /* t */
+ vli_set(result, product);
+
+ /* s1 */
+ tmp[0] = 0;
+ tmp[1] = product[5] & 0xffffffff00000000ull;
+ tmp[2] = product[6];
+ tmp[3] = product[7];
+ carry = vli_lshift(tmp, tmp, 1);
+ carry += vli_add(result, result, tmp);
+
+ /* s2 */
+ tmp[1] = product[6] << 32;
+ tmp[2] = (product[6] >> 32) | (product[7] << 32);
+ tmp[3] = product[7] >> 32;
+ carry += vli_lshift(tmp, tmp, 1);
+ carry += vli_add(result, result, tmp);
+
+ /* s3 */
+ tmp[0] = product[4];
+ tmp[1] = product[5] & 0xffffffff;
+ tmp[2] = 0;
+ tmp[3] = product[7];
+ carry += vli_add(result, result, tmp);
+
+ /* s4 */
+ tmp[0] = (product[4] >> 32) | (product[5] << 32);
+ tmp[1] = (product[5] >> 32) | (product[6] & 0xffffffff00000000ull);
+ tmp[2] = product[7];
+ tmp[3] = (product[6] >> 32) | (product[4] << 32);
+ carry += vli_add(result, result, tmp);
+
+ /* d1 */
+ tmp[0] = (product[5] >> 32) | (product[6] << 32);
+ tmp[1] = (product[6] >> 32);
+ tmp[2] = 0;
+ tmp[3] = (product[4] & 0xffffffff) | (product[5] << 32);
+ carry -= vli_sub(result, result, tmp);
+
+ /* d2 */
+ tmp[0] = product[6];
+ tmp[1] = product[7];
+ tmp[2] = 0;
+ tmp[3] = (product[4] >> 32) | (product[5] & 0xffffffff00000000ull);
+ carry -= vli_sub(result, result, tmp);
+
+ /* d3 */
+ tmp[0] = (product[6] >> 32) | (product[7] << 32);
+ tmp[1] = (product[7] >> 32) | (product[4] << 32);
+ tmp[2] = (product[4] >> 32) | (product[5] << 32);
+ tmp[3] = (product[6] << 32);
+ carry -= vli_sub(result, result, tmp);
+
+ /* d4 */
+ tmp[0] = product[7];
+ tmp[1] = product[4] & 0xffffffff00000000ull;
+ tmp[2] = product[5];
+ tmp[3] = product[6] & 0xffffffff00000000ull;
+ carry -= vli_sub(result, result, tmp);
+
+ if (carry < 0) {
+ do {
+ carry += vli_add(result, result, curve_p);
+ } while (carry < 0);
+ } else {
+ while (carry || vli_cmp(curve_p, result) != 1)
+ carry -= vli_sub(result, result, curve_p);
+ }
+}
+
+/* Computes result = (left * right) % curve_p. */
+static void vli_mod_mult_fast(u64 *result, const u64 *left, const u64 *right)
+{
+ u64 product[2 * NUM_ECC_DIGITS];
+
+ vli_mult(product, left, right);
+ vli_mmod_fast(result, product);
+}
+
+/* Computes result = left^2 % curve_p. */
+static void vli_mod_square_fast(u64 *result, const u64 *left)
+{
+ u64 product[2 * NUM_ECC_DIGITS];
+
+ vli_square(product, left);
+ vli_mmod_fast(result, product);
+}
+
+#define EVEN(vli) (!(vli[0] & 1))
+/* Computes result = (1 / p_input) % mod. All VLIs are the same size.
+ * See "From Euclid's GCD to Montgomery Multiplication to the Great Divide"
+ * https://labs.oracle.com/techrep/2001/smli_tr-2001-95.pdf
+ */
+static void vli_mod_inv(u64 *result, const u64 *input, const u64 *mod)
+{
+ u64 a[NUM_ECC_DIGITS], b[NUM_ECC_DIGITS];
+ u64 u[NUM_ECC_DIGITS], v[NUM_ECC_DIGITS];
+ u64 carry;
+ int cmp_result;
+
+ if (vli_is_zero(input)) {
+ vli_clear(result);
+ return;
+ }
+
+ vli_set(a, input);
+ vli_set(b, mod);
+ vli_clear(u);
+ u[0] = 1;
+ vli_clear(v);
+
+ while ((cmp_result = vli_cmp(a, b)) != 0) {
+ carry = 0;
+
+ if (EVEN(a)) {
+ vli_rshift1(a);
+
+ if (!EVEN(u))
+ carry = vli_add(u, u, mod);
+
+ vli_rshift1(u);
+ if (carry)
+ u[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull;
+ } else if (EVEN(b)) {
+ vli_rshift1(b);
+
+ if (!EVEN(v))
+ carry = vli_add(v, v, mod);
+
+ vli_rshift1(v);
+ if (carry)
+ v[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull;
+ } else if (cmp_result > 0) {
+ vli_sub(a, a, b);
+ vli_rshift1(a);
+
+ if (vli_cmp(u, v) < 0)
+ vli_add(u, u, mod);
+
+ vli_sub(u, u, v);
+ if (!EVEN(u))
+ carry = vli_add(u, u, mod);
+
+ vli_rshift1(u);
+ if (carry)
+ u[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull;
+ } else {
+ vli_sub(b, b, a);
+ vli_rshift1(b);
+
+ if (vli_cmp(v, u) < 0)
+ vli_add(v, v, mod);
+
+ vli_sub(v, v, u);
+ if (!EVEN(v))
+ carry = vli_add(v, v, mod);
+
+ vli_rshift1(v);
+ if (carry)
+ v[NUM_ECC_DIGITS - 1] |= 0x8000000000000000ull;
+ }
+ }
+
+ vli_set(result, u);
+}
+
+/* ------ Point operations ------ */
+
+/* Returns true if p_point is the point at infinity, false otherwise. */
+static bool ecc_point_is_zero(const struct ecc_point *point)
+{
+ return (vli_is_zero(point->x) && vli_is_zero(point->y));
+}
+
+/* Point multiplication algorithm using Montgomery's ladder with co-Z
+ * coordinates. From http://eprint.iacr.org/2011/338.pdf
+ */
+
+/* Double in place */
+static void ecc_point_double_jacobian(u64 *x1, u64 *y1, u64 *z1)
+{
+ /* t1 = x, t2 = y, t3 = z */
+ u64 t4[NUM_ECC_DIGITS];
+ u64 t5[NUM_ECC_DIGITS];
+
+ if (vli_is_zero(z1))
+ return;
+
+ vli_mod_square_fast(t4, y1); /* t4 = y1^2 */
+ vli_mod_mult_fast(t5, x1, t4); /* t5 = x1*y1^2 = A */
+ vli_mod_square_fast(t4, t4); /* t4 = y1^4 */
+ vli_mod_mult_fast(y1, y1, z1); /* t2 = y1*z1 = z3 */
+ vli_mod_square_fast(z1, z1); /* t3 = z1^2 */
+
+ vli_mod_add(x1, x1, z1, curve_p); /* t1 = x1 + z1^2 */
+ vli_mod_add(z1, z1, z1, curve_p); /* t3 = 2*z1^2 */
+ vli_mod_sub(z1, x1, z1, curve_p); /* t3 = x1 - z1^2 */
+ vli_mod_mult_fast(x1, x1, z1); /* t1 = x1^2 - z1^4 */
+
+ vli_mod_add(z1, x1, x1, curve_p); /* t3 = 2*(x1^2 - z1^4) */
+ vli_mod_add(x1, x1, z1, curve_p); /* t1 = 3*(x1^2 - z1^4) */
+ if (vli_test_bit(x1, 0)) {
+ u64 carry = vli_add(x1, x1, curve_p);
+ vli_rshift1(x1);
+ x1[NUM_ECC_DIGITS - 1] |= carry << 63;
+ } else {
+ vli_rshift1(x1);
+ }
+ /* t1 = 3/2*(x1^2 - z1^4) = B */
+
+ vli_mod_square_fast(z1, x1); /* t3 = B^2 */
+ vli_mod_sub(z1, z1, t5, curve_p); /* t3 = B^2 - A */
+ vli_mod_sub(z1, z1, t5, curve_p); /* t3 = B^2 - 2A = x3 */
+ vli_mod_sub(t5, t5, z1, curve_p); /* t5 = A - x3 */
+ vli_mod_mult_fast(x1, x1, t5); /* t1 = B * (A - x3) */
+ vli_mod_sub(t4, x1, t4, curve_p); /* t4 = B * (A - x3) - y1^4 = y3 */
+
+ vli_set(x1, z1);
+ vli_set(z1, y1);
+ vli_set(y1, t4);
+}
+
+/* Modify (x1, y1) => (x1 * z^2, y1 * z^3) */
+static void apply_z(u64 *x1, u64 *y1, u64 *z)
+{
+ u64 t1[NUM_ECC_DIGITS];
+
+ vli_mod_square_fast(t1, z); /* z^2 */
+ vli_mod_mult_fast(x1, x1, t1); /* x1 * z^2 */
+ vli_mod_mult_fast(t1, t1, z); /* z^3 */
+ vli_mod_mult_fast(y1, y1, t1); /* y1 * z^3 */
+}
+
+/* P = (x1, y1) => 2P, (x2, y2) => P' */
+static void xycz_initial_double(u64 *x1, u64 *y1, u64 *x2, u64 *y2,
+ u64 *p_initial_z)
+{
+ u64 z[NUM_ECC_DIGITS];
+
+ vli_set(x2, x1);
+ vli_set(y2, y1);
+
+ vli_clear(z);
+ z[0] = 1;
+
+ if (p_initial_z)
+ vli_set(z, p_initial_z);
+
+ apply_z(x1, y1, z);
+
+ ecc_point_double_jacobian(x1, y1, z);
+
+ apply_z(x2, y2, z);
+}
+
+/* Input P = (x1, y1, Z), Q = (x2, y2, Z)
+ * Output P' = (x1', y1', Z3), P + Q = (x3, y3, Z3)
+ * or P => P', Q => P + Q
+ */
+static void xycz_add(u64 *x1, u64 *y1, u64 *x2, u64 *y2)
+{
+ /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */
+ u64 t5[NUM_ECC_DIGITS];
+
+ vli_mod_sub(t5, x2, x1, curve_p); /* t5 = x2 - x1 */
+ vli_mod_square_fast(t5, t5); /* t5 = (x2 - x1)^2 = A */
+ vli_mod_mult_fast(x1, x1, t5); /* t1 = x1*A = B */
+ vli_mod_mult_fast(x2, x2, t5); /* t3 = x2*A = C */
+ vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y2 - y1 */
+ vli_mod_square_fast(t5, y2); /* t5 = (y2 - y1)^2 = D */
+
+ vli_mod_sub(t5, t5, x1, curve_p); /* t5 = D - B */
+ vli_mod_sub(t5, t5, x2, curve_p); /* t5 = D - B - C = x3 */
+ vli_mod_sub(x2, x2, x1, curve_p); /* t3 = C - B */
+ vli_mod_mult_fast(y1, y1, x2); /* t2 = y1*(C - B) */
+ vli_mod_sub(x2, x1, t5, curve_p); /* t3 = B - x3 */
+ vli_mod_mult_fast(y2, y2, x2); /* t4 = (y2 - y1)*(B - x3) */
+ vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y3 */
+
+ vli_set(x2, t5);
+}
+
+/* Input P = (x1, y1, Z), Q = (x2, y2, Z)
+ * Output P + Q = (x3, y3, Z3), P - Q = (x3', y3', Z3)
+ * or P => P - Q, Q => P + Q
+ */
+static void xycz_add_c(u64 *x1, u64 *y1, u64 *x2, u64 *y2)
+{
+ /* t1 = X1, t2 = Y1, t3 = X2, t4 = Y2 */
+ u64 t5[NUM_ECC_DIGITS];
+ u64 t6[NUM_ECC_DIGITS];
+ u64 t7[NUM_ECC_DIGITS];
+
+ vli_mod_sub(t5, x2, x1, curve_p); /* t5 = x2 - x1 */
+ vli_mod_square_fast(t5, t5); /* t5 = (x2 - x1)^2 = A */
+ vli_mod_mult_fast(x1, x1, t5); /* t1 = x1*A = B */
+ vli_mod_mult_fast(x2, x2, t5); /* t3 = x2*A = C */
+ vli_mod_add(t5, y2, y1, curve_p); /* t4 = y2 + y1 */
+ vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y2 - y1 */
+
+ vli_mod_sub(t6, x2, x1, curve_p); /* t6 = C - B */
+ vli_mod_mult_fast(y1, y1, t6); /* t2 = y1 * (C - B) */
+ vli_mod_add(t6, x1, x2, curve_p); /* t6 = B + C */
+ vli_mod_square_fast(x2, y2); /* t3 = (y2 - y1)^2 */
+ vli_mod_sub(x2, x2, t6, curve_p); /* t3 = x3 */
+
+ vli_mod_sub(t7, x1, x2, curve_p); /* t7 = B - x3 */
+ vli_mod_mult_fast(y2, y2, t7); /* t4 = (y2 - y1)*(B - x3) */
+ vli_mod_sub(y2, y2, y1, curve_p); /* t4 = y3 */
+
+ vli_mod_square_fast(t7, t5); /* t7 = (y2 + y1)^2 = F */
+ vli_mod_sub(t7, t7, t6, curve_p); /* t7 = x3' */
+ vli_mod_sub(t6, t7, x1, curve_p); /* t6 = x3' - B */
+ vli_mod_mult_fast(t6, t6, t5); /* t6 = (y2 + y1)*(x3' - B) */
+ vli_mod_sub(y1, t6, y1, curve_p); /* t2 = y3' */
+
+ vli_set(x1, t7);
+}
+
+static void ecc_point_mult(struct ecc_point *result,
+ const struct ecc_point *point, u64 *scalar,
+ u64 *initial_z, int num_bits)
+{
+ /* R0 and R1 */
+ u64 rx[2][NUM_ECC_DIGITS];
+ u64 ry[2][NUM_ECC_DIGITS];
+ u64 z[NUM_ECC_DIGITS];
+ int i, nb;
+
+ vli_set(rx[1], point->x);
+ vli_set(ry[1], point->y);
+
+ xycz_initial_double(rx[1], ry[1], rx[0], ry[0], initial_z);
+
+ for (i = num_bits - 2; i > 0; i--) {
+ nb = !vli_test_bit(scalar, i);
+ xycz_add_c(rx[1 - nb], ry[1 - nb], rx[nb], ry[nb]);
+ xycz_add(rx[nb], ry[nb], rx[1 - nb], ry[1 - nb]);
+ }
+
+ nb = !vli_test_bit(scalar, 0);
+ xycz_add_c(rx[1 - nb], ry[1 - nb], rx[nb], ry[nb]);
+
+ /* Find final 1/Z value. */
+ vli_mod_sub(z, rx[1], rx[0], curve_p); /* X1 - X0 */
+ vli_mod_mult_fast(z, z, ry[1 - nb]); /* Yb * (X1 - X0) */
+ vli_mod_mult_fast(z, z, point->x); /* xP * Yb * (X1 - X0) */
+ vli_mod_inv(z, z, curve_p); /* 1 / (xP * Yb * (X1 - X0)) */
+ vli_mod_mult_fast(z, z, point->y); /* yP / (xP * Yb * (X1 - X0)) */
+ vli_mod_mult_fast(z, z, rx[1 - nb]); /* Xb * yP / (xP * Yb * (X1 - X0)) */
+ /* End 1/Z calculation */
+
+ xycz_add(rx[nb], ry[nb], rx[1 - nb], ry[1 - nb]);
+
+ apply_z(rx[0], ry[0], z);
+
+ vli_set(result->x, rx[0]);
+ vli_set(result->y, ry[0]);
+}
+
+static void ecc_bytes2native(const u8 bytes[ECC_BYTES],
+ u64 native[NUM_ECC_DIGITS])
+{
+ int i;
+
+ for (i = 0; i < NUM_ECC_DIGITS; i++) {
+ const u8 *digit = bytes + 8 * (NUM_ECC_DIGITS - 1 - i);
+
+ native[NUM_ECC_DIGITS - 1 - i] =
+ ((u64) digit[0] << 0) |
+ ((u64) digit[1] << 8) |
+ ((u64) digit[2] << 16) |
+ ((u64) digit[3] << 24) |
+ ((u64) digit[4] << 32) |
+ ((u64) digit[5] << 40) |
+ ((u64) digit[6] << 48) |
+ ((u64) digit[7] << 56);
+ }
+}
+
+static void ecc_native2bytes(const u64 native[NUM_ECC_DIGITS],
+ u8 bytes[ECC_BYTES])
+{
+ int i;
+
+ for (i = 0; i < NUM_ECC_DIGITS; i++) {
+ u8 *digit = bytes + 8 * (NUM_ECC_DIGITS - 1 - i);
+
+ digit[0] = native[NUM_ECC_DIGITS - 1 - i] >> 0;
+ digit[1] = native[NUM_ECC_DIGITS - 1 - i] >> 8;
+ digit[2] = native[NUM_ECC_DIGITS - 1 - i] >> 16;
+ digit[3] = native[NUM_ECC_DIGITS - 1 - i] >> 24;
+ digit[4] = native[NUM_ECC_DIGITS - 1 - i] >> 32;
+ digit[5] = native[NUM_ECC_DIGITS - 1 - i] >> 40;
+ digit[6] = native[NUM_ECC_DIGITS - 1 - i] >> 48;
+ digit[7] = native[NUM_ECC_DIGITS - 1 - i] >> 56;
+ }
+}
+
+bool ecc_make_key(u8 public_key[64], u8 private_key[32])
+{
+ struct ecc_point pk;
+ u64 priv[NUM_ECC_DIGITS];
+ unsigned int tries = 0;
+
+ do {
+ if (tries++ >= MAX_TRIES)
+ return false;
+
+ get_random_bytes(priv, ECC_BYTES);
+
+ if (vli_is_zero(priv))
+ continue;
+
+ /* Make sure the private key is in the range [1, n-1]. */
+ if (vli_cmp(curve_n, priv) != 1)
+ continue;
+
+ ecc_point_mult(&pk, &curve_g, priv, NULL, vli_num_bits(priv));
+ } while (ecc_point_is_zero(&pk));
+
+ ecc_native2bytes(priv, private_key);
+ ecc_native2bytes(pk.x, public_key);
+ ecc_native2bytes(pk.y, &public_key[32]);
+
+ return true;
+}
+
+bool ecdh_shared_secret(const u8 public_key[64], const u8 private_key[32],
+ u8 secret[32])
+{
+ u64 priv[NUM_ECC_DIGITS];
+ u64 rand[NUM_ECC_DIGITS];
+ struct ecc_point product, pk;
+
+ get_random_bytes(rand, ECC_BYTES);
+
+ ecc_bytes2native(public_key, pk.x);
+ ecc_bytes2native(&public_key[32], pk.y);
+ ecc_bytes2native(private_key, priv);
+
+ ecc_point_mult(&product, &pk, priv, rand, vli_num_bits(priv));
+
+ ecc_native2bytes(product.x, secret);
+
+ return !ecc_point_is_zero(&product);
+}
diff --git a/net/bluetooth/ecc.h b/net/bluetooth/ecc.h
new file mode 100644
index 000000000000..8d6a2f4d1905
--- /dev/null
+++ b/net/bluetooth/ecc.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2013, Kenneth MacKay
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Create a public/private key pair.
+ * Outputs:
+ * public_key - Will be filled in with the public key.
+ * private_key - Will be filled in with the private key.
+ *
+ * Returns true if the key pair was generated successfully, false
+ * if an error occurred. The keys are with the LSB first.
+ */
+bool ecc_make_key(u8 public_key[64], u8 private_key[32]);
+
+/* Compute a shared secret given your secret key and someone else's
+ * public key.
+ * Note: It is recommended that you hash the result of ecdh_shared_secret
+ * before using it for symmetric encryption or HMAC.
+ *
+ * Inputs:
+ * public_key - The public key of the remote party
+ * private_key - Your private key.
+ *
+ * Outputs:
+ * secret - Will be filled in with the shared secret value.
+ *
+ * Returns true if the shared secret was generated successfully, false
+ * if an error occurred. Both input and output parameters are with the
+ * LSB first.
+ */
+bool ecdh_shared_secret(const u8 public_key[64], const u8 private_key[32],
+ u8 secret[32]);
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index b9517bd17190..79d84b88b8f0 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -141,10 +141,11 @@ int hci_disconnect(struct hci_conn *conn, __u8 reason)
*/
if (conn->type == ACL_LINK && conn->role == HCI_ROLE_MASTER) {
struct hci_dev *hdev = conn->hdev;
- struct hci_cp_read_clock_offset cp;
+ struct hci_cp_read_clock_offset clkoff_cp;
- cp.handle = cpu_to_le16(conn->handle);
- hci_send_cmd(hdev, HCI_OP_READ_CLOCK_OFFSET, sizeof(cp), &cp);
+ clkoff_cp.handle = cpu_to_le16(conn->handle);
+ hci_send_cmd(hdev, HCI_OP_READ_CLOCK_OFFSET, sizeof(clkoff_cp),
+ &clkoff_cp);
}
conn->state = BT_DISCONN;
@@ -415,7 +416,7 @@ static void le_conn_timeout(struct work_struct *work)
* happen with broken hardware or if low duty cycle was used
* (which doesn't have a timeout of its own).
*/
- if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {
+ if (conn->role == HCI_ROLE_SLAVE) {
u8 enable = 0x00;
hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable),
&enable);
@@ -448,6 +449,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
conn->io_capability = hdev->io_capability;
conn->remote_auth = 0xff;
conn->key_type = 0xff;
+ conn->rssi = HCI_RSSI_INVALID;
conn->tx_power = HCI_TX_POWER_INVALID;
conn->max_tx_power = HCI_TX_POWER_INVALID;
@@ -517,7 +519,7 @@ int hci_conn_del(struct hci_conn *conn)
/* Unacked frames */
hdev->acl_cnt += conn->sent;
} else if (conn->type == LE_LINK) {
- cancel_delayed_work_sync(&conn->le_conn_timeout);
+ cancel_delayed_work(&conn->le_conn_timeout);
if (hdev->le_pkts)
hdev->le_cnt += conn->sent;
@@ -544,6 +546,9 @@ int hci_conn_del(struct hci_conn *conn)
hci_conn_del_sysfs(conn);
+ if (test_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags))
+ hci_conn_params_del(conn->hdev, &conn->dst, conn->dst_type);
+
hci_dev_put(hdev);
hci_conn_put(conn);
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index cb05d7f16a34..93f92a085506 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -200,31 +200,6 @@ static const struct file_operations blacklist_fops = {
.release = single_release,
};
-static int whitelist_show(struct seq_file *f, void *p)
-{
- struct hci_dev *hdev = f->private;
- struct bdaddr_list *b;
-
- hci_dev_lock(hdev);
- list_for_each_entry(b, &hdev->whitelist, list)
- seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
- hci_dev_unlock(hdev);
-
- return 0;
-}
-
-static int whitelist_open(struct inode *inode, struct file *file)
-{
- return single_open(file, whitelist_show, inode->i_private);
-}
-
-static const struct file_operations whitelist_fops = {
- .open = whitelist_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
static int uuids_show(struct seq_file *f, void *p)
{
struct hci_dev *hdev = f->private;
@@ -299,15 +274,13 @@ static const struct file_operations inquiry_cache_fops = {
static int link_keys_show(struct seq_file *f, void *ptr)
{
struct hci_dev *hdev = f->private;
- struct list_head *p, *n;
+ struct link_key *key;
- hci_dev_lock(hdev);
- list_for_each_safe(p, n, &hdev->link_keys) {
- struct link_key *key = list_entry(p, struct link_key, list);
+ rcu_read_lock();
+ list_for_each_entry_rcu(key, &hdev->link_keys, list)
seq_printf(f, "%pMR %u %*phN %u\n", &key->bdaddr, key->type,
HCI_LINK_KEY_SIZE, key->val, key->pin_len);
- }
- hci_dev_unlock(hdev);
+ rcu_read_unlock();
return 0;
}
@@ -433,6 +406,49 @@ static const struct file_operations force_sc_support_fops = {
.llseek = default_llseek,
};
+static ssize_t force_lesc_support_read(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct hci_dev *hdev = file->private_data;
+ char buf[3];
+
+ buf[0] = test_bit(HCI_FORCE_LESC, &hdev->dbg_flags) ? 'Y': 'N';
+ buf[1] = '\n';
+ buf[2] = '\0';
+ return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t force_lesc_support_write(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct hci_dev *hdev = file->private_data;
+ char buf[32];
+ size_t buf_size = min(count, (sizeof(buf)-1));
+ bool enable;
+
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ buf[buf_size] = '\0';
+ if (strtobool(buf, &enable))
+ return -EINVAL;
+
+ if (enable == test_bit(HCI_FORCE_LESC, &hdev->dbg_flags))
+ return -EALREADY;
+
+ change_bit(HCI_FORCE_LESC, &hdev->dbg_flags);
+
+ return count;
+}
+
+static const struct file_operations force_lesc_support_fops = {
+ .open = simple_open,
+ .read = force_lesc_support_read,
+ .write = force_lesc_support_write,
+ .llseek = default_llseek,
+};
+
static ssize_t sc_only_mode_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
@@ -773,16 +789,15 @@ static const struct file_operations white_list_fops = {
static int identity_resolving_keys_show(struct seq_file *f, void *ptr)
{
struct hci_dev *hdev = f->private;
- struct list_head *p, *n;
+ struct smp_irk *irk;
- hci_dev_lock(hdev);
- list_for_each_safe(p, n, &hdev->identity_resolving_keys) {
- struct smp_irk *irk = list_entry(p, struct smp_irk, list);
+ rcu_read_lock();
+ list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
seq_printf(f, "%pMR (type %u) %*phN %pMR\n",
&irk->bdaddr, irk->addr_type,
16, irk->val, &irk->rpa);
}
- hci_dev_unlock(hdev);
+ rcu_read_unlock();
return 0;
}
@@ -803,17 +818,15 @@ static const struct file_operations identity_resolving_keys_fops = {
static int long_term_keys_show(struct seq_file *f, void *ptr)
{
struct hci_dev *hdev = f->private;
- struct list_head *p, *n;
+ struct smp_ltk *ltk;
- hci_dev_lock(hdev);
- list_for_each_safe(p, n, &hdev->long_term_keys) {
- struct smp_ltk *ltk = list_entry(p, struct smp_ltk, list);
+ rcu_read_lock();
+ list_for_each_entry_rcu(ltk, &hdev->long_term_keys, list)
seq_printf(f, "%pMR (type %u) %u 0x%02x %u %.4x %.16llx %*phN\n",
&ltk->bdaddr, ltk->bdaddr_type, ltk->authenticated,
ltk->type, ltk->enc_size, __le16_to_cpu(ltk->ediv),
__le64_to_cpu(ltk->rand), 16, ltk->val);
- }
- hci_dev_unlock(hdev);
+ rcu_read_unlock();
return 0;
}
@@ -1030,10 +1043,13 @@ static int device_list_show(struct seq_file *f, void *ptr)
{
struct hci_dev *hdev = f->private;
struct hci_conn_params *p;
+ struct bdaddr_list *b;
hci_dev_lock(hdev);
+ list_for_each_entry(b, &hdev->whitelist, list)
+ seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
list_for_each_entry(p, &hdev->le_conn_params, list) {
- seq_printf(f, "%pMR %u %u\n", &p->addr, p->addr_type,
+ seq_printf(f, "%pMR (type %u) %u\n", &p->addr, p->addr_type,
p->auto_connect);
}
hci_dev_unlock(hdev);
@@ -1147,13 +1163,16 @@ struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
hdev->req_status = HCI_REQ_PEND;
- err = hci_req_run(&req, hci_req_sync_complete);
- if (err < 0)
- return ERR_PTR(err);
-
add_wait_queue(&hdev->req_wait_q, &wait);
set_current_state(TASK_INTERRUPTIBLE);
+ err = hci_req_run(&req, hci_req_sync_complete);
+ if (err < 0) {
+ remove_wait_queue(&hdev->req_wait_q, &wait);
+ set_current_state(TASK_RUNNING);
+ return ERR_PTR(err);
+ }
+
schedule_timeout(timeout);
remove_wait_queue(&hdev->req_wait_q, &wait);
@@ -1211,10 +1230,16 @@ static int __hci_req_sync(struct hci_dev *hdev,
func(&req, opt);
+ add_wait_queue(&hdev->req_wait_q, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
err = hci_req_run(&req, hci_req_sync_complete);
if (err < 0) {
hdev->req_status = 0;
+ remove_wait_queue(&hdev->req_wait_q, &wait);
+ set_current_state(TASK_RUNNING);
+
/* ENODATA means the HCI request command queue is empty.
* This can happen when a request with conditionals doesn't
* trigger any commands to be sent. This is normal behavior
@@ -1226,9 +1251,6 @@ static int __hci_req_sync(struct hci_dev *hdev,
return err;
}
- add_wait_queue(&hdev->req_wait_q, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
-
schedule_timeout(timeout);
remove_wait_queue(&hdev->req_wait_q, &wait);
@@ -1713,6 +1735,28 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
* Parameter Request
*/
+ /* If the controller supports Extended Scanner Filter
+ * Policies, enable the correspondig event.
+ */
+ if (hdev->le_features[0] & HCI_LE_EXT_SCAN_POLICY)
+ events[1] |= 0x04; /* LE Direct Advertising
+ * Report
+ */
+
+ /* If the controller supports the LE Read Local P-256
+ * Public Key command, enable the corresponding event.
+ */
+ if (hdev->commands[34] & 0x02)
+ events[0] |= 0x80; /* LE Read Local P-256
+ * Public Key Complete
+ */
+
+ /* If the controller supports the LE Generate DHKey
+ * command, enable the corresponding event.
+ */
+ if (hdev->commands[34] & 0x04)
+ events[1] |= 0x01; /* LE Generate DHKey Complete */
+
hci_req_add(req, HCI_OP_LE_SET_EVENT_MASK, sizeof(events),
events);
@@ -1755,9 +1799,7 @@ static void hci_init4_req(struct hci_request *req, unsigned long opt)
hci_req_add(req, HCI_OP_READ_SYNC_TRAIN_PARAMS, 0, NULL);
/* Enable Secure Connections if supported and configured */
- if ((lmp_sc_capable(hdev) ||
- test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) &&
- test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) {
+ if (bredr_sc_enabled(hdev)) {
u8 support = 0x01;
hci_req_add(req, HCI_OP_WRITE_SC_SUPPORT,
sizeof(support), &support);
@@ -1811,10 +1853,10 @@ static int __hci_init(struct hci_dev *hdev)
&hdev->manufacturer);
debugfs_create_u8("hci_version", 0444, hdev->debugfs, &hdev->hci_ver);
debugfs_create_u16("hci_revision", 0444, hdev->debugfs, &hdev->hci_rev);
+ debugfs_create_file("device_list", 0444, hdev->debugfs, hdev,
+ &device_list_fops);
debugfs_create_file("blacklist", 0444, hdev->debugfs, hdev,
&blacklist_fops);
- debugfs_create_file("whitelist", 0444, hdev->debugfs, hdev,
- &whitelist_fops);
debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
debugfs_create_file("conn_info_min_age", 0644, hdev->debugfs, hdev,
@@ -1840,6 +1882,10 @@ static int __hci_init(struct hci_dev *hdev)
hdev, &force_sc_support_fops);
debugfs_create_file("sc_only_mode", 0444, hdev->debugfs,
hdev, &sc_only_mode_fops);
+ if (lmp_le_capable(hdev))
+ debugfs_create_file("force_lesc_support", 0644,
+ hdev->debugfs, hdev,
+ &force_lesc_support_fops);
}
if (lmp_sniff_capable(hdev)) {
@@ -1893,8 +1939,6 @@ static int __hci_init(struct hci_dev *hdev)
hdev, &adv_min_interval_fops);
debugfs_create_file("adv_max_interval", 0644, hdev->debugfs,
hdev, &adv_max_interval_fops);
- debugfs_create_file("device_list", 0444, hdev->debugfs, hdev,
- &device_list_fops);
debugfs_create_u16("discov_interleaved_timeout", 0644,
hdev->debugfs,
&hdev->discov_interleaved_timeout);
@@ -2138,7 +2182,7 @@ u32 hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data,
BT_DBG("cache %p, %pMR", cache, &data->bdaddr);
- hci_remove_remote_oob_data(hdev, &data->bdaddr);
+ hci_remove_remote_oob_data(hdev, &data->bdaddr, BDADDR_BREDR);
if (!data->ssp_mode)
flags |= MGMT_DEV_FOUND_LEGACY_PAIRING;
@@ -2584,6 +2628,11 @@ static int hci_dev_do_close(struct hci_dev *hdev)
if (test_bit(HCI_MGMT, &hdev->dev_flags))
cancel_delayed_work_sync(&hdev->rpa_expired);
+ /* Avoid potential lockdep warnings from the *_flush() calls by
+ * ensuring the workqueue is empty up front.
+ */
+ drain_workqueue(hdev->workqueue);
+
hci_dev_lock(hdev);
hci_inquiry_cache_flush(hdev);
hci_pend_le_actions_clear(hdev);
@@ -2707,6 +2756,11 @@ int hci_dev_reset(__u16 dev)
skb_queue_purge(&hdev->rx_q);
skb_queue_purge(&hdev->cmd_q);
+ /* Avoid potential lockdep warnings from the *_flush() calls by
+ * ensuring the workqueue is empty up front.
+ */
+ drain_workqueue(hdev->workqueue);
+
hci_dev_lock(hdev);
hci_inquiry_cache_flush(hdev);
hci_conn_hash_flush(hdev);
@@ -3112,35 +3166,31 @@ void hci_uuids_clear(struct hci_dev *hdev)
void hci_link_keys_clear(struct hci_dev *hdev)
{
- struct list_head *p, *n;
-
- list_for_each_safe(p, n, &hdev->link_keys) {
- struct link_key *key;
-
- key = list_entry(p, struct link_key, list);
+ struct link_key *key;
- list_del(p);
- kfree(key);
+ list_for_each_entry_rcu(key, &hdev->link_keys, list) {
+ list_del_rcu(&key->list);
+ kfree_rcu(key, rcu);
}
}
void hci_smp_ltks_clear(struct hci_dev *hdev)
{
- struct smp_ltk *k, *tmp;
+ struct smp_ltk *k;
- list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) {
- list_del(&k->list);
- kfree(k);
+ list_for_each_entry_rcu(k, &hdev->long_term_keys, list) {
+ list_del_rcu(&k->list);
+ kfree_rcu(k, rcu);
}
}
void hci_smp_irks_clear(struct hci_dev *hdev)
{
- struct smp_irk *k, *tmp;
+ struct smp_irk *k;
- list_for_each_entry_safe(k, tmp, &hdev->identity_resolving_keys, list) {
- list_del(&k->list);
- kfree(k);
+ list_for_each_entry_rcu(k, &hdev->identity_resolving_keys, list) {
+ list_del_rcu(&k->list);
+ kfree_rcu(k, rcu);
}
}
@@ -3148,9 +3198,14 @@ struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
{
struct link_key *k;
- list_for_each_entry(k, &hdev->link_keys, list)
- if (bacmp(bdaddr, &k->bdaddr) == 0)
+ rcu_read_lock();
+ list_for_each_entry_rcu(k, &hdev->link_keys, list) {
+ if (bacmp(bdaddr, &k->bdaddr) == 0) {
+ rcu_read_unlock();
return k;
+ }
+ }
+ rcu_read_unlock();
return NULL;
}
@@ -3174,6 +3229,10 @@ static bool hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn,
if (!conn)
return true;
+ /* BR/EDR key derived using SC from an LE link */
+ if (conn->type == LE_LINK)
+ return true;
+
/* Neither local nor remote side had no-bonding as requirement */
if (conn->auth_type > 0x01 && conn->remote_auth > 0x01)
return true;
@@ -3199,34 +3258,22 @@ static u8 ltk_role(u8 type)
return HCI_ROLE_SLAVE;
}
-struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand,
- u8 role)
+struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 addr_type, u8 role)
{
struct smp_ltk *k;
- list_for_each_entry(k, &hdev->long_term_keys, list) {
- if (k->ediv != ediv || k->rand != rand)
- continue;
-
- if (ltk_role(k->type) != role)
+ rcu_read_lock();
+ list_for_each_entry_rcu(k, &hdev->long_term_keys, list) {
+ if (addr_type != k->bdaddr_type || bacmp(bdaddr, &k->bdaddr))
continue;
- return k;
- }
-
- return NULL;
-}
-
-struct smp_ltk *hci_find_ltk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 addr_type, u8 role)
-{
- struct smp_ltk *k;
-
- list_for_each_entry(k, &hdev->long_term_keys, list)
- if (addr_type == k->bdaddr_type &&
- bacmp(bdaddr, &k->bdaddr) == 0 &&
- ltk_role(k->type) == role)
+ if (smp_ltk_is_sc(k) || ltk_role(k->type) == role) {
+ rcu_read_unlock();
return k;
+ }
+ }
+ rcu_read_unlock();
return NULL;
}
@@ -3235,17 +3282,22 @@ struct smp_irk *hci_find_irk_by_rpa(struct hci_dev *hdev, bdaddr_t *rpa)
{
struct smp_irk *irk;
- list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
- if (!bacmp(&irk->rpa, rpa))
+ rcu_read_lock();
+ list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
+ if (!bacmp(&irk->rpa, rpa)) {
+ rcu_read_unlock();
return irk;
+ }
}
- list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
+ list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
if (smp_irk_matches(hdev, irk->val, rpa)) {
bacpy(&irk->rpa, rpa);
+ rcu_read_unlock();
return irk;
}
}
+ rcu_read_unlock();
return NULL;
}
@@ -3259,11 +3311,15 @@ struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr,
if (addr_type == ADDR_LE_DEV_RANDOM && (bdaddr->b[5] & 0xc0) != 0xc0)
return NULL;
- list_for_each_entry(irk, &hdev->identity_resolving_keys, list) {
+ rcu_read_lock();
+ list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
if (addr_type == irk->addr_type &&
- bacmp(bdaddr, &irk->bdaddr) == 0)
+ bacmp(bdaddr, &irk->bdaddr) == 0) {
+ rcu_read_unlock();
return irk;
+ }
}
+ rcu_read_unlock();
return NULL;
}
@@ -3284,7 +3340,7 @@ struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn,
key = kzalloc(sizeof(*key), GFP_KERNEL);
if (!key)
return NULL;
- list_add(&key->list, &hdev->link_keys);
+ list_add_rcu(&key->list, &hdev->link_keys);
}
BT_DBG("%s key for %pMR type %u", hdev->name, bdaddr, type);
@@ -3322,14 +3378,14 @@ struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr,
struct smp_ltk *key, *old_key;
u8 role = ltk_role(type);
- old_key = hci_find_ltk_by_addr(hdev, bdaddr, addr_type, role);
+ old_key = hci_find_ltk(hdev, bdaddr, addr_type, role);
if (old_key)
key = old_key;
else {
key = kzalloc(sizeof(*key), GFP_KERNEL);
if (!key)
return NULL;
- list_add(&key->list, &hdev->long_term_keys);
+ list_add_rcu(&key->list, &hdev->long_term_keys);
}
bacpy(&key->bdaddr, bdaddr);
@@ -3358,7 +3414,7 @@ struct smp_irk *hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr,
bacpy(&irk->bdaddr, bdaddr);
irk->addr_type = addr_type;
- list_add(&irk->list, &hdev->identity_resolving_keys);
+ list_add_rcu(&irk->list, &hdev->identity_resolving_keys);
}
memcpy(irk->val, val, 16);
@@ -3377,25 +3433,25 @@ int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr)
BT_DBG("%s removing %pMR", hdev->name, bdaddr);
- list_del(&key->list);
- kfree(key);
+ list_del_rcu(&key->list);
+ kfree_rcu(key, rcu);
return 0;
}
int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type)
{
- struct smp_ltk *k, *tmp;
+ struct smp_ltk *k;
int removed = 0;
- list_for_each_entry_safe(k, tmp, &hdev->long_term_keys, list) {
+ list_for_each_entry_rcu(k, &hdev->long_term_keys, list) {
if (bacmp(bdaddr, &k->bdaddr) || k->bdaddr_type != bdaddr_type)
continue;
BT_DBG("%s removing %pMR", hdev->name, bdaddr);
- list_del(&k->list);
- kfree(k);
+ list_del_rcu(&k->list);
+ kfree_rcu(k, rcu);
removed++;
}
@@ -3404,16 +3460,16 @@ int hci_remove_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 bdaddr_type)
void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type)
{
- struct smp_irk *k, *tmp;
+ struct smp_irk *k;
- list_for_each_entry_safe(k, tmp, &hdev->identity_resolving_keys, list) {
+ list_for_each_entry_rcu(k, &hdev->identity_resolving_keys, list) {
if (bacmp(bdaddr, &k->bdaddr) || k->addr_type != addr_type)
continue;
BT_DBG("%s removing %pMR", hdev->name, bdaddr);
- list_del(&k->list);
- kfree(k);
+ list_del_rcu(&k->list);
+ kfree_rcu(k, rcu);
}
}
@@ -3437,26 +3493,31 @@ static void hci_cmd_timeout(struct work_struct *work)
}
struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev,
- bdaddr_t *bdaddr)
+ bdaddr_t *bdaddr, u8 bdaddr_type)
{
struct oob_data *data;
- list_for_each_entry(data, &hdev->remote_oob_data, list)
- if (bacmp(bdaddr, &data->bdaddr) == 0)
- return data;
+ list_for_each_entry(data, &hdev->remote_oob_data, list) {
+ if (bacmp(bdaddr, &data->bdaddr) != 0)
+ continue;
+ if (data->bdaddr_type != bdaddr_type)
+ continue;
+ return data;
+ }
return NULL;
}
-int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr)
+int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
+ u8 bdaddr_type)
{
struct oob_data *data;
- data = hci_find_remote_oob_data(hdev, bdaddr);
+ data = hci_find_remote_oob_data(hdev, bdaddr, bdaddr_type);
if (!data)
return -ENOENT;
- BT_DBG("%s removing %pMR", hdev->name, bdaddr);
+ BT_DBG("%s removing %pMR (%u)", hdev->name, bdaddr, bdaddr_type);
list_del(&data->list);
kfree(data);
@@ -3475,52 +3536,37 @@ void hci_remote_oob_data_clear(struct hci_dev *hdev)
}
int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 *hash, u8 *randomizer)
+ u8 bdaddr_type, u8 *hash192, u8 *rand192,
+ u8 *hash256, u8 *rand256)
{
struct oob_data *data;
- data = hci_find_remote_oob_data(hdev, bdaddr);
+ data = hci_find_remote_oob_data(hdev, bdaddr, bdaddr_type);
if (!data) {
data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
bacpy(&data->bdaddr, bdaddr);
+ data->bdaddr_type = bdaddr_type;
list_add(&data->list, &hdev->remote_oob_data);
}
- memcpy(data->hash192, hash, sizeof(data->hash192));
- memcpy(data->randomizer192, randomizer, sizeof(data->randomizer192));
-
- memset(data->hash256, 0, sizeof(data->hash256));
- memset(data->randomizer256, 0, sizeof(data->randomizer256));
-
- BT_DBG("%s for %pMR", hdev->name, bdaddr);
-
- return 0;
-}
-
-int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
- u8 *hash192, u8 *randomizer192,
- u8 *hash256, u8 *randomizer256)
-{
- struct oob_data *data;
-
- data = hci_find_remote_oob_data(hdev, bdaddr);
- if (!data) {
- data = kmalloc(sizeof(*data), GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
- bacpy(&data->bdaddr, bdaddr);
- list_add(&data->list, &hdev->remote_oob_data);
+ if (hash192 && rand192) {
+ memcpy(data->hash192, hash192, sizeof(data->hash192));
+ memcpy(data->rand192, rand192, sizeof(data->rand192));
+ } else {
+ memset(data->hash192, 0, sizeof(data->hash192));
+ memset(data->rand192, 0, sizeof(data->rand192));
}
- memcpy(data->hash192, hash192, sizeof(data->hash192));
- memcpy(data->randomizer192, randomizer192, sizeof(data->randomizer192));
-
- memcpy(data->hash256, hash256, sizeof(data->hash256));
- memcpy(data->randomizer256, randomizer256, sizeof(data->randomizer256));
+ if (hash256 && rand256) {
+ memcpy(data->hash256, hash256, sizeof(data->hash256));
+ memcpy(data->rand256, rand256, sizeof(data->rand256));
+ } else {
+ memset(data->hash256, 0, sizeof(data->hash256));
+ memset(data->rand256, 0, sizeof(data->rand256));
+ }
BT_DBG("%s for %pMR", hdev->name, bdaddr);
@@ -4220,6 +4266,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
hci_remote_oob_data_clear(hdev);
hci_bdaddr_list_clear(&hdev->le_white_list);
hci_conn_params_clear_all(hdev);
+ hci_discovery_filter_clear(hdev);
hci_dev_unlock(hdev);
hci_dev_put(hdev);
@@ -4244,6 +4291,24 @@ int hci_resume_dev(struct hci_dev *hdev)
}
EXPORT_SYMBOL(hci_resume_dev);
+/* Reset HCI device */
+int hci_reset_dev(struct hci_dev *hdev)
+{
+ const u8 hw_err[] = { HCI_EV_HARDWARE_ERROR, 0x01, 0x00 };
+ struct sk_buff *skb;
+
+ skb = bt_skb_alloc(3, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+ memcpy(skb_put(skb, 3), hw_err, 3);
+
+ /* Send Hardware Error to upper stack */
+ return hci_recv_frame(hdev, skb);
+}
+EXPORT_SYMBOL(hci_reset_dev);
+
/* Receive frame from HCI drivers */
int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
@@ -4477,7 +4542,7 @@ int hci_req_run(struct hci_request *req, hci_req_complete_t complete)
BT_DBG("length %u", skb_queue_len(&req->cmd_q));
- /* If an error occured during request building, remove all HCI
+ /* If an error occurred during request building, remove all HCI
* commands queued on the HCI request queue.
*/
if (req->err) {
@@ -4546,7 +4611,7 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
return -ENOMEM;
}
- /* Stand-alone HCI commands must be flaged as
+ /* Stand-alone HCI commands must be flagged as
* single-command requests.
*/
bt_cb(skb)->req.start = true;
@@ -4566,7 +4631,7 @@ void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen);
- /* If an error occured during request building, there is no point in
+ /* If an error occurred during request building, there is no point in
* queueing the HCI command. We can simply return.
*/
if (req->err)
@@ -4661,8 +4726,12 @@ static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue,
skb_shinfo(skb)->frag_list = NULL;
- /* Queue all fragments atomically */
- spin_lock(&queue->lock);
+ /* Queue all fragments atomically. We need to use spin_lock_bh
+ * here because of 6LoWPAN links, as there this function is
+ * called from softirq and using normal spin lock could cause
+ * deadlocks.
+ */
+ spin_lock_bh(&queue->lock);
__skb_queue_tail(queue, skb);
@@ -4679,7 +4748,7 @@ static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue,
__skb_queue_tail(queue, skb);
} while (list);
- spin_unlock(&queue->lock);
+ spin_unlock_bh(&queue->lock);
}
}
@@ -5570,6 +5639,19 @@ void hci_req_add_le_passive_scan(struct hci_request *req)
*/
filter_policy = update_white_list(req);
+ /* When the controller is using random resolvable addresses and
+ * with that having LE privacy enabled, then controllers with
+ * Extended Scanner Filter Policies support can now enable support
+ * for handling directed advertising.
+ *
+ * So instead of using filter polices 0x00 (no whitelist)
+ * and 0x01 (whitelist enabled) use the new filter policies
+ * 0x02 (no whitelist) and 0x03 (whitelist enabled).
+ */
+ if (test_bit(HCI_PRIVACY, &hdev->dev_flags) &&
+ (hdev->le_features[0] & HCI_LE_EXT_SCAN_POLICY))
+ filter_policy |= 0x02;
+
memset(&param_cp, 0, sizeof(param_cp));
param_cp.type = LE_SCAN_PASSIVE;
param_cp.interval = cpu_to_le16(hdev->le_scan_interval);
@@ -5621,6 +5703,15 @@ void hci_update_background_scan(struct hci_dev *hdev)
if (hdev->discovery.state != DISCOVERY_STOPPED)
return;
+ /* Reset RSSI and UUID filters when starting background scanning
+ * since these filters are meant for service discovery only.
+ *
+ * The Start Discovery and Start Service Discovery operations
+ * ensure to set proper values for RSSI threshold and UUID
+ * filter list. So it is safe to just reset them here.
+ */
+ hci_discovery_filter_clear(hdev);
+
hci_req_init(&req, hdev);
if (list_empty(&hdev->pend_le_conns) &&
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 8b0a2a6de419..322abbbbcef9 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -189,6 +189,9 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
clear_bit(HCI_RESET, &hdev->flags);
+ if (status)
+ return;
+
/* Reset all non-persistent flags */
hdev->dev_flags &= ~HCI_PERSISTENT_MASK;
@@ -205,6 +208,8 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
hdev->le_scan_type = LE_SCAN_PASSIVE;
hdev->ssp_debug_mode = 0;
+
+ hci_bdaddr_list_clear(&hdev->le_white_list);
}
static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
@@ -989,8 +994,8 @@ static void hci_cc_read_local_oob_data(struct hci_dev *hdev,
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
hci_dev_lock(hdev);
- mgmt_read_local_oob_data_complete(hdev, rp->hash, rp->randomizer,
- NULL, NULL, rp->status);
+ mgmt_read_local_oob_data_complete(hdev, rp->hash, rp->rand, NULL, NULL,
+ rp->status);
hci_dev_unlock(hdev);
}
@@ -1002,8 +1007,8 @@ static void hci_cc_read_local_oob_ext_data(struct hci_dev *hdev,
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
hci_dev_lock(hdev);
- mgmt_read_local_oob_data_complete(hdev, rp->hash192, rp->randomizer192,
- rp->hash256, rp->randomizer256,
+ mgmt_read_local_oob_data_complete(hdev, rp->hash192, rp->rand192,
+ rp->hash256, rp->rand256,
rp->status);
hci_dev_unlock(hdev);
}
@@ -1045,7 +1050,7 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb)
hci_dev_lock(hdev);
- /* If we're doing connection initation as peripheral. Set a
+ /* If we're doing connection initiation as peripheral. Set a
* timeout in case something goes wrong.
*/
if (*sent) {
@@ -1576,9 +1581,15 @@ static void hci_check_pending_name(struct hci_dev *hdev, struct hci_conn *conn,
struct discovery_state *discov = &hdev->discovery;
struct inquiry_entry *e;
- if (conn && !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
- mgmt_device_connected(hdev, bdaddr, ACL_LINK, 0x00, 0, name,
- name_len, conn->dev_class);
+ /* Update the mgmt connected state if necessary. Be careful with
+ * conn objects that exist but are not (yet) connected however.
+ * Only those in BT_CONFIG or BT_CONNECTED states can be
+ * considered connected.
+ */
+ if (conn &&
+ (conn->state == BT_CONFIG || conn->state == BT_CONNECTED) &&
+ !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
+ mgmt_device_connected(hdev, conn, 0, name, name_len);
if (discov->state == DISCOVERY_STOPPED)
return;
@@ -1943,6 +1954,29 @@ unlock:
hci_dev_unlock(hdev);
}
+static void hci_cs_switch_role(struct hci_dev *hdev, u8 status)
+{
+ struct hci_cp_switch_role *cp;
+ struct hci_conn *conn;
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+ if (!status)
+ return;
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_SWITCH_ROLE);
+ if (!cp)
+ return;
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
+ if (conn)
+ clear_bit(HCI_CONN_RSWITCH_PEND, &conn->flags);
+
+ hci_dev_unlock(hdev);
+}
+
static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
@@ -2009,13 +2043,14 @@ static void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
data.pscan_mode = info->pscan_mode;
memcpy(data.dev_class, info->dev_class, 3);
data.clock_offset = info->clock_offset;
- data.rssi = 0x00;
+ data.rssi = HCI_RSSI_INVALID;
data.ssp_mode = 0x00;
flags = hci_inquiry_cache_update(hdev, &data, false);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
- info->dev_class, 0, flags, NULL, 0, NULL, 0);
+ info->dev_class, HCI_RSSI_INVALID,
+ flags, NULL, 0, NULL, 0);
}
hci_dev_unlock(hdev);
@@ -2536,9 +2571,7 @@ static void hci_remote_features_evt(struct hci_dev *hdev,
cp.pscan_rep_mode = 0x02;
hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp);
} else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
- mgmt_device_connected(hdev, &conn->dst, conn->type,
- conn->dst_type, 0, NULL, 0,
- conn->dev_class);
+ mgmt_device_connected(hdev, conn, 0, NULL, 0);
if (!hci_outgoing_auth_needed(hdev, conn)) {
conn->state = BT_CONNECTED;
@@ -2848,6 +2881,10 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cs_create_conn(hdev, ev->status);
break;
+ case HCI_OP_DISCONNECT:
+ hci_cs_disconnect(hdev, ev->status);
+ break;
+
case HCI_OP_ADD_SCO:
hci_cs_add_sco(hdev, ev->status);
break;
@@ -2876,24 +2913,24 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cs_setup_sync_conn(hdev, ev->status);
break;
- case HCI_OP_SNIFF_MODE:
- hci_cs_sniff_mode(hdev, ev->status);
+ case HCI_OP_CREATE_PHY_LINK:
+ hci_cs_create_phylink(hdev, ev->status);
break;
- case HCI_OP_EXIT_SNIFF_MODE:
- hci_cs_exit_sniff_mode(hdev, ev->status);
+ case HCI_OP_ACCEPT_PHY_LINK:
+ hci_cs_accept_phylink(hdev, ev->status);
break;
- case HCI_OP_DISCONNECT:
- hci_cs_disconnect(hdev, ev->status);
+ case HCI_OP_SNIFF_MODE:
+ hci_cs_sniff_mode(hdev, ev->status);
break;
- case HCI_OP_CREATE_PHY_LINK:
- hci_cs_create_phylink(hdev, ev->status);
+ case HCI_OP_EXIT_SNIFF_MODE:
+ hci_cs_exit_sniff_mode(hdev, ev->status);
break;
- case HCI_OP_ACCEPT_PHY_LINK:
- hci_cs_accept_phylink(hdev, ev->status);
+ case HCI_OP_SWITCH_ROLE:
+ hci_cs_switch_role(hdev, ev->status);
break;
case HCI_OP_LE_CREATE_CONN:
@@ -2923,6 +2960,13 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
}
}
+static void hci_hardware_error_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_ev_hardware_error *ev = (void *) skb->data;
+
+ BT_ERR("%s hardware error 0x%2.2x", hdev->name, ev->code);
+}
+
static void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_role_change *ev = (void *) skb->data;
@@ -3148,6 +3192,38 @@ unlock:
hci_dev_unlock(hdev);
}
+static void conn_set_key(struct hci_conn *conn, u8 key_type, u8 pin_len)
+{
+ if (key_type == HCI_LK_CHANGED_COMBINATION)
+ return;
+
+ conn->pin_length = pin_len;
+ conn->key_type = key_type;
+
+ switch (key_type) {
+ case HCI_LK_LOCAL_UNIT:
+ case HCI_LK_REMOTE_UNIT:
+ case HCI_LK_DEBUG_COMBINATION:
+ return;
+ case HCI_LK_COMBINATION:
+ if (pin_len == 16)
+ conn->pending_sec_level = BT_SECURITY_HIGH;
+ else
+ conn->pending_sec_level = BT_SECURITY_MEDIUM;
+ break;
+ case HCI_LK_UNAUTH_COMBINATION_P192:
+ case HCI_LK_UNAUTH_COMBINATION_P256:
+ conn->pending_sec_level = BT_SECURITY_MEDIUM;
+ break;
+ case HCI_LK_AUTH_COMBINATION_P192:
+ conn->pending_sec_level = BT_SECURITY_HIGH;
+ break;
+ case HCI_LK_AUTH_COMBINATION_P256:
+ conn->pending_sec_level = BT_SECURITY_FIPS;
+ break;
+ }
+}
+
static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_link_key_req *ev = (void *) skb->data;
@@ -3174,6 +3250,8 @@ static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
if (conn) {
+ clear_bit(HCI_CONN_NEW_LINK_KEY, &conn->flags);
+
if ((key->type == HCI_LK_UNAUTH_COMBINATION_P192 ||
key->type == HCI_LK_UNAUTH_COMBINATION_P256) &&
conn->auth_type != 0xff && (conn->auth_type & 0x01)) {
@@ -3189,8 +3267,7 @@ static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
goto not_found;
}
- conn->key_type = key->type;
- conn->pin_length = key->pin_len;
+ conn_set_key(conn, key->type, key->pin_len);
}
bacpy(&cp.bdaddr, &ev->bdaddr);
@@ -3220,16 +3297,15 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_dev_lock(hdev);
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr);
- if (conn) {
- hci_conn_hold(conn);
- conn->disc_timeout = HCI_DISCONN_TIMEOUT;
- pin_len = conn->pin_length;
+ if (!conn)
+ goto unlock;
- if (ev->key_type != HCI_LK_CHANGED_COMBINATION)
- conn->key_type = ev->key_type;
+ hci_conn_hold(conn);
+ conn->disc_timeout = HCI_DISCONN_TIMEOUT;
+ hci_conn_drop(conn);
- hci_conn_drop(conn);
- }
+ set_bit(HCI_CONN_NEW_LINK_KEY, &conn->flags);
+ conn_set_key(conn, ev->key_type, conn->pin_length);
if (!test_bit(HCI_MGMT, &hdev->dev_flags))
goto unlock;
@@ -3239,6 +3315,12 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb)
if (!key)
goto unlock;
+ /* Update connection information since adding the key will have
+ * fixed up the type in the case of changed combination keys.
+ */
+ if (ev->key_type == HCI_LK_CHANGED_COMBINATION)
+ conn_set_key(conn, key->type, key->pin_len);
+
mgmt_new_link_key(hdev, key, persistent);
/* Keep debug keys around only if the HCI_KEEP_DEBUG_KEYS flag
@@ -3248,15 +3330,16 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb)
*/
if (key->type == HCI_LK_DEBUG_COMBINATION &&
!test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags)) {
- list_del(&key->list);
- kfree(key);
- } else if (conn) {
- if (persistent)
- clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags);
- else
- set_bit(HCI_CONN_FLUSH_KEY, &conn->flags);
+ list_del_rcu(&key->list);
+ kfree_rcu(key, rcu);
+ goto unlock;
}
+ if (persistent)
+ clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags);
+ else
+ set_bit(HCI_CONN_FLUSH_KEY, &conn->flags);
+
unlock:
hci_dev_unlock(hdev);
}
@@ -3434,9 +3517,7 @@ static void hci_remote_ext_features_evt(struct hci_dev *hdev,
cp.pscan_rep_mode = 0x02;
hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp);
} else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
- mgmt_device_connected(hdev, &conn->dst, conn->type,
- conn->dst_type, 0, NULL, 0,
- conn->dev_class);
+ mgmt_device_connected(hdev, conn, 0, NULL, 0);
if (!hci_outgoing_auth_needed(hdev, conn)) {
conn->state = BT_CONNECTED;
@@ -3693,7 +3774,7 @@ static void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
cp.authentication = conn->auth_type;
- if (hci_find_remote_oob_data(hdev, &conn->dst) &&
+ if (hci_find_remote_oob_data(hdev, &conn->dst, BDADDR_BREDR) &&
(conn->out || test_bit(HCI_CONN_REMOTE_OOB, &conn->flags)))
cp.oob_data = 0x01;
else
@@ -3948,18 +4029,16 @@ static void hci_remote_oob_data_request_evt(struct hci_dev *hdev,
if (!test_bit(HCI_MGMT, &hdev->dev_flags))
goto unlock;
- data = hci_find_remote_oob_data(hdev, &ev->bdaddr);
+ data = hci_find_remote_oob_data(hdev, &ev->bdaddr, BDADDR_BREDR);
if (data) {
- if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) {
+ if (bredr_sc_enabled(hdev)) {
struct hci_cp_remote_oob_ext_data_reply cp;
bacpy(&cp.bdaddr, &ev->bdaddr);
memcpy(cp.hash192, data->hash192, sizeof(cp.hash192));
- memcpy(cp.randomizer192, data->randomizer192,
- sizeof(cp.randomizer192));
+ memcpy(cp.rand192, data->rand192, sizeof(cp.rand192));
memcpy(cp.hash256, data->hash256, sizeof(cp.hash256));
- memcpy(cp.randomizer256, data->randomizer256,
- sizeof(cp.randomizer256));
+ memcpy(cp.rand256, data->rand256, sizeof(cp.rand256));
hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_EXT_DATA_REPLY,
sizeof(cp), &cp);
@@ -3968,8 +4047,7 @@ static void hci_remote_oob_data_request_evt(struct hci_dev *hdev,
bacpy(&cp.bdaddr, &ev->bdaddr);
memcpy(cp.hash, data->hash192, sizeof(cp.hash));
- memcpy(cp.randomizer, data->randomizer192,
- sizeof(cp.randomizer));
+ memcpy(cp.rand, data->rand192, sizeof(cp.rand));
hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_REPLY,
sizeof(cp), &cp);
@@ -4214,8 +4292,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
}
if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
- mgmt_device_connected(hdev, &conn->dst, conn->type,
- conn->dst_type, 0, NULL, 0, NULL);
+ mgmt_device_connected(hdev, conn, 0, NULL, 0);
conn->sec_level = BT_SECURITY_LOW;
conn->handle = __le16_to_cpu(ev->handle);
@@ -4269,25 +4346,26 @@ static void hci_le_conn_update_complete_evt(struct hci_dev *hdev,
}
/* This function requires the caller holds hdev->lock */
-static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
- u8 addr_type, u8 adv_type)
+static struct hci_conn *check_pending_le_conn(struct hci_dev *hdev,
+ bdaddr_t *addr,
+ u8 addr_type, u8 adv_type)
{
struct hci_conn *conn;
struct hci_conn_params *params;
/* If the event is not connectable don't proceed further */
if (adv_type != LE_ADV_IND && adv_type != LE_ADV_DIRECT_IND)
- return;
+ return NULL;
/* Ignore if the device is blocked */
if (hci_bdaddr_list_lookup(&hdev->blacklist, addr, addr_type))
- return;
+ return NULL;
/* Most controller will fail if we try to create new connections
* while we have an existing one in slave role.
*/
if (hdev->conn_hash.le_num_slave > 0)
- return;
+ return NULL;
/* If we're not connectable only connect devices that we have in
* our pend_le_conns list.
@@ -4295,7 +4373,7 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
params = hci_pend_le_action_lookup(&hdev->pend_le_conns,
addr, addr_type);
if (!params)
- return;
+ return NULL;
switch (params->auto_connect) {
case HCI_AUTO_CONN_DIRECT:
@@ -4304,7 +4382,7 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
* incoming connections from slave devices.
*/
if (adv_type != LE_ADV_DIRECT_IND)
- return;
+ return NULL;
break;
case HCI_AUTO_CONN_ALWAYS:
/* Devices advertising with ADV_IND or ADV_DIRECT_IND
@@ -4315,7 +4393,7 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
*/
break;
default:
- return;
+ return NULL;
}
conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW,
@@ -4328,7 +4406,7 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
* count consistent once the connection is established.
*/
params->conn = hci_conn_get(conn);
- return;
+ return conn;
}
switch (PTR_ERR(conn)) {
@@ -4341,17 +4419,48 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
break;
default:
BT_DBG("Failed to connect: err %ld", PTR_ERR(conn));
+ return NULL;
}
+
+ return NULL;
}
static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
- u8 bdaddr_type, s8 rssi, u8 *data, u8 len)
+ u8 bdaddr_type, bdaddr_t *direct_addr,
+ u8 direct_addr_type, s8 rssi, u8 *data, u8 len)
{
struct discovery_state *d = &hdev->discovery;
struct smp_irk *irk;
+ struct hci_conn *conn;
bool match;
u32 flags;
+ /* If the direct address is present, then this report is from
+ * a LE Direct Advertising Report event. In that case it is
+ * important to see if the address is matching the local
+ * controller address.
+ */
+ if (direct_addr) {
+ /* Only resolvable random addresses are valid for these
+ * kind of reports and others can be ignored.
+ */
+ if (!hci_bdaddr_is_rpa(direct_addr, direct_addr_type))
+ return;
+
+ /* If the controller is not using resolvable random
+ * addresses, then this report can be ignored.
+ */
+ if (!test_bit(HCI_PRIVACY, &hdev->dev_flags))
+ return;
+
+ /* If the local IRK of the controller does not match
+ * with the resolvable random address provided, then
+ * this report can be ignored.
+ */
+ if (!smp_irk_matches(hdev, hdev->irk, direct_addr))
+ return;
+ }
+
/* Check if we need to convert to identity address */
irk = hci_get_irk(hdev, bdaddr, bdaddr_type);
if (irk) {
@@ -4360,7 +4469,14 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
}
/* Check if we have been requested to connect to this device */
- check_pending_le_conn(hdev, bdaddr, bdaddr_type, type);
+ conn = check_pending_le_conn(hdev, bdaddr, bdaddr_type, type);
+ if (conn && type == LE_ADV_IND) {
+ /* Store report for later inclusion by
+ * mgmt_device_connected
+ */
+ memcpy(conn->le_adv_data, data, len);
+ conn->le_adv_data_len = len;
+ }
/* Passive scanning shouldn't trigger any device found events,
* except for devices marked as CONN_REPORT for which we do send
@@ -4481,7 +4597,8 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb)
rssi = ev->data[ev->length];
process_adv_report(hdev, ev->evt_type, &ev->bdaddr,
- ev->bdaddr_type, rssi, ev->data, ev->length);
+ ev->bdaddr_type, NULL, 0, rssi,
+ ev->data, ev->length);
ptr += sizeof(*ev) + ev->length + 1;
}
@@ -4505,10 +4622,20 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
if (conn == NULL)
goto not_found;
- ltk = hci_find_ltk(hdev, ev->ediv, ev->rand, conn->role);
- if (ltk == NULL)
+ ltk = hci_find_ltk(hdev, &conn->dst, conn->dst_type, conn->role);
+ if (!ltk)
goto not_found;
+ if (smp_ltk_is_sc(ltk)) {
+ /* With SC both EDiv and Rand are set to zero */
+ if (ev->ediv || ev->rand)
+ goto not_found;
+ } else {
+ /* For non-SC keys check that EDiv and Rand match */
+ if (ev->ediv != ltk->ediv || ev->rand != ltk->rand)
+ goto not_found;
+ }
+
memcpy(cp.ltk, ltk->val, sizeof(ltk->val));
cp.handle = cpu_to_le16(conn->handle);
@@ -4526,8 +4653,8 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
*/
if (ltk->type == SMP_STK) {
set_bit(HCI_CONN_STK_ENCRYPT, &conn->flags);
- list_del(&ltk->list);
- kfree(ltk);
+ list_del_rcu(&ltk->list);
+ kfree_rcu(ltk, rcu);
} else {
clear_bit(HCI_CONN_STK_ENCRYPT, &conn->flags);
}
@@ -4612,6 +4739,27 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev,
hci_send_cmd(hdev, HCI_OP_LE_CONN_PARAM_REQ_REPLY, sizeof(cp), &cp);
}
+static void hci_le_direct_adv_report_evt(struct hci_dev *hdev,
+ struct sk_buff *skb)
+{
+ u8 num_reports = skb->data[0];
+ void *ptr = &skb->data[1];
+
+ hci_dev_lock(hdev);
+
+ while (num_reports--) {
+ struct hci_ev_le_direct_adv_info *ev = ptr;
+
+ process_adv_report(hdev, ev->evt_type, &ev->bdaddr,
+ ev->bdaddr_type, &ev->direct_addr,
+ ev->direct_addr_type, ev->rssi, NULL, 0);
+
+ ptr += sizeof(*ev);
+ }
+
+ hci_dev_unlock(hdev);
+}
+
static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_le_meta *le_ev = (void *) skb->data;
@@ -4639,6 +4787,10 @@ static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_le_remote_conn_param_req_evt(hdev, skb);
break;
+ case HCI_EV_LE_DIRECT_ADV_REPORT:
+ hci_le_direct_adv_report_evt(hdev, skb);
+ break;
+
default:
break;
}
@@ -4735,6 +4887,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
hci_cmd_status_evt(hdev, skb);
break;
+ case HCI_EV_HARDWARE_ERROR:
+ hci_hardware_error_evt(hdev, skb);
+ break;
+
case HCI_EV_ROLE_CHANGE:
hci_role_change_evt(hdev, skb);
break;
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 115f149362ba..2c245fdf319a 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -878,7 +878,7 @@ static int hci_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
}
skb_reset_transport_header(skb);
- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
switch (hci_pi(sk)->channel) {
case HCI_CHANNEL_RAW:
@@ -947,7 +947,7 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
if (!skb)
goto done;
- if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
+ if (memcpy_from_msg(skb_put(skb, len), msg, len)) {
err = -EFAULT;
goto drop;
}
@@ -987,7 +987,7 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
skb_queue_tail(&hdev->raw_q, skb);
queue_work(hdev->workqueue, &hdev->tx_work);
} else {
- /* Stand-alone HCI commands must be flaged as
+ /* Stand-alone HCI commands must be flagged as
* single-command requests.
*/
bt_cb(skb)->req.start = true;
diff --git a/net/bluetooth/hidp/Kconfig b/net/bluetooth/hidp/Kconfig
index 9332bc7aa851..bc8610b24077 100644
--- a/net/bluetooth/hidp/Kconfig
+++ b/net/bluetooth/hidp/Kconfig
@@ -1,6 +1,6 @@
config BT_HIDP
tristate "HIDP protocol support"
- depends on BT && INPUT
+ depends on BT_BREDR && INPUT
select HID
help
HIDP (Human Interface Device Protocol) is a transport layer
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index 1b7d605706aa..cc25d0b74b36 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -736,14 +736,10 @@ static int hidp_setup_hid(struct hidp_session *session,
struct hid_device *hid;
int err;
- session->rd_data = kzalloc(req->rd_size, GFP_KERNEL);
- if (!session->rd_data)
- return -ENOMEM;
+ session->rd_data = memdup_user(req->rd_data, req->rd_size);
+ if (IS_ERR(session->rd_data))
+ return PTR_ERR(session->rd_data);
- if (copy_from_user(session->rd_data, req->rd_data, req->rd_size)) {
- err = -EFAULT;
- goto fault;
- }
session->rd_size = req->rd_size;
hid = hid_allocate_device();
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index b6f9777e057d..a2b6dfa38a0c 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -46,7 +46,6 @@
bool disable_ertm;
static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN | L2CAP_FEAT_UCD;
-static u8 l2cap_fixed_chan[8] = { L2CAP_FC_SIG_BREDR | L2CAP_FC_CONNLESS, };
static LIST_HEAD(chan_list);
static DEFINE_RWLOCK(chan_list_lock);
@@ -424,6 +423,9 @@ struct l2cap_chan *l2cap_chan_create(void)
mutex_init(&chan->lock);
+ /* Set default lock nesting level */
+ atomic_set(&chan->nesting, L2CAP_NESTING_NORMAL);
+
write_lock(&chan_list_lock);
list_add(&chan->global_l, &chan_list);
write_unlock(&chan_list_lock);
@@ -567,7 +569,8 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
__clear_chan_timer(chan);
- BT_DBG("chan %p, conn %p, err %d", chan, conn, err);
+ BT_DBG("chan %p, conn %p, err %d, state %s", chan, conn, err,
+ state_to_string(chan->state));
chan->ops->teardown(chan, err);
@@ -836,7 +839,10 @@ static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
if (!skb)
return;
- if (lmp_no_flush_capable(conn->hcon->hdev))
+ /* Use NO_FLUSH if supported or we have an LE link (which does
+ * not support auto-flushing packets) */
+ if (lmp_no_flush_capable(conn->hcon->hdev) ||
+ conn->hcon->type == LE_LINK)
flags = ACL_START_NO_FLUSH;
else
flags = ACL_START;
@@ -870,8 +876,13 @@ static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb)
return;
}
- if (!test_bit(FLAG_FLUSHABLE, &chan->flags) &&
- lmp_no_flush_capable(hcon->hdev))
+ /* Use NO_FLUSH for LE links (where this is the only option) or
+ * if the BR/EDR link supports it and flushing has not been
+ * explicitly requested (through FLAG_FLUSHABLE).
+ */
+ if (hcon->type == LE_LINK ||
+ (!test_bit(FLAG_FLUSHABLE, &chan->flags) &&
+ lmp_no_flush_capable(hcon->hdev)))
flags = ACL_START_NO_FLUSH;
else
flags = ACL_START;
@@ -1108,10 +1119,10 @@ static bool __amp_capable(struct l2cap_chan *chan)
struct hci_dev *hdev;
bool amp_available = false;
- if (!conn->hs_enabled)
+ if (!(conn->local_fixed_chan & L2CAP_FC_A2MP))
return false;
- if (!(conn->fixed_chan_mask & L2CAP_FC_A2MP))
+ if (!(conn->remote_fixed_chan & L2CAP_FC_A2MP))
return false;
read_lock(&hci_dev_list_lock);
@@ -2092,8 +2103,7 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan,
struct sk_buff **frag;
int sent = 0;
- if (chan->ops->memcpy_fromiovec(chan, skb_put(skb, count),
- msg->msg_iov, count))
+ if (copy_from_iter(skb_put(skb, count), count, &msg->msg_iter) != count)
return -EFAULT;
sent += count;
@@ -2113,8 +2123,8 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan,
*frag = tmp;
- if (chan->ops->memcpy_fromiovec(chan, skb_put(*frag, count),
- msg->msg_iov, count))
+ if (copy_from_iter(skb_put(*frag, count), count,
+ &msg->msg_iter) != count)
return -EFAULT;
sent += count;
@@ -3084,12 +3094,14 @@ static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask)
static inline bool __l2cap_ews_supported(struct l2cap_conn *conn)
{
- return conn->hs_enabled && conn->feat_mask & L2CAP_FEAT_EXT_WINDOW;
+ return ((conn->local_fixed_chan & L2CAP_FC_A2MP) &&
+ (conn->feat_mask & L2CAP_FEAT_EXT_WINDOW));
}
static inline bool __l2cap_efs_supported(struct l2cap_conn *conn)
{
- return conn->hs_enabled && conn->feat_mask & L2CAP_FEAT_EXT_FLOW;
+ return ((conn->local_fixed_chan & L2CAP_FC_A2MP) &&
+ (conn->feat_mask & L2CAP_FEAT_EXT_FLOW));
}
static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan,
@@ -3318,7 +3330,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data)
break;
case L2CAP_CONF_EWS:
- if (!chan->conn->hs_enabled)
+ if (!(chan->conn->local_fixed_chan & L2CAP_FC_A2MP))
return -ECONNREFUSED;
set_bit(FLAG_EXT_CTRL, &chan->flags);
@@ -3873,9 +3885,7 @@ static int l2cap_connect_req(struct l2cap_conn *conn,
hci_dev_lock(hdev);
if (test_bit(HCI_MGMT, &hdev->dev_flags) &&
!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &hcon->flags))
- mgmt_device_connected(hdev, &hcon->dst, hcon->type,
- hcon->dst_type, 0, NULL, 0,
- hcon->dev_class);
+ mgmt_device_connected(hdev, hcon, 0, NULL, 0);
hci_dev_unlock(hdev);
l2cap_connect(conn, cmd, data, L2CAP_CONN_RSP, 0);
@@ -4084,7 +4094,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn,
chan->num_conf_req++;
}
- /* Got Conf Rsp PENDING from remote side and asume we sent
+ /* Got Conf Rsp PENDING from remote side and assume we sent
Conf Rsp PENDING in the code above */
if (test_bit(CONF_REM_CONF_PEND, &chan->conf_state) &&
test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) {
@@ -4324,7 +4334,7 @@ static inline int l2cap_information_req(struct l2cap_conn *conn,
if (!disable_ertm)
feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING
| L2CAP_FEAT_FCS;
- if (conn->hs_enabled)
+ if (conn->local_fixed_chan & L2CAP_FC_A2MP)
feat_mask |= L2CAP_FEAT_EXT_FLOW
| L2CAP_FEAT_EXT_WINDOW;
@@ -4335,14 +4345,10 @@ static inline int l2cap_information_req(struct l2cap_conn *conn,
u8 buf[12];
struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf;
- if (conn->hs_enabled)
- l2cap_fixed_chan[0] |= L2CAP_FC_A2MP;
- else
- l2cap_fixed_chan[0] &= ~L2CAP_FC_A2MP;
-
rsp->type = cpu_to_le16(L2CAP_IT_FIXED_CHAN);
rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS);
- memcpy(rsp->data, l2cap_fixed_chan, sizeof(l2cap_fixed_chan));
+ rsp->data[0] = conn->local_fixed_chan;
+ memset(rsp->data + 1, 0, 7);
l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf),
buf);
} else {
@@ -4408,7 +4414,7 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn,
break;
case L2CAP_IT_FIXED_CHAN:
- conn->fixed_chan_mask = rsp->data[0];
+ conn->remote_fixed_chan = rsp->data[0];
conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
conn->info_ident = 0;
@@ -4432,7 +4438,7 @@ static int l2cap_create_channel_req(struct l2cap_conn *conn,
if (cmd_len != sizeof(*req))
return -EPROTO;
- if (!conn->hs_enabled)
+ if (!(conn->local_fixed_chan & L2CAP_FC_A2MP))
return -EINVAL;
psm = le16_to_cpu(req->psm);
@@ -4862,7 +4868,7 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn,
BT_DBG("icid 0x%4.4x, dest_amp_id %d", icid, req->dest_amp_id);
- if (!conn->hs_enabled)
+ if (!(conn->local_fixed_chan & L2CAP_FC_A2MP))
return -EINVAL;
chan = l2cap_get_chan_by_dcid(conn, icid);
@@ -5217,9 +5223,10 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
u8 *data)
{
struct l2cap_le_conn_rsp *rsp = (struct l2cap_le_conn_rsp *) data;
+ struct hci_conn *hcon = conn->hcon;
u16 dcid, mtu, mps, credits, result;
struct l2cap_chan *chan;
- int err;
+ int err, sec_level;
if (cmd_len < sizeof(*rsp))
return -EPROTO;
@@ -5258,6 +5265,26 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
l2cap_chan_ready(chan);
break;
+ case L2CAP_CR_AUTHENTICATION:
+ case L2CAP_CR_ENCRYPTION:
+ /* If we already have MITM protection we can't do
+ * anything.
+ */
+ if (hcon->sec_level > BT_SECURITY_MEDIUM) {
+ l2cap_chan_del(chan, ECONNREFUSED);
+ break;
+ }
+
+ sec_level = hcon->sec_level + 1;
+ if (chan->sec_level < sec_level)
+ chan->sec_level = sec_level;
+
+ /* We'll need to send a new Connect Request */
+ clear_bit(FLAG_LE_CONN_REQ_SENT, &chan->flags);
+
+ smp_conn_security(hcon, chan->sec_level);
+ break;
+
default:
l2cap_chan_del(chan, ECONNREFUSED);
break;
@@ -5390,7 +5417,8 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
mutex_lock(&conn->chan_lock);
l2cap_chan_lock(pchan);
- if (!smp_sufficient_security(conn->hcon, pchan->sec_level)) {
+ if (!smp_sufficient_security(conn->hcon, pchan->sec_level,
+ SMP_ALLOW_STK)) {
result = L2CAP_CR_AUTHENTICATION;
chan = NULL;
goto response_unlock;
@@ -5494,6 +5522,7 @@ static inline int l2cap_le_credits(struct l2cap_conn *conn,
if (credits > max_credits) {
BT_ERR("LE credits overflow");
l2cap_send_disconn_req(chan, ECONNRESET);
+ l2cap_chan_unlock(chan);
/* Return 0 so that we don't trigger an unnecessary
* command reject packet.
@@ -6931,9 +6960,15 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon)
conn->feat_mask = 0;
- if (hcon->type == ACL_LINK)
- conn->hs_enabled = test_bit(HCI_HS_ENABLED,
- &hcon->hdev->dev_flags);
+ conn->local_fixed_chan = L2CAP_FC_SIG_BREDR | L2CAP_FC_CONNLESS;
+
+ if (hcon->type == ACL_LINK &&
+ test_bit(HCI_HS_ENABLED, &hcon->hdev->dev_flags))
+ conn->local_fixed_chan |= L2CAP_FC_A2MP;
+
+ if (bredr_sc_enabled(hcon->hdev) &&
+ test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags))
+ conn->local_fixed_chan |= L2CAP_FC_SMP_BREDR;
mutex_init(&conn->ident_lock);
mutex_init(&conn->chan_lock);
@@ -7330,7 +7365,8 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
l2cap_start_connection(chan);
else
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
- } else if (chan->state == BT_CONNECT2) {
+ } else if (chan->state == BT_CONNECT2 &&
+ chan->mode != L2CAP_MODE_LE_FLOWCTL) {
struct l2cap_conn_rsp rsp;
__u16 res, stat;
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 31f106e61ca2..f65caf41953f 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -285,6 +285,12 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
sk->sk_max_ack_backlog = backlog;
sk->sk_ack_backlog = 0;
+ /* Listening channels need to use nested locking in order not to
+ * cause lockdep warnings when the created child channels end up
+ * being locked in the same thread as the parent channel.
+ */
+ atomic_set(&chan->nesting, L2CAP_NESTING_PARENT);
+
chan->state = BT_LISTEN;
sk->sk_state = BT_LISTEN;
@@ -301,7 +307,7 @@ static int l2cap_sock_accept(struct socket *sock, struct socket *newsock,
long timeo;
int err = 0;
- lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
+ lock_sock_nested(sk, L2CAP_NESTING_PARENT);
timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
@@ -333,7 +339,7 @@ static int l2cap_sock_accept(struct socket *sock, struct socket *newsock,
release_sock(sk);
timeo = schedule_timeout(timeo);
- lock_sock_nested(sk, SINGLE_DEPTH_NESTING);
+ lock_sock_nested(sk, L2CAP_NESTING_PARENT);
}
__set_current_state(TASK_RUNNING);
remove_wait_queue(sk_sleep(sk), &wait);
@@ -1096,6 +1102,8 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
chan = l2cap_pi(sk)->chan;
conn = chan->conn;
+ BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
+
if (conn)
mutex_lock(&conn->chan_lock);
@@ -1153,12 +1161,16 @@ static void l2cap_sock_cleanup_listen(struct sock *parent)
{
struct sock *sk;
- BT_DBG("parent %p", parent);
+ BT_DBG("parent %p state %s", parent,
+ state_to_string(parent->sk_state));
/* Close not yet accepted channels */
while ((sk = bt_accept_dequeue(parent, NULL))) {
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
+ BT_DBG("child chan %p state %s", chan,
+ state_to_string(chan->state));
+
l2cap_chan_lock(chan);
__clear_chan_timer(chan);
l2cap_chan_close(chan, ECONNRESET);
@@ -1246,7 +1258,16 @@ static void l2cap_sock_teardown_cb(struct l2cap_chan *chan, int err)
struct sock *sk = chan->data;
struct sock *parent;
- lock_sock(sk);
+ BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
+
+ /* This callback can be called both for server (BT_LISTEN)
+ * sockets as well as "normal" ones. To avoid lockdep warnings
+ * with child socket locking (through l2cap_sock_cleanup_listen)
+ * we need separation into separate nesting levels. The simplest
+ * way to accomplish this is to inherit the nesting level used
+ * for the channel.
+ */
+ lock_sock_nested(sk, atomic_read(&chan->nesting));
parent = bt_sk(sk)->parent;
@@ -1315,13 +1336,6 @@ static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan,
return skb;
}
-static int l2cap_sock_memcpy_fromiovec_cb(struct l2cap_chan *chan,
- unsigned char *kdata,
- struct iovec *iov, int len)
-{
- return memcpy_fromiovec(kdata, iov, len);
-}
-
static void l2cap_sock_ready_cb(struct l2cap_chan *chan)
{
struct sock *sk = chan->data;
@@ -1406,7 +1420,6 @@ static const struct l2cap_ops l2cap_chan_ops = {
.set_shutdown = l2cap_sock_set_shutdown_cb,
.get_sndtimeo = l2cap_sock_get_sndtimeo_cb,
.alloc_skb = l2cap_sock_alloc_skb_cb,
- .memcpy_fromiovec = l2cap_sock_memcpy_fromiovec_cb,
};
static void l2cap_sock_destruct(struct sock *sk)
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index efb71b022ab6..7384f1161336 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -35,7 +35,7 @@
#include "smp.h"
#define MGMT_VERSION 1
-#define MGMT_REVISION 7
+#define MGMT_REVISION 8
static const u16 mgmt_commands[] = {
MGMT_OP_READ_INDEX_LIST,
@@ -93,6 +93,7 @@ static const u16 mgmt_commands[] = {
MGMT_OP_READ_CONFIG_INFO,
MGMT_OP_SET_EXTERNAL_CONFIG,
MGMT_OP_SET_PUBLIC_ADDRESS,
+ MGMT_OP_START_SERVICE_DISCOVERY,
};
static const u16 mgmt_events[] = {
@@ -134,8 +135,10 @@ struct pending_cmd {
u16 opcode;
int index;
void *param;
+ size_t param_len;
struct sock *sk;
void *user_data;
+ void (*cmd_complete)(struct pending_cmd *cmd, u8 status);
};
/* HCI to MGMT error code conversion table */
@@ -574,6 +577,7 @@ static u32 get_supported_settings(struct hci_dev *hdev)
if (lmp_le_capable(hdev)) {
settings |= MGMT_SETTING_LE;
settings |= MGMT_SETTING_ADVERTISING;
+ settings |= MGMT_SETTING_SECURE_CONN;
settings |= MGMT_SETTING_PRIVACY;
}
@@ -1202,14 +1206,13 @@ static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
cmd->opcode = opcode;
cmd->index = hdev->id;
- cmd->param = kmalloc(len, GFP_KERNEL);
+ cmd->param = kmemdup(data, len, GFP_KERNEL);
if (!cmd->param) {
kfree(cmd);
return NULL;
}
- if (data)
- memcpy(cmd->param, data, len);
+ cmd->param_len = len;
cmd->sk = sk;
sock_hold(sk);
@@ -1469,6 +1472,32 @@ static void cmd_status_rsp(struct pending_cmd *cmd, void *data)
mgmt_pending_remove(cmd);
}
+static void cmd_complete_rsp(struct pending_cmd *cmd, void *data)
+{
+ if (cmd->cmd_complete) {
+ u8 *status = data;
+
+ cmd->cmd_complete(cmd, *status);
+ mgmt_pending_remove(cmd);
+
+ return;
+ }
+
+ cmd_status_rsp(cmd, data);
+}
+
+static void generic_cmd_complete(struct pending_cmd *cmd, u8 status)
+{
+ cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param,
+ cmd->param_len);
+}
+
+static void addr_cmd_complete(struct pending_cmd *cmd, u8 status)
+{
+ cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param,
+ sizeof(struct mgmt_addr_info));
+}
+
static u8 mgmt_bredr_support(struct hci_dev *hdev)
{
if (!lmp_bredr_capable(hdev))
@@ -2725,10 +2754,40 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
}
if (cp->addr.type == BDADDR_BREDR) {
+ /* If disconnection is requested, then look up the
+ * connection. If the remote device is connected, it
+ * will be later used to terminate the link.
+ *
+ * Setting it to NULL explicitly will cause no
+ * termination of the link.
+ */
+ if (cp->disconnect)
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
+ &cp->addr.bdaddr);
+ else
+ conn = NULL;
+
err = hci_remove_link_key(hdev, &cp->addr.bdaddr);
} else {
u8 addr_type;
+ conn = hci_conn_hash_lookup_ba(hdev, LE_LINK,
+ &cp->addr.bdaddr);
+ if (conn) {
+ /* Defer clearing up the connection parameters
+ * until closing to give a chance of keeping
+ * them if a repairing happens.
+ */
+ set_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags);
+
+ /* If disconnection is not requested, then
+ * clear the connection variable so that the
+ * link is not terminated.
+ */
+ if (!cp->disconnect)
+ conn = NULL;
+ }
+
if (cp->addr.type == BDADDR_LE_PUBLIC)
addr_type = ADDR_LE_DEV_PUBLIC;
else
@@ -2736,8 +2795,6 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
hci_remove_irk(hdev, &cp->addr.bdaddr, addr_type);
- hci_conn_params_del(hdev, &cp->addr.bdaddr, addr_type);
-
err = hci_remove_ltk(hdev, &cp->addr.bdaddr, addr_type);
}
@@ -2747,17 +2804,9 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
goto unlock;
}
- if (cp->disconnect) {
- if (cp->addr.type == BDADDR_BREDR)
- conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
- &cp->addr.bdaddr);
- else
- conn = hci_conn_hash_lookup_ba(hdev, LE_LINK,
- &cp->addr.bdaddr);
- } else {
- conn = NULL;
- }
-
+ /* If the connection variable is set, then termination of the
+ * link is requested.
+ */
if (!conn) {
err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, 0,
&rp, sizeof(rp));
@@ -2772,6 +2821,8 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
goto unlock;
}
+ cmd->cmd_complete = addr_cmd_complete;
+
dc.handle = cpu_to_le16(conn->handle);
dc.reason = 0x13; /* Remote User Terminated Connection */
err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
@@ -2835,6 +2886,8 @@ static int disconnect(struct sock *sk, struct hci_dev *hdev, void *data,
goto failed;
}
+ cmd->cmd_complete = generic_cmd_complete;
+
err = hci_disconnect(conn, HCI_ERROR_REMOTE_USER_TERM);
if (err < 0)
mgmt_pending_remove(cmd);
@@ -2987,6 +3040,8 @@ static int pin_code_reply(struct sock *sk, struct hci_dev *hdev, void *data,
goto failed;
}
+ cmd->cmd_complete = addr_cmd_complete;
+
bacpy(&reply.bdaddr, &cp->addr.bdaddr);
reply.pin_len = cp->pin_len;
memcpy(reply.pin_code, cp->pin_code, sizeof(reply.pin_code));
@@ -3062,6 +3117,11 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status)
hci_conn_put(conn);
mgmt_pending_remove(cmd);
+
+ /* The device is paired so there is no need to remove
+ * its connection parameters anymore.
+ */
+ clear_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags);
}
void mgmt_smp_complete(struct hci_conn *conn, bool complete)
@@ -3071,7 +3131,7 @@ void mgmt_smp_complete(struct hci_conn *conn, bool complete)
cmd = find_pairing(conn);
if (cmd)
- pairing_complete(cmd, status);
+ cmd->cmd_complete(cmd, status);
}
static void pairing_complete_cb(struct hci_conn *conn, u8 status)
@@ -3084,7 +3144,7 @@ static void pairing_complete_cb(struct hci_conn *conn, u8 status)
if (!cmd)
BT_DBG("Unable to find a pending command");
else
- pairing_complete(cmd, mgmt_status(status));
+ cmd->cmd_complete(cmd, mgmt_status(status));
}
static void le_pairing_complete_cb(struct hci_conn *conn, u8 status)
@@ -3100,7 +3160,7 @@ static void le_pairing_complete_cb(struct hci_conn *conn, u8 status)
if (!cmd)
BT_DBG("Unable to find a pending command");
else
- pairing_complete(cmd, mgmt_status(status));
+ cmd->cmd_complete(cmd, mgmt_status(status));
}
static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
@@ -3197,6 +3257,8 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
goto unlock;
}
+ cmd->cmd_complete = pairing_complete;
+
/* For LE, just connecting isn't a proof that the pairing finished */
if (cp->addr.type == BDADDR_BREDR) {
conn->connect_cfm_cb = pairing_complete_cb;
@@ -3313,6 +3375,8 @@ static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev,
goto done;
}
+ cmd->cmd_complete = addr_cmd_complete;
+
/* Continue with pairing via HCI */
if (hci_op == HCI_OP_USER_PASSKEY_REPLY) {
struct hci_cp_user_passkey_reply cp;
@@ -3537,7 +3601,7 @@ static int read_local_oob_data(struct sock *sk, struct hci_dev *hdev,
goto unlock;
}
- if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags))
+ if (bredr_sc_enabled(hdev))
err = hci_send_cmd(hdev, HCI_OP_READ_LOCAL_OOB_EXT_DATA,
0, NULL);
else
@@ -3564,8 +3628,17 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
struct mgmt_cp_add_remote_oob_data *cp = data;
u8 status;
+ if (cp->addr.type != BDADDR_BREDR) {
+ err = cmd_complete(sk, hdev->id,
+ MGMT_OP_ADD_REMOTE_OOB_DATA,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
+ goto unlock;
+ }
+
err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr,
- cp->hash, cp->randomizer);
+ cp->addr.type, cp->hash,
+ cp->rand, NULL, NULL);
if (err < 0)
status = MGMT_STATUS_FAILED;
else
@@ -3575,13 +3648,28 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
status, &cp->addr, sizeof(cp->addr));
} else if (len == MGMT_ADD_REMOTE_OOB_EXT_DATA_SIZE) {
struct mgmt_cp_add_remote_oob_ext_data *cp = data;
+ u8 *rand192, *hash192;
u8 status;
- err = hci_add_remote_oob_ext_data(hdev, &cp->addr.bdaddr,
- cp->hash192,
- cp->randomizer192,
- cp->hash256,
- cp->randomizer256);
+ if (cp->addr.type != BDADDR_BREDR) {
+ err = cmd_complete(sk, hdev->id,
+ MGMT_OP_ADD_REMOTE_OOB_DATA,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
+ goto unlock;
+ }
+
+ if (bdaddr_type_is_le(cp->addr.type)) {
+ rand192 = NULL;
+ hash192 = NULL;
+ } else {
+ rand192 = cp->rand192;
+ hash192 = cp->hash192;
+ }
+
+ err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr,
+ cp->addr.type, hash192, rand192,
+ cp->hash256, cp->rand256);
if (err < 0)
status = MGMT_STATUS_FAILED;
else
@@ -3595,6 +3683,7 @@ static int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
MGMT_STATUS_INVALID_PARAMS);
}
+unlock:
hci_dev_unlock(hdev);
return err;
}
@@ -3608,14 +3697,26 @@ static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
BT_DBG("%s", hdev->name);
+ if (cp->addr.type != BDADDR_BREDR)
+ return cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->addr, sizeof(cp->addr));
+
hci_dev_lock(hdev);
- err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr);
+ if (!bacmp(&cp->addr.bdaddr, BDADDR_ANY)) {
+ hci_remote_oob_data_clear(hdev);
+ status = MGMT_STATUS_SUCCESS;
+ goto done;
+ }
+
+ err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr, cp->addr.type);
if (err < 0)
status = MGMT_STATUS_INVALID_PARAMS;
else
status = MGMT_STATUS_SUCCESS;
+done:
err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
status, &cp->addr, sizeof(cp->addr));
@@ -3623,64 +3724,150 @@ static int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev,
return err;
}
-static int mgmt_start_discovery_failed(struct hci_dev *hdev, u8 status)
+static bool trigger_discovery(struct hci_request *req, u8 *status)
{
- struct pending_cmd *cmd;
- u8 type;
+ struct hci_dev *hdev = req->hdev;
+ struct hci_cp_le_set_scan_param param_cp;
+ struct hci_cp_le_set_scan_enable enable_cp;
+ struct hci_cp_inquiry inq_cp;
+ /* General inquiry access code (GIAC) */
+ u8 lap[3] = { 0x33, 0x8b, 0x9e };
+ u8 own_addr_type;
int err;
- hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+ switch (hdev->discovery.type) {
+ case DISCOV_TYPE_BREDR:
+ *status = mgmt_bredr_support(hdev);
+ if (*status)
+ return false;
- cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
- if (!cmd)
- return -ENOENT;
+ if (test_bit(HCI_INQUIRY, &hdev->flags)) {
+ *status = MGMT_STATUS_BUSY;
+ return false;
+ }
- type = hdev->discovery.type;
+ hci_inquiry_cache_flush(hdev);
- err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
- &type, sizeof(type));
- mgmt_pending_remove(cmd);
+ memset(&inq_cp, 0, sizeof(inq_cp));
+ memcpy(&inq_cp.lap, lap, sizeof(inq_cp.lap));
+ inq_cp.length = DISCOV_BREDR_INQUIRY_LEN;
+ hci_req_add(req, HCI_OP_INQUIRY, sizeof(inq_cp), &inq_cp);
+ break;
- return err;
+ case DISCOV_TYPE_LE:
+ case DISCOV_TYPE_INTERLEAVED:
+ *status = mgmt_le_support(hdev);
+ if (*status)
+ return false;
+
+ if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED &&
+ !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
+ *status = MGMT_STATUS_NOT_SUPPORTED;
+ return false;
+ }
+
+ if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) {
+ /* Don't let discovery abort an outgoing
+ * connection attempt that's using directed
+ * advertising.
+ */
+ if (hci_conn_hash_lookup_state(hdev, LE_LINK,
+ BT_CONNECT)) {
+ *status = MGMT_STATUS_REJECTED;
+ return false;
+ }
+
+ disable_advertising(req);
+ }
+
+ /* If controller is scanning, it means the background scanning
+ * is running. Thus, we should temporarily stop it in order to
+ * set the discovery scanning parameters.
+ */
+ if (test_bit(HCI_LE_SCAN, &hdev->dev_flags))
+ hci_req_add_le_scan_disable(req);
+
+ memset(&param_cp, 0, sizeof(param_cp));
+
+ /* All active scans will be done with either a resolvable
+ * private address (when privacy feature has been enabled)
+ * or unresolvable private address.
+ */
+ err = hci_update_random_address(req, true, &own_addr_type);
+ if (err < 0) {
+ *status = MGMT_STATUS_FAILED;
+ return false;
+ }
+
+ param_cp.type = LE_SCAN_ACTIVE;
+ param_cp.interval = cpu_to_le16(DISCOV_LE_SCAN_INT);
+ param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN);
+ param_cp.own_address_type = own_addr_type;
+ hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
+ &param_cp);
+
+ memset(&enable_cp, 0, sizeof(enable_cp));
+ enable_cp.enable = LE_SCAN_ENABLE;
+ enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
+ hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp),
+ &enable_cp);
+ break;
+
+ default:
+ *status = MGMT_STATUS_INVALID_PARAMS;
+ return false;
+ }
+
+ return true;
}
static void start_discovery_complete(struct hci_dev *hdev, u8 status)
{
- unsigned long timeout = 0;
+ struct pending_cmd *cmd;
+ unsigned long timeout;
BT_DBG("status %d", status);
+ hci_dev_lock(hdev);
+
+ cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
+ if (!cmd)
+ cmd = mgmt_pending_find(MGMT_OP_START_SERVICE_DISCOVERY, hdev);
+
+ if (cmd) {
+ cmd->cmd_complete(cmd, mgmt_status(status));
+ mgmt_pending_remove(cmd);
+ }
+
if (status) {
- hci_dev_lock(hdev);
- mgmt_start_discovery_failed(hdev, status);
- hci_dev_unlock(hdev);
- return;
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+ goto unlock;
}
- hci_dev_lock(hdev);
hci_discovery_set_state(hdev, DISCOVERY_FINDING);
- hci_dev_unlock(hdev);
switch (hdev->discovery.type) {
case DISCOV_TYPE_LE:
timeout = msecs_to_jiffies(DISCOV_LE_TIMEOUT);
break;
-
case DISCOV_TYPE_INTERLEAVED:
timeout = msecs_to_jiffies(hdev->discov_interleaved_timeout);
break;
-
case DISCOV_TYPE_BREDR:
+ timeout = 0;
break;
-
default:
BT_ERR("Invalid discovery type %d", hdev->discovery.type);
+ timeout = 0;
+ break;
}
- if (!timeout)
- return;
+ if (timeout)
+ queue_delayed_work(hdev->workqueue,
+ &hdev->le_scan_disable, timeout);
- queue_delayed_work(hdev->workqueue, &hdev->le_scan_disable, timeout);
+unlock:
+ hci_dev_unlock(hdev);
}
static int start_discovery(struct sock *sk, struct hci_dev *hdev,
@@ -3688,13 +3875,8 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
{
struct mgmt_cp_start_discovery *cp = data;
struct pending_cmd *cmd;
- struct hci_cp_le_set_scan_param param_cp;
- struct hci_cp_le_set_scan_enable enable_cp;
- struct hci_cp_inquiry inq_cp;
struct hci_request req;
- /* General inquiry access code (GIAC) */
- u8 lap[3] = { 0x33, 0x8b, 0x9e };
- u8 status, own_addr_type;
+ u8 status;
int err;
BT_DBG("%s", hdev->name);
@@ -3702,176 +3884,188 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
hci_dev_lock(hdev);
if (!hdev_is_powered(hdev)) {
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_NOT_POWERED);
+ err = cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+ MGMT_STATUS_NOT_POWERED,
+ &cp->type, sizeof(cp->type));
goto failed;
}
- if (test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags)) {
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_BUSY);
+ if (hdev->discovery.state != DISCOVERY_STOPPED ||
+ test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags)) {
+ err = cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+ MGMT_STATUS_BUSY, &cp->type,
+ sizeof(cp->type));
goto failed;
}
- if (hdev->discovery.state != DISCOVERY_STOPPED) {
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_BUSY);
- goto failed;
- }
-
- cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, NULL, 0);
+ cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, data, len);
if (!cmd) {
err = -ENOMEM;
goto failed;
}
+ cmd->cmd_complete = generic_cmd_complete;
+
+ /* Clear the discovery filter first to free any previously
+ * allocated memory for the UUID list.
+ */
+ hci_discovery_filter_clear(hdev);
+
hdev->discovery.type = cp->type;
+ hdev->discovery.report_invalid_rssi = false;
hci_req_init(&req, hdev);
- switch (hdev->discovery.type) {
- case DISCOV_TYPE_BREDR:
- status = mgmt_bredr_support(hdev);
- if (status) {
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- status);
- mgmt_pending_remove(cmd);
- goto failed;
- }
+ if (!trigger_discovery(&req, &status)) {
+ err = cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+ status, &cp->type, sizeof(cp->type));
+ mgmt_pending_remove(cmd);
+ goto failed;
+ }
- if (test_bit(HCI_INQUIRY, &hdev->flags)) {
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_BUSY);
- mgmt_pending_remove(cmd);
- goto failed;
- }
+ err = hci_req_run(&req, start_discovery_complete);
+ if (err < 0) {
+ mgmt_pending_remove(cmd);
+ goto failed;
+ }
- hci_inquiry_cache_flush(hdev);
+ hci_discovery_set_state(hdev, DISCOVERY_STARTING);
- memset(&inq_cp, 0, sizeof(inq_cp));
- memcpy(&inq_cp.lap, lap, sizeof(inq_cp.lap));
- inq_cp.length = DISCOV_BREDR_INQUIRY_LEN;
- hci_req_add(&req, HCI_OP_INQUIRY, sizeof(inq_cp), &inq_cp);
- break;
+failed:
+ hci_dev_unlock(hdev);
+ return err;
+}
- case DISCOV_TYPE_LE:
- case DISCOV_TYPE_INTERLEAVED:
- status = mgmt_le_support(hdev);
- if (status) {
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- status);
- mgmt_pending_remove(cmd);
- goto failed;
- }
+static void service_discovery_cmd_complete(struct pending_cmd *cmd, u8 status)
+{
+ cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param, 1);
+}
- if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED &&
- !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_NOT_SUPPORTED);
- mgmt_pending_remove(cmd);
- goto failed;
- }
+static int start_service_discovery(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
+{
+ struct mgmt_cp_start_service_discovery *cp = data;
+ struct pending_cmd *cmd;
+ struct hci_request req;
+ const u16 max_uuid_count = ((U16_MAX - sizeof(*cp)) / 16);
+ u16 uuid_count, expected_len;
+ u8 status;
+ int err;
- if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) {
- /* Don't let discovery abort an outgoing
- * connection attempt that's using directed
- * advertising.
- */
- if (hci_conn_hash_lookup_state(hdev, LE_LINK,
- BT_CONNECT)) {
- err = cmd_status(sk, hdev->id,
- MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_REJECTED);
- mgmt_pending_remove(cmd);
- goto failed;
- }
+ BT_DBG("%s", hdev->name);
- disable_advertising(&req);
- }
+ hci_dev_lock(hdev);
- /* If controller is scanning, it means the background scanning
- * is running. Thus, we should temporarily stop it in order to
- * set the discovery scanning parameters.
- */
- if (test_bit(HCI_LE_SCAN, &hdev->dev_flags))
- hci_req_add_le_scan_disable(&req);
+ if (!hdev_is_powered(hdev)) {
+ err = cmd_complete(sk, hdev->id,
+ MGMT_OP_START_SERVICE_DISCOVERY,
+ MGMT_STATUS_NOT_POWERED,
+ &cp->type, sizeof(cp->type));
+ goto failed;
+ }
- memset(&param_cp, 0, sizeof(param_cp));
+ if (hdev->discovery.state != DISCOVERY_STOPPED ||
+ test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags)) {
+ err = cmd_complete(sk, hdev->id,
+ MGMT_OP_START_SERVICE_DISCOVERY,
+ MGMT_STATUS_BUSY, &cp->type,
+ sizeof(cp->type));
+ goto failed;
+ }
- /* All active scans will be done with either a resolvable
- * private address (when privacy feature has been enabled)
- * or unresolvable private address.
- */
- err = hci_update_random_address(&req, true, &own_addr_type);
- if (err < 0) {
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_FAILED);
+ uuid_count = __le16_to_cpu(cp->uuid_count);
+ if (uuid_count > max_uuid_count) {
+ BT_ERR("service_discovery: too big uuid_count value %u",
+ uuid_count);
+ err = cmd_complete(sk, hdev->id,
+ MGMT_OP_START_SERVICE_DISCOVERY,
+ MGMT_STATUS_INVALID_PARAMS, &cp->type,
+ sizeof(cp->type));
+ goto failed;
+ }
+
+ expected_len = sizeof(*cp) + uuid_count * 16;
+ if (expected_len != len) {
+ BT_ERR("service_discovery: expected %u bytes, got %u bytes",
+ expected_len, len);
+ err = cmd_complete(sk, hdev->id,
+ MGMT_OP_START_SERVICE_DISCOVERY,
+ MGMT_STATUS_INVALID_PARAMS, &cp->type,
+ sizeof(cp->type));
+ goto failed;
+ }
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_START_SERVICE_DISCOVERY,
+ hdev, data, len);
+ if (!cmd) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ cmd->cmd_complete = service_discovery_cmd_complete;
+
+ /* Clear the discovery filter first to free any previously
+ * allocated memory for the UUID list.
+ */
+ hci_discovery_filter_clear(hdev);
+
+ hdev->discovery.type = cp->type;
+ hdev->discovery.rssi = cp->rssi;
+ hdev->discovery.uuid_count = uuid_count;
+
+ if (uuid_count > 0) {
+ hdev->discovery.uuids = kmemdup(cp->uuids, uuid_count * 16,
+ GFP_KERNEL);
+ if (!hdev->discovery.uuids) {
+ err = cmd_complete(sk, hdev->id,
+ MGMT_OP_START_SERVICE_DISCOVERY,
+ MGMT_STATUS_FAILED,
+ &cp->type, sizeof(cp->type));
mgmt_pending_remove(cmd);
goto failed;
}
+ }
- param_cp.type = LE_SCAN_ACTIVE;
- param_cp.interval = cpu_to_le16(DISCOV_LE_SCAN_INT);
- param_cp.window = cpu_to_le16(DISCOV_LE_SCAN_WIN);
- param_cp.own_address_type = own_addr_type;
- hci_req_add(&req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
- &param_cp);
-
- memset(&enable_cp, 0, sizeof(enable_cp));
- enable_cp.enable = LE_SCAN_ENABLE;
- enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
- hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp),
- &enable_cp);
- break;
+ hci_req_init(&req, hdev);
- default:
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_INVALID_PARAMS);
+ if (!trigger_discovery(&req, &status)) {
+ err = cmd_complete(sk, hdev->id,
+ MGMT_OP_START_SERVICE_DISCOVERY,
+ status, &cp->type, sizeof(cp->type));
mgmt_pending_remove(cmd);
goto failed;
}
err = hci_req_run(&req, start_discovery_complete);
- if (err < 0)
+ if (err < 0) {
mgmt_pending_remove(cmd);
- else
- hci_discovery_set_state(hdev, DISCOVERY_STARTING);
+ goto failed;
+ }
+
+ hci_discovery_set_state(hdev, DISCOVERY_STARTING);
failed:
hci_dev_unlock(hdev);
return err;
}
-static int mgmt_stop_discovery_failed(struct hci_dev *hdev, u8 status)
+static void stop_discovery_complete(struct hci_dev *hdev, u8 status)
{
struct pending_cmd *cmd;
- int err;
-
- cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
- if (!cmd)
- return -ENOENT;
- err = cmd_complete(cmd->sk, hdev->id, cmd->opcode, mgmt_status(status),
- &hdev->discovery.type, sizeof(hdev->discovery.type));
- mgmt_pending_remove(cmd);
-
- return err;
-}
-
-static void stop_discovery_complete(struct hci_dev *hdev, u8 status)
-{
BT_DBG("status %d", status);
hci_dev_lock(hdev);
- if (status) {
- mgmt_stop_discovery_failed(hdev, status);
- goto unlock;
+ cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
+ if (cmd) {
+ cmd->cmd_complete(cmd, mgmt_status(status));
+ mgmt_pending_remove(cmd);
}
- hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+ if (!status)
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
-unlock:
hci_dev_unlock(hdev);
}
@@ -3901,12 +4095,14 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data,
goto unlock;
}
- cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, NULL, 0);
+ cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, data, len);
if (!cmd) {
err = -ENOMEM;
goto unlock;
}
+ cmd->cmd_complete = generic_cmd_complete;
+
hci_req_init(&req, hdev);
hci_stop_discovery(&req);
@@ -4506,18 +4702,13 @@ static int set_secure_conn(struct sock *sk, struct hci_dev *hdev,
{
struct mgmt_mode *cp = data;
struct pending_cmd *cmd;
- u8 val, status;
+ u8 val;
int err;
BT_DBG("request for %s", hdev->name);
- status = mgmt_bredr_support(hdev);
- if (status)
- return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
- status);
-
- if (!lmp_sc_capable(hdev) &&
- !test_bit(HCI_FORCE_SC, &hdev->dbg_flags))
+ if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags) &&
+ !lmp_sc_capable(hdev) && !test_bit(HCI_FORCE_SC, &hdev->dbg_flags))
return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN,
MGMT_STATUS_NOT_SUPPORTED);
@@ -4527,7 +4718,10 @@ static int set_secure_conn(struct sock *sk, struct hci_dev *hdev,
hci_dev_lock(hdev);
- if (!hdev_is_powered(hdev)) {
+ if (!hdev_is_powered(hdev) ||
+ (!lmp_sc_capable(hdev) &&
+ !test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) ||
+ !test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
bool changed;
if (cp->val) {
@@ -4844,18 +5038,26 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
else
addr_type = ADDR_LE_DEV_RANDOM;
- if (key->master)
- type = SMP_LTK;
- else
- type = SMP_LTK_SLAVE;
-
switch (key->type) {
case MGMT_LTK_UNAUTHENTICATED:
authenticated = 0x00;
+ type = key->master ? SMP_LTK : SMP_LTK_SLAVE;
break;
case MGMT_LTK_AUTHENTICATED:
authenticated = 0x01;
+ type = key->master ? SMP_LTK : SMP_LTK_SLAVE;
+ break;
+ case MGMT_LTK_P256_UNAUTH:
+ authenticated = 0x00;
+ type = SMP_LTK_P256;
+ break;
+ case MGMT_LTK_P256_AUTH:
+ authenticated = 0x01;
+ type = SMP_LTK_P256;
break;
+ case MGMT_LTK_P256_DEBUG:
+ authenticated = 0x00;
+ type = SMP_LTK_P256_DEBUG;
default:
continue;
}
@@ -4873,67 +5075,42 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
return err;
}
-struct cmd_conn_lookup {
- struct hci_conn *conn;
- bool valid_tx_power;
- u8 mgmt_status;
-};
-
-static void get_conn_info_complete(struct pending_cmd *cmd, void *data)
+static void conn_info_cmd_complete(struct pending_cmd *cmd, u8 status)
{
- struct cmd_conn_lookup *match = data;
- struct mgmt_cp_get_conn_info *cp;
- struct mgmt_rp_get_conn_info rp;
struct hci_conn *conn = cmd->user_data;
+ struct mgmt_rp_get_conn_info rp;
- if (conn != match->conn)
- return;
-
- cp = (struct mgmt_cp_get_conn_info *) cmd->param;
+ memcpy(&rp.addr, cmd->param, sizeof(rp.addr));
- memset(&rp, 0, sizeof(rp));
- bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
- rp.addr.type = cp->addr.type;
-
- if (!match->mgmt_status) {
+ if (status == MGMT_STATUS_SUCCESS) {
rp.rssi = conn->rssi;
-
- if (match->valid_tx_power) {
- rp.tx_power = conn->tx_power;
- rp.max_tx_power = conn->max_tx_power;
- } else {
- rp.tx_power = HCI_TX_POWER_INVALID;
- rp.max_tx_power = HCI_TX_POWER_INVALID;
- }
+ rp.tx_power = conn->tx_power;
+ rp.max_tx_power = conn->max_tx_power;
+ } else {
+ rp.rssi = HCI_RSSI_INVALID;
+ rp.tx_power = HCI_TX_POWER_INVALID;
+ rp.max_tx_power = HCI_TX_POWER_INVALID;
}
- cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO,
- match->mgmt_status, &rp, sizeof(rp));
+ cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO, status,
+ &rp, sizeof(rp));
hci_conn_drop(conn);
hci_conn_put(conn);
-
- mgmt_pending_remove(cmd);
}
-static void conn_info_refresh_complete(struct hci_dev *hdev, u8 status)
+static void conn_info_refresh_complete(struct hci_dev *hdev, u8 hci_status)
{
struct hci_cp_read_rssi *cp;
+ struct pending_cmd *cmd;
struct hci_conn *conn;
- struct cmd_conn_lookup match;
u16 handle;
+ u8 status;
- BT_DBG("status 0x%02x", status);
+ BT_DBG("status 0x%02x", hci_status);
hci_dev_lock(hdev);
- /* TX power data is valid in case request completed successfully,
- * otherwise we assume it's not valid. At the moment we assume that
- * either both or none of current and max values are valid to keep code
- * simple.
- */
- match.valid_tx_power = !status;
-
/* Commands sent in request are either Read RSSI or Read Transmit Power
* Level so we check which one was last sent to retrieve connection
* handle. Both commands have handle as first parameter so it's safe to
@@ -4946,29 +5123,29 @@ static void conn_info_refresh_complete(struct hci_dev *hdev, u8 status)
cp = hci_sent_cmd_data(hdev, HCI_OP_READ_RSSI);
if (!cp) {
cp = hci_sent_cmd_data(hdev, HCI_OP_READ_TX_POWER);
- status = 0;
+ status = MGMT_STATUS_SUCCESS;
+ } else {
+ status = mgmt_status(hci_status);
}
if (!cp) {
- BT_ERR("invalid sent_cmd in response");
+ BT_ERR("invalid sent_cmd in conn_info response");
goto unlock;
}
handle = __le16_to_cpu(cp->handle);
conn = hci_conn_hash_lookup_handle(hdev, handle);
if (!conn) {
- BT_ERR("unknown handle (%d) in response", handle);
+ BT_ERR("unknown handle (%d) in conn_info response", handle);
goto unlock;
}
- match.conn = conn;
- match.mgmt_status = mgmt_status(status);
+ cmd = mgmt_pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn);
+ if (!cmd)
+ goto unlock;
- /* Cache refresh is complete, now reply for mgmt request for given
- * connection only.
- */
- mgmt_pending_foreach(MGMT_OP_GET_CONN_INFO, hdev,
- get_conn_info_complete, &match);
+ cmd->cmd_complete(cmd, status);
+ mgmt_pending_remove(cmd);
unlock:
hci_dev_unlock(hdev);
@@ -5014,6 +5191,12 @@ static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
goto unlock;
}
+ if (mgmt_pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn)) {
+ err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO,
+ MGMT_STATUS_BUSY, &rp, sizeof(rp));
+ goto unlock;
+ }
+
/* To avoid client trying to guess when to poll again for information we
* calculate conn info age as random value between min/max set in hdev.
*/
@@ -5069,6 +5252,7 @@ static int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data,
hci_conn_hold(conn);
cmd->user_data = hci_conn_get(conn);
+ cmd->cmd_complete = conn_info_cmd_complete;
conn->conn_info_timestamp = jiffies;
} else {
@@ -5086,10 +5270,40 @@ unlock:
return err;
}
-static void get_clock_info_complete(struct hci_dev *hdev, u8 status)
+static void clock_info_cmd_complete(struct pending_cmd *cmd, u8 status)
{
- struct mgmt_cp_get_clock_info *cp;
+ struct hci_conn *conn = cmd->user_data;
struct mgmt_rp_get_clock_info rp;
+ struct hci_dev *hdev;
+
+ memset(&rp, 0, sizeof(rp));
+ memcpy(&rp.addr, &cmd->param, sizeof(rp.addr));
+
+ if (status)
+ goto complete;
+
+ hdev = hci_dev_get(cmd->index);
+ if (hdev) {
+ rp.local_clock = cpu_to_le32(hdev->clock);
+ hci_dev_put(hdev);
+ }
+
+ if (conn) {
+ rp.piconet_clock = cpu_to_le32(conn->clock);
+ rp.accuracy = cpu_to_le16(conn->clock_accuracy);
+ }
+
+complete:
+ cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, &rp, sizeof(rp));
+
+ if (conn) {
+ hci_conn_drop(conn);
+ hci_conn_put(conn);
+ }
+}
+
+static void get_clock_info_complete(struct hci_dev *hdev, u8 status)
+{
struct hci_cp_read_clock *hci_cp;
struct pending_cmd *cmd;
struct hci_conn *conn;
@@ -5113,29 +5327,8 @@ static void get_clock_info_complete(struct hci_dev *hdev, u8 status)
if (!cmd)
goto unlock;
- cp = cmd->param;
-
- memset(&rp, 0, sizeof(rp));
- memcpy(&rp.addr, &cp->addr, sizeof(rp.addr));
-
- if (status)
- goto send_rsp;
-
- rp.local_clock = cpu_to_le32(hdev->clock);
-
- if (conn) {
- rp.piconet_clock = cpu_to_le32(conn->clock);
- rp.accuracy = cpu_to_le16(conn->clock_accuracy);
- }
-
-send_rsp:
- cmd_complete(cmd->sk, cmd->index, cmd->opcode, mgmt_status(status),
- &rp, sizeof(rp));
+ cmd->cmd_complete(cmd, mgmt_status(status));
mgmt_pending_remove(cmd);
- if (conn) {
- hci_conn_drop(conn);
- hci_conn_put(conn);
- }
unlock:
hci_dev_unlock(hdev);
@@ -5191,6 +5384,8 @@ static int get_clock_info(struct sock *sk, struct hci_dev *hdev, void *data,
goto unlock;
}
+ cmd->cmd_complete = clock_info_cmd_complete;
+
hci_req_init(&req, hdev);
memset(&hci_cp, 0, sizeof(hci_cp));
@@ -5680,6 +5875,7 @@ static const struct mgmt_handler {
{ read_config_info, false, MGMT_READ_CONFIG_INFO_SIZE },
{ set_external_config, false, MGMT_SET_EXTERNAL_CONFIG_SIZE },
{ set_public_address, false, MGMT_SET_PUBLIC_ADDRESS_SIZE },
+ { start_service_discovery,true, MGMT_START_SERVICE_DISCOVERY_SIZE },
};
int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
@@ -5701,7 +5897,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
if (!buf)
return -ENOMEM;
- if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) {
+ if (memcpy_from_msg(buf, msg, msglen)) {
err = -EFAULT;
goto done;
}
@@ -5816,7 +6012,7 @@ void mgmt_index_removed(struct hci_dev *hdev)
if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
return;
- mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status);
+ mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status);
if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags))
mgmt_event(MGMT_EV_UNCONF_INDEX_REMOVED, hdev, NULL, 0, NULL);
@@ -5951,7 +6147,7 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
}
mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
- mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status_not_powered);
+ mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status_not_powered);
if (memcmp(hdev->dev_class, zero_cod, sizeof(zero_cod)) != 0)
mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev,
@@ -6035,8 +6231,19 @@ void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
static u8 mgmt_ltk_type(struct smp_ltk *ltk)
{
- if (ltk->authenticated)
- return MGMT_LTK_AUTHENTICATED;
+ switch (ltk->type) {
+ case SMP_LTK:
+ case SMP_LTK_SLAVE:
+ if (ltk->authenticated)
+ return MGMT_LTK_AUTHENTICATED;
+ return MGMT_LTK_UNAUTHENTICATED;
+ case SMP_LTK_P256:
+ if (ltk->authenticated)
+ return MGMT_LTK_P256_AUTH;
+ return MGMT_LTK_P256_UNAUTH;
+ case SMP_LTK_P256_DEBUG:
+ return MGMT_LTK_P256_DEBUG;
+ }
return MGMT_LTK_UNAUTHENTICATED;
}
@@ -6171,26 +6378,36 @@ static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data,
return eir_len;
}
-void mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
- u8 addr_type, u32 flags, u8 *name, u8 name_len,
- u8 *dev_class)
+void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn,
+ u32 flags, u8 *name, u8 name_len)
{
char buf[512];
struct mgmt_ev_device_connected *ev = (void *) buf;
u16 eir_len = 0;
- bacpy(&ev->addr.bdaddr, bdaddr);
- ev->addr.type = link_to_bdaddr(link_type, addr_type);
+ bacpy(&ev->addr.bdaddr, &conn->dst);
+ ev->addr.type = link_to_bdaddr(conn->type, conn->dst_type);
ev->flags = __cpu_to_le32(flags);
- if (name_len > 0)
- eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE,
- name, name_len);
+ /* We must ensure that the EIR Data fields are ordered and
+ * unique. Keep it simple for now and avoid the problem by not
+ * adding any BR/EDR data to the LE adv.
+ */
+ if (conn->le_adv_data_len > 0) {
+ memcpy(&ev->eir[eir_len],
+ conn->le_adv_data, conn->le_adv_data_len);
+ eir_len = conn->le_adv_data_len;
+ } else {
+ if (name_len > 0)
+ eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE,
+ name, name_len);
- if (dev_class && memcmp(dev_class, "\0\0\0", 3) != 0)
- eir_len = eir_append_data(ev->eir, eir_len,
- EIR_CLASS_OF_DEV, dev_class, 3);
+ if (memcmp(conn->dev_class, "\0\0\0", 3) != 0)
+ eir_len = eir_append_data(ev->eir, eir_len,
+ EIR_CLASS_OF_DEV,
+ conn->dev_class, 3);
+ }
ev->eir_len = cpu_to_le16(eir_len);
@@ -6200,15 +6417,9 @@ void mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
static void disconnect_rsp(struct pending_cmd *cmd, void *data)
{
- struct mgmt_cp_disconnect *cp = cmd->param;
struct sock **sk = data;
- struct mgmt_rp_disconnect rp;
-
- bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
- rp.addr.type = cp->addr.type;
- cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, 0, &rp,
- sizeof(rp));
+ cmd->cmd_complete(cmd, 0);
*sk = cmd->sk;
sock_hold(*sk);
@@ -6220,16 +6431,10 @@ static void unpair_device_rsp(struct pending_cmd *cmd, void *data)
{
struct hci_dev *hdev = data;
struct mgmt_cp_unpair_device *cp = cmd->param;
- struct mgmt_rp_unpair_device rp;
-
- memset(&rp, 0, sizeof(rp));
- bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
- rp.addr.type = cp->addr.type;
device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, cmd->sk);
- cmd_complete(cmd->sk, cmd->index, cmd->opcode, 0, &rp, sizeof(rp));
-
+ cmd->cmd_complete(cmd, 0);
mgmt_pending_remove(cmd);
}
@@ -6290,7 +6495,6 @@ void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
{
u8 bdaddr_type = link_to_bdaddr(link_type, addr_type);
struct mgmt_cp_disconnect *cp;
- struct mgmt_rp_disconnect rp;
struct pending_cmd *cmd;
mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp,
@@ -6308,12 +6512,7 @@ void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
if (cp->addr.type != bdaddr_type)
return;
- bacpy(&rp.addr.bdaddr, bdaddr);
- rp.addr.type = bdaddr_type;
-
- cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT,
- mgmt_status(status), &rp, sizeof(rp));
-
+ cmd->cmd_complete(cmd, mgmt_status(status));
mgmt_pending_remove(cmd);
}
@@ -6352,18 +6551,12 @@ void mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 status)
{
struct pending_cmd *cmd;
- struct mgmt_rp_pin_code_reply rp;
cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev);
if (!cmd)
return;
- bacpy(&rp.addr.bdaddr, bdaddr);
- rp.addr.type = BDADDR_BREDR;
-
- cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY,
- mgmt_status(status), &rp, sizeof(rp));
-
+ cmd->cmd_complete(cmd, mgmt_status(status));
mgmt_pending_remove(cmd);
}
@@ -6371,18 +6564,12 @@ void mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 status)
{
struct pending_cmd *cmd;
- struct mgmt_rp_pin_code_reply rp;
cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev);
if (!cmd)
return;
- bacpy(&rp.addr.bdaddr, bdaddr);
- rp.addr.type = BDADDR_BREDR;
-
- cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY,
- mgmt_status(status), &rp, sizeof(rp));
-
+ cmd->cmd_complete(cmd, mgmt_status(status));
mgmt_pending_remove(cmd);
}
@@ -6422,21 +6609,15 @@ static int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 opcode)
{
struct pending_cmd *cmd;
- struct mgmt_rp_user_confirm_reply rp;
- int err;
cmd = mgmt_pending_find(opcode, hdev);
if (!cmd)
return -ENOENT;
- bacpy(&rp.addr.bdaddr, bdaddr);
- rp.addr.type = link_to_bdaddr(link_type, addr_type);
- err = cmd_complete(cmd->sk, hdev->id, opcode, mgmt_status(status),
- &rp, sizeof(rp));
-
+ cmd->cmd_complete(cmd, mgmt_status(status));
mgmt_pending_remove(cmd);
- return err;
+ return 0;
}
int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr,
@@ -6693,8 +6874,8 @@ void mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status)
}
void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
- u8 *randomizer192, u8 *hash256,
- u8 *randomizer256, u8 status)
+ u8 *rand192, u8 *hash256, u8 *rand256,
+ u8 status)
{
struct pending_cmd *cmd;
@@ -6708,17 +6889,14 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA,
mgmt_status(status));
} else {
- if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags) &&
- hash256 && randomizer256) {
+ if (bredr_sc_enabled(hdev) && hash256 && rand256) {
struct mgmt_rp_read_local_oob_ext_data rp;
memcpy(rp.hash192, hash192, sizeof(rp.hash192));
- memcpy(rp.randomizer192, randomizer192,
- sizeof(rp.randomizer192));
+ memcpy(rp.rand192, rand192, sizeof(rp.rand192));
memcpy(rp.hash256, hash256, sizeof(rp.hash256));
- memcpy(rp.randomizer256, randomizer256,
- sizeof(rp.randomizer256));
+ memcpy(rp.rand256, rand256, sizeof(rp.rand256));
cmd_complete(cmd->sk, hdev->id,
MGMT_OP_READ_LOCAL_OOB_DATA, 0,
@@ -6727,8 +6905,7 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
struct mgmt_rp_read_local_oob_data rp;
memcpy(rp.hash, hash192, sizeof(rp.hash));
- memcpy(rp.randomizer, randomizer192,
- sizeof(rp.randomizer));
+ memcpy(rp.rand, rand192, sizeof(rp.rand));
cmd_complete(cmd->sk, hdev->id,
MGMT_OP_READ_LOCAL_OOB_DATA, 0,
@@ -6739,6 +6916,73 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192,
mgmt_pending_remove(cmd);
}
+static inline bool has_uuid(u8 *uuid, u16 uuid_count, u8 (*uuids)[16])
+{
+ int i;
+
+ for (i = 0; i < uuid_count; i++) {
+ if (!memcmp(uuid, uuids[i], 16))
+ return true;
+ }
+
+ return false;
+}
+
+static bool eir_has_uuids(u8 *eir, u16 eir_len, u16 uuid_count, u8 (*uuids)[16])
+{
+ u16 parsed = 0;
+
+ while (parsed < eir_len) {
+ u8 field_len = eir[0];
+ u8 uuid[16];
+ int i;
+
+ if (field_len == 0)
+ break;
+
+ if (eir_len - parsed < field_len + 1)
+ break;
+
+ switch (eir[1]) {
+ case EIR_UUID16_ALL:
+ case EIR_UUID16_SOME:
+ for (i = 0; i + 3 <= field_len; i += 2) {
+ memcpy(uuid, bluetooth_base_uuid, 16);
+ uuid[13] = eir[i + 3];
+ uuid[12] = eir[i + 2];
+ if (has_uuid(uuid, uuid_count, uuids))
+ return true;
+ }
+ break;
+ case EIR_UUID32_ALL:
+ case EIR_UUID32_SOME:
+ for (i = 0; i + 5 <= field_len; i += 4) {
+ memcpy(uuid, bluetooth_base_uuid, 16);
+ uuid[15] = eir[i + 5];
+ uuid[14] = eir[i + 4];
+ uuid[13] = eir[i + 3];
+ uuid[12] = eir[i + 2];
+ if (has_uuid(uuid, uuid_count, uuids))
+ return true;
+ }
+ break;
+ case EIR_UUID128_ALL:
+ case EIR_UUID128_SOME:
+ for (i = 0; i + 17 <= field_len; i += 16) {
+ memcpy(uuid, eir + i + 2, 16);
+ if (has_uuid(uuid, uuid_count, uuids))
+ return true;
+ }
+ break;
+ }
+
+ parsed += field_len + 1;
+ eir += field_len + 1;
+ }
+
+ return false;
+}
+
void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type, u8 *dev_class, s8 rssi, u32 flags,
u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len)
@@ -6746,6 +6990,7 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
char buf[512];
struct mgmt_ev_device_found *ev = (void *) buf;
size_t ev_size;
+ bool match;
/* Don't send events for a non-kernel initiated discovery. With
* LE one exception is if we have pend_le_reports > 0 in which
@@ -6758,6 +7003,18 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
return;
}
+ /* When using service discovery with a RSSI threshold, then check
+ * if such a RSSI threshold is specified. If a RSSI threshold has
+ * been specified, then all results with a RSSI smaller than the
+ * RSSI threshold will be dropped.
+ *
+ * For BR/EDR devices (pre 1.2) providing no RSSI during inquiry,
+ * the results are also dropped.
+ */
+ if (hdev->discovery.rssi != HCI_RSSI_INVALID &&
+ (rssi < hdev->discovery.rssi || rssi == HCI_RSSI_INVALID))
+ return;
+
/* Make sure that the buffer is big enough. The 5 extra bytes
* are for the potential CoD field.
*/
@@ -6766,20 +7023,75 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
memset(buf, 0, sizeof(buf));
+ /* In case of device discovery with BR/EDR devices (pre 1.2), the
+ * RSSI value was reported as 0 when not available. This behavior
+ * is kept when using device discovery. This is required for full
+ * backwards compatibility with the API.
+ *
+ * However when using service discovery, the value 127 will be
+ * returned when the RSSI is not available.
+ */
+ if (rssi == HCI_RSSI_INVALID && !hdev->discovery.report_invalid_rssi)
+ rssi = 0;
+
bacpy(&ev->addr.bdaddr, bdaddr);
ev->addr.type = link_to_bdaddr(link_type, addr_type);
ev->rssi = rssi;
ev->flags = cpu_to_le32(flags);
- if (eir_len > 0)
+ if (eir_len > 0) {
+ /* When using service discovery and a list of UUID is
+ * provided, results with no matching UUID should be
+ * dropped. In case there is a match the result is
+ * kept and checking possible scan response data
+ * will be skipped.
+ */
+ if (hdev->discovery.uuid_count > 0) {
+ match = eir_has_uuids(eir, eir_len,
+ hdev->discovery.uuid_count,
+ hdev->discovery.uuids);
+ if (!match)
+ return;
+ }
+
+ /* Copy EIR or advertising data into event */
memcpy(ev->eir, eir, eir_len);
+ } else {
+ /* When using service discovery and a list of UUID is
+ * provided, results with empty EIR or advertising data
+ * should be dropped since they do not match any UUID.
+ */
+ if (hdev->discovery.uuid_count > 0)
+ return;
+ }
if (dev_class && !eir_has_data_type(ev->eir, eir_len, EIR_CLASS_OF_DEV))
eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV,
dev_class, 3);
- if (scan_rsp_len > 0)
+ if (scan_rsp_len > 0) {
+ /* When using service discovery and a list of UUID is
+ * provided, results with no matching UUID should be
+ * dropped if there is no previous match from the
+ * advertising data.
+ */
+ if (hdev->discovery.uuid_count > 0) {
+ if (!match && !eir_has_uuids(scan_rsp, scan_rsp_len,
+ hdev->discovery.uuid_count,
+ hdev->discovery.uuids))
+ return;
+ }
+
+ /* Append scan response data to event */
memcpy(ev->eir + eir_len, scan_rsp, scan_rsp_len);
+ } else {
+ /* When using service discovery and a list of UUID is
+ * provided, results with empty scan response and no
+ * previous matched advertising data should be dropped.
+ */
+ if (hdev->discovery.uuid_count > 0 && !match)
+ return;
+ }
ev->eir_len = cpu_to_le16(eir_len + scan_rsp_len);
ev_size = sizeof(*ev) + eir_len + scan_rsp_len;
@@ -6813,23 +7125,9 @@ void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
void mgmt_discovering(struct hci_dev *hdev, u8 discovering)
{
struct mgmt_ev_discovering ev;
- struct pending_cmd *cmd;
BT_DBG("%s discovering %u", hdev->name, discovering);
- if (discovering)
- cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev);
- else
- cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev);
-
- if (cmd != NULL) {
- u8 type = hdev->discovery.type;
-
- cmd_complete(cmd->sk, hdev->id, cmd->opcode, 0, &type,
- sizeof(type));
- mgmt_pending_remove(cmd);
- }
-
memset(&ev, 0, sizeof(ev));
ev.type = hdev->discovery.type;
ev.discovering = discovering;
diff --git a/net/bluetooth/rfcomm/Kconfig b/net/bluetooth/rfcomm/Kconfig
index 18d352ea2bc7..335df7515220 100644
--- a/net/bluetooth/rfcomm/Kconfig
+++ b/net/bluetooth/rfcomm/Kconfig
@@ -1,6 +1,6 @@
config BT_RFCOMM
tristate "RFCOMM protocol support"
- depends on BT
+ depends on BT_BREDR
help
RFCOMM provides connection oriented stream transport. RFCOMM
support is required for Dialup Networking, OBEX and other Bluetooth
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index af73bc3acb40..73f8c75abe6e 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -78,8 +78,10 @@ static struct rfcomm_session *rfcomm_session_del(struct rfcomm_session *s);
#define __get_type(b) ((b & 0xef))
#define __test_ea(b) ((b & 0x01))
-#define __test_cr(b) ((b & 0x02))
-#define __test_pf(b) ((b & 0x10))
+#define __test_cr(b) (!!(b & 0x02))
+#define __test_pf(b) (!!(b & 0x10))
+
+#define __session_dir(s) ((s)->initiator ? 0x00 : 0x01)
#define __addr(cr, dlci) (((dlci & 0x3f) << 2) | (cr << 1) | 0x01)
#define __ctrl(type, pf) (((type & 0xef) | (pf << 4)))
@@ -101,11 +103,11 @@ static struct rfcomm_session *rfcomm_session_del(struct rfcomm_session *s);
#define __get_rpn_stop_bits(line) (((line) >> 2) & 0x1)
#define __get_rpn_parity(line) (((line) >> 3) & 0x7)
+static DECLARE_WAIT_QUEUE_HEAD(rfcomm_wq);
+
static void rfcomm_schedule(void)
{
- if (!rfcomm_thread)
- return;
- wake_up_process(rfcomm_thread);
+ wake_up_all(&rfcomm_wq);
}
/* ---- RFCOMM FCS computation ---- */
@@ -388,7 +390,7 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst,
return err;
}
- dlci = __dlci(!s->initiator, channel);
+ dlci = __dlci(__session_dir(s), channel);
/* Check if DLCI already exists */
if (rfcomm_dlc_get(s, dlci))
@@ -543,7 +545,7 @@ struct rfcomm_dlc *rfcomm_dlc_exists(bdaddr_t *src, bdaddr_t *dst, u8 channel)
rfcomm_lock();
s = rfcomm_session_get(src, dst);
if (s) {
- dlci = __dlci(!s->initiator, channel);
+ dlci = __dlci(__session_dir(s), channel);
dlc = rfcomm_dlc_get(s, dlci);
}
rfcomm_unlock();
@@ -904,7 +906,7 @@ static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type)
hdr->len = __len8(sizeof(*mcc) + 1);
mcc = (void *) ptr; ptr += sizeof(*mcc);
- mcc->type = __mcc_type(cr, RFCOMM_NSC);
+ mcc->type = __mcc_type(0, RFCOMM_NSC);
mcc->len = __len8(1);
/* Type that we didn't like */
@@ -2086,24 +2088,22 @@ static void rfcomm_kill_listener(void)
static int rfcomm_run(void *unused)
{
+ DEFINE_WAIT_FUNC(wait, woken_wake_function);
BT_DBG("");
set_user_nice(current, -10);
rfcomm_add_listener(BDADDR_ANY);
- while (1) {
- set_current_state(TASK_INTERRUPTIBLE);
-
- if (kthread_should_stop())
- break;
+ add_wait_queue(&rfcomm_wq, &wait);
+ while (!kthread_should_stop()) {
/* Process stuff */
rfcomm_process_sessions();
- schedule();
+ wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}
- __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&rfcomm_wq, &wait);
rfcomm_kill_listener();
diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c
index 8bbbb5ec468c..2348176401a0 100644
--- a/net/bluetooth/rfcomm/sock.c
+++ b/net/bluetooth/rfcomm/sock.c
@@ -588,7 +588,7 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
}
skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE);
- err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
+ err = memcpy_from_msg(skb_put(skb, size), msg, size);
if (err) {
kfree_skb(skb);
if (sent == 0)
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index 7ee9e4ab00f8..30e5ea3f1ad3 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -285,7 +285,7 @@ static int sco_send_frame(struct sock *sk, struct msghdr *msg, int len)
if (!skb)
return err;
- if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
+ if (memcpy_from_msg(skb_put(skb, len), msg, len)) {
kfree_skb(skb);
return -EFAULT;
}
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index f09b6b65cf6b..6a46252fe66f 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -29,14 +29,34 @@
#include <net/bluetooth/l2cap.h>
#include <net/bluetooth/mgmt.h>
+#include "ecc.h"
#include "smp.h"
+/* Low-level debug macros to be used for stuff that we don't want
+ * accidentially in dmesg, i.e. the values of the various crypto keys
+ * and the inputs & outputs of crypto functions.
+ */
+#ifdef DEBUG
+#define SMP_DBG(fmt, ...) printk(KERN_DEBUG "%s: " fmt, __func__, \
+ ##__VA_ARGS__)
+#else
+#define SMP_DBG(fmt, ...) no_printk(KERN_DEBUG "%s: " fmt, __func__, \
+ ##__VA_ARGS__)
+#endif
+
#define SMP_ALLOW_CMD(smp, code) set_bit(code, &smp->allow_cmd)
+/* Keys which are not distributed with Secure Connections */
+#define SMP_SC_NO_DIST (SMP_DIST_ENC_KEY | SMP_DIST_LINK_KEY);
+
#define SMP_TIMEOUT msecs_to_jiffies(30000)
-#define AUTH_REQ_MASK 0x07
-#define KEY_DIST_MASK 0x07
+#define AUTH_REQ_MASK(dev) (test_bit(HCI_SC_ENABLED, &(dev)->dev_flags) ? \
+ 0x1f : 0x07)
+#define KEY_DIST_MASK 0x07
+
+/* Maximum message length that can be passed to aes_cmac */
+#define CMAC_MSG_MAX 80
enum {
SMP_FLAG_TK_VALID,
@@ -44,6 +64,12 @@ enum {
SMP_FLAG_MITM_AUTH,
SMP_FLAG_COMPLETE,
SMP_FLAG_INITIATOR,
+ SMP_FLAG_SC,
+ SMP_FLAG_REMOTE_PK,
+ SMP_FLAG_DEBUG_KEY,
+ SMP_FLAG_WAIT_USER,
+ SMP_FLAG_DHKEY_PENDING,
+ SMP_FLAG_OOB,
};
struct smp_chan {
@@ -57,6 +83,7 @@ struct smp_chan {
u8 rrnd[16]; /* SMP Pairing Random (remote) */
u8 pcnf[16]; /* SMP Pairing Confirm */
u8 tk[16]; /* SMP Temporary Key */
+ u8 rr[16];
u8 enc_key_size;
u8 remote_key_dist;
bdaddr_t id_addr;
@@ -67,9 +94,43 @@ struct smp_chan {
struct smp_ltk *ltk;
struct smp_ltk *slave_ltk;
struct smp_irk *remote_irk;
+ u8 *link_key;
unsigned long flags;
+ u8 method;
+ u8 passkey_round;
+
+ /* Secure Connections variables */
+ u8 local_pk[64];
+ u8 local_sk[32];
+ u8 remote_pk[64];
+ u8 dhkey[32];
+ u8 mackey[16];
struct crypto_blkcipher *tfm_aes;
+ struct crypto_hash *tfm_cmac;
+};
+
+/* These debug key values are defined in the SMP section of the core
+ * specification. debug_pk is the public debug key and debug_sk the
+ * private debug key.
+ */
+static const u8 debug_pk[64] = {
+ 0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc,
+ 0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef,
+ 0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e,
+ 0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20,
+
+ 0x8b, 0xd2, 0x89, 0x15, 0xd0, 0x8e, 0x1c, 0x74,
+ 0x24, 0x30, 0xed, 0x8f, 0xc2, 0x45, 0x63, 0x76,
+ 0x5c, 0x15, 0x52, 0x5a, 0xbf, 0x9a, 0x32, 0x63,
+ 0x6d, 0xeb, 0x2a, 0x65, 0x49, 0x9c, 0x80, 0xdc,
+};
+
+static const u8 debug_sk[32] = {
+ 0xbd, 0x1a, 0x3c, 0xcd, 0xa6, 0xb8, 0x99, 0x58,
+ 0x99, 0xb7, 0x40, 0xeb, 0x7b, 0x60, 0xff, 0x4a,
+ 0x50, 0x3f, 0x10, 0xd2, 0xe3, 0xb3, 0xc9, 0x74,
+ 0x38, 0x5f, 0xc5, 0xa3, 0xd4, 0xf6, 0x49, 0x3f,
};
static inline void swap_buf(const u8 *src, u8 *dst, size_t len)
@@ -80,14 +141,22 @@ static inline void swap_buf(const u8 *src, u8 *dst, size_t len)
dst[len - 1 - i] = src[i];
}
-static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
+/* The following functions map to the LE SC SMP crypto functions
+ * AES-CMAC, f4, f5, f6, g2 and h6.
+ */
+
+static int aes_cmac(struct crypto_hash *tfm, const u8 k[16], const u8 *m,
+ size_t len, u8 mac[16])
{
- struct blkcipher_desc desc;
+ uint8_t tmp[16], mac_msb[16], msg_msb[CMAC_MSG_MAX];
+ struct hash_desc desc;
struct scatterlist sg;
- uint8_t tmp[16], data[16];
int err;
- if (tfm == NULL) {
+ if (len > CMAC_MSG_MAX)
+ return -EFBIG;
+
+ if (!tfm) {
BT_ERR("tfm %p", tfm);
return -EINVAL;
}
@@ -95,112 +164,237 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
desc.tfm = tfm;
desc.flags = 0;
- /* The most significant octet of key corresponds to k[0] */
+ crypto_hash_init(&desc);
+
+ /* Swap key and message from LSB to MSB */
swap_buf(k, tmp, 16);
+ swap_buf(m, msg_msb, len);
- err = crypto_blkcipher_setkey(tfm, tmp, 16);
+ SMP_DBG("msg (len %zu) %*phN", len, (int) len, m);
+ SMP_DBG("key %16phN", k);
+
+ err = crypto_hash_setkey(tfm, tmp, 16);
if (err) {
BT_ERR("cipher setkey failed: %d", err);
return err;
}
- /* Most significant octet of plaintextData corresponds to data[0] */
- swap_buf(r, data, 16);
+ sg_init_one(&sg, msg_msb, len);
- sg_init_one(&sg, data, 16);
+ err = crypto_hash_update(&desc, &sg, len);
+ if (err) {
+ BT_ERR("Hash update error %d", err);
+ return err;
+ }
- err = crypto_blkcipher_encrypt(&desc, &sg, &sg, 16);
+ err = crypto_hash_final(&desc, mac_msb);
+ if (err) {
+ BT_ERR("Hash final error %d", err);
+ return err;
+ }
+
+ swap_buf(mac_msb, mac, 16);
+
+ SMP_DBG("mac %16phN", mac);
+
+ return 0;
+}
+
+static int smp_f4(struct crypto_hash *tfm_cmac, const u8 u[32], const u8 v[32],
+ const u8 x[16], u8 z, u8 res[16])
+{
+ u8 m[65];
+ int err;
+
+ SMP_DBG("u %32phN", u);
+ SMP_DBG("v %32phN", v);
+ SMP_DBG("x %16phN z %02x", x, z);
+
+ m[0] = z;
+ memcpy(m + 1, v, 32);
+ memcpy(m + 33, u, 32);
+
+ err = aes_cmac(tfm_cmac, x, m, sizeof(m), res);
if (err)
- BT_ERR("Encrypt data error %d", err);
+ return err;
- /* Most significant octet of encryptedData corresponds to data[0] */
- swap_buf(data, r, 16);
+ SMP_DBG("res %16phN", res);
return err;
}
-static int smp_ah(struct crypto_blkcipher *tfm, u8 irk[16], u8 r[3], u8 res[3])
+static int smp_f5(struct crypto_hash *tfm_cmac, u8 w[32], u8 n1[16], u8 n2[16],
+ u8 a1[7], u8 a2[7], u8 mackey[16], u8 ltk[16])
{
- u8 _res[16];
+ /* The btle, salt and length "magic" values are as defined in
+ * the SMP section of the Bluetooth core specification. In ASCII
+ * the btle value ends up being 'btle'. The salt is just a
+ * random number whereas length is the value 256 in little
+ * endian format.
+ */
+ const u8 btle[4] = { 0x65, 0x6c, 0x74, 0x62 };
+ const u8 salt[16] = { 0xbe, 0x83, 0x60, 0x5a, 0xdb, 0x0b, 0x37, 0x60,
+ 0x38, 0xa5, 0xf5, 0xaa, 0x91, 0x83, 0x88, 0x6c };
+ const u8 length[2] = { 0x00, 0x01 };
+ u8 m[53], t[16];
int err;
- /* r' = padding || r */
- memcpy(_res, r, 3);
- memset(_res + 3, 0, 13);
+ SMP_DBG("w %32phN", w);
+ SMP_DBG("n1 %16phN n2 %16phN", n1, n2);
+ SMP_DBG("a1 %7phN a2 %7phN", a1, a2);
- err = smp_e(tfm, irk, _res);
- if (err) {
- BT_ERR("Encrypt error");
+ err = aes_cmac(tfm_cmac, salt, w, 32, t);
+ if (err)
return err;
- }
- /* The output of the random address function ah is:
- * ah(h, r) = e(k, r') mod 2^24
- * The output of the security function e is then truncated to 24 bits
- * by taking the least significant 24 bits of the output of e as the
- * result of ah.
- */
- memcpy(res, _res, 3);
+ SMP_DBG("t %16phN", t);
+
+ memcpy(m, length, 2);
+ memcpy(m + 2, a2, 7);
+ memcpy(m + 9, a1, 7);
+ memcpy(m + 16, n2, 16);
+ memcpy(m + 32, n1, 16);
+ memcpy(m + 48, btle, 4);
+
+ m[52] = 0; /* Counter */
+
+ err = aes_cmac(tfm_cmac, t, m, sizeof(m), mackey);
+ if (err)
+ return err;
+
+ SMP_DBG("mackey %16phN", mackey);
+
+ m[52] = 1; /* Counter */
+
+ err = aes_cmac(tfm_cmac, t, m, sizeof(m), ltk);
+ if (err)
+ return err;
+
+ SMP_DBG("ltk %16phN", ltk);
return 0;
}
-bool smp_irk_matches(struct hci_dev *hdev, u8 irk[16], bdaddr_t *bdaddr)
+static int smp_f6(struct crypto_hash *tfm_cmac, const u8 w[16],
+ const u8 n1[16], u8 n2[16], const u8 r[16],
+ const u8 io_cap[3], const u8 a1[7], const u8 a2[7],
+ u8 res[16])
{
- struct l2cap_chan *chan = hdev->smp_data;
- struct crypto_blkcipher *tfm;
- u8 hash[3];
+ u8 m[65];
int err;
- if (!chan || !chan->data)
- return false;
+ SMP_DBG("w %16phN", w);
+ SMP_DBG("n1 %16phN n2 %16phN", n1, n2);
+ SMP_DBG("r %16phN io_cap %3phN a1 %7phN a2 %7phN", r, io_cap, a1, a2);
- tfm = chan->data;
+ memcpy(m, a2, 7);
+ memcpy(m + 7, a1, 7);
+ memcpy(m + 14, io_cap, 3);
+ memcpy(m + 17, r, 16);
+ memcpy(m + 33, n2, 16);
+ memcpy(m + 49, n1, 16);
- BT_DBG("RPA %pMR IRK %*phN", bdaddr, 16, irk);
+ err = aes_cmac(tfm_cmac, w, m, sizeof(m), res);
+ if (err)
+ return err;
- err = smp_ah(tfm, irk, &bdaddr->b[3], hash);
+ BT_DBG("res %16phN", res);
+
+ return err;
+}
+
+static int smp_g2(struct crypto_hash *tfm_cmac, const u8 u[32], const u8 v[32],
+ const u8 x[16], const u8 y[16], u32 *val)
+{
+ u8 m[80], tmp[16];
+ int err;
+
+ SMP_DBG("u %32phN", u);
+ SMP_DBG("v %32phN", v);
+ SMP_DBG("x %16phN y %16phN", x, y);
+
+ memcpy(m, y, 16);
+ memcpy(m + 16, v, 32);
+ memcpy(m + 48, u, 32);
+
+ err = aes_cmac(tfm_cmac, x, m, sizeof(m), tmp);
if (err)
- return false;
+ return err;
- return !memcmp(bdaddr->b, hash, 3);
+ *val = get_unaligned_le32(tmp);
+ *val %= 1000000;
+
+ SMP_DBG("val %06u", *val);
+
+ return 0;
}
-int smp_generate_rpa(struct hci_dev *hdev, u8 irk[16], bdaddr_t *rpa)
+static int smp_h6(struct crypto_hash *tfm_cmac, const u8 w[16],
+ const u8 key_id[4], u8 res[16])
{
- struct l2cap_chan *chan = hdev->smp_data;
- struct crypto_blkcipher *tfm;
int err;
- if (!chan || !chan->data)
- return -EOPNOTSUPP;
+ SMP_DBG("w %16phN key_id %4phN", w, key_id);
- tfm = chan->data;
+ err = aes_cmac(tfm_cmac, w, key_id, 4, res);
+ if (err)
+ return err;
- get_random_bytes(&rpa->b[3], 3);
+ SMP_DBG("res %16phN", res);
- rpa->b[5] &= 0x3f; /* Clear two most significant bits */
- rpa->b[5] |= 0x40; /* Set second most significant bit */
+ return err;
+}
- err = smp_ah(tfm, irk, &rpa->b[3], rpa->b);
- if (err < 0)
+/* The following functions map to the legacy SMP crypto functions e, c1,
+ * s1 and ah.
+ */
+
+static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
+{
+ struct blkcipher_desc desc;
+ struct scatterlist sg;
+ uint8_t tmp[16], data[16];
+ int err;
+
+ if (!tfm) {
+ BT_ERR("tfm %p", tfm);
+ return -EINVAL;
+ }
+
+ desc.tfm = tfm;
+ desc.flags = 0;
+
+ /* The most significant octet of key corresponds to k[0] */
+ swap_buf(k, tmp, 16);
+
+ err = crypto_blkcipher_setkey(tfm, tmp, 16);
+ if (err) {
+ BT_ERR("cipher setkey failed: %d", err);
return err;
+ }
- BT_DBG("RPA %pMR", rpa);
+ /* Most significant octet of plaintextData corresponds to data[0] */
+ swap_buf(r, data, 16);
- return 0;
+ sg_init_one(&sg, data, 16);
+
+ err = crypto_blkcipher_encrypt(&desc, &sg, &sg, 16);
+ if (err)
+ BT_ERR("Encrypt data error %d", err);
+
+ /* Most significant octet of encryptedData corresponds to data[0] */
+ swap_buf(data, r, 16);
+
+ return err;
}
-static int smp_c1(struct smp_chan *smp, u8 k[16], u8 r[16], u8 preq[7],
- u8 pres[7], u8 _iat, bdaddr_t *ia, u8 _rat, bdaddr_t *ra,
- u8 res[16])
+static int smp_c1(struct crypto_blkcipher *tfm_aes, const u8 k[16],
+ const u8 r[16], const u8 preq[7], const u8 pres[7], u8 _iat,
+ const bdaddr_t *ia, u8 _rat, const bdaddr_t *ra, u8 res[16])
{
- struct hci_dev *hdev = smp->conn->hcon->hdev;
u8 p1[16], p2[16];
int err;
- BT_DBG("%s", hdev->name);
-
memset(p1, 0, 16);
/* p1 = pres || preq || _rat || _iat */
@@ -218,7 +412,7 @@ static int smp_c1(struct smp_chan *smp, u8 k[16], u8 r[16], u8 preq[7],
u128_xor((u128 *) res, (u128 *) r, (u128 *) p1);
/* res = e(k, res) */
- err = smp_e(smp->tfm_aes, k, res);
+ err = smp_e(tfm_aes, k, res);
if (err) {
BT_ERR("Encrypt data error");
return err;
@@ -228,32 +422,103 @@ static int smp_c1(struct smp_chan *smp, u8 k[16], u8 r[16], u8 preq[7],
u128_xor((u128 *) res, (u128 *) res, (u128 *) p2);
/* res = e(k, res) */
- err = smp_e(smp->tfm_aes, k, res);
+ err = smp_e(tfm_aes, k, res);
if (err)
BT_ERR("Encrypt data error");
return err;
}
-static int smp_s1(struct smp_chan *smp, u8 k[16], u8 r1[16], u8 r2[16],
- u8 _r[16])
+static int smp_s1(struct crypto_blkcipher *tfm_aes, const u8 k[16],
+ const u8 r1[16], const u8 r2[16], u8 _r[16])
{
- struct hci_dev *hdev = smp->conn->hcon->hdev;
int err;
- BT_DBG("%s", hdev->name);
-
/* Just least significant octets from r1 and r2 are considered */
memcpy(_r, r2, 8);
memcpy(_r + 8, r1, 8);
- err = smp_e(smp->tfm_aes, k, _r);
+ err = smp_e(tfm_aes, k, _r);
if (err)
BT_ERR("Encrypt data error");
return err;
}
+static int smp_ah(struct crypto_blkcipher *tfm, const u8 irk[16],
+ const u8 r[3], u8 res[3])
+{
+ u8 _res[16];
+ int err;
+
+ /* r' = padding || r */
+ memcpy(_res, r, 3);
+ memset(_res + 3, 0, 13);
+
+ err = smp_e(tfm, irk, _res);
+ if (err) {
+ BT_ERR("Encrypt error");
+ return err;
+ }
+
+ /* The output of the random address function ah is:
+ * ah(h, r) = e(k, r') mod 2^24
+ * The output of the security function e is then truncated to 24 bits
+ * by taking the least significant 24 bits of the output of e as the
+ * result of ah.
+ */
+ memcpy(res, _res, 3);
+
+ return 0;
+}
+
+bool smp_irk_matches(struct hci_dev *hdev, const u8 irk[16],
+ const bdaddr_t *bdaddr)
+{
+ struct l2cap_chan *chan = hdev->smp_data;
+ struct crypto_blkcipher *tfm;
+ u8 hash[3];
+ int err;
+
+ if (!chan || !chan->data)
+ return false;
+
+ tfm = chan->data;
+
+ BT_DBG("RPA %pMR IRK %*phN", bdaddr, 16, irk);
+
+ err = smp_ah(tfm, irk, &bdaddr->b[3], hash);
+ if (err)
+ return false;
+
+ return !memcmp(bdaddr->b, hash, 3);
+}
+
+int smp_generate_rpa(struct hci_dev *hdev, const u8 irk[16], bdaddr_t *rpa)
+{
+ struct l2cap_chan *chan = hdev->smp_data;
+ struct crypto_blkcipher *tfm;
+ int err;
+
+ if (!chan || !chan->data)
+ return -EOPNOTSUPP;
+
+ tfm = chan->data;
+
+ get_random_bytes(&rpa->b[3], 3);
+
+ rpa->b[5] &= 0x3f; /* Clear two most significant bits */
+ rpa->b[5] |= 0x40; /* Set second most significant bit */
+
+ err = smp_ah(tfm, irk, &rpa->b[3], rpa->b);
+ if (err < 0)
+ return err;
+
+ BT_DBG("RPA %pMR", rpa);
+
+ return 0;
+}
+
static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
{
struct l2cap_chan *chan = conn->smp;
@@ -274,8 +539,7 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
memset(&msg, 0, sizeof(msg));
- msg.msg_iov = (struct iovec *) &iv;
- msg.msg_iovlen = 2;
+ iov_iter_kvec(&msg.msg_iter, WRITE | ITER_KVEC, iv, 2, 1 + len);
l2cap_chan_send(chan, &msg, 1 + len);
@@ -288,17 +552,22 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data)
schedule_delayed_work(&smp->security_timer, SMP_TIMEOUT);
}
-static __u8 authreq_to_seclevel(__u8 authreq)
+static u8 authreq_to_seclevel(u8 authreq)
{
- if (authreq & SMP_AUTH_MITM)
- return BT_SECURITY_HIGH;
- else
+ if (authreq & SMP_AUTH_MITM) {
+ if (authreq & SMP_AUTH_SC)
+ return BT_SECURITY_FIPS;
+ else
+ return BT_SECURITY_HIGH;
+ } else {
return BT_SECURITY_MEDIUM;
+ }
}
static __u8 seclevel_to_authreq(__u8 sec_level)
{
switch (sec_level) {
+ case BT_SECURITY_FIPS:
case BT_SECURITY_HIGH:
return SMP_AUTH_MITM | SMP_AUTH_BONDING;
case BT_SECURITY_MEDIUM:
@@ -316,7 +585,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
struct smp_chan *smp = chan->data;
struct hci_conn *hcon = conn->hcon;
struct hci_dev *hdev = hcon->hdev;
- u8 local_dist = 0, remote_dist = 0;
+ u8 local_dist = 0, remote_dist = 0, oob_flag = SMP_OOB_NOT_PRESENT;
if (test_bit(HCI_BONDABLE, &conn->hcon->hdev->dev_flags)) {
local_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN;
@@ -332,24 +601,52 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
if (test_bit(HCI_PRIVACY, &hdev->dev_flags))
local_dist |= SMP_DIST_ID_KEY;
+ if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags) &&
+ (authreq & SMP_AUTH_SC)) {
+ struct oob_data *oob_data;
+ u8 bdaddr_type;
+
+ if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
+ local_dist |= SMP_DIST_LINK_KEY;
+ remote_dist |= SMP_DIST_LINK_KEY;
+ }
+
+ if (hcon->dst_type == ADDR_LE_DEV_PUBLIC)
+ bdaddr_type = BDADDR_LE_PUBLIC;
+ else
+ bdaddr_type = BDADDR_LE_RANDOM;
+
+ oob_data = hci_find_remote_oob_data(hdev, &hcon->dst,
+ bdaddr_type);
+ if (oob_data) {
+ set_bit(SMP_FLAG_OOB, &smp->flags);
+ oob_flag = SMP_OOB_PRESENT;
+ memcpy(smp->rr, oob_data->rand256, 16);
+ memcpy(smp->pcnf, oob_data->hash256, 16);
+ }
+
+ } else {
+ authreq &= ~SMP_AUTH_SC;
+ }
+
if (rsp == NULL) {
req->io_capability = conn->hcon->io_capability;
- req->oob_flag = SMP_OOB_NOT_PRESENT;
+ req->oob_flag = oob_flag;
req->max_key_size = SMP_MAX_ENC_KEY_SIZE;
req->init_key_dist = local_dist;
req->resp_key_dist = remote_dist;
- req->auth_req = (authreq & AUTH_REQ_MASK);
+ req->auth_req = (authreq & AUTH_REQ_MASK(hdev));
smp->remote_key_dist = remote_dist;
return;
}
rsp->io_capability = conn->hcon->io_capability;
- rsp->oob_flag = SMP_OOB_NOT_PRESENT;
+ rsp->oob_flag = oob_flag;
rsp->max_key_size = SMP_MAX_ENC_KEY_SIZE;
rsp->init_key_dist = req->init_key_dist & remote_dist;
rsp->resp_key_dist = req->resp_key_dist & local_dist;
- rsp->auth_req = (authreq & AUTH_REQ_MASK);
+ rsp->auth_req = (authreq & AUTH_REQ_MASK(hdev));
smp->remote_key_dist = rsp->init_key_dist;
}
@@ -372,6 +669,7 @@ static void smp_chan_destroy(struct l2cap_conn *conn)
{
struct l2cap_chan *chan = conn->smp;
struct smp_chan *smp = chan->data;
+ struct hci_conn *hcon = conn->hcon;
bool complete;
BUG_ON(!smp);
@@ -379,34 +677,46 @@ static void smp_chan_destroy(struct l2cap_conn *conn)
cancel_delayed_work_sync(&smp->security_timer);
complete = test_bit(SMP_FLAG_COMPLETE, &smp->flags);
- mgmt_smp_complete(conn->hcon, complete);
+ mgmt_smp_complete(hcon, complete);
kfree(smp->csrk);
kfree(smp->slave_csrk);
+ kfree(smp->link_key);
crypto_free_blkcipher(smp->tfm_aes);
+ crypto_free_hash(smp->tfm_cmac);
+
+ /* Ensure that we don't leave any debug key around if debug key
+ * support hasn't been explicitly enabled.
+ */
+ if (smp->ltk && smp->ltk->type == SMP_LTK_P256_DEBUG &&
+ !test_bit(HCI_KEEP_DEBUG_KEYS, &hcon->hdev->dev_flags)) {
+ list_del_rcu(&smp->ltk->list);
+ kfree_rcu(smp->ltk, rcu);
+ smp->ltk = NULL;
+ }
/* If pairing failed clean up any keys we might have */
if (!complete) {
if (smp->ltk) {
- list_del(&smp->ltk->list);
- kfree(smp->ltk);
+ list_del_rcu(&smp->ltk->list);
+ kfree_rcu(smp->ltk, rcu);
}
if (smp->slave_ltk) {
- list_del(&smp->slave_ltk->list);
- kfree(smp->slave_ltk);
+ list_del_rcu(&smp->slave_ltk->list);
+ kfree_rcu(smp->slave_ltk, rcu);
}
if (smp->remote_irk) {
- list_del(&smp->remote_irk->list);
- kfree(smp->remote_irk);
+ list_del_rcu(&smp->remote_irk->list);
+ kfree_rcu(smp->remote_irk, rcu);
}
}
chan->data = NULL;
kfree(smp);
- hci_conn_drop(conn->hcon);
+ hci_conn_drop(hcon);
}
static void smp_failure(struct l2cap_conn *conn, u8 reason)
@@ -430,6 +740,7 @@ static void smp_failure(struct l2cap_conn *conn, u8 reason)
#define REQ_PASSKEY 0x02
#define CFM_PASSKEY 0x03
#define REQ_OOB 0x04
+#define DSP_PASSKEY 0x05
#define OVERLAP 0xFF
static const u8 gen_method[5][5] = {
@@ -440,6 +751,14 @@ static const u8 gen_method[5][5] = {
{ CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, OVERLAP },
};
+static const u8 sc_method[5][5] = {
+ { JUST_WORKS, JUST_CFM, REQ_PASSKEY, JUST_WORKS, REQ_PASSKEY },
+ { JUST_WORKS, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, CFM_PASSKEY },
+ { DSP_PASSKEY, DSP_PASSKEY, REQ_PASSKEY, JUST_WORKS, DSP_PASSKEY },
+ { JUST_WORKS, JUST_CFM, JUST_WORKS, JUST_WORKS, JUST_CFM },
+ { DSP_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, CFM_PASSKEY },
+};
+
static u8 get_auth_method(struct smp_chan *smp, u8 local_io, u8 remote_io)
{
/* If either side has unknown io_caps, use JUST_CFM (which gets
@@ -449,6 +768,9 @@ static u8 get_auth_method(struct smp_chan *smp, u8 local_io, u8 remote_io)
remote_io > SMP_IO_KEYBOARD_DISPLAY)
return JUST_CFM;
+ if (test_bit(SMP_FLAG_SC, &smp->flags))
+ return sc_method[remote_io][local_io];
+
return gen_method[remote_io][local_io];
}
@@ -458,7 +780,6 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
struct hci_conn *hcon = conn->hcon;
struct l2cap_chan *chan = conn->smp;
struct smp_chan *smp = chan->data;
- u8 method;
u32 passkey = 0;
int ret = 0;
@@ -475,26 +796,28 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
* table.
*/
if (!(auth & SMP_AUTH_MITM))
- method = JUST_CFM;
+ smp->method = JUST_CFM;
else
- method = get_auth_method(smp, local_io, remote_io);
+ smp->method = get_auth_method(smp, local_io, remote_io);
/* Don't confirm locally initiated pairing attempts */
- if (method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR, &smp->flags))
- method = JUST_WORKS;
+ if (smp->method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR,
+ &smp->flags))
+ smp->method = JUST_WORKS;
/* Don't bother user space with no IO capabilities */
- if (method == JUST_CFM && hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
- method = JUST_WORKS;
+ if (smp->method == JUST_CFM &&
+ hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
+ smp->method = JUST_WORKS;
/* If Just Works, Continue with Zero TK */
- if (method == JUST_WORKS) {
+ if (smp->method == JUST_WORKS) {
set_bit(SMP_FLAG_TK_VALID, &smp->flags);
return 0;
}
/* Not Just Works/Confirm results in MITM Authentication */
- if (method != JUST_CFM) {
+ if (smp->method != JUST_CFM) {
set_bit(SMP_FLAG_MITM_AUTH, &smp->flags);
if (hcon->pending_sec_level < BT_SECURITY_HIGH)
hcon->pending_sec_level = BT_SECURITY_HIGH;
@@ -503,15 +826,15 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
/* If both devices have Keyoard-Display I/O, the master
* Confirms and the slave Enters the passkey.
*/
- if (method == OVERLAP) {
+ if (smp->method == OVERLAP) {
if (hcon->role == HCI_ROLE_MASTER)
- method = CFM_PASSKEY;
+ smp->method = CFM_PASSKEY;
else
- method = REQ_PASSKEY;
+ smp->method = REQ_PASSKEY;
}
/* Generate random passkey. */
- if (method == CFM_PASSKEY) {
+ if (smp->method == CFM_PASSKEY) {
memset(smp->tk, 0, sizeof(smp->tk));
get_random_bytes(&passkey, sizeof(passkey));
passkey %= 1000000;
@@ -520,12 +843,10 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
set_bit(SMP_FLAG_TK_VALID, &smp->flags);
}
- hci_dev_lock(hcon->hdev);
-
- if (method == REQ_PASSKEY)
+ if (smp->method == REQ_PASSKEY)
ret = mgmt_user_passkey_request(hcon->hdev, &hcon->dst,
hcon->type, hcon->dst_type);
- else if (method == JUST_CFM)
+ else if (smp->method == JUST_CFM)
ret = mgmt_user_confirm_request(hcon->hdev, &hcon->dst,
hcon->type, hcon->dst_type,
passkey, 1);
@@ -534,8 +855,6 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth,
hcon->type, hcon->dst_type,
passkey, 0);
- hci_dev_unlock(hcon->hdev);
-
return ret;
}
@@ -547,7 +866,7 @@ static u8 smp_confirm(struct smp_chan *smp)
BT_DBG("conn %p", conn);
- ret = smp_c1(smp, smp->tk, smp->prnd, smp->preq, smp->prsp,
+ ret = smp_c1(smp->tfm_aes, smp->tk, smp->prnd, smp->preq, smp->prsp,
conn->hcon->init_addr_type, &conn->hcon->init_addr,
conn->hcon->resp_addr_type, &conn->hcon->resp_addr,
cp.confirm_val);
@@ -578,7 +897,7 @@ static u8 smp_random(struct smp_chan *smp)
BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
- ret = smp_c1(smp, smp->tk, smp->rrnd, smp->preq, smp->prsp,
+ ret = smp_c1(smp->tfm_aes, smp->tk, smp->rrnd, smp->preq, smp->prsp,
hcon->init_addr_type, &hcon->init_addr,
hcon->resp_addr_type, &hcon->resp_addr, confirm);
if (ret)
@@ -594,7 +913,7 @@ static u8 smp_random(struct smp_chan *smp)
__le64 rand = 0;
__le16 ediv = 0;
- smp_s1(smp, smp->tk, smp->rrnd, smp->prnd, stk);
+ smp_s1(smp->tfm_aes, smp->tk, smp->rrnd, smp->prnd, stk);
memset(stk + smp->enc_key_size, 0,
SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
@@ -613,7 +932,7 @@ static u8 smp_random(struct smp_chan *smp)
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
smp->prnd);
- smp_s1(smp, smp->tk, smp->prnd, smp->rrnd, stk);
+ smp_s1(smp->tfm_aes, smp->tk, smp->prnd, smp->rrnd, stk);
memset(stk + smp->enc_key_size, 0,
SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
@@ -648,11 +967,13 @@ static void smp_notify_keys(struct l2cap_conn *conn)
mgmt_new_irk(hdev, smp->remote_irk);
/* Now that user space can be considered to know the
* identity address track the connection based on it
- * from now on.
+ * from now on (assuming this is an LE link).
*/
- bacpy(&hcon->dst, &smp->remote_irk->bdaddr);
- hcon->dst_type = smp->remote_irk->addr_type;
- queue_work(hdev->workqueue, &conn->id_addr_update_work);
+ if (hcon->type == LE_LINK) {
+ bacpy(&hcon->dst, &smp->remote_irk->bdaddr);
+ hcon->dst_type = smp->remote_irk->addr_type;
+ queue_work(hdev->workqueue, &conn->id_addr_update_work);
+ }
/* When receiving an indentity resolving key for
* a remote device that does not use a resolvable
@@ -665,16 +986,26 @@ static void smp_notify_keys(struct l2cap_conn *conn)
* just remove it.
*/
if (!bacmp(&smp->remote_irk->rpa, BDADDR_ANY)) {
- list_del(&smp->remote_irk->list);
- kfree(smp->remote_irk);
+ list_del_rcu(&smp->remote_irk->list);
+ kfree_rcu(smp->remote_irk, rcu);
smp->remote_irk = NULL;
}
}
- /* The LTKs and CSRKs should be persistent only if both sides
- * had the bonding bit set in their authentication requests.
- */
- persistent = !!((req->auth_req & rsp->auth_req) & SMP_AUTH_BONDING);
+ if (hcon->type == ACL_LINK) {
+ if (hcon->key_type == HCI_LK_DEBUG_COMBINATION)
+ persistent = false;
+ else
+ persistent = !test_bit(HCI_CONN_FLUSH_KEY,
+ &hcon->flags);
+ } else {
+ /* The LTKs and CSRKs should be persistent only if both sides
+ * had the bonding bit set in their authentication requests.
+ */
+ persistent = !!((req->auth_req & rsp->auth_req) &
+ SMP_AUTH_BONDING);
+ }
+
if (smp->csrk) {
smp->csrk->bdaddr_type = hcon->dst_type;
@@ -699,6 +1030,81 @@ static void smp_notify_keys(struct l2cap_conn *conn)
bacpy(&smp->slave_ltk->bdaddr, &hcon->dst);
mgmt_new_ltk(hdev, smp->slave_ltk, persistent);
}
+
+ if (smp->link_key) {
+ struct link_key *key;
+ u8 type;
+
+ if (test_bit(SMP_FLAG_DEBUG_KEY, &smp->flags))
+ type = HCI_LK_DEBUG_COMBINATION;
+ else if (hcon->sec_level == BT_SECURITY_FIPS)
+ type = HCI_LK_AUTH_COMBINATION_P256;
+ else
+ type = HCI_LK_UNAUTH_COMBINATION_P256;
+
+ key = hci_add_link_key(hdev, smp->conn->hcon, &hcon->dst,
+ smp->link_key, type, 0, &persistent);
+ if (key) {
+ mgmt_new_link_key(hdev, key, persistent);
+
+ /* Don't keep debug keys around if the relevant
+ * flag is not set.
+ */
+ if (!test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags) &&
+ key->type == HCI_LK_DEBUG_COMBINATION) {
+ list_del_rcu(&key->list);
+ kfree_rcu(key, rcu);
+ }
+ }
+ }
+}
+
+static void sc_add_ltk(struct smp_chan *smp)
+{
+ struct hci_conn *hcon = smp->conn->hcon;
+ u8 key_type, auth;
+
+ if (test_bit(SMP_FLAG_DEBUG_KEY, &smp->flags))
+ key_type = SMP_LTK_P256_DEBUG;
+ else
+ key_type = SMP_LTK_P256;
+
+ if (hcon->pending_sec_level == BT_SECURITY_FIPS)
+ auth = 1;
+ else
+ auth = 0;
+
+ memset(smp->tk + smp->enc_key_size, 0,
+ SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
+
+ smp->ltk = hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type,
+ key_type, auth, smp->tk, smp->enc_key_size,
+ 0, 0);
+}
+
+static void sc_generate_link_key(struct smp_chan *smp)
+{
+ /* These constants are as specified in the core specification.
+ * In ASCII they spell out to 'tmp1' and 'lebr'.
+ */
+ const u8 tmp1[4] = { 0x31, 0x70, 0x6d, 0x74 };
+ const u8 lebr[4] = { 0x72, 0x62, 0x65, 0x6c };
+
+ smp->link_key = kzalloc(16, GFP_KERNEL);
+ if (!smp->link_key)
+ return;
+
+ if (smp_h6(smp->tfm_cmac, smp->tk, tmp1, smp->link_key)) {
+ kfree(smp->link_key);
+ smp->link_key = NULL;
+ return;
+ }
+
+ if (smp_h6(smp->tfm_cmac, smp->link_key, lebr, smp->link_key)) {
+ kfree(smp->link_key);
+ smp->link_key = NULL;
+ return;
+ }
}
static void smp_allow_key_dist(struct smp_chan *smp)
@@ -715,6 +1121,35 @@ static void smp_allow_key_dist(struct smp_chan *smp)
SMP_ALLOW_CMD(smp, SMP_CMD_SIGN_INFO);
}
+static void sc_generate_ltk(struct smp_chan *smp)
+{
+ /* These constants are as specified in the core specification.
+ * In ASCII they spell out to 'tmp2' and 'brle'.
+ */
+ const u8 tmp2[4] = { 0x32, 0x70, 0x6d, 0x74 };
+ const u8 brle[4] = { 0x65, 0x6c, 0x72, 0x62 };
+ struct hci_conn *hcon = smp->conn->hcon;
+ struct hci_dev *hdev = hcon->hdev;
+ struct link_key *key;
+
+ key = hci_find_link_key(hdev, &hcon->dst);
+ if (!key) {
+ BT_ERR("%s No Link Key found to generate LTK", hdev->name);
+ return;
+ }
+
+ if (key->type == HCI_LK_DEBUG_COMBINATION)
+ set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags);
+
+ if (smp_h6(smp->tfm_cmac, key->val, tmp2, smp->tk))
+ return;
+
+ if (smp_h6(smp->tfm_cmac, smp->tk, brle, smp->tk))
+ return;
+
+ sc_add_ltk(smp);
+}
+
static void smp_distribute_keys(struct smp_chan *smp)
{
struct smp_cmd_pairing *req, *rsp;
@@ -743,6 +1178,16 @@ static void smp_distribute_keys(struct smp_chan *smp)
*keydist &= req->resp_key_dist;
}
+ if (test_bit(SMP_FLAG_SC, &smp->flags)) {
+ if (hcon->type == LE_LINK && (*keydist & SMP_DIST_LINK_KEY))
+ sc_generate_link_key(smp);
+ if (hcon->type == ACL_LINK && (*keydist & SMP_DIST_ENC_KEY))
+ sc_generate_ltk(smp);
+
+ /* Clear the keys which are generated but not distributed */
+ *keydist &= ~SMP_SC_NO_DIST;
+ }
+
BT_DBG("keydist 0x%x", *keydist);
if (*keydist & SMP_DIST_ENC_KEY) {
@@ -854,6 +1299,14 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
return NULL;
}
+ smp->tfm_cmac = crypto_alloc_hash("cmac(aes)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(smp->tfm_cmac)) {
+ BT_ERR("Unable to create CMAC crypto context");
+ crypto_free_blkcipher(smp->tfm_aes);
+ kfree(smp);
+ return NULL;
+ }
+
smp->conn = conn;
chan->data = smp;
@@ -866,6 +1319,213 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
return smp;
}
+static int sc_mackey_and_ltk(struct smp_chan *smp, u8 mackey[16], u8 ltk[16])
+{
+ struct hci_conn *hcon = smp->conn->hcon;
+ u8 *na, *nb, a[7], b[7];
+
+ if (hcon->out) {
+ na = smp->prnd;
+ nb = smp->rrnd;
+ } else {
+ na = smp->rrnd;
+ nb = smp->prnd;
+ }
+
+ memcpy(a, &hcon->init_addr, 6);
+ memcpy(b, &hcon->resp_addr, 6);
+ a[6] = hcon->init_addr_type;
+ b[6] = hcon->resp_addr_type;
+
+ return smp_f5(smp->tfm_cmac, smp->dhkey, na, nb, a, b, mackey, ltk);
+}
+
+static void sc_dhkey_check(struct smp_chan *smp)
+{
+ struct hci_conn *hcon = smp->conn->hcon;
+ struct smp_cmd_dhkey_check check;
+ u8 a[7], b[7], *local_addr, *remote_addr;
+ u8 io_cap[3], r[16];
+
+ memcpy(a, &hcon->init_addr, 6);
+ memcpy(b, &hcon->resp_addr, 6);
+ a[6] = hcon->init_addr_type;
+ b[6] = hcon->resp_addr_type;
+
+ if (hcon->out) {
+ local_addr = a;
+ remote_addr = b;
+ memcpy(io_cap, &smp->preq[1], 3);
+ } else {
+ local_addr = b;
+ remote_addr = a;
+ memcpy(io_cap, &smp->prsp[1], 3);
+ }
+
+ memset(r, 0, sizeof(r));
+
+ if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY)
+ put_unaligned_le32(hcon->passkey_notify, r);
+
+ if (smp->method == REQ_OOB)
+ memcpy(r, smp->rr, 16);
+
+ smp_f6(smp->tfm_cmac, smp->mackey, smp->prnd, smp->rrnd, r, io_cap,
+ local_addr, remote_addr, check.e);
+
+ smp_send_cmd(smp->conn, SMP_CMD_DHKEY_CHECK, sizeof(check), &check);
+}
+
+static u8 sc_passkey_send_confirm(struct smp_chan *smp)
+{
+ struct l2cap_conn *conn = smp->conn;
+ struct hci_conn *hcon = conn->hcon;
+ struct smp_cmd_pairing_confirm cfm;
+ u8 r;
+
+ r = ((hcon->passkey_notify >> smp->passkey_round) & 0x01);
+ r |= 0x80;
+
+ get_random_bytes(smp->prnd, sizeof(smp->prnd));
+
+ if (smp_f4(smp->tfm_cmac, smp->local_pk, smp->remote_pk, smp->prnd, r,
+ cfm.confirm_val))
+ return SMP_UNSPECIFIED;
+
+ smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cfm), &cfm);
+
+ return 0;
+}
+
+static u8 sc_passkey_round(struct smp_chan *smp, u8 smp_op)
+{
+ struct l2cap_conn *conn = smp->conn;
+ struct hci_conn *hcon = conn->hcon;
+ struct hci_dev *hdev = hcon->hdev;
+ u8 cfm[16], r;
+
+ /* Ignore the PDU if we've already done 20 rounds (0 - 19) */
+ if (smp->passkey_round >= 20)
+ return 0;
+
+ switch (smp_op) {
+ case SMP_CMD_PAIRING_RANDOM:
+ r = ((hcon->passkey_notify >> smp->passkey_round) & 0x01);
+ r |= 0x80;
+
+ if (smp_f4(smp->tfm_cmac, smp->remote_pk, smp->local_pk,
+ smp->rrnd, r, cfm))
+ return SMP_UNSPECIFIED;
+
+ if (memcmp(smp->pcnf, cfm, 16))
+ return SMP_CONFIRM_FAILED;
+
+ smp->passkey_round++;
+
+ if (smp->passkey_round == 20) {
+ /* Generate MacKey and LTK */
+ if (sc_mackey_and_ltk(smp, smp->mackey, smp->tk))
+ return SMP_UNSPECIFIED;
+ }
+
+ /* The round is only complete when the initiator
+ * receives pairing random.
+ */
+ if (!hcon->out) {
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM,
+ sizeof(smp->prnd), smp->prnd);
+ if (smp->passkey_round == 20)
+ SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
+ else
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+ return 0;
+ }
+
+ /* Start the next round */
+ if (smp->passkey_round != 20)
+ return sc_passkey_round(smp, 0);
+
+ /* Passkey rounds are complete - start DHKey Check */
+ sc_dhkey_check(smp);
+ SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
+
+ break;
+
+ case SMP_CMD_PAIRING_CONFIRM:
+ if (test_bit(SMP_FLAG_WAIT_USER, &smp->flags)) {
+ set_bit(SMP_FLAG_CFM_PENDING, &smp->flags);
+ return 0;
+ }
+
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RANDOM);
+
+ if (hcon->out) {
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM,
+ sizeof(smp->prnd), smp->prnd);
+ return 0;
+ }
+
+ return sc_passkey_send_confirm(smp);
+
+ case SMP_CMD_PUBLIC_KEY:
+ default:
+ /* Initiating device starts the round */
+ if (!hcon->out)
+ return 0;
+
+ BT_DBG("%s Starting passkey round %u", hdev->name,
+ smp->passkey_round + 1);
+
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+
+ return sc_passkey_send_confirm(smp);
+ }
+
+ return 0;
+}
+
+static int sc_user_reply(struct smp_chan *smp, u16 mgmt_op, __le32 passkey)
+{
+ struct l2cap_conn *conn = smp->conn;
+ struct hci_conn *hcon = conn->hcon;
+ u8 smp_op;
+
+ clear_bit(SMP_FLAG_WAIT_USER, &smp->flags);
+
+ switch (mgmt_op) {
+ case MGMT_OP_USER_PASSKEY_NEG_REPLY:
+ smp_failure(smp->conn, SMP_PASSKEY_ENTRY_FAILED);
+ return 0;
+ case MGMT_OP_USER_CONFIRM_NEG_REPLY:
+ smp_failure(smp->conn, SMP_NUMERIC_COMP_FAILED);
+ return 0;
+ case MGMT_OP_USER_PASSKEY_REPLY:
+ hcon->passkey_notify = le32_to_cpu(passkey);
+ smp->passkey_round = 0;
+
+ if (test_and_clear_bit(SMP_FLAG_CFM_PENDING, &smp->flags))
+ smp_op = SMP_CMD_PAIRING_CONFIRM;
+ else
+ smp_op = 0;
+
+ if (sc_passkey_round(smp, smp_op))
+ return -EIO;
+
+ return 0;
+ }
+
+ /* Initiator sends DHKey check first */
+ if (hcon->out) {
+ sc_dhkey_check(smp);
+ SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
+ } else if (test_and_clear_bit(SMP_FLAG_DHKEY_PENDING, &smp->flags)) {
+ sc_dhkey_check(smp);
+ sc_add_ltk(smp);
+ }
+
+ return 0;
+}
+
int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
{
struct l2cap_conn *conn = hcon->l2cap_data;
@@ -891,6 +1551,11 @@ int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
smp = chan->data;
+ if (test_bit(SMP_FLAG_SC, &smp->flags)) {
+ err = sc_user_reply(smp, mgmt_op, passkey);
+ goto unlock;
+ }
+
switch (mgmt_op) {
case MGMT_OP_USER_PASSKEY_REPLY:
value = le32_to_cpu(passkey);
@@ -926,6 +1591,46 @@ unlock:
return err;
}
+static void build_bredr_pairing_cmd(struct smp_chan *smp,
+ struct smp_cmd_pairing *req,
+ struct smp_cmd_pairing *rsp)
+{
+ struct l2cap_conn *conn = smp->conn;
+ struct hci_dev *hdev = conn->hcon->hdev;
+ u8 local_dist = 0, remote_dist = 0;
+
+ if (test_bit(HCI_BONDABLE, &hdev->dev_flags)) {
+ local_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN;
+ remote_dist = SMP_DIST_ENC_KEY | SMP_DIST_SIGN;
+ }
+
+ if (test_bit(HCI_RPA_RESOLVING, &hdev->dev_flags))
+ remote_dist |= SMP_DIST_ID_KEY;
+
+ if (test_bit(HCI_PRIVACY, &hdev->dev_flags))
+ local_dist |= SMP_DIST_ID_KEY;
+
+ if (!rsp) {
+ memset(req, 0, sizeof(*req));
+
+ req->init_key_dist = local_dist;
+ req->resp_key_dist = remote_dist;
+ req->max_key_size = SMP_MAX_ENC_KEY_SIZE;
+
+ smp->remote_key_dist = remote_dist;
+
+ return;
+ }
+
+ memset(rsp, 0, sizeof(*rsp));
+
+ rsp->max_key_size = SMP_MAX_ENC_KEY_SIZE;
+ rsp->init_key_dist = req->init_key_dist & remote_dist;
+ rsp->resp_key_dist = req->resp_key_dist & local_dist;
+
+ smp->remote_key_dist = rsp->init_key_dist;
+}
+
static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct smp_cmd_pairing rsp, *req = (void *) skb->data;
@@ -952,16 +1657,49 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
return SMP_UNSPECIFIED;
/* We didn't start the pairing, so match remote */
- auth = req->auth_req & AUTH_REQ_MASK;
+ auth = req->auth_req & AUTH_REQ_MASK(hdev);
if (!test_bit(HCI_BONDABLE, &hdev->dev_flags) &&
(auth & SMP_AUTH_BONDING))
return SMP_PAIRING_NOTSUPP;
+ if (test_bit(HCI_SC_ONLY, &hdev->dev_flags) && !(auth & SMP_AUTH_SC))
+ return SMP_AUTH_REQUIREMENTS;
+
smp->preq[0] = SMP_CMD_PAIRING_REQ;
memcpy(&smp->preq[1], req, sizeof(*req));
skb_pull(skb, sizeof(*req));
+ /* SMP over BR/EDR requires special treatment */
+ if (conn->hcon->type == ACL_LINK) {
+ /* We must have a BR/EDR SC link */
+ if (!test_bit(HCI_CONN_AES_CCM, &conn->hcon->flags))
+ return SMP_CROSS_TRANSP_NOT_ALLOWED;
+
+ set_bit(SMP_FLAG_SC, &smp->flags);
+
+ build_bredr_pairing_cmd(smp, req, &rsp);
+
+ key_size = min(req->max_key_size, rsp.max_key_size);
+ if (check_enc_key_size(conn, key_size))
+ return SMP_ENC_KEY_SIZE;
+
+ /* Clear bits which are generated but not distributed */
+ smp->remote_key_dist &= ~SMP_SC_NO_DIST;
+
+ smp->prsp[0] = SMP_CMD_PAIRING_RSP;
+ memcpy(&smp->prsp[1], &rsp, sizeof(rsp));
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp);
+
+ smp_distribute_keys(smp);
+ return 0;
+ }
+
+ build_pairing_cmd(conn, req, &rsp, auth);
+
+ if (rsp.auth_req & SMP_AUTH_SC)
+ set_bit(SMP_FLAG_SC, &smp->flags);
+
if (conn->hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
sec_level = BT_SECURITY_MEDIUM;
else
@@ -970,7 +1708,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
if (sec_level > conn->hcon->pending_sec_level)
conn->hcon->pending_sec_level = sec_level;
- /* If we need MITM check that it can be acheived */
+ /* If we need MITM check that it can be achieved */
if (conn->hcon->pending_sec_level >= BT_SECURITY_HIGH) {
u8 method;
@@ -980,8 +1718,6 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
return SMP_AUTH_REQUIREMENTS;
}
- build_pairing_cmd(conn, req, &rsp, auth);
-
key_size = min(req->max_key_size, rsp.max_key_size);
if (check_enc_key_size(conn, key_size))
return SMP_ENC_KEY_SIZE;
@@ -992,7 +1728,18 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
memcpy(&smp->prsp[1], &rsp, sizeof(rsp));
smp_send_cmd(conn, SMP_CMD_PAIRING_RSP, sizeof(rsp), &rsp);
- SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+
+ clear_bit(SMP_FLAG_INITIATOR, &smp->flags);
+
+ if (test_bit(SMP_FLAG_SC, &smp->flags)) {
+ SMP_ALLOW_CMD(smp, SMP_CMD_PUBLIC_KEY);
+ /* Clear bits which are generated but not distributed */
+ smp->remote_key_dist &= ~SMP_SC_NO_DIST;
+ /* Wait for Public Key from Initiating Device */
+ return 0;
+ } else {
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+ }
/* Request setup of TK */
ret = tk_request(conn, 0, auth, rsp.io_capability, req->io_capability);
@@ -1002,11 +1749,46 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
return 0;
}
+static u8 sc_send_public_key(struct smp_chan *smp)
+{
+ struct hci_dev *hdev = smp->conn->hcon->hdev;
+
+ BT_DBG("");
+
+ if (test_bit(HCI_USE_DEBUG_KEYS, &hdev->dev_flags)) {
+ BT_DBG("Using debug keys");
+ memcpy(smp->local_pk, debug_pk, 64);
+ memcpy(smp->local_sk, debug_sk, 32);
+ set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags);
+ } else {
+ while (true) {
+ /* Generate local key pair for Secure Connections */
+ if (!ecc_make_key(smp->local_pk, smp->local_sk))
+ return SMP_UNSPECIFIED;
+
+ /* This is unlikely, but we need to check that
+ * we didn't accidentially generate a debug key.
+ */
+ if (memcmp(smp->local_sk, debug_sk, 32))
+ break;
+ }
+ }
+
+ SMP_DBG("Local Public Key X: %32phN", smp->local_pk);
+ SMP_DBG("Local Public Key Y: %32phN", &smp->local_pk[32]);
+ SMP_DBG("Local Private Key: %32phN", smp->local_sk);
+
+ smp_send_cmd(smp->conn, SMP_CMD_PUBLIC_KEY, 64, smp->local_pk);
+
+ return 0;
+}
+
static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct smp_cmd_pairing *req, *rsp = (void *) skb->data;
struct l2cap_chan *chan = conn->smp;
struct smp_chan *smp = chan->data;
+ struct hci_dev *hdev = conn->hcon->hdev;
u8 key_size, auth;
int ret;
@@ -1026,9 +1808,33 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
if (check_enc_key_size(conn, key_size))
return SMP_ENC_KEY_SIZE;
- auth = rsp->auth_req & AUTH_REQ_MASK;
+ auth = rsp->auth_req & AUTH_REQ_MASK(hdev);
+
+ if (test_bit(HCI_SC_ONLY, &hdev->dev_flags) && !(auth & SMP_AUTH_SC))
+ return SMP_AUTH_REQUIREMENTS;
+
+ smp->prsp[0] = SMP_CMD_PAIRING_RSP;
+ memcpy(&smp->prsp[1], rsp, sizeof(*rsp));
- /* If we need MITM check that it can be acheived */
+ /* Update remote key distribution in case the remote cleared
+ * some bits that we had enabled in our request.
+ */
+ smp->remote_key_dist &= rsp->resp_key_dist;
+
+ /* For BR/EDR this means we're done and can start phase 3 */
+ if (conn->hcon->type == ACL_LINK) {
+ /* Clear bits which are generated but not distributed */
+ smp->remote_key_dist &= ~SMP_SC_NO_DIST;
+ smp_distribute_keys(smp);
+ return 0;
+ }
+
+ if ((req->auth_req & SMP_AUTH_SC) && (auth & SMP_AUTH_SC))
+ set_bit(SMP_FLAG_SC, &smp->flags);
+ else if (conn->hcon->pending_sec_level > BT_SECURITY_HIGH)
+ conn->hcon->pending_sec_level = BT_SECURITY_HIGH;
+
+ /* If we need MITM check that it can be achieved */
if (conn->hcon->pending_sec_level >= BT_SECURITY_HIGH) {
u8 method;
@@ -1040,14 +1846,18 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
get_random_bytes(smp->prnd, sizeof(smp->prnd));
- smp->prsp[0] = SMP_CMD_PAIRING_RSP;
- memcpy(&smp->prsp[1], rsp, sizeof(*rsp));
-
/* Update remote key distribution in case the remote cleared
* some bits that we had enabled in our request.
*/
smp->remote_key_dist &= rsp->resp_key_dist;
+ if (test_bit(SMP_FLAG_SC, &smp->flags)) {
+ /* Clear bits which are generated but not distributed */
+ smp->remote_key_dist &= ~SMP_SC_NO_DIST;
+ SMP_ALLOW_CMD(smp, SMP_CMD_PUBLIC_KEY);
+ return sc_send_public_key(smp);
+ }
+
auth |= req->auth_req;
ret = tk_request(conn, 0, auth, req->io_capability, rsp->io_capability);
@@ -1063,6 +1873,28 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
return 0;
}
+static u8 sc_check_confirm(struct smp_chan *smp)
+{
+ struct l2cap_conn *conn = smp->conn;
+
+ BT_DBG("");
+
+ /* Public Key exchange must happen before any other steps */
+ if (!test_bit(SMP_FLAG_REMOTE_PK, &smp->flags))
+ return SMP_UNSPECIFIED;
+
+ if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY)
+ return sc_passkey_round(smp, SMP_CMD_PAIRING_CONFIRM);
+
+ if (conn->hcon->out) {
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
+ smp->prnd);
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RANDOM);
+ }
+
+ return 0;
+}
+
static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct l2cap_chan *chan = conn->smp;
@@ -1076,6 +1908,9 @@ static u8 smp_cmd_pairing_confirm(struct l2cap_conn *conn, struct sk_buff *skb)
memcpy(smp->pcnf, skb->data, sizeof(smp->pcnf));
skb_pull(skb, sizeof(smp->pcnf));
+ if (test_bit(SMP_FLAG_SC, &smp->flags))
+ return sc_check_confirm(smp);
+
if (conn->hcon->out) {
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
smp->prnd);
@@ -1095,6 +1930,10 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
{
struct l2cap_chan *chan = conn->smp;
struct smp_chan *smp = chan->data;
+ struct hci_conn *hcon = conn->hcon;
+ u8 *pkax, *pkbx, *na, *nb;
+ u32 passkey;
+ int err;
BT_DBG("conn %p", conn);
@@ -1104,7 +1943,75 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb)
memcpy(smp->rrnd, skb->data, sizeof(smp->rrnd));
skb_pull(skb, sizeof(smp->rrnd));
- return smp_random(smp);
+ if (!test_bit(SMP_FLAG_SC, &smp->flags))
+ return smp_random(smp);
+
+ if (hcon->out) {
+ pkax = smp->local_pk;
+ pkbx = smp->remote_pk;
+ na = smp->prnd;
+ nb = smp->rrnd;
+ } else {
+ pkax = smp->remote_pk;
+ pkbx = smp->local_pk;
+ na = smp->rrnd;
+ nb = smp->prnd;
+ }
+
+ if (smp->method == REQ_OOB) {
+ if (!hcon->out)
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM,
+ sizeof(smp->prnd), smp->prnd);
+ SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
+ goto mackey_and_ltk;
+ }
+
+ /* Passkey entry has special treatment */
+ if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY)
+ return sc_passkey_round(smp, SMP_CMD_PAIRING_RANDOM);
+
+ if (hcon->out) {
+ u8 cfm[16];
+
+ err = smp_f4(smp->tfm_cmac, smp->remote_pk, smp->local_pk,
+ smp->rrnd, 0, cfm);
+ if (err)
+ return SMP_UNSPECIFIED;
+
+ if (memcmp(smp->pcnf, cfm, 16))
+ return SMP_CONFIRM_FAILED;
+ } else {
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
+ smp->prnd);
+ SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
+ }
+
+mackey_and_ltk:
+ /* Generate MacKey and LTK */
+ err = sc_mackey_and_ltk(smp, smp->mackey, smp->tk);
+ if (err)
+ return SMP_UNSPECIFIED;
+
+ if (smp->method == JUST_WORKS || smp->method == REQ_OOB) {
+ if (hcon->out) {
+ sc_dhkey_check(smp);
+ SMP_ALLOW_CMD(smp, SMP_CMD_DHKEY_CHECK);
+ }
+ return 0;
+ }
+
+ err = smp_g2(smp->tfm_cmac, pkax, pkbx, na, nb, &passkey);
+ if (err)
+ return SMP_UNSPECIFIED;
+
+ err = mgmt_user_confirm_request(hcon->hdev, &hcon->dst, hcon->type,
+ hcon->dst_type, passkey, 0);
+ if (err)
+ return SMP_UNSPECIFIED;
+
+ set_bit(SMP_FLAG_WAIT_USER, &smp->flags);
+
+ return 0;
}
static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
@@ -1112,8 +2019,7 @@ static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
struct smp_ltk *key;
struct hci_conn *hcon = conn->hcon;
- key = hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type,
- hcon->role);
+ key = hci_find_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, hcon->role);
if (!key)
return false;
@@ -1132,20 +2038,21 @@ static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
return true;
}
-bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level)
+bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level,
+ enum smp_key_pref key_pref)
{
if (sec_level == BT_SECURITY_LOW)
return true;
- /* If we're encrypted with an STK always claim insufficient
- * security. This way we allow the connection to be re-encrypted
- * with an LTK, even if the LTK provides the same level of
- * security. Only exception is if we don't have an LTK (e.g.
- * because of key distribution bits).
+ /* If we're encrypted with an STK but the caller prefers using
+ * LTK claim insufficient security. This way we allow the
+ * connection to be re-encrypted with an LTK, even if the LTK
+ * provides the same level of security. Only exception is if we
+ * don't have an LTK (e.g. because of key distribution bits).
*/
- if (test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags) &&
- hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type,
- hcon->role))
+ if (key_pref == SMP_USE_LTK &&
+ test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags) &&
+ hci_find_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, hcon->role))
return false;
if (hcon->sec_level >= sec_level)
@@ -1159,6 +2066,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
struct smp_cmd_security_req *rp = (void *) skb->data;
struct smp_cmd_pairing cp;
struct hci_conn *hcon = conn->hcon;
+ struct hci_dev *hdev = hcon->hdev;
struct smp_chan *smp;
u8 sec_level, auth;
@@ -1170,14 +2078,17 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
if (hcon->role != HCI_ROLE_MASTER)
return SMP_CMD_NOTSUPP;
- auth = rp->auth_req & AUTH_REQ_MASK;
+ auth = rp->auth_req & AUTH_REQ_MASK(hdev);
+
+ if (test_bit(HCI_SC_ONLY, &hdev->dev_flags) && !(auth & SMP_AUTH_SC))
+ return SMP_AUTH_REQUIREMENTS;
if (hcon->io_capability == HCI_IO_NO_INPUT_OUTPUT)
sec_level = BT_SECURITY_MEDIUM;
else
sec_level = authreq_to_seclevel(auth);
- if (smp_sufficient_security(hcon, sec_level))
+ if (smp_sufficient_security(hcon, sec_level, SMP_USE_LTK))
return 0;
if (sec_level > hcon->pending_sec_level)
@@ -1227,7 +2138,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
if (!test_bit(HCI_LE_ENABLED, &hcon->hdev->dev_flags))
return 1;
- if (smp_sufficient_security(hcon, sec_level))
+ if (smp_sufficient_security(hcon, sec_level, SMP_USE_LTK))
return 1;
if (sec_level > hcon->pending_sec_level)
@@ -1253,6 +2164,9 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
authreq = seclevel_to_authreq(sec_level);
+ if (test_bit(HCI_SC_ENABLED, &hcon->hdev->dev_flags))
+ authreq |= SMP_AUTH_SC;
+
/* Require MITM if IO Capability allows or the security level
* requires it.
*/
@@ -1329,7 +2243,6 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
skb_pull(skb, sizeof(*rp));
- hci_dev_lock(hdev);
authenticated = (hcon->sec_level == BT_SECURITY_HIGH);
ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, SMP_LTK,
authenticated, smp->tk, smp->enc_key_size,
@@ -1337,7 +2250,6 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb)
smp->ltk = ltk;
if (!(smp->remote_key_dist & KEY_DIST_MASK))
smp_distribute_keys(smp);
- hci_dev_unlock(hdev);
return 0;
}
@@ -1384,8 +2296,6 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn,
skb_pull(skb, sizeof(*info));
- hci_dev_lock(hcon->hdev);
-
/* Strictly speaking the Core Specification (4.1) allows sending
* an empty address which would force us to rely on just the IRK
* as "identity information". However, since such
@@ -1413,8 +2323,6 @@ distribute:
if (!(smp->remote_key_dist & KEY_DIST_MASK))
smp_distribute_keys(smp);
- hci_dev_unlock(hcon->hdev);
-
return 0;
}
@@ -1423,7 +2331,6 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb)
struct smp_cmd_sign_info *rp = (void *) skb->data;
struct l2cap_chan *chan = conn->smp;
struct smp_chan *smp = chan->data;
- struct hci_dev *hdev = conn->hcon->hdev;
struct smp_csrk *csrk;
BT_DBG("conn %p", conn);
@@ -1436,7 +2343,6 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb)
skb_pull(skb, sizeof(*rp));
- hci_dev_lock(hdev);
csrk = kzalloc(sizeof(*csrk), GFP_KERNEL);
if (csrk) {
csrk->master = 0x01;
@@ -1444,7 +2350,234 @@ static int smp_cmd_sign_info(struct l2cap_conn *conn, struct sk_buff *skb)
}
smp->csrk = csrk;
smp_distribute_keys(smp);
- hci_dev_unlock(hdev);
+
+ return 0;
+}
+
+static u8 sc_select_method(struct smp_chan *smp)
+{
+ struct l2cap_conn *conn = smp->conn;
+ struct hci_conn *hcon = conn->hcon;
+ struct smp_cmd_pairing *local, *remote;
+ u8 local_mitm, remote_mitm, local_io, remote_io, method;
+
+ if (test_bit(SMP_FLAG_OOB, &smp->flags))
+ return REQ_OOB;
+
+ /* The preq/prsp contain the raw Pairing Request/Response PDUs
+ * which are needed as inputs to some crypto functions. To get
+ * the "struct smp_cmd_pairing" from them we need to skip the
+ * first byte which contains the opcode.
+ */
+ if (hcon->out) {
+ local = (void *) &smp->preq[1];
+ remote = (void *) &smp->prsp[1];
+ } else {
+ local = (void *) &smp->prsp[1];
+ remote = (void *) &smp->preq[1];
+ }
+
+ local_io = local->io_capability;
+ remote_io = remote->io_capability;
+
+ local_mitm = (local->auth_req & SMP_AUTH_MITM);
+ remote_mitm = (remote->auth_req & SMP_AUTH_MITM);
+
+ /* If either side wants MITM, look up the method from the table,
+ * otherwise use JUST WORKS.
+ */
+ if (local_mitm || remote_mitm)
+ method = get_auth_method(smp, local_io, remote_io);
+ else
+ method = JUST_WORKS;
+
+ /* Don't confirm locally initiated pairing attempts */
+ if (method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR, &smp->flags))
+ method = JUST_WORKS;
+
+ return method;
+}
+
+static int smp_cmd_public_key(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+ struct smp_cmd_public_key *key = (void *) skb->data;
+ struct hci_conn *hcon = conn->hcon;
+ struct l2cap_chan *chan = conn->smp;
+ struct smp_chan *smp = chan->data;
+ struct hci_dev *hdev = hcon->hdev;
+ struct smp_cmd_pairing_confirm cfm;
+ int err;
+
+ BT_DBG("conn %p", conn);
+
+ if (skb->len < sizeof(*key))
+ return SMP_INVALID_PARAMS;
+
+ memcpy(smp->remote_pk, key, 64);
+
+ /* Non-initiating device sends its public key after receiving
+ * the key from the initiating device.
+ */
+ if (!hcon->out) {
+ err = sc_send_public_key(smp);
+ if (err)
+ return err;
+ }
+
+ SMP_DBG("Remote Public Key X: %32phN", smp->remote_pk);
+ SMP_DBG("Remote Public Key Y: %32phN", &smp->remote_pk[32]);
+
+ if (!ecdh_shared_secret(smp->remote_pk, smp->local_sk, smp->dhkey))
+ return SMP_UNSPECIFIED;
+
+ SMP_DBG("DHKey %32phN", smp->dhkey);
+
+ set_bit(SMP_FLAG_REMOTE_PK, &smp->flags);
+
+ smp->method = sc_select_method(smp);
+
+ BT_DBG("%s selected method 0x%02x", hdev->name, smp->method);
+
+ /* JUST_WORKS and JUST_CFM result in an unauthenticated key */
+ if (smp->method == JUST_WORKS || smp->method == JUST_CFM)
+ hcon->pending_sec_level = BT_SECURITY_MEDIUM;
+ else
+ hcon->pending_sec_level = BT_SECURITY_FIPS;
+
+ if (!memcmp(debug_pk, smp->remote_pk, 64))
+ set_bit(SMP_FLAG_DEBUG_KEY, &smp->flags);
+
+ if (smp->method == DSP_PASSKEY) {
+ get_random_bytes(&hcon->passkey_notify,
+ sizeof(hcon->passkey_notify));
+ hcon->passkey_notify %= 1000000;
+ hcon->passkey_entered = 0;
+ smp->passkey_round = 0;
+ if (mgmt_user_passkey_notify(hdev, &hcon->dst, hcon->type,
+ hcon->dst_type,
+ hcon->passkey_notify,
+ hcon->passkey_entered))
+ return SMP_UNSPECIFIED;
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+ return sc_passkey_round(smp, SMP_CMD_PUBLIC_KEY);
+ }
+
+ if (smp->method == REQ_OOB) {
+ err = smp_f4(smp->tfm_cmac, smp->remote_pk, smp->remote_pk,
+ smp->rr, 0, cfm.confirm_val);
+ if (err)
+ return SMP_UNSPECIFIED;
+
+ if (memcmp(cfm.confirm_val, smp->pcnf, 16))
+ return SMP_CONFIRM_FAILED;
+
+ if (hcon->out)
+ smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM,
+ sizeof(smp->prnd), smp->prnd);
+
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RANDOM);
+
+ return 0;
+ }
+
+ if (hcon->out)
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+
+ if (smp->method == REQ_PASSKEY) {
+ if (mgmt_user_passkey_request(hdev, &hcon->dst, hcon->type,
+ hcon->dst_type))
+ return SMP_UNSPECIFIED;
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_CONFIRM);
+ set_bit(SMP_FLAG_WAIT_USER, &smp->flags);
+ return 0;
+ }
+
+ /* The Initiating device waits for the non-initiating device to
+ * send the confirm value.
+ */
+ if (conn->hcon->out)
+ return 0;
+
+ err = smp_f4(smp->tfm_cmac, smp->local_pk, smp->remote_pk, smp->prnd,
+ 0, cfm.confirm_val);
+ if (err)
+ return SMP_UNSPECIFIED;
+
+ smp_send_cmd(conn, SMP_CMD_PAIRING_CONFIRM, sizeof(cfm), &cfm);
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RANDOM);
+
+ return 0;
+}
+
+static int smp_cmd_dhkey_check(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+ struct smp_cmd_dhkey_check *check = (void *) skb->data;
+ struct l2cap_chan *chan = conn->smp;
+ struct hci_conn *hcon = conn->hcon;
+ struct smp_chan *smp = chan->data;
+ u8 a[7], b[7], *local_addr, *remote_addr;
+ u8 io_cap[3], r[16], e[16];
+ int err;
+
+ BT_DBG("conn %p", conn);
+
+ if (skb->len < sizeof(*check))
+ return SMP_INVALID_PARAMS;
+
+ memcpy(a, &hcon->init_addr, 6);
+ memcpy(b, &hcon->resp_addr, 6);
+ a[6] = hcon->init_addr_type;
+ b[6] = hcon->resp_addr_type;
+
+ if (hcon->out) {
+ local_addr = a;
+ remote_addr = b;
+ memcpy(io_cap, &smp->prsp[1], 3);
+ } else {
+ local_addr = b;
+ remote_addr = a;
+ memcpy(io_cap, &smp->preq[1], 3);
+ }
+
+ memset(r, 0, sizeof(r));
+
+ if (smp->method == REQ_PASSKEY || smp->method == DSP_PASSKEY)
+ put_unaligned_le32(hcon->passkey_notify, r);
+
+ err = smp_f6(smp->tfm_cmac, smp->mackey, smp->rrnd, smp->prnd, r,
+ io_cap, remote_addr, local_addr, e);
+ if (err)
+ return SMP_UNSPECIFIED;
+
+ if (memcmp(check->e, e, 16))
+ return SMP_DHKEY_CHECK_FAILED;
+
+ if (!hcon->out) {
+ if (test_bit(SMP_FLAG_WAIT_USER, &smp->flags)) {
+ set_bit(SMP_FLAG_DHKEY_PENDING, &smp->flags);
+ return 0;
+ }
+
+ /* Slave sends DHKey check as response to master */
+ sc_dhkey_check(smp);
+ }
+
+ sc_add_ltk(smp);
+
+ if (hcon->out) {
+ hci_le_start_enc(hcon, 0, 0, smp->tk);
+ hcon->enc_key_size = smp->enc_key_size;
+ }
+
+ return 0;
+}
+
+static int smp_cmd_keypress_notify(struct l2cap_conn *conn,
+ struct sk_buff *skb)
+{
+ struct smp_cmd_keypress_notify *kp = (void *) skb->data;
+
+ BT_DBG("value 0x%02x", kp->value);
return 0;
}
@@ -1457,11 +2590,6 @@ static int smp_sig_channel(struct l2cap_chan *chan, struct sk_buff *skb)
__u8 code, reason;
int err = 0;
- if (hcon->type != LE_LINK) {
- kfree_skb(skb);
- return 0;
- }
-
if (skb->len < 1)
return -EILSEQ;
@@ -1533,6 +2661,18 @@ static int smp_sig_channel(struct l2cap_chan *chan, struct sk_buff *skb)
reason = smp_cmd_sign_info(conn, skb);
break;
+ case SMP_CMD_PUBLIC_KEY:
+ reason = smp_cmd_public_key(conn, skb);
+ break;
+
+ case SMP_CMD_DHKEY_CHECK:
+ reason = smp_cmd_dhkey_check(conn, skb);
+ break;
+
+ case SMP_CMD_KEYPRESS_NOTIFY:
+ reason = smp_cmd_keypress_notify(conn, skb);
+ break;
+
default:
BT_DBG("Unknown command code 0x%2.2x", code);
reason = SMP_CMD_NOTSUPP;
@@ -1568,6 +2708,74 @@ static void smp_teardown_cb(struct l2cap_chan *chan, int err)
l2cap_chan_put(chan);
}
+static void bredr_pairing(struct l2cap_chan *chan)
+{
+ struct l2cap_conn *conn = chan->conn;
+ struct hci_conn *hcon = conn->hcon;
+ struct hci_dev *hdev = hcon->hdev;
+ struct smp_cmd_pairing req;
+ struct smp_chan *smp;
+
+ BT_DBG("chan %p", chan);
+
+ /* Only new pairings are interesting */
+ if (!test_bit(HCI_CONN_NEW_LINK_KEY, &hcon->flags))
+ return;
+
+ /* Don't bother if we're not encrypted */
+ if (!test_bit(HCI_CONN_ENCRYPT, &hcon->flags))
+ return;
+
+ /* Only master may initiate SMP over BR/EDR */
+ if (hcon->role != HCI_ROLE_MASTER)
+ return;
+
+ /* Secure Connections support must be enabled */
+ if (!test_bit(HCI_SC_ENABLED, &hdev->dev_flags))
+ return;
+
+ /* BR/EDR must use Secure Connections for SMP */
+ if (!test_bit(HCI_CONN_AES_CCM, &hcon->flags) &&
+ !test_bit(HCI_FORCE_LESC, &hdev->dbg_flags))
+ return;
+
+ /* If our LE support is not enabled don't do anything */
+ if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
+ return;
+
+ /* Don't bother if remote LE support is not enabled */
+ if (!lmp_host_le_capable(hcon))
+ return;
+
+ /* Remote must support SMP fixed chan for BR/EDR */
+ if (!(conn->remote_fixed_chan & L2CAP_FC_SMP_BREDR))
+ return;
+
+ /* Don't bother if SMP is already ongoing */
+ if (chan->data)
+ return;
+
+ smp = smp_chan_create(conn);
+ if (!smp) {
+ BT_ERR("%s unable to create SMP context for BR/EDR",
+ hdev->name);
+ return;
+ }
+
+ set_bit(SMP_FLAG_SC, &smp->flags);
+
+ BT_DBG("%s starting SMP over BR/EDR", hdev->name);
+
+ /* Prepare and send the BR/EDR SMP Pairing Request */
+ build_bredr_pairing_cmd(smp, &req, NULL);
+
+ smp->preq[0] = SMP_CMD_PAIRING_REQ;
+ memcpy(&smp->preq[1], &req, sizeof(req));
+
+ smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(req), &req);
+ SMP_ALLOW_CMD(smp, SMP_CMD_PAIRING_RSP);
+}
+
static void smp_resume_cb(struct l2cap_chan *chan)
{
struct smp_chan *smp = chan->data;
@@ -1576,6 +2784,11 @@ static void smp_resume_cb(struct l2cap_chan *chan)
BT_DBG("chan %p", chan);
+ if (hcon->type == ACL_LINK) {
+ bredr_pairing(chan);
+ return;
+ }
+
if (!smp)
return;
@@ -1590,11 +2803,15 @@ static void smp_resume_cb(struct l2cap_chan *chan)
static void smp_ready_cb(struct l2cap_chan *chan)
{
struct l2cap_conn *conn = chan->conn;
+ struct hci_conn *hcon = conn->hcon;
BT_DBG("chan %p", chan);
conn->smp = chan;
l2cap_chan_hold(chan);
+
+ if (hcon->type == ACL_LINK && test_bit(HCI_CONN_ENCRYPT, &hcon->flags))
+ bredr_pairing(chan);
}
static int smp_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
@@ -1647,7 +2864,6 @@ static const struct l2cap_ops smp_chan_ops = {
.suspend = l2cap_chan_no_suspend,
.set_shutdown = l2cap_chan_no_set_shutdown,
.get_sndtimeo = l2cap_chan_no_get_sndtimeo,
- .memcpy_fromiovec = l2cap_chan_no_memcpy_fromiovec,
};
static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan)
@@ -1668,6 +2884,13 @@ static inline struct l2cap_chan *smp_new_conn_cb(struct l2cap_chan *pchan)
chan->omtu = pchan->omtu;
chan->mode = pchan->mode;
+ /* Other L2CAP channels may request SMP routines in order to
+ * change the security level. This means that the SMP channel
+ * lock must be considered in its own category to avoid lockdep
+ * warnings.
+ */
+ atomic_set(&chan->nesting, L2CAP_NESTING_SMP);
+
BT_DBG("created chan %p", chan);
return chan;
@@ -1689,56 +2912,58 @@ static const struct l2cap_ops smp_root_chan_ops = {
.resume = l2cap_chan_no_resume,
.set_shutdown = l2cap_chan_no_set_shutdown,
.get_sndtimeo = l2cap_chan_no_get_sndtimeo,
- .memcpy_fromiovec = l2cap_chan_no_memcpy_fromiovec,
};
-int smp_register(struct hci_dev *hdev)
+static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid)
{
struct l2cap_chan *chan;
struct crypto_blkcipher *tfm_aes;
- BT_DBG("%s", hdev->name);
+ if (cid == L2CAP_CID_SMP_BREDR) {
+ tfm_aes = NULL;
+ goto create_chan;
+ }
- tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
+ tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, 0);
if (IS_ERR(tfm_aes)) {
- int err = PTR_ERR(tfm_aes);
BT_ERR("Unable to create crypto context");
- return err;
+ return ERR_PTR(PTR_ERR(tfm_aes));
}
+create_chan:
chan = l2cap_chan_create();
if (!chan) {
crypto_free_blkcipher(tfm_aes);
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
}
chan->data = tfm_aes;
- l2cap_add_scid(chan, L2CAP_CID_SMP);
+ l2cap_add_scid(chan, cid);
l2cap_chan_set_defaults(chan);
bacpy(&chan->src, &hdev->bdaddr);
- chan->src_type = BDADDR_LE_PUBLIC;
+ if (cid == L2CAP_CID_SMP)
+ chan->src_type = BDADDR_LE_PUBLIC;
+ else
+ chan->src_type = BDADDR_BREDR;
chan->state = BT_LISTEN;
chan->mode = L2CAP_MODE_BASIC;
chan->imtu = L2CAP_DEFAULT_MTU;
chan->ops = &smp_root_chan_ops;
- hdev->smp_data = chan;
+ /* Set correct nesting level for a parent/listening channel */
+ atomic_set(&chan->nesting, L2CAP_NESTING_PARENT);
- return 0;
+ return chan;
}
-void smp_unregister(struct hci_dev *hdev)
+static void smp_del_chan(struct l2cap_chan *chan)
{
- struct l2cap_chan *chan = hdev->smp_data;
- struct crypto_blkcipher *tfm_aes;
-
- if (!chan)
- return;
+ struct crypto_blkcipher *tfm_aes;
- BT_DBG("%s chan %p", hdev->name, chan);
+ BT_DBG("chan %p", chan);
tfm_aes = chan->data;
if (tfm_aes) {
@@ -1746,6 +2971,52 @@ void smp_unregister(struct hci_dev *hdev)
crypto_free_blkcipher(tfm_aes);
}
- hdev->smp_data = NULL;
l2cap_chan_put(chan);
}
+
+int smp_register(struct hci_dev *hdev)
+{
+ struct l2cap_chan *chan;
+
+ BT_DBG("%s", hdev->name);
+
+ chan = smp_add_cid(hdev, L2CAP_CID_SMP);
+ if (IS_ERR(chan))
+ return PTR_ERR(chan);
+
+ hdev->smp_data = chan;
+
+ if (!lmp_sc_capable(hdev) &&
+ !test_bit(HCI_FORCE_LESC, &hdev->dbg_flags))
+ return 0;
+
+ chan = smp_add_cid(hdev, L2CAP_CID_SMP_BREDR);
+ if (IS_ERR(chan)) {
+ int err = PTR_ERR(chan);
+ chan = hdev->smp_data;
+ hdev->smp_data = NULL;
+ smp_del_chan(chan);
+ return err;
+ }
+
+ hdev->smp_bredr_data = chan;
+
+ return 0;
+}
+
+void smp_unregister(struct hci_dev *hdev)
+{
+ struct l2cap_chan *chan;
+
+ if (hdev->smp_bredr_data) {
+ chan = hdev->smp_bredr_data;
+ hdev->smp_bredr_data = NULL;
+ smp_del_chan(chan);
+ }
+
+ if (hdev->smp_data) {
+ chan = hdev->smp_data;
+ hdev->smp_data = NULL;
+ smp_del_chan(chan);
+ }
+}
diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h
index 86a683a8b491..3296bf42ae80 100644
--- a/net/bluetooth/smp.h
+++ b/net/bluetooth/smp.h
@@ -50,10 +50,13 @@ struct smp_cmd_pairing {
#define SMP_DIST_ENC_KEY 0x01
#define SMP_DIST_ID_KEY 0x02
#define SMP_DIST_SIGN 0x04
+#define SMP_DIST_LINK_KEY 0x08
#define SMP_AUTH_NONE 0x00
#define SMP_AUTH_BONDING 0x01
#define SMP_AUTH_MITM 0x04
+#define SMP_AUTH_SC 0x08
+#define SMP_AUTH_KEYPRESS 0x10
#define SMP_CMD_PAIRING_CONFIRM 0x03
struct smp_cmd_pairing_confirm {
@@ -102,7 +105,23 @@ struct smp_cmd_security_req {
__u8 auth_req;
} __packed;
-#define SMP_CMD_MAX 0x0b
+#define SMP_CMD_PUBLIC_KEY 0x0c
+struct smp_cmd_public_key {
+ __u8 x[32];
+ __u8 y[32];
+} __packed;
+
+#define SMP_CMD_DHKEY_CHECK 0x0d
+struct smp_cmd_dhkey_check {
+ __u8 e[16];
+} __packed;
+
+#define SMP_CMD_KEYPRESS_NOTIFY 0x0e
+struct smp_cmd_keypress_notify {
+ __u8 value;
+} __packed;
+
+#define SMP_CMD_MAX 0x0e
#define SMP_PASSKEY_ENTRY_FAILED 0x01
#define SMP_OOB_NOT_AVAIL 0x02
@@ -114,6 +133,10 @@ struct smp_cmd_security_req {
#define SMP_UNSPECIFIED 0x08
#define SMP_REPEATED_ATTEMPTS 0x09
#define SMP_INVALID_PARAMS 0x0a
+#define SMP_DHKEY_CHECK_FAILED 0x0b
+#define SMP_NUMERIC_COMP_FAILED 0x0c
+#define SMP_BREDR_PAIRING_IN_PROGRESS 0x0d
+#define SMP_CROSS_TRANSP_NOT_ALLOWED 0x0e
#define SMP_MIN_ENC_KEY_SIZE 7
#define SMP_MAX_ENC_KEY_SIZE 16
@@ -123,23 +146,48 @@ enum {
SMP_STK,
SMP_LTK,
SMP_LTK_SLAVE,
+ SMP_LTK_P256,
+ SMP_LTK_P256_DEBUG,
};
+static inline bool smp_ltk_is_sc(struct smp_ltk *key)
+{
+ switch (key->type) {
+ case SMP_LTK_P256:
+ case SMP_LTK_P256_DEBUG:
+ return true;
+ }
+
+ return false;
+}
+
static inline u8 smp_ltk_sec_level(struct smp_ltk *key)
{
- if (key->authenticated)
- return BT_SECURITY_HIGH;
+ if (key->authenticated) {
+ if (smp_ltk_is_sc(key))
+ return BT_SECURITY_FIPS;
+ else
+ return BT_SECURITY_HIGH;
+ }
return BT_SECURITY_MEDIUM;
}
+/* Key preferences for smp_sufficient security */
+enum smp_key_pref {
+ SMP_ALLOW_STK,
+ SMP_USE_LTK,
+};
+
/* SMP Commands */
-bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level);
+bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level,
+ enum smp_key_pref key_pref);
int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);
int smp_user_confirm_reply(struct hci_conn *conn, u16 mgmt_op, __le32 passkey);
-bool smp_irk_matches(struct hci_dev *hdev, u8 irk[16], bdaddr_t *bdaddr);
-int smp_generate_rpa(struct hci_dev *hdev, u8 irk[16], bdaddr_t *rpa);
+bool smp_irk_matches(struct hci_dev *hdev, const u8 irk[16],
+ const bdaddr_t *bdaddr);
+int smp_generate_rpa(struct hci_dev *hdev, const u8 irk[16], bdaddr_t *rpa);
int smp_register(struct hci_dev *hdev);
void smp_unregister(struct hci_dev *hdev);
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index 6f6c95cfe8f2..cc36e59db7d7 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -90,7 +90,7 @@ static void fdb_rcu_free(struct rcu_head *head)
* are then updated with the new information.
* Called under RTNL.
*/
-static void fdb_add_hw(struct net_bridge *br, const unsigned char *addr)
+static void fdb_add_hw_addr(struct net_bridge *br, const unsigned char *addr)
{
int err;
struct net_bridge_port *p;
@@ -118,7 +118,7 @@ undo:
* the ports with needed information.
* Called under RTNL.
*/
-static void fdb_del_hw(struct net_bridge *br, const unsigned char *addr)
+static void fdb_del_hw_addr(struct net_bridge *br, const unsigned char *addr)
{
struct net_bridge_port *p;
@@ -133,7 +133,7 @@ static void fdb_del_hw(struct net_bridge *br, const unsigned char *addr)
static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f)
{
if (f->is_static)
- fdb_del_hw(br, f->addr.addr);
+ fdb_del_hw_addr(br, f->addr.addr);
hlist_del_rcu(&f->hlist);
fdb_notify(br, f, RTM_DELNEIGH);
@@ -481,6 +481,7 @@ static struct net_bridge_fdb_entry *fdb_create(struct hlist_head *head,
fdb->is_local = 0;
fdb->is_static = 0;
fdb->added_by_user = 0;
+ fdb->added_by_external_learn = 0;
fdb->updated = fdb->used = jiffies;
hlist_add_head_rcu(&fdb->hlist, head);
}
@@ -514,7 +515,7 @@ static int fdb_insert(struct net_bridge *br, struct net_bridge_port *source,
return -ENOMEM;
fdb->is_local = fdb->is_static = 1;
- fdb_add_hw(br, addr);
+ fdb_add_hw_addr(br, addr);
fdb_notify(br, fdb, RTM_NEWNEIGH);
return 0;
}
@@ -613,7 +614,7 @@ static int fdb_fill_info(struct sk_buff *skb, const struct net_bridge *br,
ndm->ndm_family = AF_BRIDGE;
ndm->ndm_pad1 = 0;
ndm->ndm_pad2 = 0;
- ndm->ndm_flags = 0;
+ ndm->ndm_flags = fdb->added_by_external_learn ? NTF_EXT_LEARNED : 0;
ndm->ndm_type = 0;
ndm->ndm_ifindex = fdb->dst ? fdb->dst->dev->ifindex : br->dev->ifindex;
ndm->ndm_state = fdb_to_nud(fdb);
@@ -754,19 +755,19 @@ static int fdb_add_entry(struct net_bridge_port *source, const __u8 *addr,
fdb->is_local = 1;
if (!fdb->is_static) {
fdb->is_static = 1;
- fdb_add_hw(br, addr);
+ fdb_add_hw_addr(br, addr);
}
} else if (state & NUD_NOARP) {
fdb->is_local = 0;
if (!fdb->is_static) {
fdb->is_static = 1;
- fdb_add_hw(br, addr);
+ fdb_add_hw_addr(br, addr);
}
} else {
fdb->is_local = 0;
if (fdb->is_static) {
fdb->is_static = 0;
- fdb_del_hw(br, addr);
+ fdb_del_hw_addr(br, addr);
}
}
@@ -805,33 +806,17 @@ static int __br_fdb_add(struct ndmsg *ndm, struct net_bridge_port *p,
/* Add new permanent fdb entry with RTM_NEWNEIGH */
int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
- const unsigned char *addr, u16 nlh_flags)
+ const unsigned char *addr, u16 vid, u16 nlh_flags)
{
struct net_bridge_port *p;
int err = 0;
struct net_port_vlans *pv;
- unsigned short vid = VLAN_N_VID;
if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE))) {
pr_info("bridge: RTM_NEWNEIGH with invalid state %#x\n", ndm->ndm_state);
return -EINVAL;
}
- if (tb[NDA_VLAN]) {
- if (nla_len(tb[NDA_VLAN]) != sizeof(unsigned short)) {
- pr_info("bridge: RTM_NEWNEIGH with invalid vlan\n");
- return -EINVAL;
- }
-
- vid = nla_get_u16(tb[NDA_VLAN]);
-
- if (!vid || vid >= VLAN_VID_MASK) {
- pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n",
- vid);
- return -EINVAL;
- }
- }
-
if (is_zero_ether_addr(addr)) {
pr_info("bridge: RTM_NEWNEIGH with invalid ether address\n");
return -EINVAL;
@@ -845,7 +830,7 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
}
pv = nbp_get_vlan_info(p);
- if (vid != VLAN_N_VID) {
+ if (vid) {
if (!pv || !test_bit(vid, pv->vlan_bitmap)) {
pr_info("bridge: RTM_NEWNEIGH with unconfigured "
"vlan %d on port %s\n", vid, dev->name);
@@ -903,27 +888,12 @@ static int __br_fdb_delete(struct net_bridge_port *p,
/* Remove neighbor entry with RTM_DELNEIGH */
int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
struct net_device *dev,
- const unsigned char *addr)
+ const unsigned char *addr, u16 vid)
{
struct net_bridge_port *p;
int err;
struct net_port_vlans *pv;
- unsigned short vid = VLAN_N_VID;
- if (tb[NDA_VLAN]) {
- if (nla_len(tb[NDA_VLAN]) != sizeof(unsigned short)) {
- pr_info("bridge: RTM_NEWNEIGH with invalid vlan\n");
- return -EINVAL;
- }
-
- vid = nla_get_u16(tb[NDA_VLAN]);
-
- if (!vid || vid >= VLAN_VID_MASK) {
- pr_info("bridge: RTM_NEWNEIGH with invalid vlan id %d\n",
- vid);
- return -EINVAL;
- }
- }
p = br_port_get_rtnl(dev);
if (p == NULL) {
pr_info("bridge: RTM_DELNEIGH %s not a bridge port\n",
@@ -932,7 +902,7 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
}
pv = nbp_get_vlan_info(p);
- if (vid != VLAN_N_VID) {
+ if (vid) {
if (!pv || !test_bit(vid, pv->vlan_bitmap)) {
pr_info("bridge: RTM_DELNEIGH with unconfigured "
"vlan %d on port %s\n", vid, dev->name);
@@ -1014,3 +984,91 @@ void br_fdb_unsync_static(struct net_bridge *br, struct net_bridge_port *p)
}
}
}
+
+int br_fdb_external_learn_add(struct net_device *dev,
+ const unsigned char *addr, u16 vid)
+{
+ struct net_bridge_port *p;
+ struct net_bridge *br;
+ struct hlist_head *head;
+ struct net_bridge_fdb_entry *fdb;
+ int err = 0;
+
+ rtnl_lock();
+
+ p = br_port_get_rtnl(dev);
+ if (!p) {
+ pr_info("bridge: %s not a bridge port\n", dev->name);
+ err = -EINVAL;
+ goto err_rtnl_unlock;
+ }
+
+ br = p->br;
+
+ spin_lock_bh(&br->hash_lock);
+
+ head = &br->hash[br_mac_hash(addr, vid)];
+ fdb = fdb_find(head, addr, vid);
+ if (!fdb) {
+ fdb = fdb_create(head, p, addr, vid);
+ if (!fdb) {
+ err = -ENOMEM;
+ goto err_unlock;
+ }
+ fdb->added_by_external_learn = 1;
+ fdb_notify(br, fdb, RTM_NEWNEIGH);
+ } else if (fdb->added_by_external_learn) {
+ /* Refresh entry */
+ fdb->updated = fdb->used = jiffies;
+ } else if (!fdb->added_by_user) {
+ /* Take over SW learned entry */
+ fdb->added_by_external_learn = 1;
+ fdb->updated = jiffies;
+ fdb_notify(br, fdb, RTM_NEWNEIGH);
+ }
+
+err_unlock:
+ spin_unlock_bh(&br->hash_lock);
+err_rtnl_unlock:
+ rtnl_unlock();
+
+ return err;
+}
+EXPORT_SYMBOL(br_fdb_external_learn_add);
+
+int br_fdb_external_learn_del(struct net_device *dev,
+ const unsigned char *addr, u16 vid)
+{
+ struct net_bridge_port *p;
+ struct net_bridge *br;
+ struct hlist_head *head;
+ struct net_bridge_fdb_entry *fdb;
+ int err = 0;
+
+ rtnl_lock();
+
+ p = br_port_get_rtnl(dev);
+ if (!p) {
+ pr_info("bridge: %s not a bridge port\n", dev->name);
+ err = -EINVAL;
+ goto err_rtnl_unlock;
+ }
+
+ br = p->br;
+
+ spin_lock_bh(&br->hash_lock);
+
+ head = &br->hash[br_mac_hash(addr, vid)];
+ fdb = fdb_find(head, addr, vid);
+ if (fdb && fdb->added_by_external_learn)
+ fdb_delete(br, fdb);
+ else
+ err = -ENOENT;
+
+ spin_unlock_bh(&br->hash_lock);
+err_rtnl_unlock:
+ rtnl_unlock();
+
+ return err;
+}
+EXPORT_SYMBOL(br_fdb_external_learn_del);
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
index 44cb786b925a..f96933a823e3 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -184,6 +184,11 @@ static void br_flood(struct net_bridge *br, struct sk_buff *skb,
/* Do not flood unicast traffic to ports that turn it off */
if (unicast && !(p->flags & BR_FLOOD))
continue;
+
+ /* Do not flood to ports that enable proxy ARP */
+ if (p->flags & BR_PROXYARP)
+ continue;
+
prev = maybe_deliver(prev, p, skb, __packet_hook);
if (IS_ERR(prev))
goto out;
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index 6fd5522df696..1f1de715197c 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -16,6 +16,8 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/netfilter_bridge.h>
+#include <linux/neighbour.h>
+#include <net/arp.h>
#include <linux/export.h>
#include <linux/rculist.h>
#include "br_private.h"
@@ -57,6 +59,60 @@ static int br_pass_frame_up(struct sk_buff *skb)
netif_receive_skb);
}
+static void br_do_proxy_arp(struct sk_buff *skb, struct net_bridge *br,
+ u16 vid)
+{
+ struct net_device *dev = br->dev;
+ struct neighbour *n;
+ struct arphdr *parp;
+ u8 *arpptr, *sha;
+ __be32 sip, tip;
+
+ if (dev->flags & IFF_NOARP)
+ return;
+
+ if (!pskb_may_pull(skb, arp_hdr_len(dev))) {
+ dev->stats.tx_dropped++;
+ return;
+ }
+ parp = arp_hdr(skb);
+
+ if (parp->ar_pro != htons(ETH_P_IP) ||
+ parp->ar_op != htons(ARPOP_REQUEST) ||
+ parp->ar_hln != dev->addr_len ||
+ parp->ar_pln != 4)
+ return;
+
+ arpptr = (u8 *)parp + sizeof(struct arphdr);
+ sha = arpptr;
+ arpptr += dev->addr_len; /* sha */
+ memcpy(&sip, arpptr, sizeof(sip));
+ arpptr += sizeof(sip);
+ arpptr += dev->addr_len; /* tha */
+ memcpy(&tip, arpptr, sizeof(tip));
+
+ if (ipv4_is_loopback(tip) ||
+ ipv4_is_multicast(tip))
+ return;
+
+ n = neigh_lookup(&arp_tbl, &tip, dev);
+ if (n) {
+ struct net_bridge_fdb_entry *f;
+
+ if (!(n->nud_state & NUD_VALID)) {
+ neigh_release(n);
+ return;
+ }
+
+ f = __br_fdb_get(br, n->ha, vid);
+ if (f)
+ arp_send(ARPOP_REPLY, ETH_P_ARP, sip, skb->dev, tip,
+ sha, n->ha, sha);
+
+ neigh_release(n);
+ }
+}
+
/* note: already called with rcu_read_lock */
int br_handle_frame_finish(struct sk_buff *skb)
{
@@ -98,6 +154,10 @@ int br_handle_frame_finish(struct sk_buff *skb)
dst = NULL;
if (is_broadcast_ether_addr(dest)) {
+ if (p->flags & BR_PROXYARP &&
+ skb->protocol == htons(ETH_P_ARP))
+ br_do_proxy_arp(skb, br, vid);
+
skb2 = skb;
unicast = false;
} else if (is_multicast_ether_addr(dest)) {
diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 648d79ccf462..c465876c7861 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -813,10 +813,9 @@ static void __br_multicast_send_query(struct net_bridge *br,
return;
if (port) {
- __skb_push(skb, sizeof(struct ethhdr));
skb->dev = port->dev;
NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_OUT, skb, NULL, skb->dev,
- dev_queue_xmit);
+ br_dev_queue_push_xmit);
} else {
br_multicast_select_own_querier(br, ip, skb);
netif_rx(skb);
diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c
index 1a4f32c09ad5..c190d22b6b3d 100644
--- a/net/bridge/br_netfilter.c
+++ b/net/bridge/br_netfilter.c
@@ -35,6 +35,7 @@
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/route.h>
+#include <net/netfilter/br_netfilter.h>
#include <asm/uaccess.h>
#include "br_private.h"
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 2ff9706647f2..9f5eb55a4d3a 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -60,7 +60,8 @@ static int br_port_fill_attrs(struct sk_buff *skb,
nla_put_u8(skb, IFLA_BRPORT_PROTECT, !!(p->flags & BR_ROOT_BLOCK)) ||
nla_put_u8(skb, IFLA_BRPORT_FAST_LEAVE, !!(p->flags & BR_MULTICAST_FAST_LEAVE)) ||
nla_put_u8(skb, IFLA_BRPORT_LEARNING, !!(p->flags & BR_LEARNING)) ||
- nla_put_u8(skb, IFLA_BRPORT_UNICAST_FLOOD, !!(p->flags & BR_FLOOD)))
+ nla_put_u8(skb, IFLA_BRPORT_UNICAST_FLOOD, !!(p->flags & BR_FLOOD)) ||
+ nla_put_u8(skb, IFLA_BRPORT_PROXYARP, !!(p->flags & BR_PROXYARP)))
return -EMSGSIZE;
return 0;
@@ -280,6 +281,7 @@ static const struct nla_policy br_port_policy[IFLA_BRPORT_MAX + 1] = {
[IFLA_BRPORT_MODE] = { .type = NLA_U8 },
[IFLA_BRPORT_GUARD] = { .type = NLA_U8 },
[IFLA_BRPORT_PROTECT] = { .type = NLA_U8 },
+ [IFLA_BRPORT_FAST_LEAVE]= { .type = NLA_U8 },
[IFLA_BRPORT_LEARNING] = { .type = NLA_U8 },
[IFLA_BRPORT_UNICAST_FLOOD] = { .type = NLA_U8 },
};
@@ -332,6 +334,7 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
br_set_port_flag(p, tb, IFLA_BRPORT_PROTECT, BR_ROOT_BLOCK);
br_set_port_flag(p, tb, IFLA_BRPORT_LEARNING, BR_LEARNING);
br_set_port_flag(p, tb, IFLA_BRPORT_UNICAST_FLOOD, BR_FLOOD);
+ br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP, BR_PROXYARP);
if (tb[IFLA_BRPORT_COST]) {
err = br_stp_set_path_cost(p, nla_get_u32(tb[IFLA_BRPORT_COST]));
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index 4d783d071305..aea3d1339b3f 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -98,9 +98,10 @@ struct net_bridge_fdb_entry
unsigned long updated;
unsigned long used;
mac_addr addr;
- unsigned char is_local;
- unsigned char is_static;
- unsigned char added_by_user;
+ unsigned char is_local:1,
+ is_static:1,
+ added_by_user:1,
+ added_by_external_learn:1;
__u16 vlan_id;
};
@@ -163,15 +164,6 @@ struct net_bridge_port
struct rcu_head rcu;
unsigned long flags;
-#define BR_HAIRPIN_MODE 0x00000001
-#define BR_BPDU_GUARD 0x00000002
-#define BR_ROOT_BLOCK 0x00000004
-#define BR_MULTICAST_FAST_LEAVE 0x00000008
-#define BR_ADMIN_COST 0x00000010
-#define BR_LEARNING 0x00000020
-#define BR_FLOOD 0x00000040
-#define BR_AUTO_MASK (BR_FLOOD | BR_LEARNING)
-#define BR_PROMISC 0x00000080
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
struct bridge_mcast_own_query ip4_own_query;
@@ -403,9 +395,9 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
const unsigned char *addr, u16 vid, bool added_by_user);
int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
- struct net_device *dev, const unsigned char *addr);
+ struct net_device *dev, const unsigned char *addr, u16 vid);
int br_fdb_add(struct ndmsg *nlh, struct nlattr *tb[], struct net_device *dev,
- const unsigned char *addr, u16 nlh_flags);
+ const unsigned char *addr, u16 vid, u16 nlh_flags);
int br_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
struct net_device *dev, struct net_device *fdev, int idx);
int br_fdb_sync_static(struct net_bridge *br, struct net_bridge_port *p);
diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c
index 2b047bcf42a4..fb3ebe615513 100644
--- a/net/bridge/br_stp.c
+++ b/net/bridge/br_stp.c
@@ -12,6 +12,7 @@
*/
#include <linux/kernel.h>
#include <linux/rculist.h>
+#include <net/switchdev.h>
#include "br_private.h"
#include "br_private_stp.h"
@@ -38,7 +39,13 @@ void br_log_state(const struct net_bridge_port *p)
void br_set_state(struct net_bridge_port *p, unsigned int state)
{
+ int err;
+
p->state = state;
+ err = netdev_switch_port_stp_update(p->dev, state);
+ if (err && err != -EOPNOTSUPP)
+ br_warn(p->br, "error setting offload STP state on port %u(%s)\n",
+ (unsigned int) p->port_no, p->dev->name);
}
/* called under bridge lock */
diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c
index e561cd59b8a6..2de5d91199e8 100644
--- a/net/bridge/br_sysfs_if.c
+++ b/net/bridge/br_sysfs_if.c
@@ -170,6 +170,7 @@ BRPORT_ATTR_FLAG(bpdu_guard, BR_BPDU_GUARD);
BRPORT_ATTR_FLAG(root_block, BR_ROOT_BLOCK);
BRPORT_ATTR_FLAG(learning, BR_LEARNING);
BRPORT_ATTR_FLAG(unicast_flood, BR_FLOOD);
+BRPORT_ATTR_FLAG(proxyarp, BR_PROXYARP);
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
static ssize_t show_multicast_router(struct net_bridge_port *p, char *buf)
@@ -213,6 +214,7 @@ static const struct brport_attribute *brport_attrs[] = {
&brport_attr_multicast_router,
&brport_attr_multicast_fast_leave,
#endif
+ &brport_attr_proxyarp,
NULL
};
diff --git a/net/bridge/br_vlan.c b/net/bridge/br_vlan.c
index 150048fb99b0..97b8ddf57363 100644
--- a/net/bridge/br_vlan.c
+++ b/net/bridge/br_vlan.c
@@ -199,8 +199,8 @@ bool br_allowed_ingress(struct net_bridge *br, struct net_port_vlans *v,
if (skb->vlan_proto != proto) {
/* Protocol-mismatch, empty out vlan_tci for new tag */
skb_push(skb, ETH_HLEN);
- skb = __vlan_put_tag(skb, skb->vlan_proto,
- vlan_tx_tag_get(skb));
+ skb = vlan_insert_tag_set_proto(skb, skb->vlan_proto,
+ vlan_tx_tag_get(skb));
if (unlikely(!skb))
return false;
diff --git a/net/bridge/netfilter/nf_tables_bridge.c b/net/bridge/netfilter/nf_tables_bridge.c
index 074c557ab505..19473a9371b8 100644
--- a/net/bridge/netfilter/nf_tables_bridge.c
+++ b/net/bridge/netfilter/nf_tables_bridge.c
@@ -13,6 +13,82 @@
#include <linux/module.h>
#include <linux/netfilter_bridge.h>
#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables_bridge.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <net/netfilter/nf_tables_ipv4.h>
+#include <net/netfilter/nf_tables_ipv6.h>
+
+int nft_bridge_iphdr_validate(struct sk_buff *skb)
+{
+ struct iphdr *iph;
+ u32 len;
+
+ if (!pskb_may_pull(skb, sizeof(struct iphdr)))
+ return 0;
+
+ iph = ip_hdr(skb);
+ if (iph->ihl < 5 || iph->version != 4)
+ return 0;
+
+ len = ntohs(iph->tot_len);
+ if (skb->len < len)
+ return 0;
+ else if (len < (iph->ihl*4))
+ return 0;
+
+ if (!pskb_may_pull(skb, iph->ihl*4))
+ return 0;
+
+ return 1;
+}
+EXPORT_SYMBOL_GPL(nft_bridge_iphdr_validate);
+
+int nft_bridge_ip6hdr_validate(struct sk_buff *skb)
+{
+ struct ipv6hdr *hdr;
+ u32 pkt_len;
+
+ if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
+ return 0;
+
+ hdr = ipv6_hdr(skb);
+ if (hdr->version != 6)
+ return 0;
+
+ pkt_len = ntohs(hdr->payload_len);
+ if (pkt_len + sizeof(struct ipv6hdr) > skb->len)
+ return 0;
+
+ return 1;
+}
+EXPORT_SYMBOL_GPL(nft_bridge_ip6hdr_validate);
+
+static inline void nft_bridge_set_pktinfo_ipv4(struct nft_pktinfo *pkt,
+ const struct nf_hook_ops *ops,
+ struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out)
+{
+ if (nft_bridge_iphdr_validate(skb))
+ nft_set_pktinfo_ipv4(pkt, ops, skb, in, out);
+ else
+ nft_set_pktinfo(pkt, ops, skb, in, out);
+}
+
+static inline void nft_bridge_set_pktinfo_ipv6(struct nft_pktinfo *pkt,
+ const struct nf_hook_ops *ops,
+ struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out)
+{
+#if IS_ENABLED(CONFIG_IPV6)
+ if (nft_bridge_ip6hdr_validate(skb) &&
+ nft_set_pktinfo_ipv6(pkt, ops, skb, in, out) == 0)
+ return;
+#endif
+ nft_set_pktinfo(pkt, ops, skb, in, out);
+}
static unsigned int
nft_do_chain_bridge(const struct nf_hook_ops *ops,
@@ -23,7 +99,17 @@ nft_do_chain_bridge(const struct nf_hook_ops *ops,
{
struct nft_pktinfo pkt;
- nft_set_pktinfo(&pkt, ops, skb, in, out);
+ switch (eth_hdr(skb)->h_proto) {
+ case htons(ETH_P_IP):
+ nft_bridge_set_pktinfo_ipv4(&pkt, ops, skb, in, out);
+ break;
+ case htons(ETH_P_IPV6):
+ nft_bridge_set_pktinfo_ipv6(&pkt, ops, skb, in, out);
+ break;
+ default:
+ nft_set_pktinfo(&pkt, ops, skb, in, out);
+ break;
+ }
return nft_do_chain(&pkt, ops);
}
diff --git a/net/bridge/netfilter/nft_reject_bridge.c b/net/bridge/netfilter/nft_reject_bridge.c
index 654c9018e3e7..b0330aecbf97 100644
--- a/net/bridge/netfilter/nft_reject_bridge.c
+++ b/net/bridge/netfilter/nft_reject_bridge.c
@@ -14,10 +14,12 @@
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nft_reject.h>
+#include <net/netfilter/nf_tables_bridge.h>
#include <net/netfilter/ipv4/nf_reject.h>
#include <net/netfilter/ipv6/nf_reject.h>
#include <linux/ip.h>
#include <net/ip.h>
+#include <net/ip6_checksum.h>
#include <linux/netfilter_bridge.h>
#include "../br_private.h"
@@ -34,30 +36,6 @@ static void nft_reject_br_push_etherhdr(struct sk_buff *oldskb,
skb_pull(nskb, ETH_HLEN);
}
-static int nft_reject_iphdr_validate(struct sk_buff *oldskb)
-{
- struct iphdr *iph;
- u32 len;
-
- if (!pskb_may_pull(oldskb, sizeof(struct iphdr)))
- return 0;
-
- iph = ip_hdr(oldskb);
- if (iph->ihl < 5 || iph->version != 4)
- return 0;
-
- len = ntohs(iph->tot_len);
- if (oldskb->len < len)
- return 0;
- else if (len < (iph->ihl*4))
- return 0;
-
- if (!pskb_may_pull(oldskb, iph->ihl*4))
- return 0;
-
- return 1;
-}
-
static void nft_reject_br_send_v4_tcp_reset(struct sk_buff *oldskb, int hook)
{
struct sk_buff *nskb;
@@ -65,7 +43,7 @@ static void nft_reject_br_send_v4_tcp_reset(struct sk_buff *oldskb, int hook)
const struct tcphdr *oth;
struct tcphdr _oth;
- if (!nft_reject_iphdr_validate(oldskb))
+ if (!nft_bridge_iphdr_validate(oldskb))
return;
oth = nf_reject_ip_tcphdr_get(oldskb, &_oth, hook);
@@ -100,7 +78,7 @@ static void nft_reject_br_send_v4_unreach(struct sk_buff *oldskb, int hook,
void *payload;
__wsum csum;
- if (!nft_reject_iphdr_validate(oldskb))
+ if (!nft_bridge_iphdr_validate(oldskb))
return;
/* IP header checks: fragment. */
@@ -145,25 +123,6 @@ static void nft_reject_br_send_v4_unreach(struct sk_buff *oldskb, int hook,
br_deliver(br_port_get_rcu(oldskb->dev), nskb);
}
-static int nft_reject_ip6hdr_validate(struct sk_buff *oldskb)
-{
- struct ipv6hdr *hdr;
- u32 pkt_len;
-
- if (!pskb_may_pull(oldskb, sizeof(struct ipv6hdr)))
- return 0;
-
- hdr = ipv6_hdr(oldskb);
- if (hdr->version != 6)
- return 0;
-
- pkt_len = ntohs(hdr->payload_len);
- if (pkt_len + sizeof(struct ipv6hdr) > oldskb->len)
- return 0;
-
- return 1;
-}
-
static void nft_reject_br_send_v6_tcp_reset(struct net *net,
struct sk_buff *oldskb, int hook)
{
@@ -173,7 +132,7 @@ static void nft_reject_br_send_v6_tcp_reset(struct net *net,
unsigned int otcplen;
struct ipv6hdr *nip6h;
- if (!nft_reject_ip6hdr_validate(oldskb))
+ if (!nft_bridge_ip6hdr_validate(oldskb))
return;
oth = nf_reject_ip6_tcphdr_get(oldskb, &_oth, &otcplen, hook);
@@ -206,7 +165,7 @@ static void nft_reject_br_send_v6_unreach(struct net *net,
unsigned int len;
void *payload;
- if (!nft_reject_ip6hdr_validate(oldskb))
+ if (!nft_bridge_ip6hdr_validate(oldskb))
return;
/* Include "As much of invoking packet as possible without the ICMPv6
diff --git a/net/caif/caif_socket.c b/net/caif/caif_socket.c
index 43f750e88e19..769b185fefbd 100644
--- a/net/caif/caif_socket.c
+++ b/net/caif/caif_socket.c
@@ -293,7 +293,7 @@ static int caif_seqpkt_recvmsg(struct kiocb *iocb, struct socket *sock,
copylen = len;
}
- ret = skb_copy_datagram_iovec(skb, 0, m->msg_iov, copylen);
+ ret = skb_copy_datagram_msg(skb, 0, m, copylen);
if (ret)
goto out_free;
@@ -418,7 +418,7 @@ unlock:
}
release_sock(sk);
chunk = min_t(unsigned int, skb->len, size);
- if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) {
+ if (memcpy_to_msg(msg, skb->data, chunk)) {
skb_queue_head(&sk->sk_receive_queue, skb);
if (copied == 0)
copied = -EFAULT;
@@ -535,7 +535,7 @@ static int caif_seqpkt_sendmsg(struct kiocb *kiocb, struct socket *sock,
goto err;
ret = -EINVAL;
- if (unlikely(msg->msg_iov->iov_base == NULL))
+ if (unlikely(msg->msg_iter.iov->iov_base == NULL))
goto err;
noblock = msg->msg_flags & MSG_DONTWAIT;
@@ -566,7 +566,7 @@ static int caif_seqpkt_sendmsg(struct kiocb *kiocb, struct socket *sock,
skb_reserve(skb, cf_sk->headroom);
- ret = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
+ ret = memcpy_from_msg(skb_put(skb, len), msg, len);
if (ret)
goto err;
@@ -641,7 +641,7 @@ static int caif_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
*/
size = min_t(int, size, skb_tailroom(skb));
- err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
+ err = memcpy_from_msg(skb_put(skb, size), msg, size);
if (err) {
kfree_skb(skb);
goto out_err;
diff --git a/net/can/af_can.c b/net/can/af_can.c
index ce82337521f6..66e08040ced7 100644
--- a/net/can/af_can.c
+++ b/net/can/af_can.c
@@ -64,9 +64,6 @@
#include "af_can.h"
-static __initconst const char banner[] = KERN_INFO
- "can: controller area network core (" CAN_VERSION_STRING ")\n";
-
MODULE_DESCRIPTION("Controller Area Network PF_CAN core");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>, "
@@ -524,7 +521,7 @@ static void can_rx_delete_receiver(struct rcu_head *rp)
/**
* can_rx_unregister - unsubscribe CAN frames from a specific interface
- * @dev: pointer to netdevice (NULL => unsubcribe from 'all' CAN devices list)
+ * @dev: pointer to netdevice (NULL => unsubscribe from 'all' CAN devices list)
* @can_id: CAN identifier
* @mask: CAN mask
* @func: callback function on filter match
@@ -896,7 +893,7 @@ static __init int can_init(void)
offsetof(struct can_frame, data) !=
offsetof(struct canfd_frame, data));
- printk(banner);
+ pr_info("can: controller area network core (" CAN_VERSION_STRING ")\n");
memset(&can_rx_alldev_list, 0, sizeof(can_rx_alldev_list));
diff --git a/net/can/bcm.c b/net/can/bcm.c
index dcb75c0e66c1..ee9ffd956552 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -78,8 +78,6 @@
(CAN_SFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG))
#define CAN_BCM_VERSION CAN_VERSION
-static __initconst const char banner[] = KERN_INFO
- "can: broadcast manager protocol (rev " CAN_BCM_VERSION " t)\n";
MODULE_DESCRIPTION("PF_CAN broadcast manager protocol");
MODULE_LICENSE("Dual BSD/GPL");
@@ -441,7 +439,7 @@ static void bcm_rx_update_and_send(struct bcm_op *op,
/* mark as used and throttled by default */
lastdata->can_dlc |= (RX_RECV|RX_THR);
- /* throtteling mode inactive ? */
+ /* throttling mode inactive ? */
if (!op->kt_ival2.tv64) {
/* send RX_CHANGED to the user immediately */
bcm_rx_changed(op, lastdata);
@@ -452,7 +450,7 @@ static void bcm_rx_update_and_send(struct bcm_op *op,
if (hrtimer_active(&op->thrtimer))
return;
- /* first receiption with enabled throttling mode */
+ /* first reception with enabled throttling mode */
if (!op->kt_lastmsg.tv64)
goto rx_changed_settime;
@@ -480,7 +478,7 @@ static void bcm_rx_cmp_to_index(struct bcm_op *op, unsigned int index,
const struct can_frame *rxdata)
{
/*
- * no one uses the MSBs of can_dlc for comparation,
+ * no one uses the MSBs of can_dlc for comparison,
* so we use it here to detect the first time of reception
*/
@@ -510,7 +508,7 @@ static void bcm_rx_cmp_to_index(struct bcm_op *op, unsigned int index,
}
/*
- * bcm_rx_starttimer - enable timeout monitoring for CAN frame receiption
+ * bcm_rx_starttimer - enable timeout monitoring for CAN frame reception
*/
static void bcm_rx_starttimer(struct bcm_op *op)
{
@@ -539,7 +537,7 @@ static void bcm_rx_timeout_tsklet(unsigned long data)
}
/*
- * bcm_rx_timeout_handler - when the (cyclic) CAN frame receiption timed out
+ * bcm_rx_timeout_handler - when the (cyclic) CAN frame reception timed out
*/
static enum hrtimer_restart bcm_rx_timeout_handler(struct hrtimer *hrtimer)
{
@@ -627,7 +625,7 @@ static enum hrtimer_restart bcm_rx_thr_handler(struct hrtimer *hrtimer)
}
/*
- * bcm_rx_handler - handle a CAN frame receiption
+ * bcm_rx_handler - handle a CAN frame reception
*/
static void bcm_rx_handler(struct sk_buff *skb, void *data)
{
@@ -858,8 +856,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
/* update can_frames content */
for (i = 0; i < msg_head->nframes; i++) {
- err = memcpy_fromiovec((u8 *)&op->frames[i],
- msg->msg_iov, CFSIZ);
+ err = memcpy_from_msg((u8 *)&op->frames[i], msg, CFSIZ);
if (op->frames[i].can_dlc > 8)
err = -EINVAL;
@@ -894,8 +891,7 @@ static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
op->frames = &op->sframe;
for (i = 0; i < msg_head->nframes; i++) {
- err = memcpy_fromiovec((u8 *)&op->frames[i],
- msg->msg_iov, CFSIZ);
+ err = memcpy_from_msg((u8 *)&op->frames[i], msg, CFSIZ);
if (op->frames[i].can_dlc > 8)
err = -EINVAL;
@@ -1024,9 +1020,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
if (msg_head->nframes) {
/* update can_frames content */
- err = memcpy_fromiovec((u8 *)op->frames,
- msg->msg_iov,
- msg_head->nframes * CFSIZ);
+ err = memcpy_from_msg((u8 *)op->frames, msg,
+ msg_head->nframes * CFSIZ);
if (err < 0)
return err;
@@ -1072,8 +1067,8 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
}
if (msg_head->nframes) {
- err = memcpy_fromiovec((u8 *)op->frames, msg->msg_iov,
- msg_head->nframes * CFSIZ);
+ err = memcpy_from_msg((u8 *)op->frames, msg,
+ msg_head->nframes * CFSIZ);
if (err < 0) {
if (op->frames != &op->sframe)
kfree(op->frames);
@@ -1209,7 +1204,7 @@ static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk)
can_skb_reserve(skb);
- err = memcpy_fromiovec(skb_put(skb, CFSIZ), msg->msg_iov, CFSIZ);
+ err = memcpy_from_msg(skb_put(skb, CFSIZ), msg, CFSIZ);
if (err < 0) {
kfree_skb(skb);
return err;
@@ -1285,7 +1280,7 @@ static int bcm_sendmsg(struct kiocb *iocb, struct socket *sock,
/* read message head information */
- ret = memcpy_fromiovec((u8 *)&msg_head, msg->msg_iov, MHSIZ);
+ ret = memcpy_from_msg((u8 *)&msg_head, msg, MHSIZ);
if (ret < 0)
return ret;
@@ -1558,7 +1553,7 @@ static int bcm_recvmsg(struct kiocb *iocb, struct socket *sock,
if (skb->len < size)
size = skb->len;
- err = memcpy_toiovec(msg->msg_iov, skb->data, size);
+ err = memcpy_to_msg(msg, skb->data, size);
if (err < 0) {
skb_free_datagram(sk, skb);
return err;
@@ -1615,7 +1610,7 @@ static int __init bcm_module_init(void)
{
int err;
- printk(banner);
+ pr_info("can: broadcast manager protocol (rev " CAN_BCM_VERSION " t)\n");
err = can_proto_register(&bcm_can_proto);
if (err < 0) {
diff --git a/net/can/gw.c b/net/can/gw.c
index 050a2110d43f..295f62e62eb3 100644
--- a/net/can/gw.c
+++ b/net/can/gw.c
@@ -361,7 +361,7 @@ static void can_can_gw_rcv(struct sk_buff *skb, void *data)
* The Controller Area Network controllers only accept CAN frames with
* correct CRCs - which are not visible in the controller registers.
* According to skbuff.h documentation the csum_start element for IP
- * checksums is undefined/unsued when ip_summed == CHECKSUM_UNNECESSARY.
+ * checksums is undefined/unused when ip_summed == CHECKSUM_UNNECESSARY.
* Only CAN skbs can be processed here which already have this property.
*/
diff --git a/net/can/raw.c b/net/can/raw.c
index 081e81fd017f..00c13ef23661 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -56,8 +56,6 @@
#include <net/net_namespace.h>
#define CAN_RAW_VERSION CAN_VERSION
-static __initconst const char banner[] =
- KERN_INFO "can: raw protocol (rev " CAN_RAW_VERSION ")\n";
MODULE_DESCRIPTION("PF_CAN raw protocol");
MODULE_LICENSE("Dual BSD/GPL");
@@ -703,7 +701,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct socket *sock,
can_skb_reserve(skb);
can_skb_prv(skb)->ifindex = dev->ifindex;
- err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
+ err = memcpy_from_msg(skb_put(skb, size), msg, size);
if (err < 0)
goto free_skb;
@@ -750,7 +748,7 @@ static int raw_recvmsg(struct kiocb *iocb, struct socket *sock,
else
size = skb->len;
- err = memcpy_toiovec(msg->msg_iov, skb->data, size);
+ err = memcpy_to_msg(msg, skb->data, size);
if (err < 0) {
skb_free_datagram(sk, skb);
return err;
@@ -810,7 +808,7 @@ static __init int raw_module_init(void)
{
int err;
- printk(banner);
+ pr_info("can: raw protocol (rev " CAN_RAW_VERSION ")\n");
err = can_proto_register(&raw_can_proto);
if (err < 0)
diff --git a/net/ceph/crypto.c b/net/ceph/crypto.c
index 62fc5e7a9acf..790fe89d90c0 100644
--- a/net/ceph/crypto.c
+++ b/net/ceph/crypto.c
@@ -90,11 +90,82 @@ static struct crypto_blkcipher *ceph_crypto_alloc_cipher(void)
static const u8 *aes_iv = (u8 *)CEPH_AES_IV;
+/*
+ * Should be used for buffers allocated with ceph_kvmalloc().
+ * Currently these are encrypt out-buffer (ceph_buffer) and decrypt
+ * in-buffer (msg front).
+ *
+ * Dispose of @sgt with teardown_sgtable().
+ *
+ * @prealloc_sg is to avoid memory allocation inside sg_alloc_table()
+ * in cases where a single sg is sufficient. No attempt to reduce the
+ * number of sgs by squeezing physically contiguous pages together is
+ * made though, for simplicity.
+ */
+static int setup_sgtable(struct sg_table *sgt, struct scatterlist *prealloc_sg,
+ const void *buf, unsigned int buf_len)
+{
+ struct scatterlist *sg;
+ const bool is_vmalloc = is_vmalloc_addr(buf);
+ unsigned int off = offset_in_page(buf);
+ unsigned int chunk_cnt = 1;
+ unsigned int chunk_len = PAGE_ALIGN(off + buf_len);
+ int i;
+ int ret;
+
+ if (buf_len == 0) {
+ memset(sgt, 0, sizeof(*sgt));
+ return -EINVAL;
+ }
+
+ if (is_vmalloc) {
+ chunk_cnt = chunk_len >> PAGE_SHIFT;
+ chunk_len = PAGE_SIZE;
+ }
+
+ if (chunk_cnt > 1) {
+ ret = sg_alloc_table(sgt, chunk_cnt, GFP_NOFS);
+ if (ret)
+ return ret;
+ } else {
+ WARN_ON(chunk_cnt != 1);
+ sg_init_table(prealloc_sg, 1);
+ sgt->sgl = prealloc_sg;
+ sgt->nents = sgt->orig_nents = 1;
+ }
+
+ for_each_sg(sgt->sgl, sg, sgt->orig_nents, i) {
+ struct page *page;
+ unsigned int len = min(chunk_len - off, buf_len);
+
+ if (is_vmalloc)
+ page = vmalloc_to_page(buf);
+ else
+ page = virt_to_page(buf);
+
+ sg_set_page(sg, page, len, off);
+
+ off = 0;
+ buf += len;
+ buf_len -= len;
+ }
+ WARN_ON(buf_len != 0);
+
+ return 0;
+}
+
+static void teardown_sgtable(struct sg_table *sgt)
+{
+ if (sgt->orig_nents > 1)
+ sg_free_table(sgt);
+}
+
static int ceph_aes_encrypt(const void *key, int key_len,
void *dst, size_t *dst_len,
const void *src, size_t src_len)
{
- struct scatterlist sg_in[2], sg_out[1];
+ struct scatterlist sg_in[2], prealloc_sg;
+ struct sg_table sg_out;
struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
struct blkcipher_desc desc = { .tfm = tfm, .flags = 0 };
int ret;
@@ -110,16 +181,18 @@ static int ceph_aes_encrypt(const void *key, int key_len,
*dst_len = src_len + zero_padding;
- crypto_blkcipher_setkey((void *)tfm, key, key_len);
sg_init_table(sg_in, 2);
sg_set_buf(&sg_in[0], src, src_len);
sg_set_buf(&sg_in[1], pad, zero_padding);
- sg_init_table(sg_out, 1);
- sg_set_buf(sg_out, dst, *dst_len);
+ ret = setup_sgtable(&sg_out, &prealloc_sg, dst, *dst_len);
+ if (ret)
+ goto out_tfm;
+
+ crypto_blkcipher_setkey((void *)tfm, key, key_len);
iv = crypto_blkcipher_crt(tfm)->iv;
ivsize = crypto_blkcipher_ivsize(tfm);
-
memcpy(iv, aes_iv, ivsize);
+
/*
print_hex_dump(KERN_ERR, "enc key: ", DUMP_PREFIX_NONE, 16, 1,
key, key_len, 1);
@@ -128,16 +201,22 @@ static int ceph_aes_encrypt(const void *key, int key_len,
print_hex_dump(KERN_ERR, "enc pad: ", DUMP_PREFIX_NONE, 16, 1,
pad, zero_padding, 1);
*/
- ret = crypto_blkcipher_encrypt(&desc, sg_out, sg_in,
+ ret = crypto_blkcipher_encrypt(&desc, sg_out.sgl, sg_in,
src_len + zero_padding);
- crypto_free_blkcipher(tfm);
- if (ret < 0)
+ if (ret < 0) {
pr_err("ceph_aes_crypt failed %d\n", ret);
+ goto out_sg;
+ }
/*
print_hex_dump(KERN_ERR, "enc out: ", DUMP_PREFIX_NONE, 16, 1,
dst, *dst_len, 1);
*/
- return 0;
+
+out_sg:
+ teardown_sgtable(&sg_out);
+out_tfm:
+ crypto_free_blkcipher(tfm);
+ return ret;
}
static int ceph_aes_encrypt2(const void *key, int key_len, void *dst,
@@ -145,7 +224,8 @@ static int ceph_aes_encrypt2(const void *key, int key_len, void *dst,
const void *src1, size_t src1_len,
const void *src2, size_t src2_len)
{
- struct scatterlist sg_in[3], sg_out[1];
+ struct scatterlist sg_in[3], prealloc_sg;
+ struct sg_table sg_out;
struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
struct blkcipher_desc desc = { .tfm = tfm, .flags = 0 };
int ret;
@@ -161,17 +241,19 @@ static int ceph_aes_encrypt2(const void *key, int key_len, void *dst,
*dst_len = src1_len + src2_len + zero_padding;
- crypto_blkcipher_setkey((void *)tfm, key, key_len);
sg_init_table(sg_in, 3);
sg_set_buf(&sg_in[0], src1, src1_len);
sg_set_buf(&sg_in[1], src2, src2_len);
sg_set_buf(&sg_in[2], pad, zero_padding);
- sg_init_table(sg_out, 1);
- sg_set_buf(sg_out, dst, *dst_len);
+ ret = setup_sgtable(&sg_out, &prealloc_sg, dst, *dst_len);
+ if (ret)
+ goto out_tfm;
+
+ crypto_blkcipher_setkey((void *)tfm, key, key_len);
iv = crypto_blkcipher_crt(tfm)->iv;
ivsize = crypto_blkcipher_ivsize(tfm);
-
memcpy(iv, aes_iv, ivsize);
+
/*
print_hex_dump(KERN_ERR, "enc key: ", DUMP_PREFIX_NONE, 16, 1,
key, key_len, 1);
@@ -182,23 +264,30 @@ static int ceph_aes_encrypt2(const void *key, int key_len, void *dst,
print_hex_dump(KERN_ERR, "enc pad: ", DUMP_PREFIX_NONE, 16, 1,
pad, zero_padding, 1);
*/
- ret = crypto_blkcipher_encrypt(&desc, sg_out, sg_in,
+ ret = crypto_blkcipher_encrypt(&desc, sg_out.sgl, sg_in,
src1_len + src2_len + zero_padding);
- crypto_free_blkcipher(tfm);
- if (ret < 0)
+ if (ret < 0) {
pr_err("ceph_aes_crypt2 failed %d\n", ret);
+ goto out_sg;
+ }
/*
print_hex_dump(KERN_ERR, "enc out: ", DUMP_PREFIX_NONE, 16, 1,
dst, *dst_len, 1);
*/
- return 0;
+
+out_sg:
+ teardown_sgtable(&sg_out);
+out_tfm:
+ crypto_free_blkcipher(tfm);
+ return ret;
}
static int ceph_aes_decrypt(const void *key, int key_len,
void *dst, size_t *dst_len,
const void *src, size_t src_len)
{
- struct scatterlist sg_in[1], sg_out[2];
+ struct sg_table sg_in;
+ struct scatterlist sg_out[2], prealloc_sg;
struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
struct blkcipher_desc desc = { .tfm = tfm };
char pad[16];
@@ -210,16 +299,16 @@ static int ceph_aes_decrypt(const void *key, int key_len,
if (IS_ERR(tfm))
return PTR_ERR(tfm);
- crypto_blkcipher_setkey((void *)tfm, key, key_len);
- sg_init_table(sg_in, 1);
sg_init_table(sg_out, 2);
- sg_set_buf(sg_in, src, src_len);
sg_set_buf(&sg_out[0], dst, *dst_len);
sg_set_buf(&sg_out[1], pad, sizeof(pad));
+ ret = setup_sgtable(&sg_in, &prealloc_sg, src, src_len);
+ if (ret)
+ goto out_tfm;
+ crypto_blkcipher_setkey((void *)tfm, key, key_len);
iv = crypto_blkcipher_crt(tfm)->iv;
ivsize = crypto_blkcipher_ivsize(tfm);
-
memcpy(iv, aes_iv, ivsize);
/*
@@ -228,12 +317,10 @@ static int ceph_aes_decrypt(const void *key, int key_len,
print_hex_dump(KERN_ERR, "dec in: ", DUMP_PREFIX_NONE, 16, 1,
src, src_len, 1);
*/
-
- ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, src_len);
- crypto_free_blkcipher(tfm);
+ ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in.sgl, src_len);
if (ret < 0) {
pr_err("ceph_aes_decrypt failed %d\n", ret);
- return ret;
+ goto out_sg;
}
if (src_len <= *dst_len)
@@ -251,7 +338,12 @@ static int ceph_aes_decrypt(const void *key, int key_len,
print_hex_dump(KERN_ERR, "dec out: ", DUMP_PREFIX_NONE, 16, 1,
dst, *dst_len, 1);
*/
- return 0;
+
+out_sg:
+ teardown_sgtable(&sg_in);
+out_tfm:
+ crypto_free_blkcipher(tfm);
+ return ret;
}
static int ceph_aes_decrypt2(const void *key, int key_len,
@@ -259,7 +351,8 @@ static int ceph_aes_decrypt2(const void *key, int key_len,
void *dst2, size_t *dst2_len,
const void *src, size_t src_len)
{
- struct scatterlist sg_in[1], sg_out[3];
+ struct sg_table sg_in;
+ struct scatterlist sg_out[3], prealloc_sg;
struct crypto_blkcipher *tfm = ceph_crypto_alloc_cipher();
struct blkcipher_desc desc = { .tfm = tfm };
char pad[16];
@@ -271,17 +364,17 @@ static int ceph_aes_decrypt2(const void *key, int key_len,
if (IS_ERR(tfm))
return PTR_ERR(tfm);
- sg_init_table(sg_in, 1);
- sg_set_buf(sg_in, src, src_len);
sg_init_table(sg_out, 3);
sg_set_buf(&sg_out[0], dst1, *dst1_len);
sg_set_buf(&sg_out[1], dst2, *dst2_len);
sg_set_buf(&sg_out[2], pad, sizeof(pad));
+ ret = setup_sgtable(&sg_in, &prealloc_sg, src, src_len);
+ if (ret)
+ goto out_tfm;
crypto_blkcipher_setkey((void *)tfm, key, key_len);
iv = crypto_blkcipher_crt(tfm)->iv;
ivsize = crypto_blkcipher_ivsize(tfm);
-
memcpy(iv, aes_iv, ivsize);
/*
@@ -290,12 +383,10 @@ static int ceph_aes_decrypt2(const void *key, int key_len,
print_hex_dump(KERN_ERR, "dec in: ", DUMP_PREFIX_NONE, 16, 1,
src, src_len, 1);
*/
-
- ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in, src_len);
- crypto_free_blkcipher(tfm);
+ ret = crypto_blkcipher_decrypt(&desc, sg_out, sg_in.sgl, src_len);
if (ret < 0) {
pr_err("ceph_aes_decrypt failed %d\n", ret);
- return ret;
+ goto out_sg;
}
if (src_len <= *dst1_len)
@@ -325,7 +416,11 @@ static int ceph_aes_decrypt2(const void *key, int key_len,
dst2, *dst2_len, 1);
*/
- return 0;
+out_sg:
+ teardown_sgtable(&sg_in);
+out_tfm:
+ crypto_free_blkcipher(tfm);
+ return ret;
}
diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c
index f3fc54eac09d..6f164289bde8 100644
--- a/net/ceph/osd_client.c
+++ b/net/ceph/osd_client.c
@@ -1007,8 +1007,8 @@ static void put_osd(struct ceph_osd *osd)
static void __remove_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd)
{
dout("__remove_osd %p\n", osd);
- BUG_ON(!list_empty(&osd->o_requests));
- BUG_ON(!list_empty(&osd->o_linger_requests));
+ WARN_ON(!list_empty(&osd->o_requests));
+ WARN_ON(!list_empty(&osd->o_linger_requests));
rb_erase(&osd->o_node, &osdc->osds);
list_del_init(&osd->o_osd_lru);
@@ -1254,6 +1254,8 @@ static void __unregister_linger_request(struct ceph_osd_client *osdc,
if (list_empty(&req->r_osd_item))
req->r_osd = NULL;
}
+
+ list_del_init(&req->r_req_lru_item); /* can be on notarget */
ceph_osdc_put_request(req);
}
@@ -1395,6 +1397,7 @@ static int __map_request(struct ceph_osd_client *osdc,
if (req->r_osd) {
__cancel_request(req);
list_del_init(&req->r_osd_item);
+ list_del_init(&req->r_linger_osd_item);
req->r_osd = NULL;
}
diff --git a/net/compat.c b/net/compat.c
index bc8aeefddf3f..3236b4167a32 100644
--- a/net/compat.c
+++ b/net/compat.c
@@ -31,81 +31,54 @@
#include <asm/uaccess.h>
#include <net/compat.h>
-static inline int iov_from_user_compat_to_kern(struct iovec *kiov,
- struct compat_iovec __user *uiov32,
- int niov)
+ssize_t get_compat_msghdr(struct msghdr *kmsg,
+ struct compat_msghdr __user *umsg,
+ struct sockaddr __user **save_addr,
+ struct iovec **iov)
{
- int tot_len = 0;
-
- while (niov > 0) {
- compat_uptr_t buf;
- compat_size_t len;
-
- if (get_user(len, &uiov32->iov_len) ||
- get_user(buf, &uiov32->iov_base))
- return -EFAULT;
-
- if (len > INT_MAX - tot_len)
- len = INT_MAX - tot_len;
-
- tot_len += len;
- kiov->iov_base = compat_ptr(buf);
- kiov->iov_len = (__kernel_size_t) len;
- uiov32++;
- kiov++;
- niov--;
- }
- return tot_len;
-}
-
-int get_compat_msghdr(struct msghdr *kmsg, struct compat_msghdr __user *umsg)
-{
- compat_uptr_t tmp1, tmp2, tmp3;
+ compat_uptr_t uaddr, uiov, tmp3;
+ compat_size_t nr_segs;
+ ssize_t err;
if (!access_ok(VERIFY_READ, umsg, sizeof(*umsg)) ||
- __get_user(tmp1, &umsg->msg_name) ||
+ __get_user(uaddr, &umsg->msg_name) ||
__get_user(kmsg->msg_namelen, &umsg->msg_namelen) ||
- __get_user(tmp2, &umsg->msg_iov) ||
- __get_user(kmsg->msg_iovlen, &umsg->msg_iovlen) ||
+ __get_user(uiov, &umsg->msg_iov) ||
+ __get_user(nr_segs, &umsg->msg_iovlen) ||
__get_user(tmp3, &umsg->msg_control) ||
__get_user(kmsg->msg_controllen, &umsg->msg_controllen) ||
__get_user(kmsg->msg_flags, &umsg->msg_flags))
return -EFAULT;
if (kmsg->msg_namelen > sizeof(struct sockaddr_storage))
kmsg->msg_namelen = sizeof(struct sockaddr_storage);
- kmsg->msg_name = compat_ptr(tmp1);
- kmsg->msg_iov = compat_ptr(tmp2);
kmsg->msg_control = compat_ptr(tmp3);
- return 0;
-}
-/* I've named the args so it is easy to tell whose space the pointers are in. */
-int verify_compat_iovec(struct msghdr *kern_msg, struct iovec *kern_iov,
- struct sockaddr_storage *kern_address, int mode)
-{
- int tot_len;
+ if (save_addr)
+ *save_addr = compat_ptr(uaddr);
- if (kern_msg->msg_name && kern_msg->msg_namelen) {
- if (mode == VERIFY_READ) {
- int err = move_addr_to_kernel(kern_msg->msg_name,
- kern_msg->msg_namelen,
- kern_address);
+ if (uaddr && kmsg->msg_namelen) {
+ if (!save_addr) {
+ err = move_addr_to_kernel(compat_ptr(uaddr),
+ kmsg->msg_namelen,
+ kmsg->msg_name);
if (err < 0)
return err;
}
- kern_msg->msg_name = kern_address;
} else {
- kern_msg->msg_name = NULL;
- kern_msg->msg_namelen = 0;
+ kmsg->msg_name = NULL;
+ kmsg->msg_namelen = 0;
}
- tot_len = iov_from_user_compat_to_kern(kern_iov,
- (struct compat_iovec __user *)kern_msg->msg_iov,
- kern_msg->msg_iovlen);
- if (tot_len >= 0)
- kern_msg->msg_iov = kern_iov;
+ if (nr_segs > UIO_MAXIOV)
+ return -EMSGSIZE;
- return tot_len;
+ err = compat_rw_copy_check_uvector(save_addr ? READ : WRITE,
+ compat_ptr(uiov), nr_segs,
+ UIO_FASTIOV, *iov, iov);
+ if (err >= 0)
+ iov_iter_init(&kmsg->msg_iter, save_addr ? READ : WRITE,
+ *iov, nr_segs, err);
+ return err;
}
/* Bleech... */
@@ -740,7 +713,7 @@ COMPAT_SYSCALL_DEFINE3(sendmsg, int, fd, struct compat_msghdr __user *, msg, uns
{
if (flags & MSG_CMSG_COMPAT)
return -EINVAL;
- return __sys_sendmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT);
+ return __sys_sendmsg(fd, (struct user_msghdr __user *)msg, flags | MSG_CMSG_COMPAT);
}
COMPAT_SYSCALL_DEFINE4(sendmmsg, int, fd, struct compat_mmsghdr __user *, mmsg,
@@ -756,7 +729,7 @@ COMPAT_SYSCALL_DEFINE3(recvmsg, int, fd, struct compat_msghdr __user *, msg, uns
{
if (flags & MSG_CMSG_COMPAT)
return -EINVAL;
- return __sys_recvmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT);
+ return __sys_recvmsg(fd, (struct user_msghdr __user *)msg, flags | MSG_CMSG_COMPAT);
}
COMPAT_SYSCALL_DEFINE4(recv, int, fd, void __user *, buf, compat_size_t, len, unsigned int, flags)
diff --git a/net/core/datagram.c b/net/core/datagram.c
index fdbc9a81d4c2..df493d68330c 100644
--- a/net/core/datagram.c
+++ b/net/core/datagram.c
@@ -49,6 +49,7 @@
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
+#include <linux/uio.h>
#include <net/protocol.h>
#include <linux/skbuff.h>
@@ -309,16 +310,14 @@ int skb_kill_datagram(struct sock *sk, struct sk_buff *skb, unsigned int flags)
EXPORT_SYMBOL(skb_kill_datagram);
/**
- * skb_copy_datagram_iovec - Copy a datagram to an iovec.
+ * skb_copy_datagram_iter - Copy a datagram to an iovec iterator.
* @skb: buffer to copy
* @offset: offset in the buffer to start copying from
- * @to: io vector to copy to
+ * @to: iovec iterator to copy to
* @len: amount of data to copy from buffer to iovec
- *
- * Note: the iovec is modified during the copy.
*/
-int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset,
- struct iovec *to, int len)
+int skb_copy_datagram_iter(const struct sk_buff *skb, int offset,
+ struct iov_iter *to, int len)
{
int start = skb_headlen(skb);
int i, copy = start - offset;
@@ -330,8 +329,8 @@ int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset,
if (copy > 0) {
if (copy > len)
copy = len;
- if (memcpy_toiovec(to, skb->data + offset, copy))
- goto fault;
+ if (copy_to_iter(skb->data + offset, copy, to) != copy)
+ goto short_copy;
if ((len -= copy) == 0)
return 0;
offset += copy;
@@ -346,18 +345,12 @@ int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset,
end = start + skb_frag_size(frag);
if ((copy = end - offset) > 0) {
- int err;
- u8 *vaddr;
- struct page *page = skb_frag_page(frag);
-
if (copy > len)
copy = len;
- vaddr = kmap(page);
- err = memcpy_toiovec(to, vaddr + frag->page_offset +
- offset - start, copy);
- kunmap(page);
- if (err)
- goto fault;
+ if (copy_page_to_iter(skb_frag_page(frag),
+ frag->page_offset + offset -
+ start, copy, to) != copy)
+ goto short_copy;
if (!(len -= copy))
return 0;
offset += copy;
@@ -374,9 +367,8 @@ int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset,
if ((copy = end - offset) > 0) {
if (copy > len)
copy = len;
- if (skb_copy_datagram_iovec(frag_iter,
- offset - start,
- to, copy))
+ if (skb_copy_datagram_iter(frag_iter, offset - start,
+ to, copy))
goto fault;
if ((len -= copy) == 0)
return 0;
@@ -387,113 +379,33 @@ int skb_copy_datagram_iovec(const struct sk_buff *skb, int offset,
if (!len)
return 0;
+ /* This is not really a user copy fault, but rather someone
+ * gave us a bogus length on the skb. We should probably
+ * print a warning here as it may indicate a kernel bug.
+ */
+
fault:
return -EFAULT;
-}
-EXPORT_SYMBOL(skb_copy_datagram_iovec);
-/**
- * skb_copy_datagram_const_iovec - Copy a datagram to an iovec.
- * @skb: buffer to copy
- * @offset: offset in the buffer to start copying from
- * @to: io vector to copy to
- * @to_offset: offset in the io vector to start copying to
- * @len: amount of data to copy from buffer to iovec
- *
- * Returns 0 or -EFAULT.
- * Note: the iovec is not modified during the copy.
- */
-int skb_copy_datagram_const_iovec(const struct sk_buff *skb, int offset,
- const struct iovec *to, int to_offset,
- int len)
-{
- int start = skb_headlen(skb);
- int i, copy = start - offset;
- struct sk_buff *frag_iter;
+short_copy:
+ if (iov_iter_count(to))
+ goto fault;
- /* Copy header. */
- if (copy > 0) {
- if (copy > len)
- copy = len;
- if (memcpy_toiovecend(to, skb->data + offset, to_offset, copy))
- goto fault;
- if ((len -= copy) == 0)
- return 0;
- offset += copy;
- to_offset += copy;
- }
-
- /* Copy paged appendix. Hmm... why does this look so complicated? */
- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- int end;
- const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
-
- WARN_ON(start > offset + len);
-
- end = start + skb_frag_size(frag);
- if ((copy = end - offset) > 0) {
- int err;
- u8 *vaddr;
- struct page *page = skb_frag_page(frag);
-
- if (copy > len)
- copy = len;
- vaddr = kmap(page);
- err = memcpy_toiovecend(to, vaddr + frag->page_offset +
- offset - start, to_offset, copy);
- kunmap(page);
- if (err)
- goto fault;
- if (!(len -= copy))
- return 0;
- offset += copy;
- to_offset += copy;
- }
- start = end;
- }
-
- skb_walk_frags(skb, frag_iter) {
- int end;
-
- WARN_ON(start > offset + len);
-
- end = start + frag_iter->len;
- if ((copy = end - offset) > 0) {
- if (copy > len)
- copy = len;
- if (skb_copy_datagram_const_iovec(frag_iter,
- offset - start,
- to, to_offset,
- copy))
- goto fault;
- if ((len -= copy) == 0)
- return 0;
- offset += copy;
- to_offset += copy;
- }
- start = end;
- }
- if (!len)
- return 0;
-
-fault:
- return -EFAULT;
+ return 0;
}
-EXPORT_SYMBOL(skb_copy_datagram_const_iovec);
+EXPORT_SYMBOL(skb_copy_datagram_iter);
/**
- * skb_copy_datagram_from_iovec - Copy a datagram from an iovec.
+ * skb_copy_datagram_from_iter - Copy a datagram from an iov_iter.
* @skb: buffer to copy
* @offset: offset in the buffer to start copying to
- * @from: io vector to copy to
- * @from_offset: offset in the io vector to start copying from
+ * @from: the copy source
* @len: amount of data to copy to buffer from iovec
*
* Returns 0 or -EFAULT.
- * Note: the iovec is not modified during the copy.
*/
-int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset,
- const struct iovec *from, int from_offset,
+int skb_copy_datagram_from_iter(struct sk_buff *skb, int offset,
+ struct iov_iter *from,
int len)
{
int start = skb_headlen(skb);
@@ -504,13 +416,11 @@ int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset,
if (copy > 0) {
if (copy > len)
copy = len;
- if (memcpy_fromiovecend(skb->data + offset, from, from_offset,
- copy))
+ if (copy_from_iter(skb->data + offset, copy, from) != copy)
goto fault;
if ((len -= copy) == 0)
return 0;
offset += copy;
- from_offset += copy;
}
/* Copy paged appendix. Hmm... why does this look so complicated? */
@@ -522,24 +432,19 @@ int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset,
end = start + skb_frag_size(frag);
if ((copy = end - offset) > 0) {
- int err;
- u8 *vaddr;
- struct page *page = skb_frag_page(frag);
+ size_t copied;
if (copy > len)
copy = len;
- vaddr = kmap(page);
- err = memcpy_fromiovecend(vaddr + frag->page_offset +
- offset - start,
- from, from_offset, copy);
- kunmap(page);
- if (err)
+ copied = copy_page_from_iter(skb_frag_page(frag),
+ frag->page_offset + offset - start,
+ copy, from);
+ if (copied != copy)
goto fault;
if (!(len -= copy))
return 0;
offset += copy;
- from_offset += copy;
}
start = end;
}
@@ -553,16 +458,13 @@ int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset,
if ((copy = end - offset) > 0) {
if (copy > len)
copy = len;
- if (skb_copy_datagram_from_iovec(frag_iter,
- offset - start,
- from,
- from_offset,
- copy))
+ if (skb_copy_datagram_from_iter(frag_iter,
+ offset - start,
+ from, copy))
goto fault;
if ((len -= copy) == 0)
return 0;
offset += copy;
- from_offset += copy;
}
start = end;
}
@@ -572,101 +474,82 @@ int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset,
fault:
return -EFAULT;
}
-EXPORT_SYMBOL(skb_copy_datagram_from_iovec);
+EXPORT_SYMBOL(skb_copy_datagram_from_iter);
/**
- * zerocopy_sg_from_iovec - Build a zerocopy datagram from an iovec
+ * zerocopy_sg_from_iter - Build a zerocopy datagram from an iov_iter
* @skb: buffer to copy
- * @from: io vector to copy from
- * @offset: offset in the io vector to start copying from
- * @count: amount of vectors to copy to buffer from
+ * @from: the source to copy from
*
* The function will first copy up to headlen, and then pin the userspace
* pages and build frags through them.
*
* Returns 0, -EFAULT or -EMSGSIZE.
- * Note: the iovec is not modified during the copy
*/
-int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *from,
- int offset, size_t count)
+int zerocopy_sg_from_iter(struct sk_buff *skb, struct iov_iter *from)
{
- int len = iov_length(from, count) - offset;
+ int len = iov_iter_count(from);
int copy = min_t(int, skb_headlen(skb), len);
- int size;
- int i = 0;
+ int frag = 0;
/* copy up to skb headlen */
- if (skb_copy_datagram_from_iovec(skb, 0, from, offset, copy))
+ if (skb_copy_datagram_from_iter(skb, 0, from, copy))
return -EFAULT;
- if (len == copy)
- return 0;
-
- offset += copy;
- while (count--) {
- struct page *page[MAX_SKB_FRAGS];
- int num_pages;
- unsigned long base;
+ while (iov_iter_count(from)) {
+ struct page *pages[MAX_SKB_FRAGS];
+ size_t start;
+ ssize_t copied;
unsigned long truesize;
+ int n = 0;
- /* Skip over from offset and copied */
- if (offset >= from->iov_len) {
- offset -= from->iov_len;
- ++from;
- continue;
- }
- len = from->iov_len - offset;
- base = (unsigned long)from->iov_base + offset;
- size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT;
- if (i + size > MAX_SKB_FRAGS)
+ if (frag == MAX_SKB_FRAGS)
return -EMSGSIZE;
- num_pages = get_user_pages_fast(base, size, 0, &page[i]);
- if (num_pages != size) {
- release_pages(&page[i], num_pages, 0);
+
+ copied = iov_iter_get_pages(from, pages, ~0U,
+ MAX_SKB_FRAGS - frag, &start);
+ if (copied < 0)
return -EFAULT;
- }
- truesize = size * PAGE_SIZE;
- skb->data_len += len;
- skb->len += len;
+
+ iov_iter_advance(from, copied);
+
+ truesize = PAGE_ALIGN(copied + start);
+ skb->data_len += copied;
+ skb->len += copied;
skb->truesize += truesize;
atomic_add(truesize, &skb->sk->sk_wmem_alloc);
- while (len) {
- int off = base & ~PAGE_MASK;
- int size = min_t(int, len, PAGE_SIZE - off);
- skb_fill_page_desc(skb, i, page[i], off, size);
- base += size;
- len -= size;
- i++;
+ while (copied) {
+ int size = min_t(int, copied, PAGE_SIZE - start);
+ skb_fill_page_desc(skb, frag++, pages[n], start, size);
+ start = 0;
+ copied -= size;
+ n++;
}
- offset = 0;
- ++from;
}
return 0;
}
-EXPORT_SYMBOL(zerocopy_sg_from_iovec);
+EXPORT_SYMBOL(zerocopy_sg_from_iter);
static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset,
- u8 __user *to, int len,
+ struct iov_iter *to, int len,
__wsum *csump)
{
int start = skb_headlen(skb);
int i, copy = start - offset;
struct sk_buff *frag_iter;
int pos = 0;
+ int n;
/* Copy header. */
if (copy > 0) {
- int err = 0;
if (copy > len)
copy = len;
- *csump = csum_and_copy_to_user(skb->data + offset, to, copy,
- *csump, &err);
- if (err)
+ n = csum_and_copy_to_iter(skb->data + offset, copy, csump, to);
+ if (n != copy)
goto fault;
if ((len -= copy) == 0)
return 0;
offset += copy;
- to += copy;
pos = copy;
}
@@ -678,26 +561,22 @@ static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset,
end = start + skb_frag_size(frag);
if ((copy = end - offset) > 0) {
- __wsum csum2;
- int err = 0;
- u8 *vaddr;
+ __wsum csum2 = 0;
struct page *page = skb_frag_page(frag);
+ u8 *vaddr = kmap(page);
if (copy > len)
copy = len;
- vaddr = kmap(page);
- csum2 = csum_and_copy_to_user(vaddr +
- frag->page_offset +
- offset - start,
- to, copy, 0, &err);
+ n = csum_and_copy_to_iter(vaddr + frag->page_offset +
+ offset - start, copy,
+ &csum2, to);
kunmap(page);
- if (err)
+ if (n != copy)
goto fault;
*csump = csum_block_add(*csump, csum2, pos);
if (!(len -= copy))
return 0;
offset += copy;
- to += copy;
pos += copy;
}
start = end;
@@ -722,7 +601,6 @@ static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset,
if ((len -= copy) == 0)
return 0;
offset += copy;
- to += copy;
pos += copy;
}
start = end;
@@ -775,20 +653,19 @@ __sum16 __skb_checksum_complete(struct sk_buff *skb)
EXPORT_SYMBOL(__skb_checksum_complete);
/**
- * skb_copy_and_csum_datagram_iovec - Copy and checksum skb to user iovec.
+ * skb_copy_and_csum_datagram_msg - Copy and checksum skb to user iovec.
* @skb: skbuff
* @hlen: hardware length
- * @iov: io vector
+ * @msg: destination
*
* Caller _must_ check that skb will fit to this iovec.
*
* Returns: 0 - success.
* -EINVAL - checksum failure.
- * -EFAULT - fault during copy. Beware, in this case iovec
- * can be modified!
+ * -EFAULT - fault during copy.
*/
-int skb_copy_and_csum_datagram_iovec(struct sk_buff *skb,
- int hlen, struct iovec *iov)
+int skb_copy_and_csum_datagram_msg(struct sk_buff *skb,
+ int hlen, struct msghdr *msg)
{
__wsum csum;
int chunk = skb->len - hlen;
@@ -796,28 +673,20 @@ int skb_copy_and_csum_datagram_iovec(struct sk_buff *skb,
if (!chunk)
return 0;
- /* Skip filled elements.
- * Pretty silly, look at memcpy_toiovec, though 8)
- */
- while (!iov->iov_len)
- iov++;
-
- if (iov->iov_len < chunk) {
+ if (iov_iter_count(&msg->msg_iter) < chunk) {
if (__skb_checksum_complete(skb))
goto csum_error;
- if (skb_copy_datagram_iovec(skb, hlen, iov, chunk))
+ if (skb_copy_datagram_msg(skb, hlen, msg, chunk))
goto fault;
} else {
csum = csum_partial(skb->data, hlen, skb->csum);
- if (skb_copy_and_csum_datagram(skb, hlen, iov->iov_base,
+ if (skb_copy_and_csum_datagram(skb, hlen, &msg->msg_iter,
chunk, &csum))
goto fault;
if (csum_fold(csum))
goto csum_error;
if (unlikely(skb->ip_summed == CHECKSUM_COMPLETE))
netdev_rx_csum_fault(skb->dev);
- iov->iov_len -= chunk;
- iov->iov_base += chunk;
}
return 0;
csum_error:
@@ -825,7 +694,7 @@ csum_error:
fault:
return -EFAULT;
}
-EXPORT_SYMBOL(skb_copy_and_csum_datagram_iovec);
+EXPORT_SYMBOL(skb_copy_and_csum_datagram_msg);
/**
* datagram_poll - generic datagram poll
diff --git a/net/core/dev.c b/net/core/dev.c
index 945bbd001359..f411c28d0a66 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -118,6 +118,7 @@
#include <linux/if_vlan.h>
#include <linux/ip.h>
#include <net/ip.h>
+#include <net/mpls.h>
#include <linux/ipv6.h>
#include <linux/in.h>
#include <linux/jhash.h>
@@ -133,6 +134,7 @@
#include <linux/vmalloc.h>
#include <linux/if_macvlan.h>
#include <linux/errqueue.h>
+#include <linux/hrtimer.h>
#include "net-sysfs.h"
@@ -1435,22 +1437,17 @@ EXPORT_SYMBOL(dev_close);
*/
void dev_disable_lro(struct net_device *dev)
{
- /*
- * If we're trying to disable lro on a vlan device
- * use the underlying physical device instead
- */
- if (is_vlan_dev(dev))
- dev = vlan_dev_real_dev(dev);
-
- /* the same for macvlan devices */
- if (netif_is_macvlan(dev))
- dev = macvlan_dev_real_dev(dev);
+ struct net_device *lower_dev;
+ struct list_head *iter;
dev->wanted_features &= ~NETIF_F_LRO;
netdev_update_features(dev);
if (unlikely(dev->features & NETIF_F_LRO))
netdev_WARN(dev, "failed to disable LRO!\n");
+
+ netdev_for_each_lower_dev(dev, lower_dev, iter)
+ dev_disable_lro(lower_dev);
}
EXPORT_SYMBOL(dev_disable_lro);
@@ -2530,7 +2527,7 @@ static netdev_features_t net_mpls_features(struct sk_buff *skb,
netdev_features_t features,
__be16 type)
{
- if (type == htons(ETH_P_MPLS_UC) || type == htons(ETH_P_MPLS_MC))
+ if (eth_p_mpls(type))
features &= skb->dev->mpls_features;
return features;
@@ -2647,12 +2644,8 @@ static struct sk_buff *validate_xmit_vlan(struct sk_buff *skb,
netdev_features_t features)
{
if (vlan_tx_tag_present(skb) &&
- !vlan_hw_offload_capable(features, skb->vlan_proto)) {
- skb = __vlan_put_tag(skb, skb->vlan_proto,
- vlan_tx_tag_get(skb));
- if (skb)
- skb->vlan_tci = 0;
- }
+ !vlan_hw_offload_capable(features, skb->vlan_proto))
+ skb = __vlan_hwaccel_push_inside(skb);
return skb;
}
@@ -3304,7 +3297,7 @@ static int enqueue_to_backlog(struct sk_buff *skb, int cpu,
rps_lock(sd);
qlen = skb_queue_len(&sd->input_pkt_queue);
if (qlen <= netdev_max_backlog && !skb_flow_limit(skb, qlen)) {
- if (skb_queue_len(&sd->input_pkt_queue)) {
+ if (qlen) {
enqueue:
__skb_queue_tail(&sd->input_pkt_queue, skb);
input_queue_tail_incr_save(sd, qtail);
@@ -4179,7 +4172,7 @@ struct sk_buff *napi_get_frags(struct napi_struct *napi)
struct sk_buff *skb = napi->skb;
if (!skb) {
- skb = netdev_alloc_skb_ip_align(napi->dev, GRO_MAX_HEAD);
+ skb = napi_alloc_skb(napi, GRO_MAX_HEAD);
napi->skb = skb;
}
return skb;
@@ -4316,20 +4309,28 @@ static void net_rps_action_and_irq_enable(struct softnet_data *sd)
local_irq_enable();
}
+static bool sd_has_rps_ipi_waiting(struct softnet_data *sd)
+{
+#ifdef CONFIG_RPS
+ return sd->rps_ipi_list != NULL;
+#else
+ return false;
+#endif
+}
+
static int process_backlog(struct napi_struct *napi, int quota)
{
int work = 0;
struct softnet_data *sd = container_of(napi, struct softnet_data, backlog);
-#ifdef CONFIG_RPS
/* Check if we have pending ipi, its better to send them now,
* not waiting net_rx_action() end.
*/
- if (sd->rps_ipi_list) {
+ if (sd_has_rps_ipi_waiting(sd)) {
local_irq_disable();
net_rps_action_and_irq_enable(sd);
}
-#endif
+
napi->weight = weight_p;
local_irq_disable();
while (1) {
@@ -4356,7 +4357,6 @@ static int process_backlog(struct napi_struct *napi, int quota)
* We can use a plain write instead of clear_bit(),
* and we dont need an smp_mb() memory barrier.
*/
- list_del(&napi->poll_list);
napi->state = 0;
rps_unlock(sd);
@@ -4376,7 +4376,8 @@ static int process_backlog(struct napi_struct *napi, int quota)
* __napi_schedule - schedule for receive
* @n: entry to schedule
*
- * The entry's receive function will be scheduled to run
+ * The entry's receive function will be scheduled to run.
+ * Consider using __napi_schedule_irqoff() if hard irqs are masked.
*/
void __napi_schedule(struct napi_struct *n)
{
@@ -4388,18 +4389,29 @@ void __napi_schedule(struct napi_struct *n)
}
EXPORT_SYMBOL(__napi_schedule);
+/**
+ * __napi_schedule_irqoff - schedule for receive
+ * @n: entry to schedule
+ *
+ * Variant of __napi_schedule() assuming hard irqs are masked
+ */
+void __napi_schedule_irqoff(struct napi_struct *n)
+{
+ ____napi_schedule(this_cpu_ptr(&softnet_data), n);
+}
+EXPORT_SYMBOL(__napi_schedule_irqoff);
+
void __napi_complete(struct napi_struct *n)
{
BUG_ON(!test_bit(NAPI_STATE_SCHED, &n->state));
- BUG_ON(n->gro_list);
- list_del(&n->poll_list);
+ list_del_init(&n->poll_list);
smp_mb__before_atomic();
clear_bit(NAPI_STATE_SCHED, &n->state);
}
EXPORT_SYMBOL(__napi_complete);
-void napi_complete(struct napi_struct *n)
+void napi_complete_done(struct napi_struct *n, int work_done)
{
unsigned long flags;
@@ -4410,12 +4422,28 @@ void napi_complete(struct napi_struct *n)
if (unlikely(test_bit(NAPI_STATE_NPSVC, &n->state)))
return;
- napi_gro_flush(n, false);
- local_irq_save(flags);
- __napi_complete(n);
- local_irq_restore(flags);
+ if (n->gro_list) {
+ unsigned long timeout = 0;
+
+ if (work_done)
+ timeout = n->dev->gro_flush_timeout;
+
+ if (timeout)
+ hrtimer_start(&n->timer, ns_to_ktime(timeout),
+ HRTIMER_MODE_REL_PINNED);
+ else
+ napi_gro_flush(n, false);
+ }
+ if (likely(list_empty(&n->poll_list))) {
+ WARN_ON_ONCE(!test_and_clear_bit(NAPI_STATE_SCHED, &n->state));
+ } else {
+ /* If n->poll_list is not empty, we need to mask irqs */
+ local_irq_save(flags);
+ __napi_complete(n);
+ local_irq_restore(flags);
+ }
}
-EXPORT_SYMBOL(napi_complete);
+EXPORT_SYMBOL(napi_complete_done);
/* must be called under rcu_read_lock(), as we dont take a reference */
struct napi_struct *napi_by_id(unsigned int napi_id)
@@ -4469,10 +4497,23 @@ void napi_hash_del(struct napi_struct *napi)
}
EXPORT_SYMBOL_GPL(napi_hash_del);
+static enum hrtimer_restart napi_watchdog(struct hrtimer *timer)
+{
+ struct napi_struct *napi;
+
+ napi = container_of(timer, struct napi_struct, timer);
+ if (napi->gro_list)
+ napi_schedule(napi);
+
+ return HRTIMER_NORESTART;
+}
+
void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
int (*poll)(struct napi_struct *, int), int weight)
{
INIT_LIST_HEAD(&napi->poll_list);
+ hrtimer_init(&napi->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
+ napi->timer.function = napi_watchdog;
napi->gro_count = 0;
napi->gro_list = NULL;
napi->skb = NULL;
@@ -4491,6 +4532,20 @@ void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
}
EXPORT_SYMBOL(netif_napi_add);
+void napi_disable(struct napi_struct *n)
+{
+ might_sleep();
+ set_bit(NAPI_STATE_DISABLE, &n->state);
+
+ while (test_and_set_bit(NAPI_STATE_SCHED, &n->state))
+ msleep(1);
+
+ hrtimer_cancel(&n->timer);
+
+ clear_bit(NAPI_STATE_DISABLE, &n->state);
+}
+EXPORT_SYMBOL(napi_disable);
+
void netif_napi_del(struct napi_struct *napi)
{
list_del_init(&napi->dev_list);
@@ -4507,29 +4562,28 @@ static void net_rx_action(struct softirq_action *h)
struct softnet_data *sd = this_cpu_ptr(&softnet_data);
unsigned long time_limit = jiffies + 2;
int budget = netdev_budget;
+ LIST_HEAD(list);
+ LIST_HEAD(repoll);
void *have;
local_irq_disable();
+ list_splice_init(&sd->poll_list, &list);
+ local_irq_enable();
- while (!list_empty(&sd->poll_list)) {
+ while (!list_empty(&list)) {
struct napi_struct *n;
int work, weight;
- /* If softirq window is exhuasted then punt.
+ /* If softirq window is exhausted then punt.
* Allow this to run for 2 jiffies since which will allow
* an average latency of 1.5/HZ.
*/
if (unlikely(budget <= 0 || time_after_eq(jiffies, time_limit)))
goto softnet_break;
- local_irq_enable();
- /* Even though interrupts have been re-enabled, this
- * access is safe because interrupts can only add new
- * entries to the tail of this list, and only ->poll()
- * calls can remove this head entry from the list.
- */
- n = list_first_entry(&sd->poll_list, struct napi_struct, poll_list);
+ n = list_first_entry(&list, struct napi_struct, poll_list);
+ list_del_init(&n->poll_list);
have = netpoll_poll_lock(n);
@@ -4551,8 +4605,6 @@ static void net_rx_action(struct softirq_action *h)
budget -= work;
- local_irq_disable();
-
/* Drivers must not modify the NAPI state if they
* consume the entire weight. In such cases this code
* still "owns" the NAPI instance and therefore can
@@ -4560,32 +4612,40 @@ static void net_rx_action(struct softirq_action *h)
*/
if (unlikely(work == weight)) {
if (unlikely(napi_disable_pending(n))) {
- local_irq_enable();
napi_complete(n);
- local_irq_disable();
} else {
if (n->gro_list) {
/* flush too old packets
* If HZ < 1000, flush all packets.
*/
- local_irq_enable();
napi_gro_flush(n, HZ >= 1000);
- local_irq_disable();
}
- list_move_tail(&n->poll_list, &sd->poll_list);
+ list_add_tail(&n->poll_list, &repoll);
}
}
netpoll_poll_unlock(have);
}
+
+ if (!sd_has_rps_ipi_waiting(sd) &&
+ list_empty(&list) &&
+ list_empty(&repoll))
+ return;
out:
+ local_irq_disable();
+
+ list_splice_tail_init(&sd->poll_list, &list);
+ list_splice_tail(&repoll, &list);
+ list_splice(&list, &sd->poll_list);
+ if (!list_empty(&sd->poll_list))
+ __raise_softirq_irqoff(NET_RX_SOFTIRQ);
+
net_rps_action_and_irq_enable(sd);
return;
softnet_break:
sd->time_squeeze++;
- __raise_softirq_irqoff(NET_RX_SOFTIRQ);
goto out;
}
@@ -5786,7 +5846,7 @@ EXPORT_SYMBOL(dev_change_carrier);
* Get device physical port ID
*/
int dev_get_phys_port_id(struct net_device *dev,
- struct netdev_phys_port_id *ppid)
+ struct netdev_phys_item_id *ppid)
{
const struct net_device_ops *ops = dev->netdev_ops;
@@ -5865,6 +5925,8 @@ static void rollback_registered_many(struct list_head *head)
synchronize_net();
list_for_each_entry(dev, head, unreg_list) {
+ struct sk_buff *skb = NULL;
+
/* Shutdown queueing discipline. */
dev_shutdown(dev);
@@ -5874,6 +5936,11 @@ static void rollback_registered_many(struct list_head *head)
*/
call_netdevice_notifiers(NETDEV_UNREGISTER, dev);
+ if (!dev->rtnl_link_ops ||
+ dev->rtnl_link_state == RTNL_LINK_INITIALIZED)
+ skb = rtmsg_ifinfo_build_skb(RTM_DELLINK, dev, ~0U,
+ GFP_KERNEL);
+
/*
* Flush the unicast and multicast chains
*/
@@ -5883,9 +5950,8 @@ static void rollback_registered_many(struct list_head *head)
if (dev->netdev_ops->ndo_uninit)
dev->netdev_ops->ndo_uninit(dev);
- if (!dev->rtnl_link_ops ||
- dev->rtnl_link_state == RTNL_LINK_INITIALIZED)
- rtmsg_ifinfo(RTM_DELLINK, dev, ~0U, GFP_KERNEL);
+ if (skb)
+ rtmsg_ifinfo_send(skb, dev, GFP_KERNEL);
/* Notifier chain MUST detach us all upper devices. */
WARN_ON(netdev_has_any_upper_dev(dev));
@@ -7200,11 +7266,10 @@ static void __net_exit rtnl_lock_unregistering(struct list_head *net_list)
*/
struct net *net;
bool unregistering;
- DEFINE_WAIT(wait);
+ DEFINE_WAIT_FUNC(wait, woken_wake_function);
+ add_wait_queue(&netdev_unregistering_wq, &wait);
for (;;) {
- prepare_to_wait(&netdev_unregistering_wq, &wait,
- TASK_UNINTERRUPTIBLE);
unregistering = false;
rtnl_lock();
list_for_each_entry(net, net_list, exit_list) {
@@ -7216,9 +7281,10 @@ static void __net_exit rtnl_lock_unregistering(struct list_head *net_list)
if (!unregistering)
break;
__rtnl_unlock();
- schedule();
+
+ wait_woken(&wait, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}
- finish_wait(&netdev_unregistering_wq, &wait);
+ remove_wait_queue(&netdev_unregistering_wq, &wait);
}
static void __net_exit default_device_exit_batch(struct list_head *net_list)
diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
index b6b230600b97..c0548d268e1a 100644
--- a/net/core/dev_addr_lists.c
+++ b/net/core/dev_addr_lists.c
@@ -278,8 +278,8 @@ int __hw_addr_sync_dev(struct netdev_hw_addr_list *list,
EXPORT_SYMBOL(__hw_addr_sync_dev);
/**
- * __hw_addr_unsync_dev - Remove synchonized addresses from device
- * @list: address list to remove syncronized addresses from
+ * __hw_addr_unsync_dev - Remove synchronized addresses from device
+ * @list: address list to remove synchronized addresses from
* @dev: device to sync
* @unsync: function to call if address should be removed
*
diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c
index 72e899a3efda..b94b1d293506 100644
--- a/net/core/dev_ioctl.c
+++ b/net/core/dev_ioctl.c
@@ -142,10 +142,12 @@ static int dev_ifsioc_locked(struct net *net, struct ifreq *ifr, unsigned int cm
case SIOCGIFHWADDR:
if (!dev->addr_len)
- memset(ifr->ifr_hwaddr.sa_data, 0, sizeof ifr->ifr_hwaddr.sa_data);
+ 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));
+ min(sizeof(ifr->ifr_hwaddr.sa_data),
+ (size_t)dev->addr_len));
ifr->ifr_hwaddr.sa_family = dev->type;
return 0;
@@ -265,7 +267,8 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
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));
+ min(sizeof(ifr->ifr_hwaddr.sa_data),
+ (size_t)dev->addr_len));
call_netdevice_notifiers(NETDEV_CHANGEADDR, dev);
return 0;
diff --git a/net/core/dst.c b/net/core/dst.c
index a028409ee438..e956ce6d1378 100644
--- a/net/core/dst.c
+++ b/net/core/dst.c
@@ -327,30 +327,6 @@ void __dst_destroy_metrics_generic(struct dst_entry *dst, unsigned long old)
}
EXPORT_SYMBOL(__dst_destroy_metrics_generic);
-/**
- * __skb_dst_set_noref - sets skb dst, without a reference
- * @skb: buffer
- * @dst: dst entry
- * @force: if force is set, use noref version even for DST_NOCACHE entries
- *
- * Sets skb dst, assuming a reference was not taken on dst
- * skb_dst_drop() should not dst_release() this dst
- */
-void __skb_dst_set_noref(struct sk_buff *skb, struct dst_entry *dst, bool force)
-{
- WARN_ON(!rcu_read_lock_held() && !rcu_read_lock_bh_held());
- /* If dst not in cache, we must take a reference, because
- * dst_release() will destroy dst as soon as its refcount becomes zero
- */
- if (unlikely((dst->flags & DST_NOCACHE) && !force)) {
- dst_hold(dst);
- skb_dst_set(skb, dst);
- } else {
- skb->_skb_refdst = (unsigned long)dst | SKB_DST_NOREF;
- }
-}
-EXPORT_SYMBOL(__skb_dst_set_noref);
-
/* Dirty hack. We did it in 2.2 (in __dst_free),
* we have _very_ good reasons not to repeat
* this mistake in 2.3, but we have no choice
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 06dfb293e5aa..550892cd6b3f 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -25,6 +25,7 @@
#include <linux/slab.h>
#include <linux/rtnetlink.h>
#include <linux/sched.h>
+#include <linux/net.h>
/*
* Some useful ethtool_ops methods that're device independent.
@@ -84,7 +85,6 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN]
[NETIF_F_GSO_IPIP_BIT] = "tx-ipip-segmentation",
[NETIF_F_GSO_SIT_BIT] = "tx-sit-segmentation",
[NETIF_F_GSO_UDP_TUNNEL_BIT] = "tx-udp_tnl-segmentation",
- [NETIF_F_GSO_MPLS_BIT] = "tx-mpls-segmentation",
[NETIF_F_FCOE_CRC_BIT] = "tx-checksum-fcoe-crc",
[NETIF_F_SCTP_CSUM_BIT] = "tx-checksum-sctp",
@@ -100,6 +100,12 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN]
[NETIF_F_BUSY_POLL_BIT] = "busy-poll",
};
+static const char
+rss_hash_func_strings[ETH_RSS_HASH_FUNCS_COUNT][ETH_GSTRING_LEN] = {
+ [ETH_RSS_HASH_TOP_BIT] = "toeplitz",
+ [ETH_RSS_HASH_XOR_BIT] = "xor",
+};
+
static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
{
struct ethtool_gfeatures cmd = {
@@ -185,6 +191,9 @@ static int __ethtool_get_sset_count(struct net_device *dev, int sset)
if (sset == ETH_SS_FEATURES)
return ARRAY_SIZE(netdev_features_strings);
+ if (sset == ETH_SS_RSS_HASH_FUNCS)
+ return ARRAY_SIZE(rss_hash_func_strings);
+
if (ops->get_sset_count && ops->get_strings)
return ops->get_sset_count(dev, sset);
else
@@ -199,6 +208,9 @@ static void __ethtool_get_strings(struct net_device *dev,
if (stringset == ETH_SS_FEATURES)
memcpy(data, netdev_features_strings,
sizeof(netdev_features_strings));
+ else if (stringset == ETH_SS_RSS_HASH_FUNCS)
+ memcpy(data, rss_hash_func_strings,
+ sizeof(rss_hash_func_strings));
else
/* ops->get_strings is valid because checked earlier */
ops->get_strings(dev, stringset, data);
@@ -574,6 +586,16 @@ static int ethtool_copy_validate_indir(u32 *indir, void __user *useraddr,
return 0;
}
+u8 netdev_rss_key[NETDEV_RSS_KEY_LEN];
+
+void netdev_rss_key_fill(void *buffer, size_t len)
+{
+ BUG_ON(len > sizeof(netdev_rss_key));
+ net_get_random_once(netdev_rss_key, sizeof(netdev_rss_key));
+ memcpy(buffer, netdev_rss_key, len);
+}
+EXPORT_SYMBOL(netdev_rss_key_fill);
+
static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
void __user *useraddr)
{
@@ -608,7 +630,7 @@ static noinline_for_stack int ethtool_get_rxfh_indir(struct net_device *dev,
if (!indir)
return -ENOMEM;
- ret = dev->ethtool_ops->get_rxfh(dev, indir, NULL);
+ ret = dev->ethtool_ops->get_rxfh(dev, indir, NULL, NULL);
if (ret)
goto out;
@@ -669,7 +691,7 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
goto out;
}
- ret = ops->set_rxfh(dev, indir, NULL);
+ ret = ops->set_rxfh(dev, indir, NULL, ETH_RSS_HASH_NO_CHANGE);
out:
kfree(indir);
@@ -687,12 +709,11 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev,
u32 total_size;
u32 indir_bytes;
u32 *indir = NULL;
+ u8 dev_hfunc = 0;
u8 *hkey = NULL;
u8 *rss_config;
- if (!(dev->ethtool_ops->get_rxfh_indir_size ||
- dev->ethtool_ops->get_rxfh_key_size) ||
- !dev->ethtool_ops->get_rxfh)
+ if (!ops->get_rxfh)
return -EOPNOTSUPP;
if (ops->get_rxfh_indir_size)
@@ -700,16 +721,14 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev,
if (ops->get_rxfh_key_size)
dev_key_size = ops->get_rxfh_key_size(dev);
- if ((dev_key_size + dev_indir_size) == 0)
- return -EOPNOTSUPP;
-
if (copy_from_user(&rxfh, useraddr, sizeof(rxfh)))
return -EFAULT;
user_indir_size = rxfh.indir_size;
user_key_size = rxfh.key_size;
/* Check that reserved fields are 0 for now */
- if (rxfh.rss_context || rxfh.rsvd[0] || rxfh.rsvd[1])
+ if (rxfh.rss_context || rxfh.rsvd8[0] || rxfh.rsvd8[1] ||
+ rxfh.rsvd8[2] || rxfh.rsvd32)
return -EINVAL;
rxfh.indir_size = dev_indir_size;
@@ -717,13 +736,6 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev,
if (copy_to_user(useraddr, &rxfh, sizeof(rxfh)))
return -EFAULT;
- /* If the user buffer size is 0, this is just a query for the
- * device table size and key size. Otherwise, if the User size is
- * not equal to device table size or key size it's an error.
- */
- if (!user_indir_size && !user_key_size)
- return 0;
-
if ((user_indir_size && (user_indir_size != dev_indir_size)) ||
(user_key_size && (user_key_size != dev_key_size)))
return -EINVAL;
@@ -740,14 +752,19 @@ static noinline_for_stack int ethtool_get_rxfh(struct net_device *dev,
if (user_key_size)
hkey = rss_config + indir_bytes;
- ret = dev->ethtool_ops->get_rxfh(dev, indir, hkey);
- if (!ret) {
- if (copy_to_user(useraddr +
- offsetof(struct ethtool_rxfh, rss_config[0]),
- rss_config, total_size))
- ret = -EFAULT;
- }
+ ret = dev->ethtool_ops->get_rxfh(dev, indir, hkey, &dev_hfunc);
+ if (ret)
+ goto out;
+ if (copy_to_user(useraddr + offsetof(struct ethtool_rxfh, hfunc),
+ &dev_hfunc, sizeof(rxfh.hfunc))) {
+ ret = -EFAULT;
+ } else if (copy_to_user(useraddr +
+ offsetof(struct ethtool_rxfh, rss_config[0]),
+ rss_config, total_size)) {
+ ret = -EFAULT;
+ }
+out:
kfree(rss_config);
return ret;
@@ -766,33 +783,31 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
u8 *rss_config;
u32 rss_cfg_offset = offsetof(struct ethtool_rxfh, rss_config[0]);
- if (!(ops->get_rxfh_indir_size || ops->get_rxfh_key_size) ||
- !ops->get_rxnfc || !ops->set_rxfh)
+ if (!ops->get_rxnfc || !ops->set_rxfh)
return -EOPNOTSUPP;
if (ops->get_rxfh_indir_size)
dev_indir_size = ops->get_rxfh_indir_size(dev);
if (ops->get_rxfh_key_size)
dev_key_size = dev->ethtool_ops->get_rxfh_key_size(dev);
- if ((dev_key_size + dev_indir_size) == 0)
- return -EOPNOTSUPP;
if (copy_from_user(&rxfh, useraddr, sizeof(rxfh)))
return -EFAULT;
/* Check that reserved fields are 0 for now */
- if (rxfh.rss_context || rxfh.rsvd[0] || rxfh.rsvd[1])
+ if (rxfh.rss_context || rxfh.rsvd8[0] || rxfh.rsvd8[1] ||
+ rxfh.rsvd8[2] || rxfh.rsvd32)
return -EINVAL;
- /* If either indir or hash key is valid, proceed further.
- * It is not valid to request that both be unchanged.
+ /* If either indir, hash key or function is valid, proceed further.
+ * Must request at least one change: indir size, hash key or function.
*/
if ((rxfh.indir_size &&
rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE &&
rxfh.indir_size != dev_indir_size) ||
(rxfh.key_size && (rxfh.key_size != dev_key_size)) ||
(rxfh.indir_size == ETH_RXFH_INDIR_NO_CHANGE &&
- rxfh.key_size == 0))
+ rxfh.key_size == 0 && rxfh.hfunc == ETH_RSS_HASH_NO_CHANGE))
return -EINVAL;
if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE)
@@ -835,7 +850,7 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
}
}
- ret = ops->set_rxfh(dev, indir, hkey);
+ ret = ops->set_rxfh(dev, indir, hkey, rxfh.hfunc);
out:
kfree(rss_config);
diff --git a/net/core/filter.c b/net/core/filter.c
index 647b12265e18..ec9baea10c16 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -44,6 +44,7 @@
#include <linux/ratelimit.h>
#include <linux/seccomp.h>
#include <linux/if_vlan.h>
+#include <linux/bpf.h>
/**
* sk_filter - run a packet through a socket filter
@@ -813,8 +814,12 @@ static void bpf_release_orig_filter(struct bpf_prog *fp)
static void __bpf_prog_release(struct bpf_prog *prog)
{
- bpf_release_orig_filter(prog);
- bpf_prog_free(prog);
+ if (prog->aux->prog_type == BPF_PROG_TYPE_SOCKET_FILTER) {
+ bpf_prog_put(prog);
+ } else {
+ bpf_release_orig_filter(prog);
+ bpf_prog_free(prog);
+ }
}
static void __sk_filter_release(struct sk_filter *fp)
@@ -1088,6 +1093,94 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
}
EXPORT_SYMBOL_GPL(sk_attach_filter);
+#ifdef CONFIG_BPF_SYSCALL
+int sk_attach_bpf(u32 ufd, struct sock *sk)
+{
+ struct sk_filter *fp, *old_fp;
+ struct bpf_prog *prog;
+
+ if (sock_flag(sk, SOCK_FILTER_LOCKED))
+ return -EPERM;
+
+ prog = bpf_prog_get(ufd);
+ if (IS_ERR(prog))
+ return PTR_ERR(prog);
+
+ if (prog->aux->prog_type != BPF_PROG_TYPE_SOCKET_FILTER) {
+ /* valid fd, but invalid program type */
+ bpf_prog_put(prog);
+ return -EINVAL;
+ }
+
+ fp = kmalloc(sizeof(*fp), GFP_KERNEL);
+ if (!fp) {
+ bpf_prog_put(prog);
+ return -ENOMEM;
+ }
+ fp->prog = prog;
+
+ atomic_set(&fp->refcnt, 0);
+
+ if (!sk_filter_charge(sk, fp)) {
+ __sk_filter_release(fp);
+ return -ENOMEM;
+ }
+
+ old_fp = rcu_dereference_protected(sk->sk_filter,
+ sock_owned_by_user(sk));
+ rcu_assign_pointer(sk->sk_filter, fp);
+
+ if (old_fp)
+ sk_filter_uncharge(sk, old_fp);
+
+ return 0;
+}
+
+/* allow socket filters to call
+ * bpf_map_lookup_elem(), bpf_map_update_elem(), bpf_map_delete_elem()
+ */
+static const struct bpf_func_proto *sock_filter_func_proto(enum bpf_func_id func_id)
+{
+ switch (func_id) {
+ case BPF_FUNC_map_lookup_elem:
+ return &bpf_map_lookup_elem_proto;
+ case BPF_FUNC_map_update_elem:
+ return &bpf_map_update_elem_proto;
+ case BPF_FUNC_map_delete_elem:
+ return &bpf_map_delete_elem_proto;
+ default:
+ return NULL;
+ }
+}
+
+static bool sock_filter_is_valid_access(int off, int size, enum bpf_access_type type)
+{
+ /* skb fields cannot be accessed yet */
+ return false;
+}
+
+static struct bpf_verifier_ops sock_filter_ops = {
+ .get_func_proto = sock_filter_func_proto,
+ .is_valid_access = sock_filter_is_valid_access,
+};
+
+static struct bpf_prog_type_list tl = {
+ .ops = &sock_filter_ops,
+ .type = BPF_PROG_TYPE_SOCKET_FILTER,
+};
+
+static int __init register_sock_filter_ops(void)
+{
+ bpf_register_prog_type(&tl);
+ return 0;
+}
+late_initcall(register_sock_filter_ops);
+#else
+int sk_attach_bpf(u32 ufd, struct sock *sk)
+{
+ return -EOPNOTSUPP;
+}
+#endif
int sk_detach_filter(struct sock *sk)
{
int ret = -ENOENT;
diff --git a/net/core/iovec.c b/net/core/iovec.c
index e1ec45ab1e63..dcbe98b3726a 100644
--- a/net/core/iovec.c
+++ b/net/core/iovec.c
@@ -28,53 +28,6 @@
#include <net/sock.h>
/*
- * Verify iovec. The caller must ensure that the iovec is big enough
- * to hold the message iovec.
- *
- * Save time not doing access_ok. copy_*_user will make this work
- * in any case.
- */
-
-int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *address, int mode)
-{
- int size, ct, err;
-
- if (m->msg_name && m->msg_namelen) {
- if (mode == VERIFY_READ) {
- void __user *namep;
- namep = (void __user __force *) m->msg_name;
- err = move_addr_to_kernel(namep, m->msg_namelen,
- address);
- if (err < 0)
- return err;
- }
- m->msg_name = address;
- } else {
- m->msg_name = NULL;
- m->msg_namelen = 0;
- }
-
- size = m->msg_iovlen * sizeof(struct iovec);
- if (copy_from_user(iov, (void __user __force *) m->msg_iov, size))
- return -EFAULT;
-
- m->msg_iov = iov;
- err = 0;
-
- for (ct = 0; ct < m->msg_iovlen; ct++) {
- size_t len = iov[ct].iov_len;
-
- if (len > INT_MAX - err) {
- len = INT_MAX - err;
- iov[ct].iov_len = len;
- }
- err += len;
- }
-
- return err;
-}
-
-/*
* And now for the all-in-one: copy and checksum from a user iovec
* directly to a datagram
* Calls to csum_partial but the last must be in 32 bit chunks
diff --git a/net/core/link_watch.c b/net/core/link_watch.c
index bd0767e6b2b3..49a9e3e06c08 100644
--- a/net/core/link_watch.c
+++ b/net/core/link_watch.c
@@ -21,7 +21,7 @@
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/bitops.h>
-#include <asm/types.h>
+#include <linux/types.h>
enum lw_bits {
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index ef31fef25e5a..8e38f17288d3 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -56,7 +56,6 @@ static void __neigh_notify(struct neighbour *n, int type, int flags);
static void neigh_update_notify(struct neighbour *neigh);
static int pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev);
-static struct neigh_table *neigh_tables;
#ifdef CONFIG_PROC_FS
static const struct file_operations neigh_stat_seq_fops;
#endif
@@ -87,13 +86,8 @@ static const struct file_operations neigh_stat_seq_fops;
the most complicated procedure, which we allow is dev->hard_header.
It is supposed, that dev->hard_header is simplistic and does
not make callbacks to neighbour tables.
-
- The last lock is neigh_tbl_lock. It is pure SMP lock, protecting
- list of neighbour tables. This list is used only in process context,
*/
-static DEFINE_RWLOCK(neigh_tbl_lock);
-
static int neigh_blackhole(struct neighbour *neigh, struct sk_buff *skb)
{
kfree_skb(skb);
@@ -773,7 +767,7 @@ static void neigh_periodic_work(struct work_struct *work)
if (time_after(jiffies, tbl->last_rand + 300 * HZ)) {
struct neigh_parms *p;
tbl->last_rand = jiffies;
- for (p = &tbl->parms; p; p = p->next)
+ list_for_each_entry(p, &tbl->parms_list, list)
p->reachable_time =
neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME));
}
@@ -1446,7 +1440,7 @@ static inline struct neigh_parms *lookup_neigh_parms(struct neigh_table *tbl,
{
struct neigh_parms *p;
- for (p = &tbl->parms; p; p = p->next) {
+ list_for_each_entry(p, &tbl->parms_list, list) {
if ((p->dev && p->dev->ifindex == ifindex && net_eq(neigh_parms_net(p), net)) ||
(!p->dev && !ifindex && net_eq(net, &init_net)))
return p;
@@ -1481,8 +1475,7 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev,
}
write_lock_bh(&tbl->lock);
- p->next = tbl->parms.next;
- tbl->parms.next = p;
+ list_add(&p->list, &tbl->parms.list);
write_unlock_bh(&tbl->lock);
neigh_parms_data_state_cleanall(p);
@@ -1501,24 +1494,15 @@ static void neigh_rcu_free_parms(struct rcu_head *head)
void neigh_parms_release(struct neigh_table *tbl, struct neigh_parms *parms)
{
- struct neigh_parms **p;
-
if (!parms || parms == &tbl->parms)
return;
write_lock_bh(&tbl->lock);
- for (p = &tbl->parms.next; *p; p = &(*p)->next) {
- if (*p == parms) {
- *p = parms->next;
- parms->dead = 1;
- write_unlock_bh(&tbl->lock);
- if (parms->dev)
- dev_put(parms->dev);
- call_rcu(&parms->rcu_head, neigh_rcu_free_parms);
- return;
- }
- }
+ list_del(&parms->list);
+ parms->dead = 1;
write_unlock_bh(&tbl->lock);
- neigh_dbg(1, "%s: not found\n", __func__);
+ if (parms->dev)
+ dev_put(parms->dev);
+ call_rcu(&parms->rcu_head, neigh_rcu_free_parms);
}
EXPORT_SYMBOL(neigh_parms_release);
@@ -1530,11 +1514,15 @@ static void neigh_parms_destroy(struct neigh_parms *parms)
static struct lock_class_key neigh_table_proxy_queue_class;
-static void neigh_table_init_no_netlink(struct neigh_table *tbl)
+static struct neigh_table *neigh_tables[NEIGH_NR_TABLES] __read_mostly;
+
+void neigh_table_init(int index, struct neigh_table *tbl)
{
unsigned long now = jiffies;
unsigned long phsize;
+ INIT_LIST_HEAD(&tbl->parms_list);
+ list_add(&tbl->parms.list, &tbl->parms_list);
write_pnet(&tbl->parms.net, &init_net);
atomic_set(&tbl->parms.refcnt, 1);
tbl->parms.reachable_time =
@@ -1574,34 +1562,14 @@ static void neigh_table_init_no_netlink(struct neigh_table *tbl)
tbl->last_flush = now;
tbl->last_rand = now + tbl->parms.reachable_time * 20;
-}
-
-void neigh_table_init(struct neigh_table *tbl)
-{
- struct neigh_table *tmp;
-
- neigh_table_init_no_netlink(tbl);
- write_lock(&neigh_tbl_lock);
- for (tmp = neigh_tables; tmp; tmp = tmp->next) {
- if (tmp->family == tbl->family)
- break;
- }
- tbl->next = neigh_tables;
- neigh_tables = tbl;
- write_unlock(&neigh_tbl_lock);
- if (unlikely(tmp)) {
- pr_err("Registering multiple tables for family %d\n",
- tbl->family);
- dump_stack();
- }
+ neigh_tables[index] = tbl;
}
EXPORT_SYMBOL(neigh_table_init);
-int neigh_table_clear(struct neigh_table *tbl)
+int neigh_table_clear(int index, struct neigh_table *tbl)
{
- struct neigh_table **tp;
-
+ neigh_tables[index] = NULL;
/* It is not clean... Fix it to unload IPv6 module safely */
cancel_delayed_work_sync(&tbl->gc_work);
del_timer_sync(&tbl->proxy_timer);
@@ -1609,14 +1577,6 @@ int neigh_table_clear(struct neigh_table *tbl)
neigh_ifdown(tbl, NULL);
if (atomic_read(&tbl->entries))
pr_crit("neighbour leakage\n");
- write_lock(&neigh_tbl_lock);
- for (tp = &neigh_tables; *tp; tp = &(*tp)->next) {
- if (*tp == tbl) {
- *tp = tbl->next;
- break;
- }
- }
- write_unlock(&neigh_tbl_lock);
call_rcu(&rcu_dereference_protected(tbl->nht, 1)->rcu,
neigh_hash_free_rcu);
@@ -1634,12 +1594,32 @@ int neigh_table_clear(struct neigh_table *tbl)
}
EXPORT_SYMBOL(neigh_table_clear);
+static struct neigh_table *neigh_find_table(int family)
+{
+ struct neigh_table *tbl = NULL;
+
+ switch (family) {
+ case AF_INET:
+ tbl = neigh_tables[NEIGH_ARP_TABLE];
+ break;
+ case AF_INET6:
+ tbl = neigh_tables[NEIGH_ND_TABLE];
+ break;
+ case AF_DECnet:
+ tbl = neigh_tables[NEIGH_DN_TABLE];
+ break;
+ }
+
+ return tbl;
+}
+
static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh)
{
struct net *net = sock_net(skb->sk);
struct ndmsg *ndm;
struct nlattr *dst_attr;
struct neigh_table *tbl;
+ struct neighbour *neigh;
struct net_device *dev = NULL;
int err = -EINVAL;
@@ -1660,39 +1640,31 @@ static int neigh_delete(struct sk_buff *skb, struct nlmsghdr *nlh)
}
}
- read_lock(&neigh_tbl_lock);
- for (tbl = neigh_tables; tbl; tbl = tbl->next) {
- struct neighbour *neigh;
+ tbl = neigh_find_table(ndm->ndm_family);
+ if (tbl == NULL)
+ return -EAFNOSUPPORT;
- if (tbl->family != ndm->ndm_family)
- continue;
- read_unlock(&neigh_tbl_lock);
-
- if (nla_len(dst_attr) < tbl->key_len)
- goto out;
-
- if (ndm->ndm_flags & NTF_PROXY) {
- err = pneigh_delete(tbl, net, nla_data(dst_attr), dev);
- goto out;
- }
+ if (nla_len(dst_attr) < tbl->key_len)
+ goto out;
- if (dev == NULL)
- goto out;
+ if (ndm->ndm_flags & NTF_PROXY) {
+ err = pneigh_delete(tbl, net, nla_data(dst_attr), dev);
+ goto out;
+ }
- neigh = neigh_lookup(tbl, nla_data(dst_attr), dev);
- if (neigh == NULL) {
- err = -ENOENT;
- goto out;
- }
+ if (dev == NULL)
+ goto out;
- err = neigh_update(neigh, NULL, NUD_FAILED,
- NEIGH_UPDATE_F_OVERRIDE |
- NEIGH_UPDATE_F_ADMIN);
- neigh_release(neigh);
+ neigh = neigh_lookup(tbl, nla_data(dst_attr), dev);
+ if (neigh == NULL) {
+ err = -ENOENT;
goto out;
}
- read_unlock(&neigh_tbl_lock);
- err = -EAFNOSUPPORT;
+
+ err = neigh_update(neigh, NULL, NUD_FAILED,
+ NEIGH_UPDATE_F_OVERRIDE |
+ NEIGH_UPDATE_F_ADMIN);
+ neigh_release(neigh);
out:
return err;
@@ -1700,11 +1672,14 @@ out:
static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh)
{
+ int flags = NEIGH_UPDATE_F_ADMIN | NEIGH_UPDATE_F_OVERRIDE;
struct net *net = sock_net(skb->sk);
struct ndmsg *ndm;
struct nlattr *tb[NDA_MAX+1];
struct neigh_table *tbl;
struct net_device *dev = NULL;
+ struct neighbour *neigh;
+ void *dst, *lladdr;
int err;
ASSERT_RTNL();
@@ -1728,70 +1703,60 @@ static int neigh_add(struct sk_buff *skb, struct nlmsghdr *nlh)
goto out;
}
- read_lock(&neigh_tbl_lock);
- for (tbl = neigh_tables; tbl; tbl = tbl->next) {
- int flags = NEIGH_UPDATE_F_ADMIN | NEIGH_UPDATE_F_OVERRIDE;
- struct neighbour *neigh;
- void *dst, *lladdr;
+ tbl = neigh_find_table(ndm->ndm_family);
+ if (tbl == NULL)
+ return -EAFNOSUPPORT;
- if (tbl->family != ndm->ndm_family)
- continue;
- read_unlock(&neigh_tbl_lock);
+ if (nla_len(tb[NDA_DST]) < tbl->key_len)
+ goto out;
+ dst = nla_data(tb[NDA_DST]);
+ lladdr = tb[NDA_LLADDR] ? nla_data(tb[NDA_LLADDR]) : NULL;
- if (nla_len(tb[NDA_DST]) < tbl->key_len)
- goto out;
- dst = nla_data(tb[NDA_DST]);
- lladdr = tb[NDA_LLADDR] ? nla_data(tb[NDA_LLADDR]) : NULL;
+ if (ndm->ndm_flags & NTF_PROXY) {
+ struct pneigh_entry *pn;
- if (ndm->ndm_flags & NTF_PROXY) {
- struct pneigh_entry *pn;
+ err = -ENOBUFS;
+ pn = pneigh_lookup(tbl, net, dst, dev, 1);
+ if (pn) {
+ pn->flags = ndm->ndm_flags;
+ err = 0;
+ }
+ goto out;
+ }
- err = -ENOBUFS;
- pn = pneigh_lookup(tbl, net, dst, dev, 1);
- if (pn) {
- pn->flags = ndm->ndm_flags;
- err = 0;
- }
+ if (dev == NULL)
+ goto out;
+
+ neigh = neigh_lookup(tbl, dst, dev);
+ if (neigh == NULL) {
+ if (!(nlh->nlmsg_flags & NLM_F_CREATE)) {
+ err = -ENOENT;
goto out;
}
- if (dev == NULL)
+ neigh = __neigh_lookup_errno(tbl, dst, dev);
+ if (IS_ERR(neigh)) {
+ err = PTR_ERR(neigh);
+ goto out;
+ }
+ } else {
+ if (nlh->nlmsg_flags & NLM_F_EXCL) {
+ err = -EEXIST;
+ neigh_release(neigh);
goto out;
-
- neigh = neigh_lookup(tbl, dst, dev);
- if (neigh == NULL) {
- if (!(nlh->nlmsg_flags & NLM_F_CREATE)) {
- err = -ENOENT;
- goto out;
- }
-
- neigh = __neigh_lookup_errno(tbl, dst, dev);
- if (IS_ERR(neigh)) {
- err = PTR_ERR(neigh);
- goto out;
- }
- } else {
- if (nlh->nlmsg_flags & NLM_F_EXCL) {
- err = -EEXIST;
- neigh_release(neigh);
- goto out;
- }
-
- if (!(nlh->nlmsg_flags & NLM_F_REPLACE))
- flags &= ~NEIGH_UPDATE_F_OVERRIDE;
}
- if (ndm->ndm_flags & NTF_USE) {
- neigh_event_send(neigh, NULL);
- err = 0;
- } else
- err = neigh_update(neigh, lladdr, ndm->ndm_state, flags);
- neigh_release(neigh);
- goto out;
+ if (!(nlh->nlmsg_flags & NLM_F_REPLACE))
+ flags &= ~NEIGH_UPDATE_F_OVERRIDE;
}
- read_unlock(&neigh_tbl_lock);
- err = -EAFNOSUPPORT;
+ if (ndm->ndm_flags & NTF_USE) {
+ neigh_event_send(neigh, NULL);
+ err = 0;
+ } else
+ err = neigh_update(neigh, lladdr, ndm->ndm_state, flags);
+ neigh_release(neigh);
+
out:
return err;
}
@@ -1990,7 +1955,8 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh)
struct neigh_table *tbl;
struct ndtmsg *ndtmsg;
struct nlattr *tb[NDTA_MAX+1];
- int err;
+ bool found = false;
+ int err, tidx;
err = nlmsg_parse(nlh, sizeof(*ndtmsg), tb, NDTA_MAX,
nl_neightbl_policy);
@@ -2003,19 +1969,21 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh)
}
ndtmsg = nlmsg_data(nlh);
- read_lock(&neigh_tbl_lock);
- for (tbl = neigh_tables; tbl; tbl = tbl->next) {
+
+ for (tidx = 0; tidx < NEIGH_NR_TABLES; tidx++) {
+ tbl = neigh_tables[tidx];
+ if (!tbl)
+ continue;
if (ndtmsg->ndtm_family && tbl->family != ndtmsg->ndtm_family)
continue;
-
- if (nla_strcmp(tb[NDTA_NAME], tbl->id) == 0)
+ if (nla_strcmp(tb[NDTA_NAME], tbl->id) == 0) {
+ found = true;
break;
+ }
}
- if (tbl == NULL) {
- err = -ENOENT;
- goto errout_locked;
- }
+ if (!found)
+ return -ENOENT;
/*
* We acquire tbl->lock to be nice to the periodic timers and
@@ -2126,8 +2094,6 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh)
errout_tbl_lock:
write_unlock_bh(&tbl->lock);
-errout_locked:
- read_unlock(&neigh_tbl_lock);
errout:
return err;
}
@@ -2142,10 +2108,13 @@ static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
family = ((struct rtgenmsg *) nlmsg_data(cb->nlh))->rtgen_family;
- read_lock(&neigh_tbl_lock);
- for (tbl = neigh_tables, tidx = 0; tbl; tbl = tbl->next, tidx++) {
+ for (tidx = 0; tidx < NEIGH_NR_TABLES; tidx++) {
struct neigh_parms *p;
+ tbl = neigh_tables[tidx];
+ if (!tbl)
+ continue;
+
if (tidx < tbl_skip || (family && tbl->family != family))
continue;
@@ -2154,7 +2123,9 @@ static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
NLM_F_MULTI) <= 0)
break;
- for (nidx = 0, p = tbl->parms.next; p; p = p->next) {
+ nidx = 0;
+ p = list_next_entry(&tbl->parms, list);
+ list_for_each_entry_from(p, &tbl->parms_list, list) {
if (!net_eq(neigh_parms_net(p), net))
continue;
@@ -2174,7 +2145,6 @@ static int neightbl_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
neigh_skip = 0;
}
out:
- read_unlock(&neigh_tbl_lock);
cb->args[0] = tidx;
cb->args[1] = nidx;
@@ -2357,7 +2327,6 @@ static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
int proxy = 0;
int err;
- read_lock(&neigh_tbl_lock);
family = ((struct rtgenmsg *) nlmsg_data(cb->nlh))->rtgen_family;
/* check for full ndmsg structure presence, family member is
@@ -2369,8 +2338,11 @@ static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
s_t = cb->args[0];
- for (tbl = neigh_tables, t = 0; tbl;
- tbl = tbl->next, t++) {
+ for (t = 0; t < NEIGH_NR_TABLES; t++) {
+ tbl = neigh_tables[t];
+
+ if (!tbl)
+ continue;
if (t < s_t || (family && tbl->family != family))
continue;
if (t > s_t)
@@ -2383,7 +2355,6 @@ static int neigh_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
if (err < 0)
break;
}
- read_unlock(&neigh_tbl_lock);
cb->args[0] = t;
return skb->len;
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index 9dd06699b09c..999341244434 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -12,6 +12,7 @@
#include <linux/capability.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
+#include <net/switchdev.h>
#include <linux/if_arp.h>
#include <linux/slab.h>
#include <linux/nsproxy.h>
@@ -325,6 +326,23 @@ static ssize_t tx_queue_len_store(struct device *dev,
}
NETDEVICE_SHOW_RW(tx_queue_len, fmt_ulong);
+static int change_gro_flush_timeout(struct net_device *dev, unsigned long val)
+{
+ dev->gro_flush_timeout = val;
+ return 0;
+}
+
+static ssize_t gro_flush_timeout_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ return netdev_store(dev, attr, buf, len, change_gro_flush_timeout);
+}
+NETDEVICE_SHOW_RW(gro_flush_timeout, fmt_ulong);
+
static ssize_t ifalias_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t len)
{
@@ -387,7 +405,7 @@ static ssize_t phys_port_id_show(struct device *dev,
return restart_syscall();
if (dev_isalive(netdev)) {
- struct netdev_phys_port_id ppid;
+ struct netdev_phys_item_id ppid;
ret = dev_get_phys_port_id(netdev, &ppid);
if (!ret)
@@ -399,6 +417,28 @@ static ssize_t phys_port_id_show(struct device *dev,
}
static DEVICE_ATTR_RO(phys_port_id);
+static ssize_t phys_switch_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct net_device *netdev = to_net_dev(dev);
+ ssize_t ret = -EINVAL;
+
+ if (!rtnl_trylock())
+ return restart_syscall();
+
+ if (dev_isalive(netdev)) {
+ struct netdev_phys_item_id ppid;
+
+ ret = netdev_switch_parent_id_get(netdev, &ppid);
+ if (!ret)
+ ret = sprintf(buf, "%*phN\n", ppid.id_len, ppid.id);
+ }
+ rtnl_unlock();
+
+ return ret;
+}
+static DEVICE_ATTR_RO(phys_switch_id);
+
static struct attribute *net_class_attrs[] = {
&dev_attr_netdev_group.attr,
&dev_attr_type.attr,
@@ -422,7 +462,9 @@ static struct attribute *net_class_attrs[] = {
&dev_attr_mtu.attr,
&dev_attr_flags.attr,
&dev_attr_tx_queue_len.attr,
+ &dev_attr_gro_flush_timeout.attr,
&dev_attr_phys_port_id.attr,
+ &dev_attr_phys_switch_id.attr,
NULL,
};
ATTRIBUTE_GROUPS(net_class);
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index e6645b4f330a..e0ad5d16c9c5 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -79,8 +79,7 @@ static int netpoll_start_xmit(struct sk_buff *skb, struct net_device *dev,
if (vlan_tx_tag_present(skb) &&
!vlan_hw_offload_capable(features, skb->vlan_proto)) {
- skb = __vlan_put_tag(skb, skb->vlan_proto,
- vlan_tx_tag_get(skb));
+ skb = __vlan_hwaccel_push_inside(skb);
if (unlikely(!skb)) {
/* This is actually a packet drop, but we
* don't want the code that calls this
@@ -88,7 +87,6 @@ static int netpoll_start_xmit(struct sk_buff *skb, struct net_device *dev,
*/
goto out;
}
- skb->vlan_tci = 0;
}
status = netdev_start_xmit(skb, dev, txq, false);
diff --git a/net/core/pktgen.c b/net/core/pktgen.c
index 443256bdcddc..da934fc3faa8 100644
--- a/net/core/pktgen.c
+++ b/net/core/pktgen.c
@@ -3728,8 +3728,7 @@ static int pktgen_remove_device(struct pktgen_thread *t,
/* Remove proc before if_list entry, because add_device uses
* list to determine if interface already exist, avoid race
* with proc_create_data() */
- if (pkt_dev->entry)
- proc_remove(pkt_dev->entry);
+ proc_remove(pkt_dev->entry);
/* And update the thread if_list */
_rem_dev_from_if_list(t, pkt_dev);
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index a6882686ca3a..d06107d36ec8 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -36,6 +36,7 @@
#include <linux/mutex.h>
#include <linux/if_addr.h>
#include <linux/if_bridge.h>
+#include <linux/if_vlan.h>
#include <linux/pci.h>
#include <linux/etherdevice.h>
@@ -43,6 +44,7 @@
#include <linux/inet.h>
#include <linux/netdevice.h>
+#include <net/switchdev.h>
#include <net/ip.h>
#include <net/protocol.h>
#include <net/arp.h>
@@ -365,11 +367,10 @@ static void rtnl_lock_unregistering_all(void)
{
struct net *net;
bool unregistering;
- DEFINE_WAIT(wait);
+ DEFINE_WAIT_FUNC(wait, woken_wake_function);
+ add_wait_queue(&netdev_unregistering_wq, &wait);
for (;;) {
- prepare_to_wait(&netdev_unregistering_wq, &wait,
- TASK_UNINTERRUPTIBLE);
unregistering = false;
rtnl_lock();
for_each_net(net) {
@@ -381,9 +382,10 @@ static void rtnl_lock_unregistering_all(void)
if (!unregistering)
break;
__rtnl_unlock();
- schedule();
+
+ wait_woken(&wait, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}
- finish_wait(&netdev_unregistering_wq, &wait);
+ remove_wait_queue(&netdev_unregistering_wq, &wait);
}
/**
@@ -868,7 +870,8 @@ static noinline size_t if_nlmsg_size(const struct net_device *dev,
+ rtnl_port_size(dev, ext_filter_mask) /* IFLA_VF_PORTS + IFLA_PORT_SELF */
+ rtnl_link_get_size(dev) /* IFLA_LINKINFO */
+ rtnl_link_get_af_size(dev) /* IFLA_AF_SPEC */
- + nla_total_size(MAX_PHYS_PORT_ID_LEN); /* IFLA_PHYS_PORT_ID */
+ + nla_total_size(MAX_PHYS_ITEM_ID_LEN) /* IFLA_PHYS_PORT_ID */
+ + nla_total_size(MAX_PHYS_ITEM_ID_LEN); /* IFLA_PHYS_SWITCH_ID */
}
static int rtnl_vf_ports_fill(struct sk_buff *skb, struct net_device *dev)
@@ -952,7 +955,7 @@ static int rtnl_port_fill(struct sk_buff *skb, struct net_device *dev,
static int rtnl_phys_port_id_fill(struct sk_buff *skb, struct net_device *dev)
{
int err;
- struct netdev_phys_port_id ppid;
+ struct netdev_phys_item_id ppid;
err = dev_get_phys_port_id(dev, &ppid);
if (err) {
@@ -967,6 +970,24 @@ static int rtnl_phys_port_id_fill(struct sk_buff *skb, struct net_device *dev)
return 0;
}
+static int rtnl_phys_switch_id_fill(struct sk_buff *skb, struct net_device *dev)
+{
+ int err;
+ struct netdev_phys_item_id psid;
+
+ err = netdev_switch_parent_id_get(dev, &psid);
+ if (err) {
+ if (err == -EOPNOTSUPP)
+ return 0;
+ return err;
+ }
+
+ if (nla_put(skb, IFLA_PHYS_SWITCH_ID, psid.id_len, psid.id))
+ return -EMSGSIZE;
+
+ return 0;
+}
+
static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
int type, u32 pid, u32 seq, u32 change,
unsigned int flags, u32 ext_filter_mask)
@@ -1039,6 +1060,9 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
if (rtnl_phys_port_id_fill(skb, dev))
goto nla_put_failure;
+ if (rtnl_phys_switch_id_fill(skb, dev))
+ goto nla_put_failure;
+
attr = nla_reserve(skb, IFLA_STATS,
sizeof(struct rtnl_link_stats));
if (attr == NULL)
@@ -1196,8 +1220,9 @@ static const struct nla_policy ifla_policy[IFLA_MAX+1] = {
[IFLA_PROMISCUITY] = { .type = NLA_U32 },
[IFLA_NUM_TX_QUEUES] = { .type = NLA_U32 },
[IFLA_NUM_RX_QUEUES] = { .type = NLA_U32 },
- [IFLA_PHYS_PORT_ID] = { .type = NLA_BINARY, .len = MAX_PHYS_PORT_ID_LEN },
+ [IFLA_PHYS_PORT_ID] = { .type = NLA_BINARY, .len = MAX_PHYS_ITEM_ID_LEN },
[IFLA_CARRIER_CHANGES] = { .type = NLA_U32 }, /* ignored */
+ [IFLA_PHYS_SWITCH_ID] = { .type = NLA_BINARY, .len = MAX_PHYS_ITEM_ID_LEN },
};
static const struct nla_policy ifla_info_policy[IFLA_INFO_MAX+1] = {
@@ -1498,6 +1523,7 @@ static int do_setlink(const struct sk_buff *skb,
goto errout;
}
if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) {
+ put_net(net);
err = -EPERM;
goto errout;
}
@@ -2220,8 +2246,8 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len;
}
-void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change,
- gfp_t flags)
+struct sk_buff *rtmsg_ifinfo_build_skb(int type, struct net_device *dev,
+ unsigned int change, gfp_t flags)
{
struct net *net = dev_net(dev);
struct sk_buff *skb;
@@ -2239,11 +2265,28 @@ void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change,
kfree_skb(skb);
goto errout;
}
- rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, flags);
- return;
+ return skb;
errout:
if (err < 0)
rtnl_set_sk_err(net, RTNLGRP_LINK, err);
+ return NULL;
+}
+
+void rtmsg_ifinfo_send(struct sk_buff *skb, struct net_device *dev, gfp_t flags)
+{
+ struct net *net = dev_net(dev);
+
+ rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, flags);
+}
+
+void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change,
+ gfp_t flags)
+{
+ struct sk_buff *skb;
+
+ skb = rtmsg_ifinfo_build_skb(type, dev, change, flags);
+ if (skb)
+ rtmsg_ifinfo_send(skb, dev, flags);
}
EXPORT_SYMBOL(rtmsg_ifinfo);
@@ -2312,7 +2355,7 @@ errout:
int ndo_dflt_fdb_add(struct ndmsg *ndm,
struct nlattr *tb[],
struct net_device *dev,
- const unsigned char *addr,
+ const unsigned char *addr, u16 vid,
u16 flags)
{
int err = -EINVAL;
@@ -2338,6 +2381,28 @@ int ndo_dflt_fdb_add(struct ndmsg *ndm,
}
EXPORT_SYMBOL(ndo_dflt_fdb_add);
+static int fdb_vid_parse(struct nlattr *vlan_attr, u16 *p_vid)
+{
+ u16 vid = 0;
+
+ if (vlan_attr) {
+ if (nla_len(vlan_attr) != sizeof(u16)) {
+ pr_info("PF_BRIDGE: RTM_NEWNEIGH with invalid vlan\n");
+ return -EINVAL;
+ }
+
+ vid = nla_get_u16(vlan_attr);
+
+ if (!vid || vid >= VLAN_VID_MASK) {
+ pr_info("PF_BRIDGE: RTM_NEWNEIGH with invalid vlan id %d\n",
+ vid);
+ return -EINVAL;
+ }
+ }
+ *p_vid = vid;
+ return 0;
+}
+
static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh)
{
struct net *net = sock_net(skb->sk);
@@ -2345,6 +2410,7 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh)
struct nlattr *tb[NDA_MAX+1];
struct net_device *dev;
u8 *addr;
+ u16 vid;
int err;
err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL);
@@ -2370,6 +2436,10 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh)
addr = nla_data(tb[NDA_LLADDR]);
+ err = fdb_vid_parse(tb[NDA_VLAN], &vid);
+ if (err)
+ return err;
+
err = -EOPNOTSUPP;
/* Support fdb on master device the net/bridge default case */
@@ -2378,7 +2448,8 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh)
struct net_device *br_dev = netdev_master_upper_dev_get(dev);
const struct net_device_ops *ops = br_dev->netdev_ops;
- err = ops->ndo_fdb_add(ndm, tb, dev, addr, nlh->nlmsg_flags);
+ err = ops->ndo_fdb_add(ndm, tb, dev, addr, vid,
+ nlh->nlmsg_flags);
if (err)
goto out;
else
@@ -2389,9 +2460,10 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh)
if ((ndm->ndm_flags & NTF_SELF)) {
if (dev->netdev_ops->ndo_fdb_add)
err = dev->netdev_ops->ndo_fdb_add(ndm, tb, dev, addr,
+ vid,
nlh->nlmsg_flags);
else
- err = ndo_dflt_fdb_add(ndm, tb, dev, addr,
+ err = ndo_dflt_fdb_add(ndm, tb, dev, addr, vid,
nlh->nlmsg_flags);
if (!err) {
@@ -2409,7 +2481,7 @@ out:
int ndo_dflt_fdb_del(struct ndmsg *ndm,
struct nlattr *tb[],
struct net_device *dev,
- const unsigned char *addr)
+ const unsigned char *addr, u16 vid)
{
int err = -EINVAL;
@@ -2438,6 +2510,7 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh)
struct net_device *dev;
int err = -EINVAL;
__u8 *addr;
+ u16 vid;
if (!netlink_capable(skb, CAP_NET_ADMIN))
return -EPERM;
@@ -2465,6 +2538,10 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh)
addr = nla_data(tb[NDA_LLADDR]);
+ err = fdb_vid_parse(tb[NDA_VLAN], &vid);
+ if (err)
+ return err;
+
err = -EOPNOTSUPP;
/* Support fdb on master device the net/bridge default case */
@@ -2474,7 +2551,7 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh)
const struct net_device_ops *ops = br_dev->netdev_ops;
if (ops->ndo_fdb_del)
- err = ops->ndo_fdb_del(ndm, tb, dev, addr);
+ err = ops->ndo_fdb_del(ndm, tb, dev, addr, vid);
if (err)
goto out;
@@ -2485,9 +2562,10 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh)
/* Embedded bridge, macvlan, and any other device support */
if (ndm->ndm_flags & NTF_SELF) {
if (dev->netdev_ops->ndo_fdb_del)
- err = dev->netdev_ops->ndo_fdb_del(ndm, tb, dev, addr);
+ err = dev->netdev_ops->ndo_fdb_del(ndm, tb, dev, addr,
+ vid);
else
- err = ndo_dflt_fdb_del(ndm, tb, dev, addr);
+ err = ndo_dflt_fdb_del(ndm, tb, dev, addr, vid);
if (!err) {
rtnl_fdb_notify(dev, addr, RTM_DELNEIGH);
@@ -2627,12 +2705,22 @@ static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
return skb->len;
}
+static int brport_nla_put_flag(struct sk_buff *skb, u32 flags, u32 mask,
+ unsigned int attrnum, unsigned int flag)
+{
+ if (mask & flag)
+ return nla_put_u8(skb, attrnum, !!(flags & flag));
+ return 0;
+}
+
int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
- struct net_device *dev, u16 mode)
+ struct net_device *dev, u16 mode,
+ u32 flags, u32 mask)
{
struct nlmsghdr *nlh;
struct ifinfomsg *ifm;
struct nlattr *br_afspec;
+ struct nlattr *protinfo;
u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN;
struct net_device *br_dev = netdev_master_upper_dev_get(dev);
@@ -2664,13 +2752,46 @@ int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
if (!br_afspec)
goto nla_put_failure;
- if (nla_put_u16(skb, IFLA_BRIDGE_FLAGS, BRIDGE_FLAGS_SELF) ||
- nla_put_u16(skb, IFLA_BRIDGE_MODE, mode)) {
+ if (nla_put_u16(skb, IFLA_BRIDGE_FLAGS, BRIDGE_FLAGS_SELF)) {
nla_nest_cancel(skb, br_afspec);
goto nla_put_failure;
}
+
+ if (mode != BRIDGE_MODE_UNDEF) {
+ if (nla_put_u16(skb, IFLA_BRIDGE_MODE, mode)) {
+ nla_nest_cancel(skb, br_afspec);
+ goto nla_put_failure;
+ }
+ }
nla_nest_end(skb, br_afspec);
+ protinfo = nla_nest_start(skb, IFLA_PROTINFO | NLA_F_NESTED);
+ if (!protinfo)
+ goto nla_put_failure;
+
+ if (brport_nla_put_flag(skb, flags, mask,
+ IFLA_BRPORT_MODE, BR_HAIRPIN_MODE) ||
+ brport_nla_put_flag(skb, flags, mask,
+ IFLA_BRPORT_GUARD, BR_BPDU_GUARD) ||
+ brport_nla_put_flag(skb, flags, mask,
+ IFLA_BRPORT_FAST_LEAVE,
+ BR_MULTICAST_FAST_LEAVE) ||
+ brport_nla_put_flag(skb, flags, mask,
+ IFLA_BRPORT_PROTECT, BR_ROOT_BLOCK) ||
+ brport_nla_put_flag(skb, flags, mask,
+ IFLA_BRPORT_LEARNING, BR_LEARNING) ||
+ brport_nla_put_flag(skb, flags, mask,
+ IFLA_BRPORT_LEARNING_SYNC, BR_LEARNING_SYNC) ||
+ brport_nla_put_flag(skb, flags, mask,
+ IFLA_BRPORT_UNICAST_FLOOD, BR_FLOOD) ||
+ brport_nla_put_flag(skb, flags, mask,
+ IFLA_BRPORT_PROXYARP, BR_PROXYARP)) {
+ nla_nest_cancel(skb, protinfo);
+ goto nla_put_failure;
+ }
+
+ nla_nest_end(skb, protinfo);
+
return nlmsg_end(skb, nlh);
nla_put_failure:
nlmsg_cancel(skb, nlh);
@@ -2685,13 +2806,20 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb)
int idx = 0;
u32 portid = NETLINK_CB(cb->skb).portid;
u32 seq = cb->nlh->nlmsg_seq;
- struct nlattr *extfilt;
u32 filter_mask = 0;
- extfilt = nlmsg_find_attr(cb->nlh, sizeof(struct ifinfomsg),
- IFLA_EXT_MASK);
- if (extfilt)
- filter_mask = nla_get_u32(extfilt);
+ if (nlmsg_len(cb->nlh) > sizeof(struct ifinfomsg)) {
+ struct nlattr *extfilt;
+
+ extfilt = nlmsg_find_attr(cb->nlh, sizeof(struct ifinfomsg),
+ IFLA_EXT_MASK);
+ if (extfilt) {
+ if (nla_len(extfilt) < sizeof(filter_mask))
+ return -EINVAL;
+
+ filter_mask = nla_get_u32(extfilt);
+ }
+ }
rcu_read_lock();
for_each_netdev_rcu(net, dev) {
@@ -2798,6 +2926,9 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh)
if (br_spec) {
nla_for_each_nested(attr, br_spec, rem) {
if (nla_type(attr) == IFLA_BRIDGE_FLAGS) {
+ if (nla_len(attr) < sizeof(flags))
+ return -EINVAL;
+
have_flags = true;
flags = nla_get_u16(attr);
break;
@@ -2868,6 +2999,9 @@ static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh)
if (br_spec) {
nla_for_each_nested(attr, br_spec, rem) {
if (nla_type(attr) == IFLA_BRIDGE_FLAGS) {
+ if (nla_len(attr) < sizeof(flags))
+ return -EINVAL;
+
have_flags = true;
flags = nla_get_u16(attr);
break;
diff --git a/net/core/scm.c b/net/core/scm.c
index b442e7e25e60..3b6899b7d810 100644
--- a/net/core/scm.c
+++ b/net/core/scm.c
@@ -129,8 +129,7 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
struct cmsghdr *cmsg;
int err;
- for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg))
- {
+ for_each_cmsghdr(cmsg, msg) {
err = -EINVAL;
/* Verify that cmsg_len is at least sizeof(struct cmsghdr) */
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index c16615bfb61e..ae13ef6b3ea7 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -265,7 +265,7 @@ struct sk_buff *__alloc_skb(unsigned int size, gfp_t gfp_mask,
skb->fclone = SKB_FCLONE_ORIG;
atomic_set(&fclones->fclone_ref, 1);
- fclones->skb2.fclone = SKB_FCLONE_FREE;
+ fclones->skb2.fclone = SKB_FCLONE_CLONE;
fclones->skb2.pfmemalloc = pfmemalloc;
}
out:
@@ -336,59 +336,85 @@ struct netdev_alloc_cache {
unsigned int pagecnt_bias;
};
static DEFINE_PER_CPU(struct netdev_alloc_cache, netdev_alloc_cache);
+static DEFINE_PER_CPU(struct netdev_alloc_cache, napi_alloc_cache);
-static void *__netdev_alloc_frag(unsigned int fragsz, gfp_t gfp_mask)
+static struct page *__page_frag_refill(struct netdev_alloc_cache *nc,
+ gfp_t gfp_mask)
{
- struct netdev_alloc_cache *nc;
- void *data = NULL;
- int order;
- unsigned long flags;
+ const unsigned int order = NETDEV_FRAG_PAGE_MAX_ORDER;
+ struct page *page = NULL;
+ gfp_t gfp = gfp_mask;
+
+ if (order) {
+ gfp_mask |= __GFP_COMP | __GFP_NOWARN | __GFP_NORETRY;
+ page = alloc_pages_node(NUMA_NO_NODE, gfp_mask, order);
+ nc->frag.size = PAGE_SIZE << (page ? order : 0);
+ }
- local_irq_save(flags);
- nc = this_cpu_ptr(&netdev_alloc_cache);
- if (unlikely(!nc->frag.page)) {
+ if (unlikely(!page))
+ page = alloc_pages_node(NUMA_NO_NODE, gfp, 0);
+
+ nc->frag.page = page;
+
+ return page;
+}
+
+static void *__alloc_page_frag(struct netdev_alloc_cache __percpu *cache,
+ unsigned int fragsz, gfp_t gfp_mask)
+{
+ struct netdev_alloc_cache *nc = this_cpu_ptr(cache);
+ struct page *page = nc->frag.page;
+ unsigned int size;
+ int offset;
+
+ if (unlikely(!page)) {
refill:
- for (order = NETDEV_FRAG_PAGE_MAX_ORDER; ;) {
- gfp_t gfp = gfp_mask;
+ page = __page_frag_refill(nc, gfp_mask);
+ if (!page)
+ return NULL;
+
+ /* if size can vary use frag.size else just use PAGE_SIZE */
+ size = NETDEV_FRAG_PAGE_MAX_ORDER ? nc->frag.size : PAGE_SIZE;
- if (order)
- gfp |= __GFP_COMP | __GFP_NOWARN;
- nc->frag.page = alloc_pages(gfp, order);
- if (likely(nc->frag.page))
- break;
- if (--order < 0)
- goto end;
- }
- nc->frag.size = PAGE_SIZE << order;
/* Even if we own the page, we do not use atomic_set().
* This would break get_page_unless_zero() users.
*/
- atomic_add(NETDEV_PAGECNT_MAX_BIAS - 1,
- &nc->frag.page->_count);
- nc->pagecnt_bias = NETDEV_PAGECNT_MAX_BIAS;
- nc->frag.offset = 0;
+ atomic_add(size - 1, &page->_count);
+
+ /* reset page count bias and offset to start of new frag */
+ nc->pagecnt_bias = size;
+ nc->frag.offset = size;
}
- if (nc->frag.offset + fragsz > nc->frag.size) {
- if (atomic_read(&nc->frag.page->_count) != nc->pagecnt_bias) {
- if (!atomic_sub_and_test(nc->pagecnt_bias,
- &nc->frag.page->_count))
- goto refill;
- /* OK, page count is 0, we can safely set it */
- atomic_set(&nc->frag.page->_count,
- NETDEV_PAGECNT_MAX_BIAS);
- } else {
- atomic_add(NETDEV_PAGECNT_MAX_BIAS - nc->pagecnt_bias,
- &nc->frag.page->_count);
- }
- nc->pagecnt_bias = NETDEV_PAGECNT_MAX_BIAS;
- nc->frag.offset = 0;
+ offset = nc->frag.offset - fragsz;
+ if (unlikely(offset < 0)) {
+ if (!atomic_sub_and_test(nc->pagecnt_bias, &page->_count))
+ goto refill;
+
+ /* if size can vary use frag.size else just use PAGE_SIZE */
+ size = NETDEV_FRAG_PAGE_MAX_ORDER ? nc->frag.size : PAGE_SIZE;
+
+ /* OK, page count is 0, we can safely set it */
+ atomic_set(&page->_count, size);
+
+ /* reset page count bias and offset to start of new frag */
+ nc->pagecnt_bias = size;
+ offset = size - fragsz;
}
- data = page_address(nc->frag.page) + nc->frag.offset;
- nc->frag.offset += fragsz;
nc->pagecnt_bias--;
-end:
+ nc->frag.offset = offset;
+
+ return page_address(page) + offset;
+}
+
+static void *__netdev_alloc_frag(unsigned int fragsz, gfp_t gfp_mask)
+{
+ unsigned long flags;
+ void *data;
+
+ local_irq_save(flags);
+ data = __alloc_page_frag(&netdev_alloc_cache, fragsz, gfp_mask);
local_irq_restore(flags);
return data;
}
@@ -406,11 +432,25 @@ void *netdev_alloc_frag(unsigned int fragsz)
}
EXPORT_SYMBOL(netdev_alloc_frag);
+static void *__napi_alloc_frag(unsigned int fragsz, gfp_t gfp_mask)
+{
+ return __alloc_page_frag(&napi_alloc_cache, fragsz, gfp_mask);
+}
+
+void *napi_alloc_frag(unsigned int fragsz)
+{
+ return __napi_alloc_frag(fragsz, GFP_ATOMIC | __GFP_COLD);
+}
+EXPORT_SYMBOL(napi_alloc_frag);
+
/**
- * __netdev_alloc_skb - allocate an skbuff for rx on a specific device
- * @dev: network device to receive on
+ * __alloc_rx_skb - allocate an skbuff for rx
* @length: length to allocate
* @gfp_mask: get_free_pages mask, passed to alloc_skb
+ * @flags: If SKB_ALLOC_RX is set, __GFP_MEMALLOC will be used for
+ * allocations in case we have to fallback to __alloc_skb()
+ * If SKB_ALLOC_NAPI is set, page fragment will be allocated
+ * from napi_cache instead of netdev_cache.
*
* Allocate a new &sk_buff and assign it a usage count of one. The
* buffer has unspecified headroom built in. Users should allocate
@@ -419,11 +459,11 @@ EXPORT_SYMBOL(netdev_alloc_frag);
*
* %NULL is returned if there is no free memory.
*/
-struct sk_buff *__netdev_alloc_skb(struct net_device *dev,
- unsigned int length, gfp_t gfp_mask)
+static struct sk_buff *__alloc_rx_skb(unsigned int length, gfp_t gfp_mask,
+ int flags)
{
struct sk_buff *skb = NULL;
- unsigned int fragsz = SKB_DATA_ALIGN(length + NET_SKB_PAD) +
+ unsigned int fragsz = SKB_DATA_ALIGN(length) +
SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
if (fragsz <= PAGE_SIZE && !(gfp_mask & (__GFP_WAIT | GFP_DMA))) {
@@ -432,7 +472,9 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev,
if (sk_memalloc_socks())
gfp_mask |= __GFP_MEMALLOC;
- data = __netdev_alloc_frag(fragsz, gfp_mask);
+ data = (flags & SKB_ALLOC_NAPI) ?
+ __napi_alloc_frag(fragsz, gfp_mask) :
+ __netdev_alloc_frag(fragsz, gfp_mask);
if (likely(data)) {
skb = build_skb(data, fragsz);
@@ -440,17 +482,72 @@ struct sk_buff *__netdev_alloc_skb(struct net_device *dev,
put_page(virt_to_head_page(data));
}
} else {
- skb = __alloc_skb(length + NET_SKB_PAD, gfp_mask,
+ skb = __alloc_skb(length, gfp_mask,
SKB_ALLOC_RX, NUMA_NO_NODE);
}
+ return skb;
+}
+
+/**
+ * __netdev_alloc_skb - allocate an skbuff for rx on a specific device
+ * @dev: network device to receive on
+ * @length: length to allocate
+ * @gfp_mask: get_free_pages mask, passed to alloc_skb
+ *
+ * Allocate a new &sk_buff and assign it a usage count of one. The
+ * buffer has NET_SKB_PAD headroom built in. Users should allocate
+ * the headroom they think they need without accounting for the
+ * built in space. The built in space is used for optimisations.
+ *
+ * %NULL is returned if there is no free memory.
+ */
+struct sk_buff *__netdev_alloc_skb(struct net_device *dev,
+ unsigned int length, gfp_t gfp_mask)
+{
+ struct sk_buff *skb;
+
+ length += NET_SKB_PAD;
+ skb = __alloc_rx_skb(length, gfp_mask, 0);
+
if (likely(skb)) {
skb_reserve(skb, NET_SKB_PAD);
skb->dev = dev;
}
+
return skb;
}
EXPORT_SYMBOL(__netdev_alloc_skb);
+/**
+ * __napi_alloc_skb - allocate skbuff for rx in a specific NAPI instance
+ * @napi: napi instance this buffer was allocated for
+ * @length: length to allocate
+ * @gfp_mask: get_free_pages mask, passed to alloc_skb and alloc_pages
+ *
+ * Allocate a new sk_buff for use in NAPI receive. This buffer will
+ * attempt to allocate the head from a special reserved region used
+ * only for NAPI Rx allocation. By doing this we can save several
+ * CPU cycles by avoiding having to disable and re-enable IRQs.
+ *
+ * %NULL is returned if there is no free memory.
+ */
+struct sk_buff *__napi_alloc_skb(struct napi_struct *napi,
+ unsigned int length, gfp_t gfp_mask)
+{
+ struct sk_buff *skb;
+
+ length += NET_SKB_PAD + NET_IP_ALIGN;
+ skb = __alloc_rx_skb(length, gfp_mask, SKB_ALLOC_NAPI);
+
+ if (likely(skb)) {
+ skb_reserve(skb, NET_SKB_PAD + NET_IP_ALIGN);
+ skb->dev = napi->dev;
+ }
+
+ return skb;
+}
+EXPORT_SYMBOL(__napi_alloc_skb);
+
void skb_add_rx_frag(struct sk_buff *skb, int i, struct page *page, int off,
int size, unsigned int truesize)
{
@@ -541,33 +638,27 @@ static void kfree_skbmem(struct sk_buff *skb)
switch (skb->fclone) {
case SKB_FCLONE_UNAVAILABLE:
kmem_cache_free(skbuff_head_cache, skb);
- break;
+ return;
case SKB_FCLONE_ORIG:
fclones = container_of(skb, struct sk_buff_fclones, skb1);
- if (atomic_dec_and_test(&fclones->fclone_ref))
- kmem_cache_free(skbuff_fclone_cache, fclones);
+
+ /* We usually free the clone (TX completion) before original skb
+ * This test would have no chance to be true for the clone,
+ * while here, branch prediction will be good.
+ */
+ if (atomic_read(&fclones->fclone_ref) == 1)
+ goto fastpath;
break;
- case SKB_FCLONE_CLONE:
+ default: /* SKB_FCLONE_CLONE */
fclones = container_of(skb, struct sk_buff_fclones, skb2);
-
- /* Warning : We must perform the atomic_dec_and_test() before
- * setting skb->fclone back to SKB_FCLONE_FREE, otherwise
- * skb_clone() could set clone_ref to 2 before our decrement.
- * Anyway, if we are going to free the structure, no need to
- * rewrite skb->fclone.
- */
- if (atomic_dec_and_test(&fclones->fclone_ref)) {
- kmem_cache_free(skbuff_fclone_cache, fclones);
- } else {
- /* The clone portion is available for
- * fast-cloning again.
- */
- skb->fclone = SKB_FCLONE_FREE;
- }
break;
}
+ if (!atomic_dec_and_test(&fclones->fclone_ref))
+ return;
+fastpath:
+ kmem_cache_free(skbuff_fclone_cache, fclones);
}
static void skb_release_head_state(struct sk_buff *skb)
@@ -879,18 +970,14 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)
struct sk_buff_fclones *fclones = container_of(skb,
struct sk_buff_fclones,
skb1);
- struct sk_buff *n = &fclones->skb2;
+ struct sk_buff *n;
if (skb_orphan_frags(skb, gfp_mask))
return NULL;
if (skb->fclone == SKB_FCLONE_ORIG &&
- n->fclone == SKB_FCLONE_FREE) {
- n->fclone = SKB_FCLONE_CLONE;
- /* As our fastclone was free, clone_ref must be 1 at this point.
- * We could use atomic_inc() here, but it is faster
- * to set the final value.
- */
+ atomic_read(&fclones->fclone_ref) == 1) {
+ n = &fclones->skb2;
atomic_set(&fclones->fclone_ref, 2);
} else {
if (skb_pfmemalloc(skb))
@@ -3013,7 +3100,7 @@ struct sk_buff *skb_segment(struct sk_buff *head_skb,
if (nskb->len == len + doffset)
goto perform_csum_check;
- if (!sg) {
+ if (!sg && !nskb->remcsum_offload) {
nskb->ip_summed = CHECKSUM_NONE;
nskb->csum = skb_copy_and_csum_bits(head_skb, offset,
skb_put(nskb, len),
@@ -3085,7 +3172,7 @@ skip_fraglist:
nskb->truesize += nskb->data_len;
perform_csum_check:
- if (!csum) {
+ if (!csum && !nskb->remcsum_offload) {
nskb->csum = skb_checksum(nskb, doffset,
nskb->len - doffset, 0);
nskb->ip_summed = CHECKSUM_NONE;
@@ -3099,6 +3186,16 @@ perform_csum_check:
* (see validate_xmit_skb_list() for example)
*/
segs->prev = tail;
+
+ /* Following permits correct backpressure, for protocols
+ * using skb_set_owner_w().
+ * Idea is to tranfert ownership from head_skb to last segment.
+ */
+ if (head_skb->destructor == sock_wfree) {
+ swap(tail->truesize, head_skb->truesize);
+ swap(tail->destructor, head_skb->destructor);
+ swap(tail->sk, head_skb->sk);
+ }
return segs;
err:
@@ -4141,6 +4238,113 @@ err_free:
}
EXPORT_SYMBOL(skb_vlan_untag);
+int skb_ensure_writable(struct sk_buff *skb, int write_len)
+{
+ if (!pskb_may_pull(skb, write_len))
+ return -ENOMEM;
+
+ if (!skb_cloned(skb) || skb_clone_writable(skb, write_len))
+ return 0;
+
+ return pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
+}
+EXPORT_SYMBOL(skb_ensure_writable);
+
+/* remove VLAN header from packet and update csum accordingly. */
+static int __skb_vlan_pop(struct sk_buff *skb, u16 *vlan_tci)
+{
+ struct vlan_hdr *vhdr;
+ unsigned int offset = skb->data - skb_mac_header(skb);
+ int err;
+
+ __skb_push(skb, offset);
+ err = skb_ensure_writable(skb, VLAN_ETH_HLEN);
+ if (unlikely(err))
+ goto pull;
+
+ skb_postpull_rcsum(skb, skb->data + (2 * ETH_ALEN), VLAN_HLEN);
+
+ vhdr = (struct vlan_hdr *)(skb->data + ETH_HLEN);
+ *vlan_tci = ntohs(vhdr->h_vlan_TCI);
+
+ memmove(skb->data + VLAN_HLEN, skb->data, 2 * ETH_ALEN);
+ __skb_pull(skb, VLAN_HLEN);
+
+ vlan_set_encap_proto(skb, vhdr);
+ skb->mac_header += VLAN_HLEN;
+
+ if (skb_network_offset(skb) < ETH_HLEN)
+ skb_set_network_header(skb, ETH_HLEN);
+
+ skb_reset_mac_len(skb);
+pull:
+ __skb_pull(skb, offset);
+
+ return err;
+}
+
+int skb_vlan_pop(struct sk_buff *skb)
+{
+ u16 vlan_tci;
+ __be16 vlan_proto;
+ int err;
+
+ if (likely(vlan_tx_tag_present(skb))) {
+ skb->vlan_tci = 0;
+ } else {
+ if (unlikely((skb->protocol != htons(ETH_P_8021Q) &&
+ skb->protocol != htons(ETH_P_8021AD)) ||
+ skb->len < VLAN_ETH_HLEN))
+ return 0;
+
+ err = __skb_vlan_pop(skb, &vlan_tci);
+ if (err)
+ return err;
+ }
+ /* move next vlan tag to hw accel tag */
+ if (likely((skb->protocol != htons(ETH_P_8021Q) &&
+ skb->protocol != htons(ETH_P_8021AD)) ||
+ skb->len < VLAN_ETH_HLEN))
+ return 0;
+
+ vlan_proto = skb->protocol;
+ err = __skb_vlan_pop(skb, &vlan_tci);
+ if (unlikely(err))
+ return err;
+
+ __vlan_hwaccel_put_tag(skb, vlan_proto, vlan_tci);
+ return 0;
+}
+EXPORT_SYMBOL(skb_vlan_pop);
+
+int skb_vlan_push(struct sk_buff *skb, __be16 vlan_proto, u16 vlan_tci)
+{
+ if (vlan_tx_tag_present(skb)) {
+ unsigned int offset = skb->data - skb_mac_header(skb);
+ int err;
+
+ /* __vlan_insert_tag expect skb->data pointing to mac header.
+ * So change skb->data before calling it and change back to
+ * original position later
+ */
+ __skb_push(skb, offset);
+ err = __vlan_insert_tag(skb, skb->vlan_proto,
+ vlan_tx_tag_get(skb));
+ if (err)
+ return err;
+ skb->protocol = skb->vlan_proto;
+ skb->mac_len += VLAN_HLEN;
+ __skb_pull(skb, offset);
+
+ if (skb->ip_summed == CHECKSUM_COMPLETE)
+ skb->csum = csum_add(skb->csum, csum_partial(skb->data
+ + (2 * ETH_ALEN), VLAN_HLEN, 0));
+ }
+ __vlan_hwaccel_put_tag(skb, vlan_proto, vlan_tci);
+ return 0;
+}
+EXPORT_SYMBOL(skb_vlan_push);
+
/**
* alloc_skb_with_frags - allocate skb with page frags
*
diff --git a/net/core/sock.c b/net/core/sock.c
index 04ce26a996bd..1c7a33db1314 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -888,6 +888,19 @@ set_rcvbuf:
}
break;
+ case SO_ATTACH_BPF:
+ ret = -EINVAL;
+ if (optlen == sizeof(u32)) {
+ u32 ufd;
+
+ ret = -EFAULT;
+ if (copy_from_user(&ufd, optval, sizeof(ufd)))
+ break;
+
+ ret = sk_attach_bpf(ufd, sk);
+ }
+ break;
+
case SO_DETACH_FILTER:
ret = sk_detach_filter(sk);
break;
@@ -1213,6 +1226,10 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
v.val = sk->sk_max_pacing_rate;
break;
+ case SO_INCOMING_CPU:
+ v.val = sk->sk_incoming_cpu;
+ break;
+
default:
return -ENOPROTOOPT;
}
@@ -1517,6 +1534,7 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority)
newsk->sk_err = 0;
newsk->sk_priority = 0;
+ newsk->sk_incoming_cpu = raw_smp_processor_id();
/*
* Before updating sk_refcnt, we must commit prior changes to memory
* (Documentation/RCU/rculist_nulls.txt for details)
@@ -2473,7 +2491,7 @@ int sock_recv_errqueue(struct sock *sk, struct msghdr *msg, int len,
msg->msg_flags |= MSG_TRUNC;
copied = len;
}
- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
if (err)
goto out_free_skb;
diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c
index cf9cd13509a7..31baba2a71ce 100644
--- a/net/core/sysctl_net_core.c
+++ b/net/core/sysctl_net_core.c
@@ -26,6 +26,8 @@ static int zero = 0;
static int one = 1;
static int ushort_max = USHRT_MAX;
+static int net_msg_warn; /* Unused, but still a sysctl */
+
#ifdef CONFIG_RPS
static int rps_sock_flow_sysctl(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
@@ -215,6 +217,18 @@ static int set_default_qdisc(struct ctl_table *table, int write,
}
#endif
+static int proc_do_rss_key(struct ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ struct ctl_table fake_table;
+ char buf[NETDEV_RSS_KEY_LEN * 3];
+
+ snprintf(buf, sizeof(buf), "%*phC", NETDEV_RSS_KEY_LEN, netdev_rss_key);
+ fake_table.data = buf;
+ fake_table.maxlen = sizeof(buf);
+ return proc_dostring(&fake_table, write, buffer, lenp, ppos);
+}
+
static struct ctl_table net_core_table[] = {
#ifdef CONFIG_NET
{
@@ -263,6 +277,13 @@ static struct ctl_table net_core_table[] = {
.mode = 0644,
.proc_handler = proc_dointvec
},
+ {
+ .procname = "netdev_rss_key",
+ .data = &netdev_rss_key,
+ .maxlen = sizeof(int),
+ .mode = 0444,
+ .proc_handler = proc_do_rss_key,
+ },
#ifdef CONFIG_BPF_JIT
{
.procname = "bpf_jit_enable",
diff --git a/net/core/utils.c b/net/core/utils.c
index efc76dd9dcd1..7b803884c162 100644
--- a/net/core/utils.c
+++ b/net/core/utils.c
@@ -33,9 +33,6 @@
#include <asm/byteorder.h>
#include <asm/uaccess.h>
-int net_msg_warn __read_mostly = 1;
-EXPORT_SYMBOL(net_msg_warn);
-
DEFINE_RATELIMIT_STATE(net_ratelimit_state, 5 * HZ, 10);
/*
* All net warning printk()s should be guarded by this function.
diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c
index ca11d283bbeb..93ea80196f0e 100644
--- a/net/dcb/dcbnl.c
+++ b/net/dcb/dcbnl.c
@@ -1080,13 +1080,13 @@ static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev)
if (!app)
return -EMSGSIZE;
- spin_lock(&dcb_lock);
+ spin_lock_bh(&dcb_lock);
list_for_each_entry(itr, &dcb_app_list, list) {
if (itr->ifindex == netdev->ifindex) {
err = nla_put(skb, DCB_ATTR_IEEE_APP, sizeof(itr->app),
&itr->app);
if (err) {
- spin_unlock(&dcb_lock);
+ spin_unlock_bh(&dcb_lock);
return -EMSGSIZE;
}
}
@@ -1097,7 +1097,7 @@ static int dcbnl_ieee_fill(struct sk_buff *skb, struct net_device *netdev)
else
dcbx = -EOPNOTSUPP;
- spin_unlock(&dcb_lock);
+ spin_unlock_bh(&dcb_lock);
nla_nest_end(skb, app);
/* get peer info if available */
@@ -1234,7 +1234,7 @@ static int dcbnl_cee_fill(struct sk_buff *skb, struct net_device *netdev)
}
/* local app */
- spin_lock(&dcb_lock);
+ spin_lock_bh(&dcb_lock);
app = nla_nest_start(skb, DCB_ATTR_CEE_APP_TABLE);
if (!app)
goto dcb_unlock;
@@ -1271,7 +1271,7 @@ static int dcbnl_cee_fill(struct sk_buff *skb, struct net_device *netdev)
else
dcbx = -EOPNOTSUPP;
- spin_unlock(&dcb_lock);
+ spin_unlock_bh(&dcb_lock);
/* features flags */
if (ops->getfeatcfg) {
@@ -1326,7 +1326,7 @@ static int dcbnl_cee_fill(struct sk_buff *skb, struct net_device *netdev)
return 0;
dcb_unlock:
- spin_unlock(&dcb_lock);
+ spin_unlock_bh(&dcb_lock);
nla_put_failure:
return err;
}
@@ -1762,10 +1762,10 @@ u8 dcb_getapp(struct net_device *dev, struct dcb_app *app)
struct dcb_app_type *itr;
u8 prio = 0;
- spin_lock(&dcb_lock);
+ spin_lock_bh(&dcb_lock);
if ((itr = dcb_app_lookup(app, dev->ifindex, 0)))
prio = itr->app.priority;
- spin_unlock(&dcb_lock);
+ spin_unlock_bh(&dcb_lock);
return prio;
}
@@ -1789,7 +1789,7 @@ int dcb_setapp(struct net_device *dev, struct dcb_app *new)
if (dev->dcbnl_ops->getdcbx)
event.dcbx = dev->dcbnl_ops->getdcbx(dev);
- spin_lock(&dcb_lock);
+ spin_lock_bh(&dcb_lock);
/* Search for existing match and replace */
if ((itr = dcb_app_lookup(new, dev->ifindex, 0))) {
if (new->priority)
@@ -1804,7 +1804,7 @@ int dcb_setapp(struct net_device *dev, struct dcb_app *new)
if (new->priority)
err = dcb_app_add(new, dev->ifindex);
out:
- spin_unlock(&dcb_lock);
+ spin_unlock_bh(&dcb_lock);
if (!err)
call_dcbevent_notifiers(DCB_APP_EVENT, &event);
return err;
@@ -1823,10 +1823,10 @@ u8 dcb_ieee_getapp_mask(struct net_device *dev, struct dcb_app *app)
struct dcb_app_type *itr;
u8 prio = 0;
- spin_lock(&dcb_lock);
+ spin_lock_bh(&dcb_lock);
if ((itr = dcb_app_lookup(app, dev->ifindex, 0)))
prio |= 1 << itr->app.priority;
- spin_unlock(&dcb_lock);
+ spin_unlock_bh(&dcb_lock);
return prio;
}
@@ -1850,7 +1850,7 @@ int dcb_ieee_setapp(struct net_device *dev, struct dcb_app *new)
if (dev->dcbnl_ops->getdcbx)
event.dcbx = dev->dcbnl_ops->getdcbx(dev);
- spin_lock(&dcb_lock);
+ spin_lock_bh(&dcb_lock);
/* Search for existing match and abort if found */
if (dcb_app_lookup(new, dev->ifindex, new->priority)) {
err = -EEXIST;
@@ -1859,7 +1859,7 @@ int dcb_ieee_setapp(struct net_device *dev, struct dcb_app *new)
err = dcb_app_add(new, dev->ifindex);
out:
- spin_unlock(&dcb_lock);
+ spin_unlock_bh(&dcb_lock);
if (!err)
call_dcbevent_notifiers(DCB_APP_EVENT, &event);
return err;
@@ -1882,7 +1882,7 @@ int dcb_ieee_delapp(struct net_device *dev, struct dcb_app *del)
if (dev->dcbnl_ops->getdcbx)
event.dcbx = dev->dcbnl_ops->getdcbx(dev);
- spin_lock(&dcb_lock);
+ spin_lock_bh(&dcb_lock);
/* Search for existing match and remove it. */
if ((itr = dcb_app_lookup(del, dev->ifindex, del->priority))) {
list_del(&itr->list);
@@ -1890,7 +1890,7 @@ int dcb_ieee_delapp(struct net_device *dev, struct dcb_app *del)
err = 0;
}
- spin_unlock(&dcb_lock);
+ spin_unlock_bh(&dcb_lock);
if (!err)
call_dcbevent_notifiers(DCB_APP_EVENT, &event);
return err;
@@ -1902,12 +1902,12 @@ static void dcb_flushapp(void)
struct dcb_app_type *app;
struct dcb_app_type *tmp;
- spin_lock(&dcb_lock);
+ spin_lock_bh(&dcb_lock);
list_for_each_entry_safe(app, tmp, &dcb_app_list, list) {
list_del(&app->list);
kfree(app);
}
- spin_unlock(&dcb_lock);
+ spin_unlock_bh(&dcb_lock);
}
static int __init dcbnl_init(void)
diff --git a/net/dccp/ackvec.c b/net/dccp/ackvec.c
index ba07824af4c0..bd9e718c2a20 100644
--- a/net/dccp/ackvec.c
+++ b/net/dccp/ackvec.c
@@ -218,7 +218,7 @@ static void dccp_ackvec_add_new(struct dccp_ackvec *av, u32 num_packets,
* different underlying data structure.
*/
for (num_packets = num_cells = 1; lost_packets; ++num_cells) {
- u8 len = min(lost_packets, (u32)DCCPAV_MAX_RUNLEN);
+ u8 len = min_t(u32, lost_packets, DCCPAV_MAX_RUNLEN);
av->av_buf_head = __ackvec_idx_sub(av->av_buf_head, 1);
av->av_buf[av->av_buf_head] = DCCPAV_NOT_RECEIVED | len;
diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h
index c67816647cce..e4c144fa706f 100644
--- a/net/dccp/dccp.h
+++ b/net/dccp/dccp.h
@@ -22,8 +22,8 @@
/*
* DCCP - specific warning and debugging macros.
*/
-#define DCCP_WARN(fmt, a...) LIMIT_NETDEBUG(KERN_WARNING "%s: " fmt, \
- __func__, ##a)
+#define DCCP_WARN(fmt, ...) \
+ net_warn_ratelimited("%s: " fmt, __func__, ##__VA_ARGS__)
#define DCCP_CRIT(fmt, a...) printk(KERN_CRIT fmt " at %s:%d/%s()\n", ##a, \
__FILE__, __LINE__, __func__)
#define DCCP_BUG(a...) do { DCCP_CRIT("BUG: " a); dump_stack(); } while(0)
diff --git a/net/dccp/feat.c b/net/dccp/feat.c
index 9733ddbc96cb..1704948e6a12 100644
--- a/net/dccp/feat.c
+++ b/net/dccp/feat.c
@@ -478,7 +478,7 @@ static struct dccp_feat_entry *
* @fn_list: feature-negotiation list to update
* @feat: one of %dccp_feature_numbers
* @local: whether local (1) or remote (0) @feat_num is meant
- * @needs_mandatory: whether to use Mandatory feature negotiation options
+ * @mandatory: whether to use Mandatory feature negotiation options
* @fval: pointer to NN/SP value to be inserted (will be copied)
*/
static int dccp_feat_push_change(struct list_head *fn_list, u8 feat, u8 local,
@@ -1050,7 +1050,7 @@ static u8 dccp_feat_prefer(u8 preferred_value, u8 *array, u8 array_len)
/**
* dccp_feat_reconcile - Reconcile SP preference lists
- * @fval: SP list to reconcile into
+ * @fv: SP list to reconcile into
* @arr: received SP preference list
* @len: length of @arr in bytes
* @is_server: whether this side is the server (and @fv is the server's list)
diff --git a/net/dccp/input.c b/net/dccp/input.c
index 3c8ec7d4a34e..3bd14e885396 100644
--- a/net/dccp/input.c
+++ b/net/dccp/input.c
@@ -537,7 +537,7 @@ static int dccp_rcv_respond_partopen_state_process(struct sock *sk,
case DCCP_PKT_DATAACK:
case DCCP_PKT_ACK:
/*
- * FIXME: we should be reseting the PARTOPEN (DELACK) timer
+ * FIXME: we should be resetting the PARTOPEN (DELACK) timer
* here but only if we haven't used the DELACK timer for
* something else, like sending a delayed ack for a TIMESTAMP
* echo, etc, for now were not clearing it, sending an extra
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index 6ca645c4b48e..e45b968613a4 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -140,7 +140,6 @@ failure:
inet->inet_dport = 0;
goto out;
}
-
EXPORT_SYMBOL_GPL(dccp_v4_connect);
/*
@@ -376,7 +375,6 @@ void dccp_v4_send_check(struct sock *sk, struct sk_buff *skb)
inet->inet_saddr,
inet->inet_daddr);
}
-
EXPORT_SYMBOL_GPL(dccp_v4_send_check);
static inline u64 dccp_v4_init_sequence(const struct sk_buff *skb)
@@ -444,7 +442,6 @@ put_and_exit:
dccp_done(newsk);
goto exit;
}
-
EXPORT_SYMBOL_GPL(dccp_v4_request_recv_sock);
static struct sock *dccp_v4_hnd_req(struct sock *sk, struct sk_buff *skb)
@@ -670,7 +667,6 @@ drop:
DCCP_INC_STATS_BH(DCCP_MIB_ATTEMPTFAILS);
return -1;
}
-
EXPORT_SYMBOL_GPL(dccp_v4_conn_request);
int dccp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
@@ -729,7 +725,6 @@ discard:
kfree_skb(skb);
return 0;
}
-
EXPORT_SYMBOL_GPL(dccp_v4_do_rcv);
/**
@@ -802,7 +797,6 @@ int dccp_invalid_packet(struct sk_buff *skb)
return 0;
}
-
EXPORT_SYMBOL_GPL(dccp_invalid_packet);
/* this is called when real data arrives */
diff --git a/net/dccp/proto.c b/net/dccp/proto.c
index 5ab6627cf370..e171b780b499 100644
--- a/net/dccp/proto.c
+++ b/net/dccp/proto.c
@@ -703,7 +703,7 @@ EXPORT_SYMBOL_GPL(compat_dccp_getsockopt);
static int dccp_msghdr_parse(struct msghdr *msg, struct sk_buff *skb)
{
- struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg);
+ struct cmsghdr *cmsg;
/*
* Assign an (opaque) qpolicy priority value to skb->priority.
@@ -717,8 +717,7 @@ static int dccp_msghdr_parse(struct msghdr *msg, struct sk_buff *skb)
*/
skb->priority = 0;
- for (; cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) {
-
+ for_each_cmsghdr(cmsg, msg) {
if (!CMSG_OK(msg, cmsg))
return -EINVAL;
@@ -781,7 +780,7 @@ int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
goto out_release;
skb_reserve(skb, sk->sk_prot->max_header);
- rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
+ rc = memcpy_from_msg(skb_put(skb, len), msg, len);
if (rc != 0)
goto out_discard;
@@ -896,7 +895,7 @@ verify_sock_status:
else if (len < skb->len)
msg->msg_flags |= MSG_TRUNC;
- if (skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len)) {
+ if (skb_copy_datagram_msg(skb, 0, msg, len)) {
/* Exception. Bailout! */
len = -EFAULT;
break;
diff --git a/net/decnet/af_decnet.c b/net/decnet/af_decnet.c
index 25733d538147..810228646de3 100644
--- a/net/decnet/af_decnet.c
+++ b/net/decnet/af_decnet.c
@@ -1760,7 +1760,7 @@ static int dn_recvmsg(struct kiocb *iocb, struct socket *sock,
if ((chunk + copied) > size)
chunk = size - copied;
- if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) {
+ if (memcpy_to_msg(msg, skb->data, chunk)) {
rv = -EFAULT;
break;
}
@@ -2032,7 +2032,7 @@ static int dn_sendmsg(struct kiocb *iocb, struct socket *sock,
skb_reserve(skb, 64 + DN_MAX_NSP_DATA_HEADER);
- if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
+ if (memcpy_from_msg(skb_put(skb, len), msg, len)) {
err = -EFAULT;
goto out;
}
diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c
index c8121ceddb9e..7ca7c3143da3 100644
--- a/net/decnet/dn_neigh.c
+++ b/net/decnet/dn_neigh.c
@@ -591,7 +591,7 @@ static const struct file_operations dn_neigh_seq_fops = {
void __init dn_neigh_init(void)
{
- neigh_table_init(&dn_neigh_table);
+ neigh_table_init(NEIGH_DN_TABLE, &dn_neigh_table);
proc_create("decnet_neigh", S_IRUGO, init_net.proc_net,
&dn_neigh_seq_fops);
}
@@ -599,5 +599,5 @@ void __init dn_neigh_init(void)
void __exit dn_neigh_cleanup(void)
{
remove_proc_entry("decnet_neigh", init_net.proc_net);
- neigh_table_clear(&dn_neigh_table);
+ neigh_table_clear(NEIGH_DN_TABLE, &dn_neigh_table);
}
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index a585fd6352eb..5f8ac404535b 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -11,6 +11,17 @@ config NET_DSA
if NET_DSA
+config NET_DSA_HWMON
+ bool "Distributed Switch Architecture HWMON support"
+ default y
+ depends on HWMON && !(NET_DSA=y && HWMON=m)
+ ---help---
+ Say Y if you want to expose thermal sensor data on switches supported
+ by the Distributed Switch Architecture.
+
+ Some of those switches contain thermal sensors. This data is available
+ via the hwmon sysfs interface and exposes the onboard sensors.
+
# tagging formats
config NET_DSA_TAG_BRCM
bool
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index 6317b41c99b0..322c778487e7 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -9,6 +9,9 @@
* (at your option) any later version.
*/
+#include <linux/ctype.h>
+#include <linux/device.h>
+#include <linux/hwmon.h>
#include <linux/list.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -17,6 +20,7 @@
#include <linux/of.h>
#include <linux/of_mdio.h>
#include <linux/of_platform.h>
+#include <linux/sysfs.h>
#include "dsa_priv.h"
char dsa_driver_version[] = "0.1";
@@ -71,6 +75,104 @@ dsa_switch_probe(struct device *host_dev, int sw_addr, char **_name)
return ret;
}
+/* hwmon support ************************************************************/
+
+#ifdef CONFIG_NET_DSA_HWMON
+
+static ssize_t temp1_input_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dsa_switch *ds = dev_get_drvdata(dev);
+ int temp, ret;
+
+ ret = ds->drv->get_temp(ds, &temp);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", temp * 1000);
+}
+static DEVICE_ATTR_RO(temp1_input);
+
+static ssize_t temp1_max_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dsa_switch *ds = dev_get_drvdata(dev);
+ int temp, ret;
+
+ ret = ds->drv->get_temp_limit(ds, &temp);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", temp * 1000);
+}
+
+static ssize_t temp1_max_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct dsa_switch *ds = dev_get_drvdata(dev);
+ int temp, ret;
+
+ ret = kstrtoint(buf, 0, &temp);
+ if (ret < 0)
+ return ret;
+
+ ret = ds->drv->set_temp_limit(ds, DIV_ROUND_CLOSEST(temp, 1000));
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+static DEVICE_ATTR(temp1_max, S_IRUGO, temp1_max_show, temp1_max_store);
+
+static ssize_t temp1_max_alarm_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct dsa_switch *ds = dev_get_drvdata(dev);
+ bool alarm;
+ int ret;
+
+ ret = ds->drv->get_temp_alarm(ds, &alarm);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", alarm);
+}
+static DEVICE_ATTR_RO(temp1_max_alarm);
+
+static struct attribute *dsa_hwmon_attrs[] = {
+ &dev_attr_temp1_input.attr, /* 0 */
+ &dev_attr_temp1_max.attr, /* 1 */
+ &dev_attr_temp1_max_alarm.attr, /* 2 */
+ NULL
+};
+
+static umode_t dsa_hwmon_attrs_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct dsa_switch *ds = dev_get_drvdata(dev);
+ struct dsa_switch_driver *drv = ds->drv;
+ umode_t mode = attr->mode;
+
+ if (index == 1) {
+ if (!drv->get_temp_limit)
+ mode = 0;
+ else if (drv->set_temp_limit)
+ mode |= S_IWUSR;
+ } else if (index == 2 && !drv->get_temp_alarm) {
+ mode = 0;
+ }
+ return mode;
+}
+
+static const struct attribute_group dsa_hwmon_group = {
+ .attrs = dsa_hwmon_attrs,
+ .is_visible = dsa_hwmon_attrs_visible,
+};
+__ATTRIBUTE_GROUPS(dsa_hwmon);
+
+#endif /* CONFIG_NET_DSA_HWMON */
/* basic switch operations **************************************************/
static struct dsa_switch *
@@ -90,12 +192,12 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index,
*/
drv = dsa_switch_probe(host_dev, pd->sw_addr, &name);
if (drv == NULL) {
- printk(KERN_ERR "%s[%d]: could not detect attached switch\n",
- dst->master_netdev->name, index);
+ netdev_err(dst->master_netdev, "[%d]: could not detect attached switch\n",
+ index);
return ERR_PTR(-EINVAL);
}
- printk(KERN_INFO "%s[%d]: detected a %s switch\n",
- dst->master_netdev->name, index, name);
+ netdev_info(dst->master_netdev, "[%d]: detected a %s switch\n",
+ index, name);
/*
@@ -123,7 +225,8 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index,
if (!strcmp(name, "cpu")) {
if (dst->cpu_switch != -1) {
- printk(KERN_ERR "multiple cpu ports?!\n");
+ netdev_err(dst->master_netdev,
+ "multiple cpu ports?!\n");
ret = -EINVAL;
goto out;
}
@@ -218,16 +321,39 @@ dsa_switch_setup(struct dsa_switch_tree *dst, int index,
slave_dev = dsa_slave_create(ds, parent, i, pd->port_names[i]);
if (slave_dev == NULL) {
- printk(KERN_ERR "%s[%d]: can't create dsa "
- "slave device for port %d(%s)\n",
- dst->master_netdev->name,
- index, i, pd->port_names[i]);
+ netdev_err(dst->master_netdev, "[%d]: can't create dsa slave device for port %d(%s)\n",
+ index, i, pd->port_names[i]);
continue;
}
ds->ports[i] = slave_dev;
}
+#ifdef CONFIG_NET_DSA_HWMON
+ /* If the switch provides a temperature sensor,
+ * register with hardware monitoring subsystem.
+ * Treat registration error as non-fatal and ignore it.
+ */
+ if (drv->get_temp) {
+ const char *netname = netdev_name(dst->master_netdev);
+ char hname[IFNAMSIZ + 1];
+ int i, j;
+
+ /* Create valid hwmon 'name' attribute */
+ for (i = j = 0; i < IFNAMSIZ && netname[i]; i++) {
+ if (isalnum(netname[i]))
+ hname[j++] = netname[i];
+ }
+ hname[j] = '\0';
+ scnprintf(ds->hwmon_name, sizeof(ds->hwmon_name), "%s_dsa%d",
+ hname, index);
+ ds->hwmon_dev = hwmon_device_register_with_groups(NULL,
+ ds->hwmon_name, ds, dsa_hwmon_groups);
+ if (IS_ERR(ds->hwmon_dev))
+ ds->hwmon_dev = NULL;
+ }
+#endif /* CONFIG_NET_DSA_HWMON */
+
return ds;
out_free:
@@ -239,6 +365,10 @@ out:
static void dsa_switch_destroy(struct dsa_switch *ds)
{
+#ifdef CONFIG_NET_DSA_HWMON
+ if (ds->hwmon_dev)
+ hwmon_device_unregister(ds->hwmon_dev);
+#endif
}
#ifdef CONFIG_PM_SLEEP
@@ -396,7 +526,8 @@ static int dsa_of_setup_routing_table(struct dsa_platform_data *pd,
/* First time routing table allocation */
if (!cd->rtable) {
- cd->rtable = kmalloc(pd->nr_chips * sizeof(s8), GFP_KERNEL);
+ cd->rtable = kmalloc_array(pd->nr_chips, sizeof(s8),
+ GFP_KERNEL);
if (!cd->rtable)
return -ENOMEM;
@@ -447,6 +578,7 @@ static int dsa_of_probe(struct platform_device *pdev)
const char *port_name;
int chip_index, port_index;
const unsigned int *sw_addr, *port_reg;
+ u32 eeprom_len;
int ret;
mdio = of_parse_phandle(np, "dsa,mii-bus", 0);
@@ -475,8 +607,8 @@ static int dsa_of_probe(struct platform_device *pdev)
if (pd->nr_chips > DSA_MAX_SWITCHES)
pd->nr_chips = DSA_MAX_SWITCHES;
- pd->chip = kzalloc(pd->nr_chips * sizeof(struct dsa_chip_data),
- GFP_KERNEL);
+ pd->chip = kcalloc(pd->nr_chips, sizeof(struct dsa_chip_data),
+ GFP_KERNEL);
if (!pd->chip) {
ret = -ENOMEM;
goto out_free;
@@ -498,6 +630,9 @@ static int dsa_of_probe(struct platform_device *pdev)
if (cd->sw_addr > PHY_MAX_ADDR)
continue;
+ if (!of_property_read_u32(np, "eeprom-length", &eeprom_len))
+ cd->eeprom_len = eeprom_len;
+
for_each_available_child_of_node(child, port) {
port_reg = of_get_property(port, "reg", NULL);
if (!port_reg)
@@ -566,15 +701,13 @@ static inline void dsa_of_remove(struct platform_device *pdev)
static int dsa_probe(struct platform_device *pdev)
{
- static int dsa_version_printed;
struct dsa_platform_data *pd = pdev->dev.platform_data;
struct net_device *dev;
struct dsa_switch_tree *dst;
int i, ret;
- if (!dsa_version_printed++)
- printk(KERN_NOTICE "Distributed Switch Architecture "
- "driver version %s\n", dsa_driver_version);
+ pr_notice_once("Distributed Switch Architecture driver version %s\n",
+ dsa_driver_version);
if (pdev->dev.of_node) {
ret = dsa_of_probe(pdev);
@@ -618,9 +751,8 @@ static int dsa_probe(struct platform_device *pdev)
ds = dsa_switch_setup(dst, i, &pdev->dev, pd->chip[i].host_dev);
if (IS_ERR(ds)) {
- printk(KERN_ERR "%s[%d]: couldn't create dsa switch "
- "instance (error %ld)\n", dev->name, i,
- PTR_ERR(ds));
+ netdev_err(dev, "[%d]: couldn't create dsa switch instance (error %ld)\n",
+ i, PTR_ERR(ds));
continue;
}
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 6d1817449c36..515569ffde8a 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -249,6 +249,27 @@ static void dsa_slave_get_drvinfo(struct net_device *dev,
strlcpy(drvinfo->bus_info, "platform", sizeof(drvinfo->bus_info));
}
+static int dsa_slave_get_regs_len(struct net_device *dev)
+{
+ struct dsa_slave_priv *p = netdev_priv(dev);
+ struct dsa_switch *ds = p->parent;
+
+ if (ds->drv->get_regs_len)
+ return ds->drv->get_regs_len(ds, p->port);
+
+ return -EOPNOTSUPP;
+}
+
+static void
+dsa_slave_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *_p)
+{
+ struct dsa_slave_priv *p = netdev_priv(dev);
+ struct dsa_switch *ds = p->parent;
+
+ if (ds->drv->get_regs)
+ ds->drv->get_regs(ds, p->port, regs, _p);
+}
+
static int dsa_slave_nway_reset(struct net_device *dev)
{
struct dsa_slave_priv *p = netdev_priv(dev);
@@ -271,6 +292,44 @@ static u32 dsa_slave_get_link(struct net_device *dev)
return -EOPNOTSUPP;
}
+static int dsa_slave_get_eeprom_len(struct net_device *dev)
+{
+ struct dsa_slave_priv *p = netdev_priv(dev);
+ struct dsa_switch *ds = p->parent;
+
+ if (ds->pd->eeprom_len)
+ return ds->pd->eeprom_len;
+
+ if (ds->drv->get_eeprom_len)
+ return ds->drv->get_eeprom_len(ds);
+
+ return 0;
+}
+
+static int dsa_slave_get_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ struct dsa_slave_priv *p = netdev_priv(dev);
+ struct dsa_switch *ds = p->parent;
+
+ if (ds->drv->get_eeprom)
+ return ds->drv->get_eeprom(ds, eeprom, data);
+
+ return -EOPNOTSUPP;
+}
+
+static int dsa_slave_set_eeprom(struct net_device *dev,
+ struct ethtool_eeprom *eeprom, u8 *data)
+{
+ struct dsa_slave_priv *p = netdev_priv(dev);
+ struct dsa_switch *ds = p->parent;
+
+ if (ds->drv->set_eeprom)
+ return ds->drv->set_eeprom(ds, eeprom, data);
+
+ return -EOPNOTSUPP;
+}
+
static void dsa_slave_get_strings(struct net_device *dev,
uint32_t stringset, uint8_t *data)
{
@@ -385,8 +444,13 @@ static const struct ethtool_ops dsa_slave_ethtool_ops = {
.get_settings = dsa_slave_get_settings,
.set_settings = dsa_slave_set_settings,
.get_drvinfo = dsa_slave_get_drvinfo,
+ .get_regs_len = dsa_slave_get_regs_len,
+ .get_regs = dsa_slave_get_regs,
.nway_reset = dsa_slave_nway_reset,
.get_link = dsa_slave_get_link,
+ .get_eeprom_len = dsa_slave_get_eeprom_len,
+ .get_eeprom = dsa_slave_get_eeprom,
+ .set_eeprom = dsa_slave_set_eeprom,
.get_strings = dsa_slave_get_strings,
.get_ethtool_stats = dsa_slave_get_ethtool_stats,
.get_sset_count = dsa_slave_get_sset_count,
@@ -448,7 +512,7 @@ static int dsa_slave_fixed_link_update(struct net_device *dev,
}
/* slave device setup *******************************************************/
-static void dsa_slave_phy_setup(struct dsa_slave_priv *p,
+static int dsa_slave_phy_setup(struct dsa_slave_priv *p,
struct net_device *slave_dev)
{
struct dsa_switch *ds = p->parent;
@@ -468,8 +532,8 @@ static void dsa_slave_phy_setup(struct dsa_slave_priv *p,
*/
ret = of_phy_register_fixed_link(port_dn);
if (ret) {
- pr_err("failed to register fixed PHY\n");
- return;
+ netdev_err(slave_dev, "failed to register fixed PHY\n");
+ return ret;
}
phy_is_fixed = true;
phy_dn = port_dn;
@@ -489,11 +553,19 @@ static void dsa_slave_phy_setup(struct dsa_slave_priv *p,
/* We could not connect to a designated PHY, so use the switch internal
* MDIO bus instead
*/
- if (!p->phy)
+ if (!p->phy) {
p->phy = ds->slave_mii_bus->phy_map[p->port];
- else
- pr_info("attached PHY at address %d [%s]\n",
- p->phy->addr, p->phy->drv->name);
+ if (!p->phy)
+ return -ENODEV;
+
+ phy_connect_direct(slave_dev, p->phy, dsa_slave_adjust_link,
+ p->phy_interface);
+ } else {
+ netdev_info(slave_dev, "attached PHY at address %d [%s]\n",
+ p->phy->addr, p->phy->drv->name);
+ }
+
+ return 0;
}
int dsa_slave_suspend(struct net_device *slave_dev)
@@ -586,12 +658,17 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent,
p->old_link = -1;
p->old_duplex = -1;
- dsa_slave_phy_setup(p, slave_dev);
+ ret = dsa_slave_phy_setup(p, slave_dev);
+ if (ret) {
+ free_netdev(slave_dev);
+ return NULL;
+ }
ret = register_netdev(slave_dev);
if (ret) {
- printk(KERN_ERR "%s: error %d registering interface %s\n",
- master->name, ret, slave_dev->name);
+ netdev_err(master, "error %d registering interface %s\n",
+ ret, slave_dev->name);
+ phy_disconnect(p->phy);
free_netdev(slave_dev);
return NULL;
}
diff --git a/net/dsa/tag_dsa.c b/net/dsa/tag_dsa.c
index ce90c8bdc658..2dab27063273 100644
--- a/net/dsa/tag_dsa.c
+++ b/net/dsa/tag_dsa.c
@@ -63,8 +63,6 @@ static netdev_tx_t dsa_xmit(struct sk_buff *skb, struct net_device *dev)
dsa_header[3] = 0x00;
}
- skb->protocol = htons(ETH_P_DSA);
-
skb->dev = p->parent->dst->master_netdev;
dev_queue_xmit(skb);
diff --git a/net/dsa/tag_edsa.c b/net/dsa/tag_edsa.c
index 94fcce778679..9aeda596f7ec 100644
--- a/net/dsa/tag_edsa.c
+++ b/net/dsa/tag_edsa.c
@@ -76,8 +76,6 @@ static netdev_tx_t edsa_xmit(struct sk_buff *skb, struct net_device *dev)
edsa_header[7] = 0x00;
}
- skb->protocol = htons(ETH_P_EDSA);
-
skb->dev = p->parent->dst->master_netdev;
dev_queue_xmit(skb);
diff --git a/net/dsa/tag_trailer.c b/net/dsa/tag_trailer.c
index 115fdca34077..e268f9db8893 100644
--- a/net/dsa/tag_trailer.c
+++ b/net/dsa/tag_trailer.c
@@ -57,8 +57,6 @@ static netdev_tx_t trailer_xmit(struct sk_buff *skb, struct net_device *dev)
trailer[2] = 0x10;
trailer[3] = 0x00;
- nskb->protocol = htons(ETH_P_TRAILER);
-
nskb->dev = p->parent->dst->master_netdev;
dev_queue_xmit(nskb);
diff --git a/net/ieee802154/6lowpan_rtnl.c b/net/ieee802154/6lowpan_rtnl.c
index 44136297b673..27eaa65e88e1 100644
--- a/net/ieee802154/6lowpan_rtnl.c
+++ b/net/ieee802154/6lowpan_rtnl.c
@@ -49,8 +49,8 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/netdevice.h>
+#include <linux/ieee802154.h>
#include <net/af_ieee802154.h>
-#include <net/ieee802154.h>
#include <net/ieee802154_netdev.h>
#include <net/6lowpan.h>
#include <net/ipv6.h>
@@ -58,12 +58,13 @@
#include "reassembly.h"
static LIST_HEAD(lowpan_devices);
+static int lowpan_open_count;
/* private device info */
struct lowpan_dev_info {
struct net_device *real_dev; /* real WPAN device ptr */
struct mutex dev_list_mtx; /* mutex for list ops */
- __be16 fragment_tag;
+ u16 fragment_tag;
};
struct lowpan_dev_record {
@@ -140,24 +141,33 @@ static int lowpan_give_skb_to_devices(struct sk_buff *skb,
struct sk_buff *skb_cp;
int stat = NET_RX_SUCCESS;
+ skb->protocol = htons(ETH_P_IPV6);
+ skb->pkt_type = PACKET_HOST;
+
rcu_read_lock();
list_for_each_entry_rcu(entry, &lowpan_devices, list)
if (lowpan_dev_info(entry->ldev)->real_dev == skb->dev) {
skb_cp = skb_copy(skb, GFP_ATOMIC);
if (!skb_cp) {
- stat = -ENOMEM;
- break;
+ kfree_skb(skb);
+ rcu_read_unlock();
+ return NET_RX_DROP;
}
skb_cp->dev = entry->ldev;
stat = netif_rx(skb_cp);
+ if (stat == NET_RX_DROP)
+ break;
}
rcu_read_unlock();
+ consume_skb(skb);
+
return stat;
}
-static int process_data(struct sk_buff *skb, const struct ieee802154_hdr *hdr)
+static int
+iphc_decompress(struct sk_buff *skb, const struct ieee802154_hdr *hdr)
{
u8 iphc0, iphc1;
struct ieee802154_addr_sa sa, da;
@@ -166,13 +176,13 @@ static int process_data(struct sk_buff *skb, const struct ieee802154_hdr *hdr)
raw_dump_table(__func__, "raw skb data dump", skb->data, skb->len);
/* at least two bytes will be used for the encoding */
if (skb->len < 2)
- goto drop;
+ return -EINVAL;
if (lowpan_fetch_skb_u8(skb, &iphc0))
- goto drop;
+ return -EINVAL;
if (lowpan_fetch_skb_u8(skb, &iphc1))
- goto drop;
+ return -EINVAL;
ieee802154_addr_to_sa(&sa, &hdr->source);
ieee802154_addr_to_sa(&da, &hdr->dest);
@@ -187,27 +197,9 @@ static int process_data(struct sk_buff *skb, const struct ieee802154_hdr *hdr)
else
dap = &da.hwaddr;
- return lowpan_process_data(skb, skb->dev, sap, sa.addr_type,
- IEEE802154_ADDR_LEN, dap, da.addr_type,
- IEEE802154_ADDR_LEN, iphc0, iphc1,
- lowpan_give_skb_to_devices);
-
-drop:
- kfree_skb(skb);
- return -EINVAL;
-}
-
-static int lowpan_set_address(struct net_device *dev, void *p)
-{
- struct sockaddr *sa = p;
-
- if (netif_running(dev))
- return -EBUSY;
-
- /* TODO: validate addr */
- memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
-
- return 0;
+ return lowpan_header_decompress(skb, skb->dev, sap, sa.addr_type,
+ IEEE802154_ADDR_LEN, dap, da.addr_type,
+ IEEE802154_ADDR_LEN, iphc0, iphc1);
}
static struct sk_buff*
@@ -233,7 +225,7 @@ lowpan_alloc_frag(struct sk_buff *skb, int size,
&master_hdr->source, size);
if (rc < 0) {
kfree_skb(frag);
- return ERR_PTR(-rc);
+ return ERR_PTR(rc);
}
} else {
frag = ERR_PTR(-ENOMEM);
@@ -275,7 +267,8 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev,
dgram_size = lowpan_uncompress_size(skb, &dgram_offset) -
skb->mac_len;
- frag_tag = lowpan_dev_info(dev)->fragment_tag++;
+ frag_tag = htons(lowpan_dev_info(dev)->fragment_tag);
+ lowpan_dev_info(dev)->fragment_tag++;
frag_hdr[0] = LOWPAN_DISPATCH_FRAG1 | ((dgram_size >> 8) & 0x07);
frag_hdr[1] = dgram_size & 0xff;
@@ -294,7 +287,7 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev,
frag_len + skb_network_header_len(skb));
if (rc) {
pr_debug("%s unable to send FRAG1 packet (tag: %d)",
- __func__, frag_tag);
+ __func__, ntohs(frag_tag));
goto err;
}
@@ -315,7 +308,7 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev,
frag_len);
if (rc) {
pr_debug("%s unable to send a FRAGN packet. (tag: %d, offset: %d)\n",
- __func__, frag_tag, skb_offset);
+ __func__, ntohs(frag_tag), skb_offset);
goto err;
}
} while (skb_unprocessed > frag_cap);
@@ -410,13 +403,6 @@ static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev)
}
}
-static struct wpan_phy *lowpan_get_phy(const struct net_device *dev)
-{
- struct net_device *real_dev = lowpan_dev_info(dev)->real_dev;
-
- return ieee802154_mlme_ops(real_dev)->get_phy(real_dev);
-}
-
static __le16 lowpan_get_pan_id(const struct net_device *dev)
{
struct net_device *real_dev = lowpan_dev_info(dev)->real_dev;
@@ -453,7 +439,6 @@ static void lowpan_set_lockdep_class_one(struct net_device *dev,
&lowpan_netdev_xmit_lock_key);
}
-
static int lowpan_dev_init(struct net_device *dev)
{
netdev_for_each_tx_queue(dev, lowpan_set_lockdep_class_one, NULL);
@@ -464,12 +449,10 @@ static int lowpan_dev_init(struct net_device *dev)
static const struct net_device_ops lowpan_netdev_ops = {
.ndo_init = lowpan_dev_init,
.ndo_start_xmit = lowpan_xmit,
- .ndo_set_mac_address = lowpan_set_address,
};
static struct ieee802154_mlme_ops lowpan_mlme = {
.get_pan_id = lowpan_get_pan_id,
- .get_phy = lowpan_get_phy,
.get_short_addr = lowpan_get_short_addr,
.get_dsn = lowpan_get_dsn,
};
@@ -515,6 +498,9 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev,
if (!netif_running(dev))
goto drop_skb;
+ if (skb->pkt_type == PACKET_OTHERHOST)
+ goto drop_skb;
+
if (dev->type != ARPHRD_IEEE802154)
goto drop_skb;
@@ -523,55 +509,67 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev,
/* check that it's our buffer */
if (skb->data[0] == LOWPAN_DISPATCH_IPV6) {
- skb->protocol = htons(ETH_P_IPV6);
- skb->pkt_type = PACKET_HOST;
-
/* Pull off the 1-byte of 6lowpan header. */
skb_pull(skb, 1);
-
- ret = lowpan_give_skb_to_devices(skb, NULL);
- if (ret == NET_RX_DROP)
- goto drop;
+ return lowpan_give_skb_to_devices(skb, NULL);
} else {
switch (skb->data[0] & 0xe0) {
case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */
- ret = process_data(skb, &hdr);
- if (ret == NET_RX_DROP)
- goto drop;
- break;
+ ret = iphc_decompress(skb, &hdr);
+ if (ret < 0)
+ goto drop_skb;
+
+ return lowpan_give_skb_to_devices(skb, NULL);
case LOWPAN_DISPATCH_FRAG1: /* first fragment header */
ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAG1);
if (ret == 1) {
- ret = process_data(skb, &hdr);
- if (ret == NET_RX_DROP)
- goto drop;
+ ret = iphc_decompress(skb, &hdr);
+ if (ret < 0)
+ goto drop_skb;
+
+ return lowpan_give_skb_to_devices(skb, NULL);
+ } else if (ret == -1) {
+ return NET_RX_DROP;
+ } else {
+ return NET_RX_SUCCESS;
}
- break;
case LOWPAN_DISPATCH_FRAGN: /* next fragments headers */
ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAGN);
if (ret == 1) {
- ret = process_data(skb, &hdr);
- if (ret == NET_RX_DROP)
- goto drop;
+ ret = iphc_decompress(skb, &hdr);
+ if (ret < 0)
+ goto drop_skb;
+
+ return lowpan_give_skb_to_devices(skb, NULL);
+ } else if (ret == -1) {
+ return NET_RX_DROP;
+ } else {
+ return NET_RX_SUCCESS;
}
- break;
default:
break;
}
}
- return NET_RX_SUCCESS;
drop_skb:
kfree_skb(skb);
drop:
return NET_RX_DROP;
}
+static struct packet_type lowpan_packet_type = {
+ .type = htons(ETH_P_IEEE802154),
+ .func = lowpan_rcv,
+};
+
static int lowpan_newlink(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[])
{
struct net_device *real_dev;
struct lowpan_dev_record *entry;
+ int ret;
+
+ ASSERT_RTNL();
pr_debug("adding new link\n");
@@ -598,7 +596,7 @@ static int lowpan_newlink(struct net *src_net, struct net_device *dev,
entry->ldev = dev;
- /* Set the lowpan harware address to the wpan hardware address. */
+ /* Set the lowpan hardware address to the wpan hardware address. */
memcpy(dev->dev_addr, real_dev->dev_addr, IEEE802154_ADDR_LEN);
mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx);
@@ -606,9 +604,14 @@ static int lowpan_newlink(struct net *src_net, struct net_device *dev,
list_add_tail(&entry->list, &lowpan_devices);
mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx);
- register_netdevice(dev);
+ ret = register_netdevice(dev);
+ if (ret >= 0) {
+ if (!lowpan_open_count)
+ dev_add_pack(&lowpan_packet_type);
+ lowpan_open_count++;
+ }
- return 0;
+ return ret;
}
static void lowpan_dellink(struct net_device *dev, struct list_head *head)
@@ -619,6 +622,10 @@ static void lowpan_dellink(struct net_device *dev, struct list_head *head)
ASSERT_RTNL();
+ lowpan_open_count--;
+ if (!lowpan_open_count)
+ dev_remove_pack(&lowpan_packet_type);
+
mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx);
list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) {
if (entry->ldev == dev) {
@@ -681,11 +688,6 @@ static struct notifier_block lowpan_dev_notifier = {
.notifier_call = lowpan_device_event,
};
-static struct packet_type lowpan_packet_type = {
- .type = htons(ETH_P_IEEE802154),
- .func = lowpan_rcv,
-};
-
static int __init lowpan_init_module(void)
{
int err = 0;
@@ -698,8 +700,6 @@ static int __init lowpan_init_module(void)
if (err < 0)
goto out_frag;
- dev_add_pack(&lowpan_packet_type);
-
err = register_netdevice_notifier(&lowpan_dev_notifier);
if (err < 0)
goto out_pack;
@@ -707,7 +707,6 @@ static int __init lowpan_init_module(void)
return 0;
out_pack:
- dev_remove_pack(&lowpan_packet_type);
lowpan_netlink_fini();
out_frag:
lowpan_net_frag_exit();
@@ -719,8 +718,6 @@ static void __exit lowpan_cleanup_module(void)
{
lowpan_netlink_fini();
- dev_remove_pack(&lowpan_packet_type);
-
lowpan_net_frag_exit();
unregister_netdevice_notifier(&lowpan_dev_notifier);
diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile
index 3914b1ed4274..9f6970f2a28b 100644
--- a/net/ieee802154/Makefile
+++ b/net/ieee802154/Makefile
@@ -2,8 +2,8 @@ obj-$(CONFIG_IEEE802154) += ieee802154.o af_802154.o
obj-$(CONFIG_IEEE802154_6LOWPAN) += ieee802154_6lowpan.o
ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o
-ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o \
- header_ops.o
+ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o core.o \
+ header_ops.o sysfs.o nl802154.o
af_802154-y := af_ieee802154.o raw.o dgram.o
ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/ieee802154/af802154.h b/net/ieee802154/af802154.h
index 8330a09bfc95..343b63e6f953 100644
--- a/net/ieee802154/af802154.h
+++ b/net/ieee802154/af802154.h
@@ -12,10 +12,6 @@
* 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.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
diff --git a/net/ieee802154/af_ieee802154.c b/net/ieee802154/af_ieee802154.c
index 29e0de63001b..d0a1282cdf43 100644
--- a/net/ieee802154/af_ieee802154.c
+++ b/net/ieee802154/af_ieee802154.c
@@ -12,10 +12,6 @@
* 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.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Maxim Gorbachyov <maxim.gorbachev@siemens.com>
@@ -103,6 +99,7 @@ static int ieee802154_sock_release(struct socket *sock)
}
return 0;
}
+
static int ieee802154_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t len)
{
@@ -235,7 +232,6 @@ static const struct proto_ops ieee802154_dgram_ops = {
#endif
};
-
/* Create a socket. Initialise the socket, blank the addresses
* set the state.
*/
@@ -324,7 +320,6 @@ drop:
return NET_RX_DROP;
}
-
static struct packet_type ieee802154_packet_type = {
.type = htons(ETH_P_IEEE802154),
.func = ieee802154_rcv,
@@ -358,6 +353,7 @@ err_dgram:
out:
return rc;
}
+
static void __exit af_ieee802154_remove(void)
{
dev_remove_pack(&ieee802154_packet_type);
diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c
new file mode 100644
index 000000000000..18bc7e738507
--- /dev/null
+++ b/net/ieee802154/core.c
@@ -0,0 +1,321 @@
+/*
+ * 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.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+
+#include <net/cfg802154.h>
+#include <net/rtnetlink.h>
+
+#include "ieee802154.h"
+#include "nl802154.h"
+#include "sysfs.h"
+#include "core.h"
+
+/* RCU-protected (and RTNL for writers) */
+LIST_HEAD(cfg802154_rdev_list);
+int cfg802154_rdev_list_generation;
+
+static int wpan_phy_match(struct device *dev, const 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, str, wpan_phy_match);
+ if (!dev)
+ return NULL;
+
+ return container_of(dev, struct wpan_phy, dev);
+}
+EXPORT_SYMBOL(wpan_phy_find);
+
+struct wpan_phy_iter_data {
+ int (*fn)(struct wpan_phy *phy, void *data);
+ void *data;
+};
+
+static int wpan_phy_iter(struct device *dev, void *_data)
+{
+ struct wpan_phy_iter_data *wpid = _data;
+ struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev);
+
+ return wpid->fn(phy, wpid->data);
+}
+
+int wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data),
+ void *data)
+{
+ struct wpan_phy_iter_data wpid = {
+ .fn = fn,
+ .data = data,
+ };
+
+ return class_for_each_device(&wpan_phy_class, NULL,
+ &wpid, wpan_phy_iter);
+}
+EXPORT_SYMBOL(wpan_phy_for_each);
+
+struct cfg802154_registered_device *
+cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx)
+{
+ struct cfg802154_registered_device *result = NULL, *rdev;
+
+ ASSERT_RTNL();
+
+ list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
+ if (rdev->wpan_phy_idx == wpan_phy_idx) {
+ result = rdev;
+ break;
+ }
+ }
+
+ return result;
+}
+
+struct wpan_phy *
+wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size)
+{
+ static atomic_t wpan_phy_counter = ATOMIC_INIT(0);
+ struct cfg802154_registered_device *rdev;
+ size_t alloc_size;
+
+ alloc_size = sizeof(*rdev) + priv_size;
+ rdev = kzalloc(alloc_size, GFP_KERNEL);
+ if (!rdev)
+ return NULL;
+
+ rdev->ops = ops;
+
+ rdev->wpan_phy_idx = atomic_inc_return(&wpan_phy_counter);
+
+ if (unlikely(rdev->wpan_phy_idx < 0)) {
+ /* ugh, wrapped! */
+ atomic_dec(&wpan_phy_counter);
+ kfree(rdev);
+ return NULL;
+ }
+
+ /* atomic_inc_return makes it start at 1, make it start at 0 */
+ rdev->wpan_phy_idx--;
+
+ mutex_init(&rdev->wpan_phy.pib_lock);
+
+ INIT_LIST_HEAD(&rdev->wpan_dev_list);
+ device_initialize(&rdev->wpan_phy.dev);
+ dev_set_name(&rdev->wpan_phy.dev, "wpan-phy%d", rdev->wpan_phy_idx);
+
+ rdev->wpan_phy.dev.class = &wpan_phy_class;
+ rdev->wpan_phy.dev.platform_data = rdev;
+
+ init_waitqueue_head(&rdev->dev_wait);
+
+ return &rdev->wpan_phy;
+}
+EXPORT_SYMBOL(wpan_phy_new);
+
+int wpan_phy_register(struct wpan_phy *phy)
+{
+ struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
+ int ret;
+
+ rtnl_lock();
+ ret = device_add(&phy->dev);
+ if (ret) {
+ rtnl_unlock();
+ return ret;
+ }
+
+ list_add_rcu(&rdev->list, &cfg802154_rdev_list);
+ cfg802154_rdev_list_generation++;
+
+ /* TODO phy registered lock */
+ rtnl_unlock();
+
+ /* TODO nl802154 phy notify */
+
+ return 0;
+}
+EXPORT_SYMBOL(wpan_phy_register);
+
+void wpan_phy_unregister(struct wpan_phy *phy)
+{
+ struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(phy);
+
+ wait_event(rdev->dev_wait, ({
+ int __count;
+ rtnl_lock();
+ __count = rdev->opencount;
+ rtnl_unlock();
+ __count == 0; }));
+
+ rtnl_lock();
+ /* TODO nl802154 phy notify */
+ /* TODO phy registered lock */
+
+ WARN_ON(!list_empty(&rdev->wpan_dev_list));
+
+ /* First remove the hardware from everywhere, this makes
+ * it impossible to find from userspace.
+ */
+ list_del_rcu(&rdev->list);
+ synchronize_rcu();
+
+ cfg802154_rdev_list_generation++;
+
+ device_del(&phy->dev);
+
+ rtnl_unlock();
+}
+EXPORT_SYMBOL(wpan_phy_unregister);
+
+void wpan_phy_free(struct wpan_phy *phy)
+{
+ put_device(&phy->dev);
+}
+EXPORT_SYMBOL(wpan_phy_free);
+
+void cfg802154_dev_free(struct cfg802154_registered_device *rdev)
+{
+ kfree(rdev);
+}
+
+static void
+cfg802154_update_iface_num(struct cfg802154_registered_device *rdev,
+ int iftype, int num)
+{
+ ASSERT_RTNL();
+
+ rdev->num_running_ifaces += num;
+}
+
+static int cfg802154_netdev_notifier_call(struct notifier_block *nb,
+ unsigned long state, void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+ struct cfg802154_registered_device *rdev;
+
+ if (!wpan_dev)
+ return NOTIFY_DONE;
+
+ rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
+
+ /* TODO WARN_ON unspec type */
+
+ switch (state) {
+ /* TODO NETDEV_DEVTYPE */
+ case NETDEV_REGISTER:
+ wpan_dev->identifier = ++rdev->wpan_dev_id;
+ list_add_rcu(&wpan_dev->list, &rdev->wpan_dev_list);
+ rdev->devlist_generation++;
+
+ wpan_dev->netdev = dev;
+ break;
+ case NETDEV_DOWN:
+ cfg802154_update_iface_num(rdev, wpan_dev->iftype, -1);
+
+ rdev->opencount--;
+ wake_up(&rdev->dev_wait);
+ break;
+ case NETDEV_UP:
+ cfg802154_update_iface_num(rdev, wpan_dev->iftype, 1);
+
+ rdev->opencount++;
+ break;
+ case NETDEV_UNREGISTER:
+ /* It is possible to get NETDEV_UNREGISTER
+ * multiple times. To detect that, check
+ * that the interface is still on the list
+ * of registered interfaces, and only then
+ * remove and clean it up.
+ */
+ if (!list_empty(&wpan_dev->list)) {
+ list_del_rcu(&wpan_dev->list);
+ rdev->devlist_generation++;
+ }
+ /* synchronize (so that we won't find this netdev
+ * from other code any more) and then clear the list
+ * head so that the above code can safely check for
+ * !list_empty() to avoid double-cleanup.
+ */
+ synchronize_rcu();
+ INIT_LIST_HEAD(&wpan_dev->list);
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block cfg802154_netdev_notifier = {
+ .notifier_call = cfg802154_netdev_notifier_call,
+};
+
+static int __init wpan_phy_class_init(void)
+{
+ int rc;
+
+ rc = wpan_phy_sysfs_init();
+ if (rc)
+ goto err;
+
+ rc = register_netdevice_notifier(&cfg802154_netdev_notifier);
+ if (rc)
+ goto err_nl;
+
+ rc = ieee802154_nl_init();
+ if (rc)
+ goto err_notifier;
+
+ rc = nl802154_init();
+ if (rc)
+ goto err_ieee802154_nl;
+
+ return 0;
+
+err_ieee802154_nl:
+ ieee802154_nl_exit();
+
+err_notifier:
+ unregister_netdevice_notifier(&cfg802154_netdev_notifier);
+err_nl:
+ wpan_phy_sysfs_exit();
+err:
+ return rc;
+}
+subsys_initcall(wpan_phy_class_init);
+
+static void __exit wpan_phy_class_exit(void)
+{
+ nl802154_exit();
+ ieee802154_nl_exit();
+ unregister_netdevice_notifier(&cfg802154_netdev_notifier);
+ wpan_phy_sysfs_exit();
+}
+module_exit(wpan_phy_class_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("IEEE 802.15.4 configuration interface");
+MODULE_AUTHOR("Dmitry Eremin-Solenikov");
+
diff --git a/net/ieee802154/core.h b/net/ieee802154/core.h
new file mode 100644
index 000000000000..f3e95580caee
--- /dev/null
+++ b/net/ieee802154/core.h
@@ -0,0 +1,46 @@
+#ifndef __IEEE802154_CORE_H
+#define __IEEE802154_CORE_H
+
+#include <net/cfg802154.h>
+
+struct cfg802154_registered_device {
+ const struct cfg802154_ops *ops;
+ struct list_head list;
+
+ /* wpan_phy index, internal only */
+ int wpan_phy_idx;
+
+ /* also protected by devlist_mtx */
+ int opencount;
+ wait_queue_head_t dev_wait;
+
+ /* protected by RTNL only */
+ int num_running_ifaces;
+
+ /* associated wpan interfaces, protected by rtnl or RCU */
+ struct list_head wpan_dev_list;
+ int devlist_generation, wpan_dev_id;
+
+ /* must be last because of the way we do wpan_phy_priv(),
+ * and it should at least be aligned to NETDEV_ALIGN
+ */
+ struct wpan_phy wpan_phy __aligned(NETDEV_ALIGN);
+};
+
+static inline struct cfg802154_registered_device *
+wpan_phy_to_rdev(struct wpan_phy *wpan_phy)
+{
+ BUG_ON(!wpan_phy);
+ return container_of(wpan_phy, struct cfg802154_registered_device,
+ wpan_phy);
+}
+
+extern struct list_head cfg802154_rdev_list;
+extern int cfg802154_rdev_list_generation;
+
+/* free object */
+void cfg802154_dev_free(struct cfg802154_registered_device *rdev);
+struct cfg802154_registered_device *
+cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx);
+
+#endif /* __IEEE802154_CORE_H */
diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c
index ef2ad8aaef13..d1930b70c4aa 100644
--- a/net/ieee802154/dgram.c
+++ b/net/ieee802154/dgram.c
@@ -12,10 +12,6 @@
* 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.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
@@ -27,9 +23,9 @@
#include <linux/if_arp.h>
#include <linux/list.h>
#include <linux/slab.h>
+#include <linux/ieee802154.h>
#include <net/sock.h>
#include <net/af_ieee802154.h>
-#include <net/ieee802154.h>
#include <net/ieee802154_netdev.h>
#include <asm/ioctls.h>
@@ -158,7 +154,6 @@ static int dgram_ioctl(struct sock *sk, int cmd, unsigned long arg)
spin_unlock_bh(&sk->sk_receive_queue.lock);
return put_user(amount, (int __user *)arg);
}
-
}
return -ENOIOCTLCMD;
@@ -280,7 +275,7 @@ static int dgram_sendmsg(struct kiocb *iocb, struct sock *sk,
if (err < 0)
goto out_skb;
- err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
+ err = memcpy_from_msg(skb_put(skb, size), msg, size);
if (err < 0)
goto out_skb;
@@ -324,7 +319,7 @@ static int dgram_recvmsg(struct kiocb *iocb, struct sock *sk,
}
/* FIXME: skip headers if necessary ?! */
- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
if (err)
goto done;
diff --git a/net/ieee802154/header_ops.c b/net/ieee802154/header_ops.c
index c09294e39ca6..a051b6993177 100644
--- a/net/ieee802154/header_ops.c
+++ b/net/ieee802154/header_ops.c
@@ -14,8 +14,9 @@
* Phoebe Buckheister <phoebe.buckheister@itwm.fraunhofer.de>
*/
+#include <linux/ieee802154.h>
+
#include <net/mac802154.h>
-#include <net/ieee802154.h>
#include <net/ieee802154_netdev.h>
static int
diff --git a/net/ieee802154/ieee802154.h b/net/ieee802154/ieee802154.h
index 5d352f86979e..a5d7515b7f62 100644
--- a/net/ieee802154/ieee802154.h
+++ b/net/ieee802154/ieee802154.h
@@ -10,16 +10,12 @@
* 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.
- *
*/
#ifndef IEEE_802154_LOCAL_H
#define IEEE_802154_LOCAL_H
int __init ieee802154_nl_init(void);
-void __exit ieee802154_nl_exit(void);
+void ieee802154_nl_exit(void);
#define IEEE802154_OP(_cmd, _func) \
{ \
diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c
index 9222966f5e6d..fa1464762d0d 100644
--- a/net/ieee802154/netlink.c
+++ b/net/ieee802154/netlink.c
@@ -1,5 +1,5 @@
/*
- * Netlink inteface for IEEE 802.15.4 stack
+ * Netlink interface for IEEE 802.15.4 stack
*
* Copyright 2007, 2008 Siemens AG
*
@@ -12,10 +12,6 @@
* 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.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
@@ -77,7 +73,7 @@ out:
}
struct sk_buff *ieee802154_nl_new_reply(struct genl_info *info,
- int flags, u8 req)
+ int flags, u8 req)
{
void *hdr;
struct sk_buff *msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
@@ -151,7 +147,6 @@ static const struct genl_multicast_group ieee802154_mcgrps[] = {
[IEEE802154_BEACON_MCGRP] = { .name = IEEE802154_MCAST_BEACON_NAME, },
};
-
int __init ieee802154_nl_init(void)
{
return genl_register_family_with_ops_groups(&nl802154_family,
@@ -159,7 +154,7 @@ int __init ieee802154_nl_init(void)
ieee802154_mcgrps);
}
-void __exit ieee802154_nl_exit(void)
+void ieee802154_nl_exit(void)
{
genl_unregister_family(&nl802154_family);
}
diff --git a/net/ieee802154/nl-mac.c b/net/ieee802154/nl-mac.c
index c6bfe22bfa5e..cd919493c976 100644
--- a/net/ieee802154/nl-mac.c
+++ b/net/ieee802154/nl-mac.c
@@ -1,5 +1,5 @@
/*
- * Netlink inteface for IEEE 802.15.4 stack
+ * Netlink interface for IEEE 802.15.4 stack
*
* Copyright 2007, 2008 Siemens AG
*
@@ -12,10 +12,6 @@
* 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.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
@@ -26,16 +22,15 @@
#include <linux/kernel.h>
#include <linux/if_arp.h>
#include <linux/netdevice.h>
+#include <linux/ieee802154.h>
#include <net/netlink.h>
#include <net/genetlink.h>
#include <net/sock.h>
#include <linux/nl802154.h>
#include <linux/export.h>
#include <net/af_ieee802154.h>
-#include <net/nl802154.h>
-#include <net/ieee802154.h>
#include <net/ieee802154_netdev.h>
-#include <net/wpan-phy.h>
+#include <net/cfg802154.h>
#include "ieee802154.h"
@@ -59,186 +54,7 @@ static __le16 nla_get_shortaddr(const struct nlattr *nla)
return cpu_to_le16(nla_get_u16(nla));
}
-int ieee802154_nl_assoc_indic(struct net_device *dev,
- struct ieee802154_addr *addr,
- u8 cap)
-{
- struct sk_buff *msg;
-
- pr_debug("%s\n", __func__);
-
- if (addr->mode != IEEE802154_ADDR_LONG) {
- pr_err("%s: received non-long source address!\n", __func__);
- return -EINVAL;
- }
-
- msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_INDIC);
- if (!msg)
- return -ENOBUFS;
-
- if (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_hwaddr(msg, IEEE802154_ATTR_SRC_HW_ADDR,
- addr->extended_addr) ||
- nla_put_u8(msg, IEEE802154_ATTR_CAPABILITY, cap))
- goto nla_put_failure;
-
- return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
-
-nla_put_failure:
- nlmsg_free(msg);
- return -ENOBUFS;
-}
-EXPORT_SYMBOL(ieee802154_nl_assoc_indic);
-
-int ieee802154_nl_assoc_confirm(struct net_device *dev, __le16 short_addr,
- u8 status)
-{
- struct sk_buff *msg;
-
- pr_debug("%s\n", __func__);
-
- msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_CONF);
- if (!msg)
- return -ENOBUFS;
-
- if (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_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) ||
- nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
- goto nla_put_failure;
- return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
-
-nla_put_failure:
- nlmsg_free(msg);
- return -ENOBUFS;
-}
-EXPORT_SYMBOL(ieee802154_nl_assoc_confirm);
-
-int ieee802154_nl_disassoc_indic(struct net_device *dev,
- struct ieee802154_addr *addr,
- u8 reason)
-{
- struct sk_buff *msg;
-
- pr_debug("%s\n", __func__);
-
- msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_INDIC);
- if (!msg)
- return -ENOBUFS;
-
- if (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))
- goto nla_put_failure;
- if (addr->mode == IEEE802154_ADDR_LONG) {
- if (nla_put_hwaddr(msg, IEEE802154_ATTR_SRC_HW_ADDR,
- addr->extended_addr))
- goto nla_put_failure;
- } else {
- if (nla_put_shortaddr(msg, IEEE802154_ATTR_SRC_SHORT_ADDR,
- addr->short_addr))
- goto nla_put_failure;
- }
- if (nla_put_u8(msg, IEEE802154_ATTR_REASON, reason))
- goto nla_put_failure;
- return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
-
-nla_put_failure:
- nlmsg_free(msg);
- return -ENOBUFS;
-}
-EXPORT_SYMBOL(ieee802154_nl_disassoc_indic);
-
-int ieee802154_nl_disassoc_confirm(struct net_device *dev, u8 status)
-{
- struct sk_buff *msg;
-
- pr_debug("%s\n", __func__);
-
- msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_CONF);
- if (!msg)
- return -ENOBUFS;
-
- if (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))
- goto nla_put_failure;
- return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
-
-nla_put_failure:
- nlmsg_free(msg);
- return -ENOBUFS;
-}
-EXPORT_SYMBOL(ieee802154_nl_disassoc_confirm);
-
-int ieee802154_nl_beacon_indic(struct net_device *dev, __le16 panid,
- __le16 coord_addr)
-{
- struct sk_buff *msg;
-
- pr_debug("%s\n", __func__);
-
- msg = ieee802154_nl_create(0, IEEE802154_BEACON_NOTIFY_INDIC);
- if (!msg)
- return -ENOBUFS;
-
- if (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_shortaddr(msg, IEEE802154_ATTR_COORD_SHORT_ADDR,
- coord_addr) ||
- nla_put_shortaddr(msg, IEEE802154_ATTR_COORD_PAN_ID, panid))
- goto nla_put_failure;
- return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
-
-nla_put_failure:
- nlmsg_free(msg);
- return -ENOBUFS;
-}
-EXPORT_SYMBOL(ieee802154_nl_beacon_indic);
-
-int ieee802154_nl_scan_confirm(struct net_device *dev,
- u8 status, u8 scan_type,
- u32 unscanned, u8 page,
- u8 *edl/* , struct list_head *pan_desc_list */)
-{
- struct sk_buff *msg;
-
- pr_debug("%s\n", __func__);
-
- msg = ieee802154_nl_create(0, IEEE802154_SCAN_CONF);
- if (!msg)
- return -ENOBUFS;
-
- if (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) ||
- 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) ||
- (edl &&
- nla_put(msg, IEEE802154_ATTR_ED_LIST, 27, edl)))
- goto nla_put_failure;
- return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
-
-nla_put_failure:
- nlmsg_free(msg);
- return -ENOBUFS;
-}
-EXPORT_SYMBOL(ieee802154_nl_scan_confirm);
-
-int ieee802154_nl_start_confirm(struct net_device *dev, u8 status)
+static int ieee802154_nl_start_confirm(struct net_device *dev, u8 status)
{
struct sk_buff *msg;
@@ -278,8 +94,9 @@ static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid,
goto out;
ops = ieee802154_mlme_ops(dev);
- phy = ops->get_phy(dev);
+ phy = dev->ieee802154_ptr->wpan_phy;
BUG_ON(!phy);
+ get_device(&phy->dev);
short_addr = ops->get_short_addr(dev);
pan_id = ops->get_pan_id(dev);
@@ -296,7 +113,9 @@ static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid,
if (ops->get_mac_params) {
struct ieee802154_mac_params params;
+ rtnl_lock();
ops->get_mac_params(dev, &params);
+ rtnl_unlock();
if (nla_put_s8(msg, IEEE802154_ATTR_TXPOWER,
params.transmit_power) ||
@@ -347,7 +166,10 @@ static struct net_device *ieee802154_nl_get_dev(struct genl_info *info)
if (!dev)
return NULL;
- if (dev->type != ARPHRD_IEEE802154) {
+ /* Check on mtu is currently a hacked solution because lowpan
+ * and wpan have the same ARPHRD type.
+ */
+ if (dev->type != ARPHRD_IEEE802154 || dev->mtu != IEEE802154_MTU) {
dev_put(dev);
return NULL;
}
@@ -481,7 +303,7 @@ int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
u8 channel, bcn_ord, sf_ord;
u8 page;
int pan_coord, blx, coord_realign;
- int ret = -EOPNOTSUPP;
+ int ret = -EBUSY;
if (!info->attrs[IEEE802154_ATTR_COORD_PAN_ID] ||
!info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR] ||
@@ -497,9 +319,15 @@ int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
dev = ieee802154_nl_get_dev(info);
if (!dev)
return -ENODEV;
- if (!ieee802154_mlme_ops(dev)->start_req)
+
+ if (netif_running(dev))
goto out;
+ if (!ieee802154_mlme_ops(dev)->start_req) {
+ ret = -EOPNOTSUPP;
+ goto out;
+ }
+
addr.mode = IEEE802154_ADDR_SHORT;
addr.short_addr = nla_get_shortaddr(
info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]);
@@ -518,15 +346,21 @@ int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
else
page = 0;
-
if (addr.short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST)) {
ieee802154_nl_start_confirm(dev, IEEE802154_NO_SHORT_ADDRESS);
dev_put(dev);
return -EINVAL;
}
+ rtnl_lock();
ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page,
bcn_ord, sf_ord, pan_coord, blx, coord_realign);
+ rtnl_unlock();
+
+ /* FIXME: add validation for unused parameters to be sane
+ * for SoftMAC
+ */
+ ieee802154_nl_start_confirm(dev, IEEE802154_SUCCESS);
out:
dev_put(dev);
@@ -562,7 +396,6 @@ int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info)
else
page = 0;
-
ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels,
page, duration);
@@ -616,7 +449,11 @@ int ieee802154_dump_iface(struct sk_buff *skb, struct netlink_callback *cb)
idx = 0;
for_each_netdev(net, dev) {
- if (idx < s_idx || (dev->type != ARPHRD_IEEE802154))
+ /* Check on mtu is currently a hacked solution because lowpan
+ * and wpan have the same ARPHRD type.
+ */
+ if (idx < s_idx || dev->type != ARPHRD_IEEE802154 ||
+ dev->mtu != IEEE802154_MTU)
goto cont;
if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).portid,
@@ -666,22 +503,10 @@ int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info)
!info->attrs[IEEE802154_ATTR_FRAME_RETRIES])
goto out;
- phy = ops->get_phy(dev);
-
- if ((!phy->set_lbt && info->attrs[IEEE802154_ATTR_LBT_ENABLED]) ||
- (!phy->set_cca_mode && info->attrs[IEEE802154_ATTR_CCA_MODE]) ||
- (!phy->set_cca_ed_level &&
- info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]) ||
- (!phy->set_csma_params &&
- (info->attrs[IEEE802154_ATTR_CSMA_RETRIES] ||
- info->attrs[IEEE802154_ATTR_CSMA_MIN_BE] ||
- info->attrs[IEEE802154_ATTR_CSMA_MAX_BE])) ||
- (!phy->set_frame_retries &&
- info->attrs[IEEE802154_ATTR_FRAME_RETRIES])) {
- rc = -EOPNOTSUPP;
- goto out_phy;
- }
+ phy = dev->ieee802154_ptr->wpan_phy;
+ get_device(&phy->dev);
+ rtnl_lock();
ops->get_mac_params(dev, &params);
if (info->attrs[IEEE802154_ATTR_TXPOWER])
@@ -709,20 +534,18 @@ int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info)
params.frame_retries = nla_get_s8(info->attrs[IEEE802154_ATTR_FRAME_RETRIES]);
rc = ops->set_mac_params(dev, &params);
+ rtnl_unlock();
wpan_phy_put(phy);
dev_put(dev);
- return rc;
-out_phy:
- wpan_phy_put(phy);
+ return 0;
+
out:
dev_put(dev);
return rc;
}
-
-
static int
ieee802154_llsec_parse_key_id(struct genl_info *info,
struct ieee802154_llsec_key_id *desc)
@@ -938,8 +761,6 @@ out:
return rc;
}
-
-
struct llsec_dump_data {
struct sk_buff *skb;
int s_idx, s_idx2;
@@ -962,7 +783,11 @@ ieee802154_llsec_dump_table(struct sk_buff *skb, struct netlink_callback *cb,
int rc;
for_each_netdev(net, dev) {
- if (idx < first_dev || dev->type != ARPHRD_IEEE802154)
+ /* Check on mtu is currently a hacked solution because lowpan
+ * and wpan have the same ARPHRD type.
+ */
+ if (idx < first_dev || dev->type != ARPHRD_IEEE802154 ||
+ dev->mtu != IEEE802154_MTU)
goto skip;
data.ops = ieee802154_mlme_ops(dev);
@@ -1012,8 +837,6 @@ ieee802154_nl_llsec_change(struct sk_buff *skb, struct genl_info *info,
return rc;
}
-
-
static int
ieee802154_llsec_parse_key(struct genl_info *info,
struct ieee802154_llsec_key *key)
@@ -1158,8 +981,6 @@ int ieee802154_llsec_dump_keys(struct sk_buff *skb, struct netlink_callback *cb)
return ieee802154_llsec_dump_table(skb, cb, llsec_iter_keys);
}
-
-
static int
llsec_parse_dev(struct genl_info *info,
struct ieee802154_llsec_device *dev)
@@ -1290,8 +1111,6 @@ int ieee802154_llsec_dump_devs(struct sk_buff *skb, struct netlink_callback *cb)
return ieee802154_llsec_dump_table(skb, cb, llsec_iter_devs);
}
-
-
static int llsec_add_devkey(struct net_device *dev, struct genl_info *info)
{
struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
@@ -1406,8 +1225,6 @@ int ieee802154_llsec_dump_devkeys(struct sk_buff *skb,
return ieee802154_llsec_dump_table(skb, cb, llsec_iter_devkeys);
}
-
-
static int
llsec_parse_seclevel(struct genl_info *info,
struct ieee802154_llsec_seclevel *sl)
diff --git a/net/ieee802154/nl-phy.c b/net/ieee802154/nl-phy.c
index 972baf83411a..7baf98b14611 100644
--- a/net/ieee802154/nl-phy.c
+++ b/net/ieee802154/nl-phy.c
@@ -1,5 +1,5 @@
/*
- * Netlink inteface for IEEE 802.15.4 stack
+ * Netlink interface for IEEE 802.15.4 stack
*
* Copyright 2007, 2008 Siemens AG
*
@@ -12,10 +12,6 @@
* 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.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
@@ -27,13 +23,15 @@
#include <linux/if_arp.h>
#include <net/netlink.h>
#include <net/genetlink.h>
-#include <net/wpan-phy.h>
+#include <net/cfg802154.h>
#include <net/af_ieee802154.h>
#include <net/ieee802154_netdev.h>
#include <net/rtnetlink.h> /* for rtnl_{un,}lock */
#include <linux/nl802154.h>
#include "ieee802154.h"
+#include "rdev-ops.h"
+#include "core.h"
static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid,
u32 seq, int flags, struct wpan_phy *phy)
@@ -96,7 +94,6 @@ int ieee802154_list_phy(struct sk_buff *skb, struct genl_info *info)
if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0')
return -EINVAL; /* phy name should be null-terminated */
-
phy = wpan_phy_find(name);
if (!phy)
return -ENODEV;
@@ -207,11 +204,6 @@ int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info)
if (!msg)
goto out_dev;
- if (!phy->add_iface) {
- rc = -EINVAL;
- goto nla_put_failure;
- }
-
if (info->attrs[IEEE802154_ATTR_HW_ADDR] &&
nla_len(info->attrs[IEEE802154_ATTR_HW_ADDR]) !=
IEEE802154_ADDR_LEN) {
@@ -227,11 +219,13 @@ int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info)
}
}
- dev = phy->add_iface(phy, devname, type);
+ dev = rdev_add_virtual_intf_deprecated(wpan_phy_to_rdev(phy), devname,
+ type);
if (IS_ERR(dev)) {
rc = PTR_ERR(dev);
goto nla_put_failure;
}
+ dev_hold(dev);
if (info->attrs[IEEE802154_ATTR_HW_ADDR]) {
struct sockaddr addr;
@@ -261,7 +255,7 @@ int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info)
dev_unregister:
rtnl_lock(); /* del_iface must be called with RTNL lock */
- phy->del_iface(phy, dev);
+ rdev_del_virtual_intf_deprecated(wpan_phy_to_rdev(phy), dev);
dev_put(dev);
rtnl_unlock();
nla_put_failure:
@@ -292,8 +286,9 @@ int ieee802154_del_iface(struct sk_buff *skb, struct genl_info *info)
if (!dev)
return -ENODEV;
- phy = ieee802154_mlme_ops(dev)->get_phy(dev);
+ phy = dev->ieee802154_ptr->wpan_phy;
BUG_ON(!phy);
+ get_device(&phy->dev);
rc = -EINVAL;
/* phy name is optional, but should be checked if it's given */
@@ -323,13 +318,8 @@ int ieee802154_del_iface(struct sk_buff *skb, struct genl_info *info)
if (!msg)
goto out_dev;
- if (!phy->del_iface) {
- rc = -EINVAL;
- goto nla_put_failure;
- }
-
rtnl_lock();
- phy->del_iface(phy, dev);
+ rdev_del_virtual_intf_deprecated(wpan_phy_to_rdev(phy), dev);
/* We don't have device anymore */
dev_put(dev);
diff --git a/net/ieee802154/nl802154.c b/net/ieee802154/nl802154.c
new file mode 100644
index 000000000000..889647744697
--- /dev/null
+++ b/net/ieee802154/nl802154.c
@@ -0,0 +1,957 @@
+/* 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.
+ *
+ * Authors:
+ * Alexander Aring <aar@pengutronix.de>
+ *
+ * Based on: net/wireless/nl80211.c
+ */
+
+#include <linux/rtnetlink.h>
+
+#include <net/cfg802154.h>
+#include <net/genetlink.h>
+#include <net/mac802154.h>
+#include <net/netlink.h>
+#include <net/nl802154.h>
+#include <net/sock.h>
+
+#include "nl802154.h"
+#include "rdev-ops.h"
+#include "core.h"
+
+static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
+ struct genl_info *info);
+
+static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
+ struct genl_info *info);
+
+/* the netlink family */
+static struct genl_family nl802154_fam = {
+ .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
+ .name = NL802154_GENL_NAME, /* have users key off the name instead */
+ .hdrsize = 0, /* no private header */
+ .version = 1, /* no particular meaning now */
+ .maxattr = NL802154_ATTR_MAX,
+ .netnsok = true,
+ .pre_doit = nl802154_pre_doit,
+ .post_doit = nl802154_post_doit,
+};
+
+/* multicast groups */
+enum nl802154_multicast_groups {
+ NL802154_MCGRP_CONFIG,
+};
+
+static const struct genl_multicast_group nl802154_mcgrps[] = {
+ [NL802154_MCGRP_CONFIG] = { .name = "config", },
+};
+
+/* returns ERR_PTR values */
+static struct wpan_dev *
+__cfg802154_wpan_dev_from_attrs(struct net *netns, struct nlattr **attrs)
+{
+ struct cfg802154_registered_device *rdev;
+ struct wpan_dev *result = NULL;
+ bool have_ifidx = attrs[NL802154_ATTR_IFINDEX];
+ bool have_wpan_dev_id = attrs[NL802154_ATTR_WPAN_DEV];
+ u64 wpan_dev_id;
+ int wpan_phy_idx = -1;
+ int ifidx = -1;
+
+ ASSERT_RTNL();
+
+ if (!have_ifidx && !have_wpan_dev_id)
+ return ERR_PTR(-EINVAL);
+
+ if (have_ifidx)
+ ifidx = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]);
+ if (have_wpan_dev_id) {
+ wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]);
+ wpan_phy_idx = wpan_dev_id >> 32;
+ }
+
+ list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
+ struct wpan_dev *wpan_dev;
+
+ /* TODO netns compare */
+
+ if (have_wpan_dev_id && rdev->wpan_phy_idx != wpan_phy_idx)
+ continue;
+
+ list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
+ if (have_ifidx && wpan_dev->netdev &&
+ wpan_dev->netdev->ifindex == ifidx) {
+ result = wpan_dev;
+ break;
+ }
+ if (have_wpan_dev_id &&
+ wpan_dev->identifier == (u32)wpan_dev_id) {
+ result = wpan_dev;
+ break;
+ }
+ }
+
+ if (result)
+ break;
+ }
+
+ if (result)
+ return result;
+
+ return ERR_PTR(-ENODEV);
+}
+
+static struct cfg802154_registered_device *
+__cfg802154_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
+{
+ struct cfg802154_registered_device *rdev = NULL, *tmp;
+ struct net_device *netdev;
+
+ ASSERT_RTNL();
+
+ if (!attrs[NL802154_ATTR_WPAN_PHY] &&
+ !attrs[NL802154_ATTR_IFINDEX] &&
+ !attrs[NL802154_ATTR_WPAN_DEV])
+ return ERR_PTR(-EINVAL);
+
+ if (attrs[NL802154_ATTR_WPAN_PHY])
+ rdev = cfg802154_rdev_by_wpan_phy_idx(
+ nla_get_u32(attrs[NL802154_ATTR_WPAN_PHY]));
+
+ if (attrs[NL802154_ATTR_WPAN_DEV]) {
+ u64 wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]);
+ struct wpan_dev *wpan_dev;
+ bool found = false;
+
+ tmp = cfg802154_rdev_by_wpan_phy_idx(wpan_dev_id >> 32);
+ if (tmp) {
+ /* make sure wpan_dev exists */
+ list_for_each_entry(wpan_dev, &tmp->wpan_dev_list, list) {
+ if (wpan_dev->identifier != (u32)wpan_dev_id)
+ continue;
+ found = true;
+ break;
+ }
+
+ if (!found)
+ tmp = NULL;
+
+ if (rdev && tmp != rdev)
+ return ERR_PTR(-EINVAL);
+ rdev = tmp;
+ }
+ }
+
+ if (attrs[NL802154_ATTR_IFINDEX]) {
+ int ifindex = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]);
+
+ netdev = __dev_get_by_index(netns, ifindex);
+ if (netdev) {
+ if (netdev->ieee802154_ptr)
+ tmp = wpan_phy_to_rdev(
+ netdev->ieee802154_ptr->wpan_phy);
+ else
+ tmp = NULL;
+
+ /* not wireless device -- return error */
+ if (!tmp)
+ return ERR_PTR(-EINVAL);
+
+ /* mismatch -- return error */
+ if (rdev && tmp != rdev)
+ return ERR_PTR(-EINVAL);
+
+ rdev = tmp;
+ }
+ }
+
+ if (!rdev)
+ return ERR_PTR(-ENODEV);
+
+ /* TODO netns compare */
+
+ return rdev;
+}
+
+/* This function returns a pointer to the driver
+ * that the genl_info item that is passed refers to.
+ *
+ * The result of this can be a PTR_ERR and hence must
+ * be checked with IS_ERR() for errors.
+ */
+static struct cfg802154_registered_device *
+cfg802154_get_dev_from_info(struct net *netns, struct genl_info *info)
+{
+ return __cfg802154_rdev_from_attrs(netns, info->attrs);
+}
+
+/* policy for the attributes */
+static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = {
+ [NL802154_ATTR_WPAN_PHY] = { .type = NLA_U32 },
+ [NL802154_ATTR_WPAN_PHY_NAME] = { .type = NLA_NUL_STRING,
+ .len = 20-1 },
+
+ [NL802154_ATTR_IFINDEX] = { .type = NLA_U32 },
+ [NL802154_ATTR_IFTYPE] = { .type = NLA_U32 },
+ [NL802154_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
+
+ [NL802154_ATTR_WPAN_DEV] = { .type = NLA_U64 },
+
+ [NL802154_ATTR_PAGE] = { .type = NLA_U8, },
+ [NL802154_ATTR_CHANNEL] = { .type = NLA_U8, },
+
+ [NL802154_ATTR_TX_POWER] = { .type = NLA_S8, },
+
+ [NL802154_ATTR_CCA_MODE] = { .type = NLA_U8, },
+
+ [NL802154_ATTR_SUPPORTED_CHANNEL] = { .type = NLA_U32, },
+
+ [NL802154_ATTR_PAN_ID] = { .type = NLA_U16, },
+ [NL802154_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 },
+ [NL802154_ATTR_SHORT_ADDR] = { .type = NLA_U16, },
+
+ [NL802154_ATTR_MIN_BE] = { .type = NLA_U8, },
+ [NL802154_ATTR_MAX_BE] = { .type = NLA_U8, },
+ [NL802154_ATTR_MAX_CSMA_BACKOFFS] = { .type = NLA_U8, },
+
+ [NL802154_ATTR_MAX_FRAME_RETRIES] = { .type = NLA_S8, },
+
+ [NL802154_ATTR_LBT_MODE] = { .type = NLA_U8, },
+};
+
+/* message building helper */
+static inline void *nl802154hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
+ int flags, u8 cmd)
+{
+ /* since there is no private header just add the generic one */
+ return genlmsg_put(skb, portid, seq, &nl802154_fam, flags, cmd);
+}
+
+static int
+nl802154_send_wpan_phy_channels(struct cfg802154_registered_device *rdev,
+ struct sk_buff *msg)
+{
+ struct nlattr *nl_page;
+ unsigned long page;
+
+ nl_page = nla_nest_start(msg, NL802154_ATTR_CHANNELS_SUPPORTED);
+ if (!nl_page)
+ return -ENOBUFS;
+
+ for (page = 0; page <= IEEE802154_MAX_PAGE; page++) {
+ if (nla_put_u32(msg, NL802154_ATTR_SUPPORTED_CHANNEL,
+ rdev->wpan_phy.channels_supported[page]))
+ return -ENOBUFS;
+ }
+ nla_nest_end(msg, nl_page);
+
+ return 0;
+}
+
+static int nl802154_send_wpan_phy(struct cfg802154_registered_device *rdev,
+ enum nl802154_commands cmd,
+ struct sk_buff *msg, u32 portid, u32 seq,
+ int flags)
+{
+ void *hdr;
+
+ hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
+ if (!hdr)
+ return -ENOBUFS;
+
+ if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) ||
+ nla_put_string(msg, NL802154_ATTR_WPAN_PHY_NAME,
+ wpan_phy_name(&rdev->wpan_phy)) ||
+ nla_put_u32(msg, NL802154_ATTR_GENERATION,
+ cfg802154_rdev_list_generation))
+ goto nla_put_failure;
+
+ if (cmd != NL802154_CMD_NEW_WPAN_PHY)
+ goto finish;
+
+ /* DUMP PHY PIB */
+
+ /* current channel settings */
+ if (nla_put_u8(msg, NL802154_ATTR_PAGE,
+ rdev->wpan_phy.current_page) ||
+ nla_put_u8(msg, NL802154_ATTR_CHANNEL,
+ rdev->wpan_phy.current_channel))
+ goto nla_put_failure;
+
+ /* supported channels array */
+ if (nl802154_send_wpan_phy_channels(rdev, msg))
+ goto nla_put_failure;
+
+ /* cca mode */
+ if (nla_put_u8(msg, NL802154_ATTR_CCA_MODE,
+ rdev->wpan_phy.cca_mode))
+ goto nla_put_failure;
+
+ if (nla_put_s8(msg, NL802154_ATTR_TX_POWER,
+ rdev->wpan_phy.transmit_power))
+ goto nla_put_failure;
+
+finish:
+ return genlmsg_end(msg, hdr);
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ return -EMSGSIZE;
+}
+
+struct nl802154_dump_wpan_phy_state {
+ s64 filter_wpan_phy;
+ long start;
+
+};
+
+static int nl802154_dump_wpan_phy_parse(struct sk_buff *skb,
+ struct netlink_callback *cb,
+ struct nl802154_dump_wpan_phy_state *state)
+{
+ struct nlattr **tb = nl802154_fam.attrbuf;
+ int ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl802154_fam.hdrsize,
+ tb, nl802154_fam.maxattr, nl802154_policy);
+
+ /* TODO check if we can handle error here,
+ * we have no backward compatibility
+ */
+ if (ret)
+ return 0;
+
+ if (tb[NL802154_ATTR_WPAN_PHY])
+ state->filter_wpan_phy = nla_get_u32(tb[NL802154_ATTR_WPAN_PHY]);
+ if (tb[NL802154_ATTR_WPAN_DEV])
+ state->filter_wpan_phy = nla_get_u64(tb[NL802154_ATTR_WPAN_DEV]) >> 32;
+ if (tb[NL802154_ATTR_IFINDEX]) {
+ struct net_device *netdev;
+ struct cfg802154_registered_device *rdev;
+ int ifidx = nla_get_u32(tb[NL802154_ATTR_IFINDEX]);
+
+ /* TODO netns */
+ netdev = __dev_get_by_index(&init_net, ifidx);
+ if (!netdev)
+ return -ENODEV;
+ if (netdev->ieee802154_ptr) {
+ rdev = wpan_phy_to_rdev(
+ netdev->ieee802154_ptr->wpan_phy);
+ state->filter_wpan_phy = rdev->wpan_phy_idx;
+ }
+ }
+
+ return 0;
+}
+
+static int
+nl802154_dump_wpan_phy(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int idx = 0, ret;
+ struct nl802154_dump_wpan_phy_state *state = (void *)cb->args[0];
+ struct cfg802154_registered_device *rdev;
+
+ rtnl_lock();
+ if (!state) {
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state) {
+ rtnl_unlock();
+ return -ENOMEM;
+ }
+ state->filter_wpan_phy = -1;
+ ret = nl802154_dump_wpan_phy_parse(skb, cb, state);
+ if (ret) {
+ kfree(state);
+ rtnl_unlock();
+ return ret;
+ }
+ cb->args[0] = (long)state;
+ }
+
+ list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
+ /* TODO net ns compare */
+ if (++idx <= state->start)
+ continue;
+ if (state->filter_wpan_phy != -1 &&
+ state->filter_wpan_phy != rdev->wpan_phy_idx)
+ continue;
+ /* attempt to fit multiple wpan_phy data chunks into the skb */
+ ret = nl802154_send_wpan_phy(rdev,
+ NL802154_CMD_NEW_WPAN_PHY,
+ skb,
+ NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, NLM_F_MULTI);
+ if (ret < 0) {
+ if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
+ !skb->len && cb->min_dump_alloc < 4096) {
+ cb->min_dump_alloc = 4096;
+ rtnl_unlock();
+ return 1;
+ }
+ idx--;
+ break;
+ }
+ break;
+ }
+ rtnl_unlock();
+
+ state->start = idx;
+
+ return skb->len;
+}
+
+static int nl802154_dump_wpan_phy_done(struct netlink_callback *cb)
+{
+ kfree((void *)cb->args[0]);
+ return 0;
+}
+
+static int nl802154_get_wpan_phy(struct sk_buff *skb, struct genl_info *info)
+{
+ struct sk_buff *msg;
+ struct cfg802154_registered_device *rdev = info->user_ptr[0];
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ if (nl802154_send_wpan_phy(rdev, NL802154_CMD_NEW_WPAN_PHY, msg,
+ info->snd_portid, info->snd_seq, 0) < 0) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ return genlmsg_reply(msg, info);
+}
+
+static inline u64 wpan_dev_id(struct wpan_dev *wpan_dev)
+{
+ return (u64)wpan_dev->identifier |
+ ((u64)wpan_phy_to_rdev(wpan_dev->wpan_phy)->wpan_phy_idx << 32);
+}
+
+static int
+nl802154_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
+ struct cfg802154_registered_device *rdev,
+ struct wpan_dev *wpan_dev)
+{
+ struct net_device *dev = wpan_dev->netdev;
+ void *hdr;
+
+ hdr = nl802154hdr_put(msg, portid, seq, flags,
+ NL802154_CMD_NEW_INTERFACE);
+ if (!hdr)
+ return -1;
+
+ if (dev &&
+ (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex) ||
+ nla_put_string(msg, NL802154_ATTR_IFNAME, dev->name)))
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) ||
+ nla_put_u32(msg, NL802154_ATTR_IFTYPE, wpan_dev->iftype) ||
+ nla_put_u64(msg, NL802154_ATTR_WPAN_DEV, wpan_dev_id(wpan_dev)) ||
+ nla_put_u32(msg, NL802154_ATTR_GENERATION,
+ rdev->devlist_generation ^
+ (cfg802154_rdev_list_generation << 2)))
+ goto nla_put_failure;
+
+ /* address settings */
+ if (nla_put_le64(msg, NL802154_ATTR_EXTENDED_ADDR,
+ wpan_dev->extended_addr) ||
+ nla_put_le16(msg, NL802154_ATTR_SHORT_ADDR,
+ wpan_dev->short_addr) ||
+ nla_put_le16(msg, NL802154_ATTR_PAN_ID, wpan_dev->pan_id))
+ goto nla_put_failure;
+
+ /* ARET handling */
+ if (nla_put_s8(msg, NL802154_ATTR_MAX_FRAME_RETRIES,
+ wpan_dev->frame_retries) ||
+ nla_put_u8(msg, NL802154_ATTR_MAX_BE, wpan_dev->max_be) ||
+ nla_put_u8(msg, NL802154_ATTR_MAX_CSMA_BACKOFFS,
+ wpan_dev->csma_retries) ||
+ nla_put_u8(msg, NL802154_ATTR_MIN_BE, wpan_dev->min_be))
+ goto nla_put_failure;
+
+ /* listen before transmit */
+ if (nla_put_u8(msg, NL802154_ATTR_LBT_MODE, wpan_dev->lbt))
+ goto nla_put_failure;
+
+ return genlmsg_end(msg, hdr);
+
+nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ return -EMSGSIZE;
+}
+
+static int
+nl802154_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int wp_idx = 0;
+ int if_idx = 0;
+ int wp_start = cb->args[0];
+ int if_start = cb->args[1];
+ struct cfg802154_registered_device *rdev;
+ struct wpan_dev *wpan_dev;
+
+ rtnl_lock();
+ list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
+ /* TODO netns compare */
+ if (wp_idx < wp_start) {
+ wp_idx++;
+ continue;
+ }
+ if_idx = 0;
+
+ list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
+ if (if_idx < if_start) {
+ if_idx++;
+ continue;
+ }
+ if (nl802154_send_iface(skb, NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ rdev, wpan_dev) < 0) {
+ goto out;
+ }
+ if_idx++;
+ }
+
+ wp_idx++;
+ }
+out:
+ rtnl_unlock();
+
+ cb->args[0] = wp_idx;
+ cb->args[1] = if_idx;
+
+ return skb->len;
+}
+
+static int nl802154_get_interface(struct sk_buff *skb, struct genl_info *info)
+{
+ struct sk_buff *msg;
+ struct cfg802154_registered_device *rdev = info->user_ptr[0];
+ struct wpan_dev *wdev = info->user_ptr[1];
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ if (nl802154_send_iface(msg, info->snd_portid, info->snd_seq, 0,
+ rdev, wdev) < 0) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ return genlmsg_reply(msg, info);
+}
+
+static int nl802154_new_interface(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg802154_registered_device *rdev = info->user_ptr[0];
+ enum nl802154_iftype type = NL802154_IFTYPE_UNSPEC;
+ __le64 extended_addr = cpu_to_le64(0x0000000000000000ULL);
+
+ /* TODO avoid failing a new interface
+ * creation due to pending removal?
+ */
+
+ if (!info->attrs[NL802154_ATTR_IFNAME])
+ return -EINVAL;
+
+ if (info->attrs[NL802154_ATTR_IFTYPE]) {
+ type = nla_get_u32(info->attrs[NL802154_ATTR_IFTYPE]);
+ if (type > NL802154_IFTYPE_MAX)
+ return -EINVAL;
+ }
+
+ /* TODO add nla_get_le64 to netlink */
+ if (info->attrs[NL802154_ATTR_EXTENDED_ADDR])
+ extended_addr = (__force __le64)nla_get_u64(
+ info->attrs[NL802154_ATTR_EXTENDED_ADDR]);
+
+ if (!rdev->ops->add_virtual_intf)
+ return -EOPNOTSUPP;
+
+ return rdev_add_virtual_intf(rdev,
+ nla_data(info->attrs[NL802154_ATTR_IFNAME]),
+ type, extended_addr);
+}
+
+static int nl802154_del_interface(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg802154_registered_device *rdev = info->user_ptr[0];
+ struct wpan_dev *wpan_dev = info->user_ptr[1];
+
+ if (!rdev->ops->del_virtual_intf)
+ return -EOPNOTSUPP;
+
+ /* If we remove a wpan device without a netdev then clear
+ * user_ptr[1] so that nl802154_post_doit won't dereference it
+ * to check if it needs to do dev_put(). Otherwise it crashes
+ * since the wpan_dev has been freed, unlike with a netdev where
+ * we need the dev_put() for the netdev to really be freed.
+ */
+ if (!wpan_dev->netdev)
+ info->user_ptr[1] = NULL;
+
+ return rdev_del_virtual_intf(rdev, wpan_dev);
+}
+
+static int nl802154_set_channel(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg802154_registered_device *rdev = info->user_ptr[0];
+ u8 channel, page;
+
+ if (!info->attrs[NL802154_ATTR_PAGE] ||
+ !info->attrs[NL802154_ATTR_CHANNEL])
+ return -EINVAL;
+
+ page = nla_get_u8(info->attrs[NL802154_ATTR_PAGE]);
+ channel = nla_get_u8(info->attrs[NL802154_ATTR_CHANNEL]);
+
+ /* check 802.15.4 constraints */
+ if (page > IEEE802154_MAX_PAGE || channel > IEEE802154_MAX_CHANNEL)
+ return -EINVAL;
+
+ return rdev_set_channel(rdev, page, channel);
+}
+
+static int nl802154_set_pan_id(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg802154_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+ __le16 pan_id;
+
+ /* conflict here while tx/rx calls */
+ if (netif_running(dev))
+ return -EBUSY;
+
+ /* don't change address fields on monitor */
+ if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
+ return -EINVAL;
+
+ if (!info->attrs[NL802154_ATTR_PAN_ID])
+ return -EINVAL;
+
+ pan_id = nla_get_le16(info->attrs[NL802154_ATTR_PAN_ID]);
+
+ return rdev_set_pan_id(rdev, wpan_dev, pan_id);
+}
+
+static int nl802154_set_short_addr(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg802154_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+ __le16 short_addr;
+
+ /* conflict here while tx/rx calls */
+ if (netif_running(dev))
+ return -EBUSY;
+
+ /* don't change address fields on monitor */
+ if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
+ return -EINVAL;
+
+ if (!info->attrs[NL802154_ATTR_SHORT_ADDR])
+ return -EINVAL;
+
+ short_addr = nla_get_le16(info->attrs[NL802154_ATTR_SHORT_ADDR]);
+
+ return rdev_set_short_addr(rdev, wpan_dev, short_addr);
+}
+
+static int
+nl802154_set_backoff_exponent(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg802154_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+ u8 min_be, max_be;
+
+ /* should be set on netif open inside phy settings */
+ if (netif_running(dev))
+ return -EBUSY;
+
+ if (!info->attrs[NL802154_ATTR_MIN_BE] ||
+ !info->attrs[NL802154_ATTR_MAX_BE])
+ return -EINVAL;
+
+ min_be = nla_get_u8(info->attrs[NL802154_ATTR_MIN_BE]);
+ max_be = nla_get_u8(info->attrs[NL802154_ATTR_MAX_BE]);
+
+ /* check 802.15.4 constraints */
+ if (max_be < 3 || max_be > 8 || min_be > max_be)
+ return -EINVAL;
+
+ return rdev_set_backoff_exponent(rdev, wpan_dev, min_be, max_be);
+}
+
+static int
+nl802154_set_max_csma_backoffs(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg802154_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+ u8 max_csma_backoffs;
+
+ /* conflict here while other running iface settings */
+ if (netif_running(dev))
+ return -EBUSY;
+
+ if (!info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS])
+ return -EINVAL;
+
+ max_csma_backoffs = nla_get_u8(
+ info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS]);
+
+ /* check 802.15.4 constraints */
+ if (max_csma_backoffs > 5)
+ return -EINVAL;
+
+ return rdev_set_max_csma_backoffs(rdev, wpan_dev, max_csma_backoffs);
+}
+
+static int
+nl802154_set_max_frame_retries(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg802154_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+ s8 max_frame_retries;
+
+ if (netif_running(dev))
+ return -EBUSY;
+
+ if (!info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES])
+ return -EINVAL;
+
+ max_frame_retries = nla_get_s8(
+ info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES]);
+
+ /* check 802.15.4 constraints */
+ if (max_frame_retries < -1 || max_frame_retries > 7)
+ return -EINVAL;
+
+ return rdev_set_max_frame_retries(rdev, wpan_dev, max_frame_retries);
+}
+
+static int nl802154_set_lbt_mode(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg802154_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+ bool mode;
+
+ if (netif_running(dev))
+ return -EBUSY;
+
+ if (!info->attrs[NL802154_ATTR_LBT_MODE])
+ return -EINVAL;
+
+ mode = !!nla_get_u8(info->attrs[NL802154_ATTR_LBT_MODE]);
+ return rdev_set_lbt_mode(rdev, wpan_dev, mode);
+}
+
+#define NL802154_FLAG_NEED_WPAN_PHY 0x01
+#define NL802154_FLAG_NEED_NETDEV 0x02
+#define NL802154_FLAG_NEED_RTNL 0x04
+#define NL802154_FLAG_CHECK_NETDEV_UP 0x08
+#define NL802154_FLAG_NEED_NETDEV_UP (NL802154_FLAG_NEED_NETDEV |\
+ NL802154_FLAG_CHECK_NETDEV_UP)
+#define NL802154_FLAG_NEED_WPAN_DEV 0x10
+#define NL802154_FLAG_NEED_WPAN_DEV_UP (NL802154_FLAG_NEED_WPAN_DEV |\
+ NL802154_FLAG_CHECK_NETDEV_UP)
+
+static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg802154_registered_device *rdev;
+ struct wpan_dev *wpan_dev;
+ struct net_device *dev;
+ bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL;
+
+ if (rtnl)
+ rtnl_lock();
+
+ if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) {
+ rdev = cfg802154_get_dev_from_info(genl_info_net(info), info);
+ if (IS_ERR(rdev)) {
+ if (rtnl)
+ rtnl_unlock();
+ return PTR_ERR(rdev);
+ }
+ info->user_ptr[0] = rdev;
+ } else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV ||
+ ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
+ ASSERT_RTNL();
+ wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info),
+ info->attrs);
+ if (IS_ERR(wpan_dev)) {
+ if (rtnl)
+ rtnl_unlock();
+ return PTR_ERR(wpan_dev);
+ }
+
+ dev = wpan_dev->netdev;
+ rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
+
+ if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) {
+ if (!dev) {
+ if (rtnl)
+ rtnl_unlock();
+ return -EINVAL;
+ }
+
+ info->user_ptr[1] = dev;
+ } else {
+ info->user_ptr[1] = wpan_dev;
+ }
+
+ if (dev) {
+ if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP &&
+ !netif_running(dev)) {
+ if (rtnl)
+ rtnl_unlock();
+ return -ENETDOWN;
+ }
+
+ dev_hold(dev);
+ }
+
+ info->user_ptr[0] = rdev;
+ }
+
+ return 0;
+}
+
+static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
+ struct genl_info *info)
+{
+ if (info->user_ptr[1]) {
+ if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
+ struct wpan_dev *wpan_dev = info->user_ptr[1];
+
+ if (wpan_dev->netdev)
+ dev_put(wpan_dev->netdev);
+ } else {
+ dev_put(info->user_ptr[1]);
+ }
+ }
+
+ if (ops->internal_flags & NL802154_FLAG_NEED_RTNL)
+ rtnl_unlock();
+}
+
+static const struct genl_ops nl802154_ops[] = {
+ {
+ .cmd = NL802154_CMD_GET_WPAN_PHY,
+ .doit = nl802154_get_wpan_phy,
+ .dumpit = nl802154_dump_wpan_phy,
+ .done = nl802154_dump_wpan_phy_done,
+ .policy = nl802154_policy,
+ /* can be retrieved by unprivileged users */
+ .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
+ NL802154_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL802154_CMD_GET_INTERFACE,
+ .doit = nl802154_get_interface,
+ .dumpit = nl802154_dump_interface,
+ .policy = nl802154_policy,
+ /* can be retrieved by unprivileged users */
+ .internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
+ NL802154_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL802154_CMD_NEW_INTERFACE,
+ .doit = nl802154_new_interface,
+ .policy = nl802154_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
+ NL802154_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL802154_CMD_DEL_INTERFACE,
+ .doit = nl802154_del_interface,
+ .policy = nl802154_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
+ NL802154_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL802154_CMD_SET_CHANNEL,
+ .doit = nl802154_set_channel,
+ .policy = nl802154_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
+ NL802154_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL802154_CMD_SET_PAN_ID,
+ .doit = nl802154_set_pan_id,
+ .policy = nl802154_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL802154_FLAG_NEED_NETDEV |
+ NL802154_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL802154_CMD_SET_SHORT_ADDR,
+ .doit = nl802154_set_short_addr,
+ .policy = nl802154_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL802154_FLAG_NEED_NETDEV |
+ NL802154_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL802154_CMD_SET_BACKOFF_EXPONENT,
+ .doit = nl802154_set_backoff_exponent,
+ .policy = nl802154_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL802154_FLAG_NEED_NETDEV |
+ NL802154_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL802154_CMD_SET_MAX_CSMA_BACKOFFS,
+ .doit = nl802154_set_max_csma_backoffs,
+ .policy = nl802154_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL802154_FLAG_NEED_NETDEV |
+ NL802154_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL802154_CMD_SET_MAX_FRAME_RETRIES,
+ .doit = nl802154_set_max_frame_retries,
+ .policy = nl802154_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL802154_FLAG_NEED_NETDEV |
+ NL802154_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL802154_CMD_SET_LBT_MODE,
+ .doit = nl802154_set_lbt_mode,
+ .policy = nl802154_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL802154_FLAG_NEED_NETDEV |
+ NL802154_FLAG_NEED_RTNL,
+ },
+};
+
+/* initialisation/exit functions */
+int nl802154_init(void)
+{
+ return genl_register_family_with_ops_groups(&nl802154_fam, nl802154_ops,
+ nl802154_mcgrps);
+}
+
+void nl802154_exit(void)
+{
+ genl_unregister_family(&nl802154_fam);
+}
diff --git a/net/ieee802154/nl802154.h b/net/ieee802154/nl802154.h
new file mode 100644
index 000000000000..3846a89d0958
--- /dev/null
+++ b/net/ieee802154/nl802154.h
@@ -0,0 +1,7 @@
+#ifndef __IEEE802154_NL802154_H
+#define __IEEE802154_NL802154_H
+
+int nl802154_init(void);
+void nl802154_exit(void);
+
+#endif /* __IEEE802154_NL802154_H */
diff --git a/net/ieee802154/nl_policy.c b/net/ieee802154/nl_policy.c
index 3a703ab88348..35c432668454 100644
--- a/net/ieee802154/nl_policy.c
+++ b/net/ieee802154/nl_policy.c
@@ -12,10 +12,6 @@
* 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>
diff --git a/net/ieee802154/raw.c b/net/ieee802154/raw.c
index 9d1f64806f02..1674b115c891 100644
--- a/net/ieee802154/raw.c
+++ b/net/ieee802154/raw.c
@@ -12,10 +12,6 @@
* 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.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
@@ -154,7 +150,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk,
skb_reset_mac_header(skb);
skb_reset_network_header(skb);
- err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
+ err = memcpy_from_msg(skb_put(skb, size), msg, size);
if (err < 0)
goto out_skb;
@@ -195,7 +191,7 @@ static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
copied = len;
}
- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
if (err)
goto done;
@@ -225,7 +221,6 @@ static int raw_rcv_skb(struct sock *sk, struct sk_buff *skb)
return NET_RX_SUCCESS;
}
-
void ieee802154_raw_deliver(struct net_device *dev, struct sk_buff *skb)
{
struct sock *sk;
diff --git a/net/ieee802154/rdev-ops.h b/net/ieee802154/rdev-ops.h
new file mode 100644
index 000000000000..aff54fbd9264
--- /dev/null
+++ b/net/ieee802154/rdev-ops.h
@@ -0,0 +1,89 @@
+#ifndef __CFG802154_RDEV_OPS
+#define __CFG802154_RDEV_OPS
+
+#include <net/cfg802154.h>
+
+#include "core.h"
+
+static inline struct net_device *
+rdev_add_virtual_intf_deprecated(struct cfg802154_registered_device *rdev,
+ const char *name, int type)
+{
+ return rdev->ops->add_virtual_intf_deprecated(&rdev->wpan_phy, name,
+ type);
+}
+
+static inline void
+rdev_del_virtual_intf_deprecated(struct cfg802154_registered_device *rdev,
+ struct net_device *dev)
+{
+ rdev->ops->del_virtual_intf_deprecated(&rdev->wpan_phy, dev);
+}
+
+static inline int
+rdev_add_virtual_intf(struct cfg802154_registered_device *rdev, char *name,
+ enum nl802154_iftype type, __le64 extended_addr)
+{
+ return rdev->ops->add_virtual_intf(&rdev->wpan_phy, name, type,
+ extended_addr);
+}
+
+static inline int
+rdev_del_virtual_intf(struct cfg802154_registered_device *rdev,
+ struct wpan_dev *wpan_dev)
+{
+ return rdev->ops->del_virtual_intf(&rdev->wpan_phy, wpan_dev);
+}
+
+static inline int
+rdev_set_channel(struct cfg802154_registered_device *rdev, u8 page, u8 channel)
+{
+ return rdev->ops->set_channel(&rdev->wpan_phy, page, channel);
+}
+
+static inline int
+rdev_set_pan_id(struct cfg802154_registered_device *rdev,
+ struct wpan_dev *wpan_dev, __le16 pan_id)
+{
+ return rdev->ops->set_pan_id(&rdev->wpan_phy, wpan_dev, pan_id);
+}
+
+static inline int
+rdev_set_short_addr(struct cfg802154_registered_device *rdev,
+ struct wpan_dev *wpan_dev, __le16 short_addr)
+{
+ return rdev->ops->set_short_addr(&rdev->wpan_phy, wpan_dev, short_addr);
+}
+
+static inline int
+rdev_set_backoff_exponent(struct cfg802154_registered_device *rdev,
+ struct wpan_dev *wpan_dev, u8 min_be, u8 max_be)
+{
+ return rdev->ops->set_backoff_exponent(&rdev->wpan_phy, wpan_dev,
+ min_be, max_be);
+}
+
+static inline int
+rdev_set_max_csma_backoffs(struct cfg802154_registered_device *rdev,
+ struct wpan_dev *wpan_dev, u8 max_csma_backoffs)
+{
+ return rdev->ops->set_max_csma_backoffs(&rdev->wpan_phy, wpan_dev,
+ max_csma_backoffs);
+}
+
+static inline int
+rdev_set_max_frame_retries(struct cfg802154_registered_device *rdev,
+ struct wpan_dev *wpan_dev, s8 max_frame_retries)
+{
+ return rdev->ops->set_max_frame_retries(&rdev->wpan_phy, wpan_dev,
+ max_frame_retries);
+}
+
+static inline int
+rdev_set_lbt_mode(struct cfg802154_registered_device *rdev,
+ struct wpan_dev *wpan_dev, bool mode)
+{
+ return rdev->ops->set_lbt_mode(&rdev->wpan_phy, wpan_dev, mode);
+}
+
+#endif /* __CFG802154_RDEV_OPS */
diff --git a/net/ieee802154/reassembly.c b/net/ieee802154/reassembly.c
index 7cfcd6885225..9d980ed3ffe2 100644
--- a/net/ieee802154/reassembly.c
+++ b/net/ieee802154/reassembly.c
@@ -33,7 +33,7 @@
static const char lowpan_frags_cache_name[] = "lowpan-frags";
struct lowpan_frag_info {
- __be16 d_tag;
+ u16 d_tag;
u16 d_size;
u8 d_offset;
};
@@ -48,7 +48,7 @@ static struct inet_frags lowpan_frags;
static int lowpan_frag_reasm(struct lowpan_frag_queue *fq,
struct sk_buff *prev, struct net_device *dev);
-static unsigned int lowpan_hash_frag(__be16 tag, u16 d_size,
+static unsigned int lowpan_hash_frag(u16 tag, u16 d_size,
const struct ieee802154_addr *saddr,
const struct ieee802154_addr *daddr)
{
@@ -330,11 +330,13 @@ static int lowpan_get_frag_info(struct sk_buff *skb, const u8 frag_type,
{
bool fail;
u8 pattern = 0, low = 0;
+ __be16 d_tag = 0;
fail = lowpan_fetch_skb(skb, &pattern, 1);
fail |= lowpan_fetch_skb(skb, &low, 1);
frag_info->d_size = (pattern & 7) << 8 | low;
- fail |= lowpan_fetch_skb(skb, &frag_info->d_tag, 2);
+ fail |= lowpan_fetch_skb(skb, &d_tag, 2);
+ frag_info->d_tag = ntohs(d_tag);
if (frag_type == LOWPAN_DISPATCH_FRAGN) {
fail |= lowpan_fetch_skb(skb, &frag_info->d_offset, 1);
diff --git a/net/ieee802154/reassembly.h b/net/ieee802154/reassembly.h
index 74e4a7c98191..836b16fa001f 100644
--- a/net/ieee802154/reassembly.h
+++ b/net/ieee802154/reassembly.h
@@ -4,7 +4,7 @@
#include <net/inet_frag.h>
struct lowpan_create_arg {
- __be16 tag;
+ u16 tag;
u16 d_size;
const struct ieee802154_addr *src;
const struct ieee802154_addr *dst;
@@ -15,7 +15,7 @@ struct lowpan_create_arg {
struct lowpan_frag_queue {
struct inet_frag_queue q;
- __be16 tag;
+ u16 tag;
u16 d_size;
struct ieee802154_addr saddr;
struct ieee802154_addr daddr;
diff --git a/net/ieee802154/sysfs.c b/net/ieee802154/sysfs.c
new file mode 100644
index 000000000000..1613b9c65dfa
--- /dev/null
+++ b/net/ieee802154/sysfs.c
@@ -0,0 +1,128 @@
+/* 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.
+ *
+ * Authors:
+ * Alexander Aring <aar@pengutronix.de>
+ *
+ * Based on: net/wireless/sysfs.c
+ */
+
+#include <linux/device.h>
+
+#include <net/cfg802154.h>
+
+#include "core.h"
+#include "sysfs.h"
+
+static inline struct cfg802154_registered_device *
+dev_to_rdev(struct device *dev)
+{
+ return container_of(dev, struct cfg802154_registered_device,
+ wpan_phy.dev);
+}
+
+#define SHOW_FMT(name, fmt, member) \
+static ssize_t name ## _show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ return sprintf(buf, fmt "\n", dev_to_rdev(dev)->member); \
+} \
+static DEVICE_ATTR_RO(name)
+
+SHOW_FMT(index, "%d", wpan_phy_idx);
+
+static ssize_t name_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct wpan_phy *wpan_phy = &dev_to_rdev(dev)->wpan_phy;
+
+ return sprintf(buf, "%s\n", dev_name(&wpan_phy->dev));
+}
+static DEVICE_ATTR_RO(name);
+
+#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 = snprintf(buf, PAGE_SIZE, format_string "\n", args); \
+ mutex_unlock(&phy->pib_lock); \
+ return ret; \
+} \
+static DEVICE_ATTR_RO(name)
+
+#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(transmit_power, "%d +- 1 dB");
+MASTER_SHOW(cca_mode, "%d");
+
+static ssize_t channels_supported_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev);
+ int ret;
+ int i, len = 0;
+
+ mutex_lock(&phy->pib_lock);
+ for (i = 0; i < 32; i++) {
+ ret = snprintf(buf + len, PAGE_SIZE - len,
+ "%#09x\n", phy->channels_supported[i]);
+ if (ret < 0)
+ break;
+ len += ret;
+ }
+ mutex_unlock(&phy->pib_lock);
+ return len;
+}
+static DEVICE_ATTR_RO(channels_supported);
+
+static void wpan_phy_release(struct device *dev)
+{
+ struct cfg802154_registered_device *rdev = dev_to_rdev(dev);
+
+ cfg802154_dev_free(rdev);
+}
+
+static struct attribute *pmib_attrs[] = {
+ &dev_attr_index.attr,
+ &dev_attr_name.attr,
+ /* below will be removed soon */
+ &dev_attr_current_channel.attr,
+ &dev_attr_current_page.attr,
+ &dev_attr_channels_supported.attr,
+ &dev_attr_transmit_power.attr,
+ &dev_attr_cca_mode.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(pmib);
+
+struct class wpan_phy_class = {
+ .name = "ieee802154",
+ .dev_release = wpan_phy_release,
+ .dev_groups = pmib_groups,
+};
+
+int wpan_phy_sysfs_init(void)
+{
+ return class_register(&wpan_phy_class);
+}
+
+void wpan_phy_sysfs_exit(void)
+{
+ class_unregister(&wpan_phy_class);
+}
diff --git a/net/ieee802154/sysfs.h b/net/ieee802154/sysfs.h
new file mode 100644
index 000000000000..aa42e39ecbec
--- /dev/null
+++ b/net/ieee802154/sysfs.h
@@ -0,0 +1,9 @@
+#ifndef __IEEE802154_SYSFS_H
+#define __IEEE802154_SYSFS_H
+
+int wpan_phy_sysfs_init(void);
+void wpan_phy_sysfs_exit(void);
+
+extern struct class wpan_phy_class;
+
+#endif /* __IEEE802154_SYSFS_H */
diff --git a/net/ieee802154/wpan-class.c b/net/ieee802154/wpan-class.c
deleted file mode 100644
index 4955e0fe5883..000000000000
--- a/net/ieee802154/wpan-class.c
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * 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/slab.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/device.h>
-
-#include <net/wpan-phy.h>
-
-#include "ieee802154.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 = snprintf(buf, PAGE_SIZE, format_string "\n", args); \
- mutex_unlock(&phy->pib_lock); \
- return ret; \
-} \
-static DEVICE_ATTR_RO(name);
-
-#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(transmit_power, "%d +- 1 dB");
-MASTER_SHOW(cca_mode, "%d");
-
-static ssize_t channels_supported_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev);
- int ret;
- int i, len = 0;
-
- mutex_lock(&phy->pib_lock);
- for (i = 0; i < 32; i++) {
- ret = snprintf(buf + len, PAGE_SIZE - len,
- "%#09x\n", phy->channels_supported[i]);
- if (ret < 0)
- break;
- len += ret;
- }
- mutex_unlock(&phy->pib_lock);
- return len;
-}
-static DEVICE_ATTR_RO(channels_supported);
-
-static struct attribute *pmib_attrs[] = {
- &dev_attr_current_channel.attr,
- &dev_attr_current_page.attr,
- &dev_attr_channels_supported.attr,
- &dev_attr_transmit_power.attr,
- &dev_attr_cca_mode.attr,
- NULL,
-};
-ATTRIBUTE_GROUPS(pmib);
-
-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_groups = pmib_groups,
-};
-
-static DEFINE_MUTEX(wpan_phy_mutex);
-static int wpan_phy_idx;
-
-static int wpan_phy_match(struct device *dev, const 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, str, wpan_phy_match);
- if (!dev)
- return NULL;
-
- return container_of(dev, struct wpan_phy, dev);
-}
-EXPORT_SYMBOL(wpan_phy_find);
-
-struct wpan_phy_iter_data {
- int (*fn)(struct wpan_phy *phy, void *data);
- void *data;
-};
-
-static int wpan_phy_iter(struct device *dev, void *_data)
-{
- struct wpan_phy_iter_data *wpid = _data;
- struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev);
-
- return wpid->fn(phy, wpid->data);
-}
-
-int wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data),
- void *data)
-{
- struct wpan_phy_iter_data wpid = {
- .fn = fn,
- .data = data,
- };
-
- return class_for_each_device(&wpan_phy_class, NULL,
- &wpid, wpan_phy_iter);
-}
-EXPORT_SYMBOL(wpan_phy_for_each);
-
-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);
-
- if (!phy)
- goto out;
- 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);
- goto out;
- }
- 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;
-
- phy->current_channel = -1; /* not initialised */
- phy->current_page = 0; /* for compatibility */
-
- return phy;
-
-out:
- return NULL;
-}
-EXPORT_SYMBOL(wpan_phy_alloc);
-
-int wpan_phy_register(struct wpan_phy *phy)
-{
- 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)
-{
- int rc;
-
- rc = class_register(&wpan_phy_class);
- if (rc)
- goto err;
-
- rc = ieee802154_nl_init();
- if (rc)
- goto err_nl;
-
- return 0;
-err_nl:
- class_unregister(&wpan_phy_class);
-err:
- return rc;
-}
-subsys_initcall(wpan_phy_class_init);
-
-static void __exit wpan_phy_class_exit(void)
-{
- ieee802154_nl_exit();
- class_unregister(&wpan_phy_class);
-}
-module_exit(wpan_phy_class_exit);
-
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("IEEE 802.15.4 configuration interface");
-MODULE_AUTHOR("Dmitry Eremin-Solenikov");
-
diff --git a/net/ipv4/Kconfig b/net/ipv4/Kconfig
index e682b48e0709..bd2901604842 100644
--- a/net/ipv4/Kconfig
+++ b/net/ipv4/Kconfig
@@ -322,6 +322,15 @@ config NET_FOU
network mechanisms and optimizations for UDP (such as ECMP
and RSS) can be leveraged to provide better service.
+config NET_FOU_IP_TUNNELS
+ bool "IP: FOU encapsulation of IP tunnels"
+ depends on NET_IPIP || NET_IPGRE || IPV6_SIT
+ select NET_FOU
+ ---help---
+ Allow configuration of FOU or GUE encapsulation for IP tunnels.
+ When this option is enabled IP tunnels can be configured to use
+ FOU or GUE encapsulation.
+
config GENEVE
tristate "Generic Network Virtualization Encapsulation (Geneve)"
depends on INET
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 8b7fe5b03906..a44773c8346c 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1222,7 +1222,7 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb,
SKB_GSO_TCPV6 |
SKB_GSO_UDP_TUNNEL |
SKB_GSO_UDP_TUNNEL_CSUM |
- SKB_GSO_MPLS |
+ SKB_GSO_TUNNEL_REMCSUM |
0)))
goto out;
@@ -1386,6 +1386,17 @@ out:
return pp;
}
+int inet_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
+{
+ if (sk->sk_family == AF_INET)
+ return ip_recv_error(sk, msg, len, addr_len);
+#if IS_ENABLED(CONFIG_IPV6)
+ if (sk->sk_family == AF_INET6)
+ return pingv6_ops.ipv6_recv_error(sk, msg, len, addr_len);
+#endif
+ return -EINVAL;
+}
+
static int inet_gro_complete(struct sk_buff *skb, int nhoff)
{
__be16 newlen = htons(skb->len - nhoff);
diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
index 16acb59d665e..205e1472aa78 100644
--- a/net/ipv4/arp.c
+++ b/net/ipv4/arp.c
@@ -1292,7 +1292,7 @@ static int arp_proc_init(void);
void __init arp_init(void)
{
- neigh_table_init(&arp_tbl);
+ neigh_table_init(NEIGH_ARP_TABLE, &arp_tbl);
dev_add_pack(&arp_packet_type);
arp_proc_init();
diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c
index 4715f25dfe03..5160c710f2eb 100644
--- a/net/ipv4/cipso_ipv4.c
+++ b/net/ipv4/cipso_ipv4.c
@@ -50,7 +50,7 @@
#include <net/netlabel.h>
#include <net/cipso_ipv4.h>
#include <linux/atomic.h>
-#include <asm/bug.h>
+#include <linux/bug.h>
#include <asm/unaligned.h>
/* List of available DOI definitions */
@@ -72,6 +72,7 @@ struct cipso_v4_map_cache_bkt {
u32 size;
struct list_head list;
};
+
struct cipso_v4_map_cache_entry {
u32 hash;
unsigned char *key;
@@ -82,7 +83,8 @@ struct cipso_v4_map_cache_entry {
u32 activity;
struct list_head list;
};
-static struct cipso_v4_map_cache_bkt *cipso_v4_cache = NULL;
+
+static struct cipso_v4_map_cache_bkt *cipso_v4_cache;
/* Restricted bitmap (tag #1) flags */
int cipso_v4_rbm_optfmt = 0;
@@ -539,7 +541,7 @@ doi_add_return:
/**
* cipso_v4_doi_free - Frees a DOI definition
- * @entry: the entry's RCU field
+ * @doi_def: the DOI definition
*
* Description:
* This function frees all of the memory associated with a DOI definition.
diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c
index 360b565918c4..60173d4d3a0e 100644
--- a/net/ipv4/esp4.c
+++ b/net/ipv4/esp4.c
@@ -392,8 +392,10 @@ static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
if (elen <= 0)
goto out;
- if ((err = skb_cow_data(skb, 0, &trailer)) < 0)
+ err = skb_cow_data(skb, 0, &trailer);
+ if (err < 0)
goto out;
+
nfrags = err;
assoclen = sizeof(*esph);
@@ -601,12 +603,12 @@ static int esp_init_authenc(struct xfrm_state *x)
BUG_ON(!aalg_desc);
err = -EINVAL;
- if (aalg_desc->uinfo.auth.icv_fullbits/8 !=
+ if (aalg_desc->uinfo.auth.icv_fullbits / 8 !=
crypto_aead_authsize(aead)) {
- NETDEBUG(KERN_INFO "ESP: %s digestsize %u != %hu\n",
- x->aalg->alg_name,
- crypto_aead_authsize(aead),
- aalg_desc->uinfo.auth.icv_fullbits/8);
+ pr_info("ESP: %s digestsize %u != %hu\n",
+ x->aalg->alg_name,
+ crypto_aead_authsize(aead),
+ aalg_desc->uinfo.auth.icv_fullbits / 8);
goto free_key;
}
diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c
index f2e15738534d..8f7bd56955b0 100644
--- a/net/ipv4/fib_rules.c
+++ b/net/ipv4/fib_rules.c
@@ -62,6 +62,10 @@ int __fib_lookup(struct net *net, struct flowi4 *flp, struct fib_result *res)
else
res->tclassid = 0;
#endif
+
+ if (err == -ESRCH)
+ err = -ENETUNREACH;
+
return err;
}
EXPORT_SYMBOL_GPL(__fib_lookup);
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index e9cb2588e416..18bcaf2ff2fd 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -1143,8 +1143,9 @@ static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen)
put_child(tp, cindex, (struct rt_trie_node *)tn);
} else {
rcu_assign_pointer(t->trie, (struct rt_trie_node *)tn);
- tp = tn;
}
+
+ tp = tn;
}
if (tp && tp->pos + tp->bits > 32)
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c
index 32e78924e246..b986298a7ba3 100644
--- a/net/ipv4/fou.c
+++ b/net/ipv4/fou.c
@@ -38,21 +38,17 @@ static inline struct fou *fou_from_sock(struct sock *sk)
return sk->sk_user_data;
}
-static int fou_udp_encap_recv_deliver(struct sk_buff *skb,
- u8 protocol, size_t len)
+static void fou_recv_pull(struct sk_buff *skb, size_t len)
{
struct iphdr *iph = ip_hdr(skb);
/* Remove 'len' bytes from the packet (UDP header and
- * FOU header if present), modify the protocol to the one
- * we found, and then call rcv_encap.
+ * FOU header if present).
*/
iph->tot_len = htons(ntohs(iph->tot_len) - len);
__skb_pull(skb, len);
skb_postpull_rcsum(skb, udp_hdr(skb), len);
skb_reset_transport_header(skb);
-
- return -protocol;
}
static int fou_udp_recv(struct sock *sk, struct sk_buff *skb)
@@ -62,16 +58,56 @@ static int fou_udp_recv(struct sock *sk, struct sk_buff *skb)
if (!fou)
return 1;
- return fou_udp_encap_recv_deliver(skb, fou->protocol,
- sizeof(struct udphdr));
+ fou_recv_pull(skb, sizeof(struct udphdr));
+
+ return -fou->protocol;
+}
+
+static struct guehdr *gue_remcsum(struct sk_buff *skb, struct guehdr *guehdr,
+ void *data, size_t hdrlen, u8 ipproto)
+{
+ __be16 *pd = data;
+ size_t start = ntohs(pd[0]);
+ size_t offset = ntohs(pd[1]);
+ size_t plen = hdrlen + max_t(size_t, offset + sizeof(u16), start);
+ __wsum delta;
+
+ if (skb->remcsum_offload) {
+ /* Already processed in GRO path */
+ skb->remcsum_offload = 0;
+ return guehdr;
+ }
+
+ if (!pskb_may_pull(skb, plen))
+ return NULL;
+ guehdr = (struct guehdr *)&udp_hdr(skb)[1];
+
+ if (unlikely(skb->ip_summed != CHECKSUM_COMPLETE))
+ __skb_checksum_complete(skb);
+
+ delta = remcsum_adjust((void *)guehdr + hdrlen,
+ skb->csum, start, offset);
+
+ /* Adjust skb->csum since we changed the packet */
+ skb->csum = csum_add(skb->csum, delta);
+
+ return guehdr;
+}
+
+static int gue_control_message(struct sk_buff *skb, struct guehdr *guehdr)
+{
+ /* No support yet */
+ kfree_skb(skb);
+ return 0;
}
static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
{
struct fou *fou = fou_from_sock(sk);
- size_t len;
+ size_t len, optlen, hdrlen;
struct guehdr *guehdr;
- struct udphdr *uh;
+ void *data;
+ u16 doffset = 0;
if (!fou)
return 1;
@@ -80,25 +116,58 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
if (!pskb_may_pull(skb, len))
goto drop;
- uh = udp_hdr(skb);
- guehdr = (struct guehdr *)&uh[1];
+ guehdr = (struct guehdr *)&udp_hdr(skb)[1];
+
+ optlen = guehdr->hlen << 2;
+ len += optlen;
- len += guehdr->hlen << 2;
if (!pskb_may_pull(skb, len))
goto drop;
- uh = udp_hdr(skb);
- guehdr = (struct guehdr *)&uh[1];
+ /* guehdr may change after pull */
+ guehdr = (struct guehdr *)&udp_hdr(skb)[1];
- if (guehdr->version != 0)
- goto drop;
+ hdrlen = sizeof(struct guehdr) + optlen;
- if (guehdr->flags) {
- /* No support yet */
+ if (guehdr->version != 0 || validate_gue_flags(guehdr, optlen))
goto drop;
+
+ hdrlen = sizeof(struct guehdr) + optlen;
+
+ ip_hdr(skb)->tot_len = htons(ntohs(ip_hdr(skb)->tot_len) - len);
+
+ /* Pull csum through the guehdr now . This can be used if
+ * there is a remote checksum offload.
+ */
+ skb_postpull_rcsum(skb, udp_hdr(skb), len);
+
+ data = &guehdr[1];
+
+ if (guehdr->flags & GUE_FLAG_PRIV) {
+ __be32 flags = *(__be32 *)(data + doffset);
+
+ doffset += GUE_LEN_PRIV;
+
+ if (flags & GUE_PFLAG_REMCSUM) {
+ guehdr = gue_remcsum(skb, guehdr, data + doffset,
+ hdrlen, guehdr->proto_ctype);
+ if (!guehdr)
+ goto drop;
+
+ data = &guehdr[1];
+
+ doffset += GUE_PLEN_REMCSUM;
+ }
}
- return fou_udp_encap_recv_deliver(skb, guehdr->next_hdr, len);
+ if (unlikely(guehdr->control))
+ return gue_control_message(skb, guehdr);
+
+ __skb_pull(skb, sizeof(struct udphdr) + hdrlen);
+ skb_reset_transport_header(skb);
+
+ return -guehdr->proto_ctype;
+
drop:
kfree_skb(skb);
return 0;
@@ -133,6 +202,8 @@ static int fou_gro_complete(struct sk_buff *skb, int nhoff)
int err = -ENOSYS;
const struct net_offload **offloads;
+ udp_tunnel_gro_complete(skb, nhoff);
+
rcu_read_lock();
offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
ops = rcu_dereference(offloads[proto]);
@@ -147,6 +218,41 @@ out_unlock:
return err;
}
+static struct guehdr *gue_gro_remcsum(struct sk_buff *skb, unsigned int off,
+ struct guehdr *guehdr, void *data,
+ size_t hdrlen, u8 ipproto)
+{
+ __be16 *pd = data;
+ size_t start = ntohs(pd[0]);
+ size_t offset = ntohs(pd[1]);
+ size_t plen = hdrlen + max_t(size_t, offset + sizeof(u16), start);
+ __wsum delta;
+
+ if (skb->remcsum_offload)
+ return guehdr;
+
+ if (!NAPI_GRO_CB(skb)->csum_valid)
+ return NULL;
+
+ /* Pull checksum that will be written */
+ if (skb_gro_header_hard(skb, off + plen)) {
+ guehdr = skb_gro_header_slow(skb, off + plen, off);
+ if (!guehdr)
+ return NULL;
+ }
+
+ delta = remcsum_adjust((void *)guehdr + hdrlen,
+ NAPI_GRO_CB(skb)->csum, start, offset);
+
+ /* Adjust skb->csum since we changed the packet */
+ skb->csum = csum_add(skb->csum, delta);
+ NAPI_GRO_CB(skb)->csum = csum_add(NAPI_GRO_CB(skb)->csum, delta);
+
+ skb->remcsum_offload = 1;
+
+ return guehdr;
+}
+
static struct sk_buff **gue_gro_receive(struct sk_buff **head,
struct sk_buff *skb)
{
@@ -154,38 +260,64 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head,
const struct net_offload *ops;
struct sk_buff **pp = NULL;
struct sk_buff *p;
- u8 proto;
struct guehdr *guehdr;
- unsigned int hlen, guehlen;
- unsigned int off;
+ size_t len, optlen, hdrlen, off;
+ void *data;
+ u16 doffset = 0;
int flush = 1;
off = skb_gro_offset(skb);
- hlen = off + sizeof(*guehdr);
+ len = off + sizeof(*guehdr);
+
guehdr = skb_gro_header_fast(skb, off);
- if (skb_gro_header_hard(skb, hlen)) {
- guehdr = skb_gro_header_slow(skb, hlen, off);
+ if (skb_gro_header_hard(skb, len)) {
+ guehdr = skb_gro_header_slow(skb, len, off);
if (unlikely(!guehdr))
goto out;
}
- proto = guehdr->next_hdr;
+ optlen = guehdr->hlen << 2;
+ len += optlen;
- rcu_read_lock();
- offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
- ops = rcu_dereference(offloads[proto]);
- if (WARN_ON(!ops || !ops->callbacks.gro_receive))
- goto out_unlock;
+ if (skb_gro_header_hard(skb, len)) {
+ guehdr = skb_gro_header_slow(skb, len, off);
+ if (unlikely(!guehdr))
+ goto out;
+ }
- guehlen = sizeof(*guehdr) + (guehdr->hlen << 2);
+ if (unlikely(guehdr->control) || guehdr->version != 0 ||
+ validate_gue_flags(guehdr, optlen))
+ goto out;
- hlen = off + guehlen;
- if (skb_gro_header_hard(skb, hlen)) {
- guehdr = skb_gro_header_slow(skb, hlen, off);
- if (unlikely(!guehdr))
- goto out_unlock;
+ hdrlen = sizeof(*guehdr) + optlen;
+
+ /* Adjust NAPI_GRO_CB(skb)->csum to account for guehdr,
+ * this is needed if there is a remote checkcsum offload.
+ */
+ skb_gro_postpull_rcsum(skb, guehdr, hdrlen);
+
+ data = &guehdr[1];
+
+ if (guehdr->flags & GUE_FLAG_PRIV) {
+ __be32 flags = *(__be32 *)(data + doffset);
+
+ doffset += GUE_LEN_PRIV;
+
+ if (flags & GUE_PFLAG_REMCSUM) {
+ guehdr = gue_gro_remcsum(skb, off, guehdr,
+ data + doffset, hdrlen,
+ guehdr->proto_ctype);
+ if (!guehdr)
+ goto out;
+
+ data = &guehdr[1];
+
+ doffset += GUE_PLEN_REMCSUM;
+ }
}
+ skb_gro_pull(skb, hdrlen);
+
flush = 0;
for (p = *head; p; p = p->next) {
@@ -197,7 +329,7 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head,
guehdr2 = (struct guehdr *)(p->data + off);
/* Compare base GUE header to be equal (covers
- * hlen, version, next_hdr, and flags.
+ * hlen, version, proto_ctype, and flags.
*/
if (guehdr->word != guehdr2->word) {
NAPI_GRO_CB(p)->same_flow = 0;
@@ -212,10 +344,11 @@ static struct sk_buff **gue_gro_receive(struct sk_buff **head,
}
}
- skb_gro_pull(skb, guehlen);
-
- /* Adjusted NAPI_GRO_CB(skb)->csum after skb_gro_pull()*/
- skb_gro_postpull_rcsum(skb, guehdr, guehlen);
+ rcu_read_lock();
+ offloads = NAPI_GRO_CB(skb)->is_ipv6 ? inet6_offloads : inet_offloads;
+ ops = rcu_dereference(offloads[guehdr->proto_ctype]);
+ if (WARN_ON(!ops || !ops->callbacks.gro_receive))
+ goto out_unlock;
pp = ops->callbacks.gro_receive(head, skb);
@@ -236,7 +369,7 @@ static int gue_gro_complete(struct sk_buff *skb, int nhoff)
u8 proto;
int err = -ENOENT;
- proto = guehdr->next_hdr;
+ proto = guehdr->proto_ctype;
guehlen = sizeof(*guehdr) + (guehdr->hlen << 2);
@@ -487,6 +620,200 @@ static const struct genl_ops fou_nl_ops[] = {
},
};
+size_t fou_encap_hlen(struct ip_tunnel_encap *e)
+{
+ return sizeof(struct udphdr);
+}
+EXPORT_SYMBOL(fou_encap_hlen);
+
+size_t gue_encap_hlen(struct ip_tunnel_encap *e)
+{
+ size_t len;
+ bool need_priv = false;
+
+ len = sizeof(struct udphdr) + sizeof(struct guehdr);
+
+ if (e->flags & TUNNEL_ENCAP_FLAG_REMCSUM) {
+ len += GUE_PLEN_REMCSUM;
+ need_priv = true;
+ }
+
+ len += need_priv ? GUE_LEN_PRIV : 0;
+
+ return len;
+}
+EXPORT_SYMBOL(gue_encap_hlen);
+
+static void fou_build_udp(struct sk_buff *skb, struct ip_tunnel_encap *e,
+ struct flowi4 *fl4, u8 *protocol, __be16 sport)
+{
+ struct udphdr *uh;
+
+ skb_push(skb, sizeof(struct udphdr));
+ skb_reset_transport_header(skb);
+
+ uh = udp_hdr(skb);
+
+ uh->dest = e->dport;
+ uh->source = sport;
+ uh->len = htons(skb->len);
+ uh->check = 0;
+ udp_set_csum(!(e->flags & TUNNEL_ENCAP_FLAG_CSUM), skb,
+ fl4->saddr, fl4->daddr, skb->len);
+
+ *protocol = IPPROTO_UDP;
+}
+
+int fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
+ u8 *protocol, struct flowi4 *fl4)
+{
+ bool csum = !!(e->flags & TUNNEL_ENCAP_FLAG_CSUM);
+ int type = csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL;
+ __be16 sport;
+
+ skb = iptunnel_handle_offloads(skb, csum, type);
+
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev),
+ skb, 0, 0, false);
+ fou_build_udp(skb, e, fl4, protocol, sport);
+
+ return 0;
+}
+EXPORT_SYMBOL(fou_build_header);
+
+int gue_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
+ u8 *protocol, struct flowi4 *fl4)
+{
+ bool csum = !!(e->flags & TUNNEL_ENCAP_FLAG_CSUM);
+ int type = csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL;
+ struct guehdr *guehdr;
+ size_t hdrlen, optlen = 0;
+ __be16 sport;
+ void *data;
+ bool need_priv = false;
+
+ if ((e->flags & TUNNEL_ENCAP_FLAG_REMCSUM) &&
+ skb->ip_summed == CHECKSUM_PARTIAL) {
+ csum = false;
+ optlen += GUE_PLEN_REMCSUM;
+ type |= SKB_GSO_TUNNEL_REMCSUM;
+ need_priv = true;
+ }
+
+ optlen += need_priv ? GUE_LEN_PRIV : 0;
+
+ skb = iptunnel_handle_offloads(skb, csum, type);
+
+ if (IS_ERR(skb))
+ return PTR_ERR(skb);
+
+ /* Get source port (based on flow hash) before skb_push */
+ sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev),
+ skb, 0, 0, false);
+
+ hdrlen = sizeof(struct guehdr) + optlen;
+
+ skb_push(skb, hdrlen);
+
+ guehdr = (struct guehdr *)skb->data;
+
+ guehdr->control = 0;
+ guehdr->version = 0;
+ guehdr->hlen = optlen >> 2;
+ guehdr->flags = 0;
+ guehdr->proto_ctype = *protocol;
+
+ data = &guehdr[1];
+
+ if (need_priv) {
+ __be32 *flags = data;
+
+ guehdr->flags |= GUE_FLAG_PRIV;
+ *flags = 0;
+ data += GUE_LEN_PRIV;
+
+ if (type & SKB_GSO_TUNNEL_REMCSUM) {
+ u16 csum_start = skb_checksum_start_offset(skb);
+ __be16 *pd = data;
+
+ if (csum_start < hdrlen)
+ return -EINVAL;
+
+ csum_start -= hdrlen;
+ pd[0] = htons(csum_start);
+ pd[1] = htons(csum_start + skb->csum_offset);
+
+ if (!skb_is_gso(skb)) {
+ skb->ip_summed = CHECKSUM_NONE;
+ skb->encapsulation = 0;
+ }
+
+ *flags |= GUE_PFLAG_REMCSUM;
+ data += GUE_PLEN_REMCSUM;
+ }
+
+ }
+
+ fou_build_udp(skb, e, fl4, protocol, sport);
+
+ return 0;
+}
+EXPORT_SYMBOL(gue_build_header);
+
+#ifdef CONFIG_NET_FOU_IP_TUNNELS
+
+static const struct ip_tunnel_encap_ops __read_mostly fou_iptun_ops = {
+ .encap_hlen = fou_encap_hlen,
+ .build_header = fou_build_header,
+};
+
+static const struct ip_tunnel_encap_ops __read_mostly gue_iptun_ops = {
+ .encap_hlen = gue_encap_hlen,
+ .build_header = gue_build_header,
+};
+
+static int ip_tunnel_encap_add_fou_ops(void)
+{
+ int ret;
+
+ ret = ip_tunnel_encap_add_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU);
+ if (ret < 0) {
+ pr_err("can't add fou ops\n");
+ return ret;
+ }
+
+ ret = ip_tunnel_encap_add_ops(&gue_iptun_ops, TUNNEL_ENCAP_GUE);
+ if (ret < 0) {
+ pr_err("can't add gue ops\n");
+ ip_tunnel_encap_del_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void ip_tunnel_encap_del_fou_ops(void)
+{
+ ip_tunnel_encap_del_ops(&fou_iptun_ops, TUNNEL_ENCAP_FOU);
+ ip_tunnel_encap_del_ops(&gue_iptun_ops, TUNNEL_ENCAP_GUE);
+}
+
+#else
+
+static int ip_tunnel_encap_add_fou_ops(void)
+{
+ return 0;
+}
+
+static void ip_tunnel_encap_del_fou_ops(void)
+{
+}
+
+#endif
+
static int __init fou_init(void)
{
int ret;
@@ -494,6 +821,14 @@ static int __init fou_init(void)
ret = genl_register_family_with_ops(&fou_nl_family,
fou_nl_ops);
+ if (ret < 0)
+ goto exit;
+
+ ret = ip_tunnel_encap_add_fou_ops();
+ if (ret < 0)
+ genl_unregister_family(&fou_nl_family);
+
+exit:
return ret;
}
@@ -501,6 +836,8 @@ static void __exit fou_fini(void)
{
struct fou *fou, *next;
+ ip_tunnel_encap_del_fou_ops();
+
genl_unregister_family(&fou_nl_family);
/* Close all the FOU sockets */
diff --git a/net/ipv4/geneve.c b/net/ipv4/geneve.c
index 065cd94c640c..a457232f0131 100644
--- a/net/ipv4/geneve.c
+++ b/net/ipv4/geneve.c
@@ -104,7 +104,7 @@ static void geneve_build_header(struct genevehdr *geneveh,
memcpy(geneveh->options, options, options_len);
}
-/* Transmit a fully formated Geneve frame.
+/* Transmit a fully formatted Geneve frame.
*
* When calling this function. The skb->data should point
* to the geneve header which is fully formed.
@@ -131,19 +131,15 @@ int geneve_xmit_skb(struct geneve_sock *gs, struct rtable *rt,
if (unlikely(err))
return err;
- if (vlan_tx_tag_present(skb)) {
- if (unlikely(!__vlan_put_tag(skb,
- skb->vlan_proto,
- vlan_tx_tag_get(skb)))) {
- err = -ENOMEM;
- return err;
- }
- skb->vlan_tci = 0;
- }
+ skb = vlan_hwaccel_push_inside(skb);
+ if (unlikely(!skb))
+ return -ENOMEM;
gnvh = (struct genevehdr *)__skb_push(skb, sizeof(*gnvh) + opt_len);
geneve_build_header(gnvh, tun_flags, vni, opt_len, opt);
+ skb_set_inner_protocol(skb, htons(ETH_P_TEB));
+
return udp_tunnel_xmit_skb(gs->sock, rt, skb, src, dst,
tos, ttl, df, src_port, dst_port, xnet);
}
@@ -364,6 +360,7 @@ late_initcall(geneve_init_module);
static void __exit geneve_cleanup_module(void)
{
destroy_workqueue(geneve_wq);
+ unregister_pernet_subsys(&geneve_net_ops);
}
module_exit(geneve_cleanup_module);
diff --git a/net/ipv4/gre_offload.c b/net/ipv4/gre_offload.c
index bb5947b0ce2d..51973ddc05a6 100644
--- a/net/ipv4/gre_offload.c
+++ b/net/ipv4/gre_offload.c
@@ -247,6 +247,9 @@ static int gre_gro_complete(struct sk_buff *skb, int nhoff)
err = ptype->callbacks.gro_complete(skb, nhoff + grehlen);
rcu_read_unlock();
+
+ skb_set_inner_mac_header(skb, nhoff + grehlen);
+
return err;
}
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 5882f584910e..36f5584d93c5 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -190,7 +190,7 @@ EXPORT_SYMBOL(icmp_err_convert);
*/
struct icmp_control {
- void (*handler)(struct sk_buff *skb);
+ bool (*handler)(struct sk_buff *skb);
short error; /* This ICMP is classed as an error message */
};
@@ -746,7 +746,7 @@ static bool icmp_tag_validation(int proto)
* ICMP_PARAMETERPROB.
*/
-static void icmp_unreach(struct sk_buff *skb)
+static bool icmp_unreach(struct sk_buff *skb)
{
const struct iphdr *iph;
struct icmphdr *icmph;
@@ -784,8 +784,8 @@ static void icmp_unreach(struct sk_buff *skb)
*/
switch (net->ipv4.sysctl_ip_no_pmtu_disc) {
default:
- LIMIT_NETDEBUG(KERN_INFO pr_fmt("%pI4: fragmentation needed and DF set\n"),
- &iph->daddr);
+ net_dbg_ratelimited("%pI4: fragmentation needed and DF set\n",
+ &iph->daddr);
break;
case 2:
goto out;
@@ -798,8 +798,8 @@ static void icmp_unreach(struct sk_buff *skb)
}
break;
case ICMP_SR_FAILED:
- LIMIT_NETDEBUG(KERN_INFO pr_fmt("%pI4: Source Route Failed\n"),
- &iph->daddr);
+ net_dbg_ratelimited("%pI4: Source Route Failed\n",
+ &iph->daddr);
break;
default:
break;
@@ -839,10 +839,10 @@ static void icmp_unreach(struct sk_buff *skb)
icmp_socket_deliver(skb, info);
out:
- return;
+ return true;
out_err:
ICMP_INC_STATS_BH(net, ICMP_MIB_INERRORS);
- goto out;
+ return false;
}
@@ -850,17 +850,20 @@ out_err:
* Handle ICMP_REDIRECT.
*/
-static void icmp_redirect(struct sk_buff *skb)
+static bool icmp_redirect(struct sk_buff *skb)
{
if (skb->len < sizeof(struct iphdr)) {
ICMP_INC_STATS_BH(dev_net(skb->dev), ICMP_MIB_INERRORS);
- return;
+ return false;
}
- if (!pskb_may_pull(skb, sizeof(struct iphdr)))
- return;
+ if (!pskb_may_pull(skb, sizeof(struct iphdr))) {
+ /* there aught to be a stat */
+ return false;
+ }
icmp_socket_deliver(skb, icmp_hdr(skb)->un.gateway);
+ return true;
}
/*
@@ -875,7 +878,7 @@ static void icmp_redirect(struct sk_buff *skb)
* See also WRT handling of options once they are done and working.
*/
-static void icmp_echo(struct sk_buff *skb)
+static bool icmp_echo(struct sk_buff *skb)
{
struct net *net;
@@ -891,6 +894,8 @@ static void icmp_echo(struct sk_buff *skb)
icmp_param.head_len = sizeof(struct icmphdr);
icmp_reply(&icmp_param, skb);
}
+ /* should there be an ICMP stat for ignored echos? */
+ return true;
}
/*
@@ -900,7 +905,7 @@ static void icmp_echo(struct sk_buff *skb)
* MUST be accurate to a few minutes.
* MUST be updated at least at 15Hz.
*/
-static void icmp_timestamp(struct sk_buff *skb)
+static bool icmp_timestamp(struct sk_buff *skb)
{
struct timespec tv;
struct icmp_bxm icmp_param;
@@ -927,15 +932,17 @@ static void icmp_timestamp(struct sk_buff *skb)
icmp_param.data_len = 0;
icmp_param.head_len = sizeof(struct icmphdr) + 12;
icmp_reply(&icmp_param, skb);
-out:
- return;
+ return true;
+
out_err:
ICMP_INC_STATS_BH(dev_net(skb_dst(skb)->dev), ICMP_MIB_INERRORS);
- goto out;
+ return false;
}
-static void icmp_discard(struct sk_buff *skb)
+static bool icmp_discard(struct sk_buff *skb)
{
+ /* pretend it was a success */
+ return true;
}
/*
@@ -946,6 +953,7 @@ int icmp_rcv(struct sk_buff *skb)
struct icmphdr *icmph;
struct rtable *rt = skb_rtable(skb);
struct net *net = dev_net(rt->dst.dev);
+ bool success;
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
struct sec_path *sp = skb_sec_path(skb);
@@ -1012,7 +1020,12 @@ int icmp_rcv(struct sk_buff *skb)
}
}
- icmp_pointers[icmph->type].handler(skb);
+ success = icmp_pointers[icmph->type].handler(skb);
+
+ if (success) {
+ consume_skb(skb);
+ return 0;
+ }
drop:
kfree_skb(skb);
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index fb70e3ecc3e4..666cf364df86 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -112,17 +112,17 @@
#ifdef CONFIG_IP_MULTICAST
/* Parameter names and values are taken from igmp-v2-06 draft */
-#define IGMP_V1_Router_Present_Timeout (400*HZ)
-#define IGMP_V2_Router_Present_Timeout (400*HZ)
-#define IGMP_V2_Unsolicited_Report_Interval (10*HZ)
-#define IGMP_V3_Unsolicited_Report_Interval (1*HZ)
-#define IGMP_Query_Response_Interval (10*HZ)
-#define IGMP_Query_Robustness_Variable 2
+#define IGMP_V1_ROUTER_PRESENT_TIMEOUT (400*HZ)
+#define IGMP_V2_ROUTER_PRESENT_TIMEOUT (400*HZ)
+#define IGMP_V2_UNSOLICITED_REPORT_INTERVAL (10*HZ)
+#define IGMP_V3_UNSOLICITED_REPORT_INTERVAL (1*HZ)
+#define IGMP_QUERY_RESPONSE_INTERVAL (10*HZ)
+#define IGMP_QUERY_ROBUSTNESS_VARIABLE 2
-#define IGMP_Initial_Report_Delay (1)
+#define IGMP_INITIAL_REPORT_DELAY (1)
-/* IGMP_Initial_Report_Delay is not from IGMP specs!
+/* IGMP_INITIAL_REPORT_DELAY is not from IGMP specs!
* IGMP specs require to report membership immediately after
* joining a group, but we delay the first report by a
* small interval. It seems more natural and still does not
@@ -318,9 +318,7 @@ igmp_scount(struct ip_mc_list *pmc, int type, int gdeleted, int sdeleted)
return scount;
}
-#define igmp_skb_size(skb) (*(unsigned int *)((skb)->cb))
-
-static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size)
+static struct sk_buff *igmpv3_newpack(struct net_device *dev, unsigned int mtu)
{
struct sk_buff *skb;
struct rtable *rt;
@@ -330,6 +328,7 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size)
struct flowi4 fl4;
int hlen = LL_RESERVED_SPACE(dev);
int tlen = dev->needed_tailroom;
+ unsigned int size = mtu;
while (1) {
skb = alloc_skb(size + hlen + tlen,
@@ -341,7 +340,6 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size)
return NULL;
}
skb->priority = TC_PRIO_CONTROL;
- igmp_skb_size(skb) = size;
rt = ip_route_output_ports(net, &fl4, NULL, IGMPV3_ALL_MCR, 0,
0, 0,
@@ -354,6 +352,8 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size)
skb_dst_set(skb, &rt->dst);
skb->dev = dev;
+ skb->reserved_tailroom = skb_end_offset(skb) -
+ min(mtu, skb_end_offset(skb));
skb_reserve(skb, hlen);
skb_reset_network_header(skb);
@@ -423,8 +423,7 @@ static struct sk_buff *add_grhead(struct sk_buff *skb, struct ip_mc_list *pmc,
return skb;
}
-#define AVAILABLE(skb) ((skb) ? ((skb)->dev ? igmp_skb_size(skb) - (skb)->len : \
- skb_tailroom(skb)) : 0)
+#define AVAILABLE(skb) ((skb) ? skb_availroom(skb) : 0)
static struct sk_buff *add_grec(struct sk_buff *skb, struct ip_mc_list *pmc,
int type, int gdeleted, int sdeleted)
@@ -879,15 +878,15 @@ static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
if (ih->code == 0) {
/* Alas, old v1 router presents here. */
- max_delay = IGMP_Query_Response_Interval;
+ max_delay = IGMP_QUERY_RESPONSE_INTERVAL;
in_dev->mr_v1_seen = jiffies +
- IGMP_V1_Router_Present_Timeout;
+ IGMP_V1_ROUTER_PRESENT_TIMEOUT;
group = 0;
} else {
/* v2 router present */
max_delay = ih->code*(HZ/IGMP_TIMER_SCALE);
in_dev->mr_v2_seen = jiffies +
- IGMP_V2_Router_Present_Timeout;
+ IGMP_V2_ROUTER_PRESENT_TIMEOUT;
}
/* cancel the interface change timer */
in_dev->mr_ifc_count = 0;
@@ -899,7 +898,7 @@ static bool igmp_heard_query(struct in_device *in_dev, struct sk_buff *skb,
return true; /* ignore bogus packet; freed by caller */
} else if (IGMP_V1_SEEN(in_dev)) {
/* This is a v3 query with v1 queriers present */
- max_delay = IGMP_Query_Response_Interval;
+ max_delay = IGMP_QUERY_RESPONSE_INTERVAL;
group = 0;
} else if (IGMP_V2_SEEN(in_dev)) {
/* this is a v3 query with v2 queriers present;
@@ -1218,7 +1217,7 @@ static void igmp_group_added(struct ip_mc_list *im)
return;
if (IGMP_V1_SEEN(in_dev) || IGMP_V2_SEEN(in_dev)) {
spin_lock_bh(&im->lock);
- igmp_start_timer(im, IGMP_Initial_Report_Delay);
+ igmp_start_timer(im, IGMP_INITIAL_REPORT_DELAY);
spin_unlock_bh(&im->lock);
return;
}
@@ -1541,7 +1540,7 @@ static struct in_device *ip_mc_find_dev(struct net *net, struct ip_mreqn *imr)
int sysctl_igmp_max_memberships __read_mostly = IP_MAX_MEMBERSHIPS;
int sysctl_igmp_max_msf __read_mostly = IP_MAX_MSF;
#ifdef CONFIG_IP_MULTICAST
-int sysctl_igmp_qrv __read_mostly = IGMP_Query_Robustness_Variable;
+int sysctl_igmp_qrv __read_mostly = IGMP_QUERY_ROBUSTNESS_VARIABLE;
#endif
static int ip_mc_del1_src(struct ip_mc_list *pmc, int sfmode,
@@ -2687,11 +2686,7 @@ static int igmp_mcf_seq_show(struct seq_file *seq, void *v)
struct igmp_mcf_iter_state *state = igmp_mcf_seq_private(seq);
if (v == SEQ_START_TOKEN) {
- seq_printf(seq,
- "%3s %6s "
- "%10s %10s %6s %6s\n", "Idx",
- "Device", "MCA",
- "SRC", "INC", "EXC");
+ seq_puts(seq, "Idx Device MCA SRC INC EXC\n");
} else {
seq_printf(seq,
"%3d %6.6s 0x%08x "
diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c
index 19419b60cb37..e7920352646a 100644
--- a/net/ipv4/inet_fragment.c
+++ b/net/ipv4/inet_fragment.c
@@ -458,6 +458,6 @@ void inet_frag_maybe_warn_overflow(struct inet_frag_queue *q,
". Dropping fragment.\n";
if (PTR_ERR(q) == -ENOBUFS)
- LIMIT_NETDEBUG(KERN_WARNING "%s%s", prefix, msg);
+ net_dbg_ratelimited("%s%s", prefix, msg);
}
EXPORT_SYMBOL(inet_frag_maybe_warn_overflow);
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index 2811cc18701a..e5b6d0ddcb58 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -80,7 +80,7 @@ struct ipq {
struct inet_peer *peer;
};
-static inline u8 ip4_frag_ecn(u8 tos)
+static u8 ip4_frag_ecn(u8 tos)
{
return 1 << (tos & INET_ECN_MASK);
}
@@ -148,7 +148,7 @@ static void ip4_frag_init(struct inet_frag_queue *q, const void *a)
inet_getpeer_v4(net->ipv4.peers, arg->iph->saddr, 1) : NULL;
}
-static __inline__ void ip4_frag_free(struct inet_frag_queue *q)
+static void ip4_frag_free(struct inet_frag_queue *q)
{
struct ipq *qp;
@@ -160,7 +160,7 @@ static __inline__ void ip4_frag_free(struct inet_frag_queue *q)
/* Destruction primitives. */
-static __inline__ void ipq_put(struct ipq *ipq)
+static void ipq_put(struct ipq *ipq)
{
inet_frag_put(&ipq->q, &ip4_frags);
}
@@ -236,7 +236,7 @@ out:
/* Find the correct entry in the "incomplete datagrams" queue for
* this IP datagram, and create new one, if nothing is found.
*/
-static inline struct ipq *ip_find(struct net *net, struct iphdr *iph, u32 user)
+static struct ipq *ip_find(struct net *net, struct iphdr *iph, u32 user)
{
struct inet_frag_queue *q;
struct ip4_create_arg arg;
@@ -256,7 +256,7 @@ static inline struct ipq *ip_find(struct net *net, struct iphdr *iph, u32 user)
}
/* Is the fragment too far ahead to be part of ipq? */
-static inline int ip_frag_too_far(struct ipq *qp)
+static int ip_frag_too_far(struct ipq *qp)
{
struct inet_peer *peer = qp->peer;
unsigned int max = sysctl_ipfrag_max_dist;
@@ -618,8 +618,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *prev,
return 0;
out_nomem:
- LIMIT_NETDEBUG(KERN_ERR pr_fmt("queue_glue: no memory for gluing queue %p\n"),
- qp);
+ net_dbg_ratelimited("queue_glue: no memory for gluing queue %p\n", qp);
err = -ENOMEM;
goto out_fail;
out_oversize:
@@ -795,16 +794,16 @@ static void __init ip4_frags_ctl_register(void)
register_net_sysctl(&init_net, "net/ipv4", ip4_frags_ctl_table);
}
#else
-static inline int ip4_frags_ns_ctl_register(struct net *net)
+static int ip4_frags_ns_ctl_register(struct net *net)
{
return 0;
}
-static inline void ip4_frags_ns_ctl_unregister(struct net *net)
+static void ip4_frags_ns_ctl_unregister(struct net *net)
{
}
-static inline void __init ip4_frags_ctl_register(void)
+static void __init ip4_frags_ctl_register(void)
{
}
#endif
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 12055fdbe716..ac8491245e5b 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -789,7 +789,7 @@ static int ipgre_fill_info(struct sk_buff *skb, const struct net_device *dev)
nla_put_u16(skb, IFLA_GRE_ENCAP_DPORT,
t->encap.dport) ||
nla_put_u16(skb, IFLA_GRE_ENCAP_FLAGS,
- t->encap.dport))
+ t->encap.flags))
goto nla_put_failure;
return 0;
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index bc6471d4abcd..b50861b22b6b 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -662,12 +662,10 @@ slow_path:
if (len < left) {
len &= ~7;
}
- /*
- * Allocate buffer.
- */
- if ((skb2 = alloc_skb(len+hlen+ll_rs, GFP_ATOMIC)) == NULL) {
- NETDEBUG(KERN_INFO "IP: frag: no memory for new fragment!\n");
+ /* Allocate buffer */
+ skb2 = alloc_skb(len + hlen + ll_rs, GFP_ATOMIC);
+ if (!skb2) {
err = -ENOMEM;
goto fail;
}
@@ -754,14 +752,16 @@ EXPORT_SYMBOL(ip_fragment);
int
ip_generic_getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb)
{
- struct iovec *iov = from;
+ struct msghdr *msg = from;
if (skb->ip_summed == CHECKSUM_PARTIAL) {
- if (memcpy_fromiovecend(to, iov, offset, len) < 0)
+ /* XXX: stripping const */
+ if (memcpy_fromiovecend(to, (struct iovec *)msg->msg_iter.iov, offset, len) < 0)
return -EFAULT;
} else {
__wsum csum = 0;
- if (csum_partial_copy_fromiovecend(to, iov, offset, len, &csum) < 0)
+ /* XXX: stripping const */
+ if (csum_partial_copy_fromiovecend(to, (struct iovec *)msg->msg_iter.iov, offset, len, &csum) < 0)
return -EFAULT;
skb->csum = csum_block_add(skb->csum, csum, odd);
}
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index c373a9ad4555..8a89c738b7a3 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -192,10 +192,10 @@ int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc,
int err, val;
struct cmsghdr *cmsg;
- for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ for_each_cmsghdr(cmsg, msg) {
if (!CMSG_OK(msg, cmsg))
return -EINVAL;
-#if defined(CONFIG_IPV6)
+#if IS_ENABLED(CONFIG_IPV6)
if (allow_ipv6 &&
cmsg->cmsg_level == SOL_IPV6 &&
cmsg->cmsg_type == IPV6_PKTINFO) {
@@ -399,6 +399,22 @@ void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 inf
kfree_skb(skb);
}
+static bool ipv4_pktinfo_prepare_errqueue(const struct sock *sk,
+ const struct sk_buff *skb,
+ int ee_origin)
+{
+ struct in_pktinfo *info = PKTINFO_SKB_CB(skb);
+
+ if ((ee_origin != SO_EE_ORIGIN_TIMESTAMPING) ||
+ (!(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_CMSG)) ||
+ (!skb->dev))
+ return false;
+
+ info->ipi_spec_dst.s_addr = ip_hdr(skb)->saddr;
+ info->ipi_ifindex = skb->dev->ifindex;
+ return true;
+}
+
/*
* Handle MSG_ERRQUEUE
*/
@@ -414,6 +430,8 @@ int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
int err;
int copied;
+ WARN_ON_ONCE(sk->sk_family == AF_INET6);
+
err = -EAGAIN;
skb = sock_dequeue_err_skb(sk);
if (skb == NULL)
@@ -424,7 +442,7 @@ int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
msg->msg_flags |= MSG_TRUNC;
copied = len;
}
- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
if (err)
goto out_free_skb;
@@ -444,7 +462,9 @@ int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
sin = &errhdr.offender;
sin->sin_family = AF_UNSPEC;
- if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP) {
+
+ if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP ||
+ ipv4_pktinfo_prepare_errqueue(sk, skb, serr->ee.ee_origin)) {
struct inet_sock *inet = inet_sk(sk);
sin->sin_family = AF_INET;
@@ -1049,7 +1069,7 @@ e_inval:
}
/**
- * ipv4_pktinfo_prepare - transfert some info from rtable to skb
+ * ipv4_pktinfo_prepare - transfer some info from rtable to skb
* @sk: socket
* @skb: buffer
*
diff --git a/net/ipv4/ip_tunnel.c b/net/ipv4/ip_tunnel.c
index 0bb8e141eacc..63e745aadab6 100644
--- a/net/ipv4/ip_tunnel.c
+++ b/net/ipv4/ip_tunnel.c
@@ -56,7 +56,6 @@
#include <net/netns/generic.h>
#include <net/rtnetlink.h>
#include <net/udp.h>
-#include <net/gue.h>
#if IS_ENABLED(CONFIG_IPV6)
#include <net/ipv6.h>
@@ -491,18 +490,51 @@ EXPORT_SYMBOL_GPL(ip_tunnel_rcv);
static int ip_encap_hlen(struct ip_tunnel_encap *e)
{
- switch (e->type) {
- case TUNNEL_ENCAP_NONE:
+ const struct ip_tunnel_encap_ops *ops;
+ int hlen = -EINVAL;
+
+ if (e->type == TUNNEL_ENCAP_NONE)
return 0;
- case TUNNEL_ENCAP_FOU:
- return sizeof(struct udphdr);
- case TUNNEL_ENCAP_GUE:
- return sizeof(struct udphdr) + sizeof(struct guehdr);
- default:
+
+ if (e->type >= MAX_IPTUN_ENCAP_OPS)
return -EINVAL;
- }
+
+ rcu_read_lock();
+ ops = rcu_dereference(iptun_encaps[e->type]);
+ if (likely(ops && ops->encap_hlen))
+ hlen = ops->encap_hlen(e);
+ rcu_read_unlock();
+
+ return hlen;
}
+const struct ip_tunnel_encap_ops __rcu *
+ iptun_encaps[MAX_IPTUN_ENCAP_OPS] __read_mostly;
+
+int ip_tunnel_encap_add_ops(const struct ip_tunnel_encap_ops *ops,
+ unsigned int num)
+{
+ return !cmpxchg((const struct ip_tunnel_encap_ops **)
+ &iptun_encaps[num],
+ NULL, ops) ? 0 : -1;
+}
+EXPORT_SYMBOL(ip_tunnel_encap_add_ops);
+
+int ip_tunnel_encap_del_ops(const struct ip_tunnel_encap_ops *ops,
+ unsigned int num)
+{
+ int ret;
+
+ ret = (cmpxchg((const struct ip_tunnel_encap_ops **)
+ &iptun_encaps[num],
+ ops, NULL) == ops) ? 0 : -1;
+
+ synchronize_net();
+
+ return ret;
+}
+EXPORT_SYMBOL(ip_tunnel_encap_del_ops);
+
int ip_tunnel_encap_setup(struct ip_tunnel *t,
struct ip_tunnel_encap *ipencap)
{
@@ -526,63 +558,22 @@ int ip_tunnel_encap_setup(struct ip_tunnel *t,
}
EXPORT_SYMBOL_GPL(ip_tunnel_encap_setup);
-static int fou_build_header(struct sk_buff *skb, struct ip_tunnel_encap *e,
- size_t hdr_len, u8 *protocol, struct flowi4 *fl4)
-{
- struct udphdr *uh;
- __be16 sport;
- bool csum = !!(e->flags & TUNNEL_ENCAP_FLAG_CSUM);
- int type = csum ? SKB_GSO_UDP_TUNNEL_CSUM : SKB_GSO_UDP_TUNNEL;
-
- skb = iptunnel_handle_offloads(skb, csum, type);
-
- if (IS_ERR(skb))
- return PTR_ERR(skb);
-
- /* Get length and hash before making space in skb */
-
- sport = e->sport ? : udp_flow_src_port(dev_net(skb->dev),
- skb, 0, 0, false);
-
- skb_push(skb, hdr_len);
-
- skb_reset_transport_header(skb);
- uh = udp_hdr(skb);
-
- if (e->type == TUNNEL_ENCAP_GUE) {
- struct guehdr *guehdr = (struct guehdr *)&uh[1];
-
- guehdr->version = 0;
- guehdr->hlen = 0;
- guehdr->flags = 0;
- guehdr->next_hdr = *protocol;
- }
-
- uh->dest = e->dport;
- uh->source = sport;
- uh->len = htons(skb->len);
- uh->check = 0;
- udp_set_csum(!(e->flags & TUNNEL_ENCAP_FLAG_CSUM), skb,
- fl4->saddr, fl4->daddr, skb->len);
-
- *protocol = IPPROTO_UDP;
-
- return 0;
-}
-
int ip_tunnel_encap(struct sk_buff *skb, struct ip_tunnel *t,
u8 *protocol, struct flowi4 *fl4)
{
- switch (t->encap.type) {
- case TUNNEL_ENCAP_NONE:
+ const struct ip_tunnel_encap_ops *ops;
+ int ret = -EINVAL;
+
+ if (t->encap.type == TUNNEL_ENCAP_NONE)
return 0;
- case TUNNEL_ENCAP_FOU:
- case TUNNEL_ENCAP_GUE:
- return fou_build_header(skb, &t->encap, t->encap_hlen,
- protocol, fl4);
- default:
- return -EINVAL;
- }
+
+ rcu_read_lock();
+ ops = rcu_dereference(iptun_encaps[t->encap.type]);
+ if (likely(ops && ops->build_header))
+ ret = ops->build_header(skb, &t->encap, protocol, fl4);
+ rcu_read_unlock();
+
+ return ret;
}
EXPORT_SYMBOL(ip_tunnel_encap);
diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c
index 3e861011e4a3..1a7e979e80ba 100644
--- a/net/ipv4/ip_vti.c
+++ b/net/ipv4/ip_vti.c
@@ -528,6 +528,7 @@ static struct rtnl_link_ops vti_link_ops __read_mostly = {
.validate = vti_tunnel_validate,
.newlink = vti_newlink,
.changelink = vti_changelink,
+ .dellink = ip_tunnel_dellink,
.get_size = vti_get_size,
.fill_info = vti_fill_info,
};
diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c
index 648fa1490ea7..7fa18bc7e47f 100644
--- a/net/ipv4/ipconfig.c
+++ b/net/ipv4/ipconfig.c
@@ -115,7 +115,7 @@
*/
int ic_set_manually __initdata = 0; /* IPconfig parameters set manually */
-static int ic_enable __initdata = 0; /* IP config enabled? */
+static int ic_enable __initdata; /* IP config enabled? */
/* Protocol choice */
int ic_proto_enabled __initdata = 0
@@ -130,7 +130,7 @@ int ic_proto_enabled __initdata = 0
#endif
;
-static int ic_host_name_set __initdata = 0; /* Host name set by us? */
+static int ic_host_name_set __initdata; /* Host name set by us? */
__be32 ic_myaddr = NONE; /* My IP address */
static __be32 ic_netmask = NONE; /* Netmask for local subnet */
@@ -160,17 +160,17 @@ static u8 ic_domain[64]; /* DNS (not NIS) domain name */
static char user_dev_name[IFNAMSIZ] __initdata = { 0, };
/* Protocols supported by available interfaces */
-static int ic_proto_have_if __initdata = 0;
+static int ic_proto_have_if __initdata;
/* MTU for boot device */
-static int ic_dev_mtu __initdata = 0;
+static int ic_dev_mtu __initdata;
#ifdef IPCONFIG_DYNAMIC
static DEFINE_SPINLOCK(ic_recv_lock);
-static volatile int ic_got_reply __initdata = 0; /* Proto(s) that replied */
+static volatile int ic_got_reply __initdata; /* Proto(s) that replied */
#endif
#ifdef IPCONFIG_DHCP
-static int ic_dhcp_msgtype __initdata = 0; /* DHCP msg type received */
+static int ic_dhcp_msgtype __initdata; /* DHCP msg type received */
#endif
@@ -186,8 +186,8 @@ struct ic_device {
__be32 xid;
};
-static struct ic_device *ic_first_dev __initdata = NULL;/* List of open device */
-static struct net_device *ic_dev __initdata = NULL; /* Selected device */
+static struct ic_device *ic_first_dev __initdata; /* List of open device */
+static struct net_device *ic_dev __initdata; /* Selected device */
static bool __init ic_is_init_dev(struct net_device *dev)
{
@@ -498,7 +498,7 @@ ic_rarp_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt
struct arphdr *rarp;
unsigned char *rarp_ptr;
__be32 sip, tip;
- unsigned char *sha, *tha; /* s for "source", t for "target" */
+ unsigned char *tha; /* t for "target" */
struct ic_device *d;
if (!net_eq(dev_net(dev), &init_net))
@@ -549,7 +549,6 @@ ic_rarp_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt
goto drop_unlock; /* should never happen */
/* Extract variable-width fields */
- sha = rarp_ptr;
rarp_ptr += dev->addr_len;
memcpy(&sip, rarp_ptr, 4);
rarp_ptr += 4;
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
index 37096d64730e..40403114f00a 100644
--- a/net/ipv4/ipip.c
+++ b/net/ipv4/ipip.c
@@ -465,7 +465,7 @@ static int ipip_fill_info(struct sk_buff *skb, const struct net_device *dev)
nla_put_u16(skb, IFLA_IPTUN_ENCAP_DPORT,
tunnel->encap.dport) ||
nla_put_u16(skb, IFLA_IPTUN_ENCAP_FLAGS,
- tunnel->encap.dport))
+ tunnel->encap.flags))
goto nla_put_failure;
return 0;
diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig
index 4c019d5c3f57..59f883d9cadf 100644
--- a/net/ipv4/netfilter/Kconfig
+++ b/net/ipv4/netfilter/Kconfig
@@ -113,6 +113,15 @@ config NFT_MASQ_IPV4
This is the expression that provides IPv4 masquerading support for
nf_tables.
+config NFT_REDIR_IPV4
+ tristate "IPv4 redirect support for nf_tables"
+ depends on NF_TABLES_IPV4
+ depends on NFT_REDIR
+ select NF_NAT_REDIRECT
+ help
+ This is the expression that provides IPv4 redirect support for
+ nf_tables.
+
config NF_NAT_SNMP_BASIC
tristate "Basic SNMP-ALG support"
depends on NF_CONNTRACK_SNMP
diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile
index f4cef5af0969..7fe6c703528f 100644
--- a/net/ipv4/netfilter/Makefile
+++ b/net/ipv4/netfilter/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV4) += nft_chain_route_ipv4.o
obj-$(CONFIG_NFT_CHAIN_NAT_IPV4) += nft_chain_nat_ipv4.o
obj-$(CONFIG_NFT_REJECT_IPV4) += nft_reject_ipv4.o
obj-$(CONFIG_NFT_MASQ_IPV4) += nft_masq_ipv4.o
+obj-$(CONFIG_NFT_REDIR_IPV4) += nft_redir_ipv4.o
obj-$(CONFIG_NF_TABLES_ARP) += nf_tables_arp.o
# generic IP tables
diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
index a054fe083431..5c61328b7704 100644
--- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
+++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c
@@ -56,11 +56,11 @@ static bool ipv4_invert_tuple(struct nf_conntrack_tuple *tuple,
return true;
}
-static int ipv4_print_tuple(struct seq_file *s,
+static void ipv4_print_tuple(struct seq_file *s,
const struct nf_conntrack_tuple *tuple)
{
- return seq_printf(s, "src=%pI4 dst=%pI4 ",
- &tuple->src.u3.ip, &tuple->dst.u3.ip);
+ seq_printf(s, "src=%pI4 dst=%pI4 ",
+ &tuple->src.u3.ip, &tuple->dst.u3.ip);
}
static int ipv4_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c
index 4c48e434bb1f..a460a87e14f8 100644
--- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c
+++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4_compat.c
@@ -94,7 +94,7 @@ static void ct_seq_stop(struct seq_file *s, void *v)
}
#ifdef CONFIG_NF_CONNTRACK_SECMARK
-static int ct_show_secctx(struct seq_file *s, const struct nf_conn *ct)
+static void ct_show_secctx(struct seq_file *s, const struct nf_conn *ct)
{
int ret;
u32 len;
@@ -102,17 +102,15 @@ static int ct_show_secctx(struct seq_file *s, const struct nf_conn *ct)
ret = security_secid_to_secctx(ct->secmark, &secctx, &len);
if (ret)
- return 0;
+ return;
- ret = seq_printf(s, "secctx=%s ", secctx);
+ seq_printf(s, "secctx=%s ", secctx);
security_release_secctx(secctx, len);
- return ret;
}
#else
-static inline int ct_show_secctx(struct seq_file *s, const struct nf_conn *ct)
+static inline void ct_show_secctx(struct seq_file *s, const struct nf_conn *ct)
{
- return 0;
}
#endif
@@ -141,47 +139,52 @@ static int ct_seq_show(struct seq_file *s, void *v)
NF_CT_ASSERT(l4proto);
ret = -ENOSPC;
- if (seq_printf(s, "%-8s %u %ld ",
- l4proto->name, nf_ct_protonum(ct),
- timer_pending(&ct->timeout)
- ? (long)(ct->timeout.expires - jiffies)/HZ : 0) != 0)
- goto release;
+ seq_printf(s, "%-8s %u %ld ",
+ l4proto->name, nf_ct_protonum(ct),
+ timer_pending(&ct->timeout)
+ ? (long)(ct->timeout.expires - jiffies)/HZ : 0);
+
+ if (l4proto->print_conntrack)
+ l4proto->print_conntrack(s, ct);
- if (l4proto->print_conntrack && l4proto->print_conntrack(s, ct))
+ if (seq_has_overflowed(s))
goto release;
- if (print_tuple(s, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
- l3proto, l4proto))
+ print_tuple(s, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
+ l3proto, l4proto);
+
+ if (seq_has_overflowed(s))
goto release;
if (seq_print_acct(s, ct, IP_CT_DIR_ORIGINAL))
goto release;
if (!(test_bit(IPS_SEEN_REPLY_BIT, &ct->status)))
- if (seq_printf(s, "[UNREPLIED] "))
- goto release;
+ seq_printf(s, "[UNREPLIED] ");
- if (print_tuple(s, &ct->tuplehash[IP_CT_DIR_REPLY].tuple,
- l3proto, l4proto))
+ print_tuple(s, &ct->tuplehash[IP_CT_DIR_REPLY].tuple,
+ l3proto, l4proto);
+
+ if (seq_has_overflowed(s))
goto release;
if (seq_print_acct(s, ct, IP_CT_DIR_REPLY))
goto release;
if (test_bit(IPS_ASSURED_BIT, &ct->status))
- if (seq_printf(s, "[ASSURED] "))
- goto release;
+ seq_printf(s, "[ASSURED] ");
#ifdef CONFIG_NF_CONNTRACK_MARK
- if (seq_printf(s, "mark=%u ", ct->mark))
- goto release;
+ seq_printf(s, "mark=%u ", ct->mark);
#endif
- if (ct_show_secctx(s, ct))
- goto release;
+ ct_show_secctx(s, ct);
- if (seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use)))
+ seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use));
+
+ if (seq_has_overflowed(s))
goto release;
+
ret = 0;
release:
nf_ct_put(ct);
diff --git a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c
index b91b2641adda..80d5554b9a88 100644
--- a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c
+++ b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c
@@ -72,13 +72,13 @@ static bool icmp_invert_tuple(struct nf_conntrack_tuple *tuple,
}
/* Print out the per-protocol part of the tuple. */
-static int icmp_print_tuple(struct seq_file *s,
+static void icmp_print_tuple(struct seq_file *s,
const struct nf_conntrack_tuple *tuple)
{
- return seq_printf(s, "type=%u code=%u id=%u ",
- tuple->dst.u.icmp.type,
- tuple->dst.u.icmp.code,
- ntohs(tuple->src.u.icmp.id));
+ seq_printf(s, "type=%u code=%u id=%u ",
+ tuple->dst.u.icmp.type,
+ tuple->dst.u.icmp.code,
+ ntohs(tuple->src.u.icmp.id));
}
static unsigned int *icmp_get_timeouts(struct net *net)
diff --git a/net/ipv4/netfilter/nf_log_arp.c b/net/ipv4/netfilter/nf_log_arp.c
index ccfc78db12ee..d059182c1466 100644
--- a/net/ipv4/netfilter/nf_log_arp.c
+++ b/net/ipv4/netfilter/nf_log_arp.c
@@ -10,6 +10,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/module.h>
#include <linux/spinlock.h>
@@ -74,12 +75,12 @@ static void dump_arp_packet(struct nf_log_buf *m,
ap->mac_src, ap->ip_src, ap->mac_dst, ap->ip_dst);
}
-void nf_log_arp_packet(struct net *net, u_int8_t pf,
- unsigned int hooknum, const struct sk_buff *skb,
- const struct net_device *in,
- const struct net_device *out,
- const struct nf_loginfo *loginfo,
- const char *prefix)
+static void nf_log_arp_packet(struct net *net, u_int8_t pf,
+ unsigned int hooknum, const struct sk_buff *skb,
+ const struct net_device *in,
+ const struct net_device *out,
+ const struct nf_loginfo *loginfo,
+ const char *prefix)
{
struct nf_log_buf *m;
@@ -130,8 +131,17 @@ static int __init nf_log_arp_init(void)
if (ret < 0)
return ret;
- nf_log_register(NFPROTO_ARP, &nf_arp_logger);
+ ret = nf_log_register(NFPROTO_ARP, &nf_arp_logger);
+ if (ret < 0) {
+ pr_err("failed to register logger\n");
+ goto err1;
+ }
+
return 0;
+
+err1:
+ unregister_pernet_subsys(&nf_log_arp_net_ops);
+ return ret;
}
static void __exit nf_log_arp_exit(void)
diff --git a/net/ipv4/netfilter/nf_log_ipv4.c b/net/ipv4/netfilter/nf_log_ipv4.c
index 078bdca1b607..75101980eeee 100644
--- a/net/ipv4/netfilter/nf_log_ipv4.c
+++ b/net/ipv4/netfilter/nf_log_ipv4.c
@@ -5,6 +5,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/module.h>
#include <linux/spinlock.h>
@@ -366,8 +367,17 @@ static int __init nf_log_ipv4_init(void)
if (ret < 0)
return ret;
- nf_log_register(NFPROTO_IPV4, &nf_ip_logger);
+ ret = nf_log_register(NFPROTO_IPV4, &nf_ip_logger);
+ if (ret < 0) {
+ pr_err("failed to register logger\n");
+ goto err1;
+ }
+
return 0;
+
+err1:
+ unregister_pernet_subsys(&nf_log_ipv4_net_ops);
+ return ret;
}
static void __exit nf_log_ipv4_exit(void)
diff --git a/net/ipv4/netfilter/nf_reject_ipv4.c b/net/ipv4/netfilter/nf_reject_ipv4.c
index 1baaa83dfe5c..536da7bc598a 100644
--- a/net/ipv4/netfilter/nf_reject_ipv4.c
+++ b/net/ipv4/netfilter/nf_reject_ipv4.c
@@ -11,6 +11,7 @@
#include <net/tcp.h>
#include <net/route.h>
#include <net/dst.h>
+#include <net/netfilter/ipv4/nf_reject.h>
#include <linux/netfilter_ipv4.h>
#include <net/netfilter/ipv4/nf_reject.h>
diff --git a/net/ipv4/netfilter/nft_masq_ipv4.c b/net/ipv4/netfilter/nft_masq_ipv4.c
index c1023c445920..665de06561cd 100644
--- a/net/ipv4/netfilter/nft_masq_ipv4.c
+++ b/net/ipv4/netfilter/nft_masq_ipv4.c
@@ -24,6 +24,7 @@ static void nft_masq_ipv4_eval(const struct nft_expr *expr,
struct nf_nat_range range;
unsigned int verdict;
+ memset(&range, 0, sizeof(range));
range.flags = priv->flags;
verdict = nf_nat_masquerade_ipv4(pkt->skb, pkt->ops->hooknum,
diff --git a/net/ipv4/netfilter/nft_redir_ipv4.c b/net/ipv4/netfilter/nft_redir_ipv4.c
new file mode 100644
index 000000000000..ff2d23d8c87a
--- /dev/null
+++ b/net/ipv4/netfilter/nft_redir_ipv4.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2014 Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nf_nat.h>
+#include <net/netfilter/nf_nat_redirect.h>
+#include <net/netfilter/nft_redir.h>
+
+static void nft_redir_ipv4_eval(const struct nft_expr *expr,
+ struct nft_data data[NFT_REG_MAX + 1],
+ const struct nft_pktinfo *pkt)
+{
+ struct nft_redir *priv = nft_expr_priv(expr);
+ struct nf_nat_ipv4_multi_range_compat mr;
+ unsigned int verdict;
+
+ memset(&mr, 0, sizeof(mr));
+ if (priv->sreg_proto_min) {
+ mr.range[0].min.all = (__force __be16)
+ data[priv->sreg_proto_min].data[0];
+ mr.range[0].max.all = (__force __be16)
+ data[priv->sreg_proto_max].data[0];
+ mr.range[0].flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
+ }
+
+ mr.range[0].flags |= priv->flags;
+
+ verdict = nf_nat_redirect_ipv4(pkt->skb, &mr, pkt->ops->hooknum);
+ data[NFT_REG_VERDICT].verdict = verdict;
+}
+
+static struct nft_expr_type nft_redir_ipv4_type;
+static const struct nft_expr_ops nft_redir_ipv4_ops = {
+ .type = &nft_redir_ipv4_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_redir)),
+ .eval = nft_redir_ipv4_eval,
+ .init = nft_redir_init,
+ .dump = nft_redir_dump,
+ .validate = nft_redir_validate,
+};
+
+static struct nft_expr_type nft_redir_ipv4_type __read_mostly = {
+ .family = NFPROTO_IPV4,
+ .name = "redir",
+ .ops = &nft_redir_ipv4_ops,
+ .policy = nft_redir_policy,
+ .maxattr = NFTA_REDIR_MAX,
+ .owner = THIS_MODULE,
+};
+
+static int __init nft_redir_ipv4_module_init(void)
+{
+ return nft_register_expr(&nft_redir_ipv4_type);
+}
+
+static void __exit nft_redir_ipv4_module_exit(void)
+{
+ nft_unregister_expr(&nft_redir_ipv4_type);
+}
+
+module_init(nft_redir_ipv4_module_init);
+module_exit(nft_redir_ipv4_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>");
+MODULE_ALIAS_NFT_AF_EXPR(AF_INET, "redir");
diff --git a/net/ipv4/netfilter/nft_reject_ipv4.c b/net/ipv4/netfilter/nft_reject_ipv4.c
index ed33299c56d1..d729542bd1b7 100644
--- a/net/ipv4/netfilter/nft_reject_ipv4.c
+++ b/net/ipv4/netfilter/nft_reject_ipv4.c
@@ -19,9 +19,9 @@
#include <net/netfilter/ipv4/nf_reject.h>
#include <net/netfilter/nft_reject.h>
-void nft_reject_ipv4_eval(const struct nft_expr *expr,
- struct nft_data data[NFT_REG_MAX + 1],
- const struct nft_pktinfo *pkt)
+static void nft_reject_ipv4_eval(const struct nft_expr *expr,
+ struct nft_data data[NFT_REG_MAX + 1],
+ const struct nft_pktinfo *pkt)
{
struct nft_reject *priv = nft_expr_priv(expr);
@@ -36,7 +36,6 @@ void nft_reject_ipv4_eval(const struct nft_expr *expr,
data[NFT_REG_VERDICT].verdict = NF_DROP;
}
-EXPORT_SYMBOL_GPL(nft_reject_ipv4_eval);
static struct nft_expr_type nft_reject_ipv4_type;
static const struct nft_expr_ops nft_reject_ipv4_ops = {
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
index 57f7c9804139..c0d82f78d364 100644
--- a/net/ipv4/ping.c
+++ b/net/ipv4/ping.c
@@ -217,6 +217,8 @@ static struct sock *ping_lookup(struct net *net, struct sk_buff *skb, u16 ident)
&ipv6_hdr(skb)->daddr))
continue;
#endif
+ } else {
+ continue;
}
if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif)
@@ -660,7 +662,7 @@ int ping_common_sendmsg(int family, struct msghdr *msg, size_t len,
* Fetch the ICMP header provided by the userland.
* iovec is modified! The ICMP header is consumed.
*/
- if (memcpy_fromiovec(user_icmph, msg->msg_iov, icmph_len))
+ if (memcpy_from_msg(user_icmph, msg, icmph_len))
return -EFAULT;
if (family == AF_INET) {
@@ -809,7 +811,8 @@ back_from_confirm:
pfh.icmph.checksum = 0;
pfh.icmph.un.echo.id = inet->inet_sport;
pfh.icmph.un.echo.sequence = user_icmph.un.echo.sequence;
- pfh.iov = msg->msg_iov;
+ /* XXX: stripping const */
+ pfh.iov = (struct iovec *)msg->msg_iter.iov;
pfh.wcheck = 0;
pfh.family = AF_INET;
@@ -853,16 +856,8 @@ int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
if (flags & MSG_OOB)
goto out;
- if (flags & MSG_ERRQUEUE) {
- if (family == AF_INET) {
- return ip_recv_error(sk, msg, len, addr_len);
-#if IS_ENABLED(CONFIG_IPV6)
- } else if (family == AF_INET6) {
- return pingv6_ops.ipv6_recv_error(sk, msg, len,
- addr_len);
-#endif
- }
- }
+ if (flags & MSG_ERRQUEUE)
+ return inet_recv_error(sk, msg, len, addr_len);
skb = skb_recv_datagram(sk, flags, noblock, &err);
if (!skb)
@@ -875,7 +870,7 @@ int ping_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
}
/* Don't bother checking the checksum */
- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
if (err)
goto done;
@@ -955,7 +950,7 @@ EXPORT_SYMBOL_GPL(ping_queue_rcv_skb);
* All we need to do is get the socket.
*/
-void ping_rcv(struct sk_buff *skb)
+bool ping_rcv(struct sk_buff *skb)
{
struct sock *sk;
struct net *net = dev_net(skb->dev);
@@ -974,11 +969,11 @@ void ping_rcv(struct sk_buff *skb)
pr_debug("rcv on socket %p\n", sk);
ping_queue_rcv_skb(sk, skb_get(skb));
sock_put(sk);
- return;
+ return true;
}
pr_debug("no socket, dropping\n");
- /* We're called from icmp_rcv(). kfree_skb() is done there. */
+ return false;
}
EXPORT_SYMBOL_GPL(ping_rcv);
diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c
index 8e3eb39f84e7..8f9cd200ce20 100644
--- a/net/ipv4/proc.c
+++ b/net/ipv4/proc.c
@@ -181,6 +181,7 @@ static const struct snmp_mib snmp4_udp_list[] = {
SNMP_MIB_ITEM("RcvbufErrors", UDP_MIB_RCVBUFERRORS),
SNMP_MIB_ITEM("SndbufErrors", UDP_MIB_SNDBUFERRORS),
SNMP_MIB_ITEM("InCsumErrors", UDP_MIB_CSUMERRORS),
+ SNMP_MIB_ITEM("IgnoredMulti", UDP_MIB_IGNOREDMULTI),
SNMP_MIB_SENTINEL
};
@@ -287,6 +288,10 @@ static const struct snmp_mib snmp4_net_list[] = {
SNMP_MIB_ITEM("TCPWantZeroWindowAdv", LINUX_MIB_TCPWANTZEROWINDOWADV),
SNMP_MIB_ITEM("TCPSynRetrans", LINUX_MIB_TCPSYNRETRANS),
SNMP_MIB_ITEM("TCPOrigDataSent", LINUX_MIB_TCPORIGDATASENT),
+ SNMP_MIB_ITEM("TCPHystartTrainDetect", LINUX_MIB_TCPHYSTARTTRAINDETECT),
+ SNMP_MIB_ITEM("TCPHystartTrainCwnd", LINUX_MIB_TCPHYSTARTTRAINCWND),
+ SNMP_MIB_ITEM("TCPHystartDelayDetect", LINUX_MIB_TCPHYSTARTDELAYDETECT),
+ SNMP_MIB_ITEM("TCPHystartDelayCwnd", LINUX_MIB_TCPHYSTARTDELAYCWND),
SNMP_MIB_SENTINEL
};
@@ -296,12 +301,12 @@ static void icmpmsg_put_line(struct seq_file *seq, unsigned long *vals,
int j;
if (count) {
- seq_printf(seq, "\nIcmpMsg:");
+ seq_puts(seq, "\nIcmpMsg:");
for (j = 0; j < count; ++j)
seq_printf(seq, " %sType%u",
type[j] & 0x100 ? "Out" : "In",
type[j] & 0xff);
- seq_printf(seq, "\nIcmpMsg:");
+ seq_puts(seq, "\nIcmpMsg:");
for (j = 0; j < count; ++j)
seq_printf(seq, " %lu", vals[j]);
}
@@ -342,7 +347,7 @@ static void icmp_put(struct seq_file *seq)
seq_puts(seq, "\nIcmp: InMsgs InErrors InCsumErrors");
for (i = 0; icmpmibmap[i].name != NULL; i++)
seq_printf(seq, " In%s", icmpmibmap[i].name);
- seq_printf(seq, " OutMsgs OutErrors");
+ seq_puts(seq, " OutMsgs OutErrors");
for (i = 0; icmpmibmap[i].name != NULL; i++)
seq_printf(seq, " Out%s", icmpmibmap[i].name);
seq_printf(seq, "\nIcmp: %lu %lu %lu",
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 739db3100c23..0bb68df5055d 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -79,6 +79,16 @@
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/compat.h>
+#include <linux/uio.h>
+
+struct raw_frag_vec {
+ struct msghdr *msg;
+ union {
+ struct icmphdr icmph;
+ char c[1];
+ } hdr;
+ int hlen;
+};
static struct raw_hashinfo raw_v4_hashinfo = {
.lock = __RW_LOCK_UNLOCKED(raw_v4_hashinfo.lock),
@@ -420,53 +430,57 @@ error:
return err;
}
-static int raw_probe_proto_opt(struct flowi4 *fl4, struct msghdr *msg)
+static int raw_probe_proto_opt(struct raw_frag_vec *rfv, struct flowi4 *fl4)
{
- struct iovec *iov;
- u8 __user *type = NULL;
- u8 __user *code = NULL;
- int probed = 0;
- unsigned int i;
+ int err;
- if (!msg->msg_iov)
+ if (fl4->flowi4_proto != IPPROTO_ICMP)
return 0;
- for (i = 0; i < msg->msg_iovlen; i++) {
- iov = &msg->msg_iov[i];
- if (!iov)
- continue;
-
- switch (fl4->flowi4_proto) {
- case IPPROTO_ICMP:
- /* check if one-byte field is readable or not. */
- if (iov->iov_base && iov->iov_len < 1)
- break;
-
- if (!type) {
- type = iov->iov_base;
- /* check if code field is readable or not. */
- if (iov->iov_len > 1)
- code = type + 1;
- } else if (!code)
- code = iov->iov_base;
-
- if (type && code) {
- if (get_user(fl4->fl4_icmp_type, type) ||
- get_user(fl4->fl4_icmp_code, code))
- return -EFAULT;
- probed = 1;
- }
- break;
- default:
- probed = 1;
- break;
- }
- if (probed)
- break;
- }
+ /* We only need the first two bytes. */
+ rfv->hlen = 2;
+
+ err = memcpy_from_msg(rfv->hdr.c, rfv->msg, rfv->hlen);
+ if (err)
+ return err;
+
+ fl4->fl4_icmp_type = rfv->hdr.icmph.type;
+ fl4->fl4_icmp_code = rfv->hdr.icmph.code;
+
return 0;
}
+static int raw_getfrag(void *from, char *to, int offset, int len, int odd,
+ struct sk_buff *skb)
+{
+ struct raw_frag_vec *rfv = from;
+
+ if (offset < rfv->hlen) {
+ int copy = min(rfv->hlen - offset, len);
+
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ memcpy(to, rfv->hdr.c + offset, copy);
+ else
+ skb->csum = csum_block_add(
+ skb->csum,
+ csum_partial_copy_nocheck(rfv->hdr.c + offset,
+ to, copy, 0),
+ odd);
+
+ odd = 0;
+ offset += copy;
+ to += copy;
+ len -= copy;
+
+ if (!len)
+ return 0;
+ }
+
+ offset -= rfv->hlen;
+
+ return ip_generic_getfrag(rfv->msg, to, offset, len, odd, skb);
+}
+
static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t len)
{
@@ -480,6 +494,7 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
u8 tos;
int err;
struct ip_options_data opt_copy;
+ struct raw_frag_vec rfv;
err = -EMSGSIZE;
if (len > 0xFFFF)
@@ -585,7 +600,10 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
daddr, saddr, 0, 0);
if (!inet->hdrincl) {
- err = raw_probe_proto_opt(&fl4, msg);
+ rfv.msg = msg;
+ rfv.hlen = 0;
+
+ err = raw_probe_proto_opt(&rfv, &fl4);
if (err)
goto done;
}
@@ -607,7 +625,8 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
back_from_confirm:
if (inet->hdrincl)
- err = raw_send_hdrinc(sk, &fl4, msg->msg_iov, len,
+ /* XXX: stripping const */
+ err = raw_send_hdrinc(sk, &fl4, (struct iovec *)msg->msg_iter.iov, len,
&rt, msg->msg_flags);
else {
@@ -616,8 +635,8 @@ back_from_confirm:
if (!ipc.addr)
ipc.addr = fl4.daddr;
lock_sock(sk);
- err = ip_append_data(sk, &fl4, ip_generic_getfrag,
- msg->msg_iov, len, 0,
+ err = ip_append_data(sk, &fl4, raw_getfrag,
+ &rfv, len, 0,
&ipc, &rt, msg->msg_flags);
if (err)
ip_flush_pending_frames(sk);
@@ -718,7 +737,7 @@ static int raw_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
copied = len;
}
- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
if (err)
goto done;
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index 32b98d0207b4..45fe60c5238e 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -19,10 +19,6 @@
#include <net/tcp.h>
#include <net/route.h>
-/* Timestamps: lowest bits store TCP options */
-#define TSBITS 6
-#define TSMASK (((__u32)1 << TSBITS) - 1)
-
extern int sysctl_tcp_syncookies;
static u32 syncookie_secret[2][16-4+SHA_DIGEST_WORDS] __read_mostly;
@@ -30,6 +26,30 @@ static u32 syncookie_secret[2][16-4+SHA_DIGEST_WORDS] __read_mostly;
#define COOKIEBITS 24 /* Upper bits store count */
#define COOKIEMASK (((__u32)1 << COOKIEBITS) - 1)
+/* TCP Timestamp: 6 lowest bits of timestamp sent in the cookie SYN-ACK
+ * stores TCP options:
+ *
+ * MSB LSB
+ * | 31 ... 6 | 5 | 4 | 3 2 1 0 |
+ * | Timestamp | ECN | SACK | WScale |
+ *
+ * When we receive a valid cookie-ACK, we look at the echoed tsval (if
+ * any) to figure out which TCP options we should use for the rebuilt
+ * connection.
+ *
+ * A WScale setting of '0xf' (which is an invalid scaling value)
+ * means that original syn did not include the TCP window scaling option.
+ */
+#define TS_OPT_WSCALE_MASK 0xf
+#define TS_OPT_SACK BIT(4)
+#define TS_OPT_ECN BIT(5)
+/* There is no TS_OPT_TIMESTAMP:
+ * if ACK contains timestamp option, we already know it was
+ * requested/supported by the syn/synack exchange.
+ */
+#define TSBITS 6
+#define TSMASK (((__u32)1 << TSBITS) - 1)
+
static DEFINE_PER_CPU(__u32 [16 + 5 + SHA_WORKSPACE_WORDS],
ipv4_cookie_scratch);
@@ -67,9 +87,11 @@ __u32 cookie_init_timestamp(struct request_sock *req)
ireq = inet_rsk(req);
- options = ireq->wscale_ok ? ireq->snd_wscale : 0xf;
- options |= ireq->sack_ok << 4;
- options |= ireq->ecn_ok << 5;
+ options = ireq->wscale_ok ? ireq->snd_wscale : TS_OPT_WSCALE_MASK;
+ if (ireq->sack_ok)
+ options |= TS_OPT_SACK;
+ if (ireq->ecn_ok)
+ options |= TS_OPT_ECN;
ts = ts_now & ~TSMASK;
ts |= options;
@@ -219,16 +241,13 @@ static inline struct sock *get_cookie_sock(struct sock *sk, struct sk_buff *skb,
* additional tcp options in the timestamp.
* This extracts these options from the timestamp echo.
*
- * The lowest 4 bits store snd_wscale.
- * next 2 bits indicate SACK and ECN support.
- *
- * return false if we decode an option that should not be.
+ * return false if we decode a tcp option that is disabled
+ * on the host.
*/
-bool cookie_check_timestamp(struct tcp_options_received *tcp_opt,
- struct net *net, bool *ecn_ok)
+bool cookie_timestamp_decode(struct tcp_options_received *tcp_opt)
{
/* echoed timestamp, lowest bits contain options */
- u32 options = tcp_opt->rcv_tsecr & TSMASK;
+ u32 options = tcp_opt->rcv_tsecr;
if (!tcp_opt->saw_tstamp) {
tcp_clear_options(tcp_opt);
@@ -238,22 +257,35 @@ bool cookie_check_timestamp(struct tcp_options_received *tcp_opt,
if (!sysctl_tcp_timestamps)
return false;
- tcp_opt->sack_ok = (options & (1 << 4)) ? TCP_SACK_SEEN : 0;
- *ecn_ok = (options >> 5) & 1;
- if (*ecn_ok && !net->ipv4.sysctl_tcp_ecn)
- return false;
+ tcp_opt->sack_ok = (options & TS_OPT_SACK) ? TCP_SACK_SEEN : 0;
if (tcp_opt->sack_ok && !sysctl_tcp_sack)
return false;
- if ((options & 0xf) == 0xf)
+ if ((options & TS_OPT_WSCALE_MASK) == TS_OPT_WSCALE_MASK)
return true; /* no window scaling */
tcp_opt->wscale_ok = 1;
- tcp_opt->snd_wscale = options & 0xf;
+ tcp_opt->snd_wscale = options & TS_OPT_WSCALE_MASK;
+
return sysctl_tcp_window_scaling != 0;
}
-EXPORT_SYMBOL(cookie_check_timestamp);
+EXPORT_SYMBOL(cookie_timestamp_decode);
+
+bool cookie_ecn_ok(const struct tcp_options_received *tcp_opt,
+ const struct net *net, const struct dst_entry *dst)
+{
+ bool ecn_ok = tcp_opt->rcv_tsecr & TS_OPT_ECN;
+
+ if (!ecn_ok)
+ return false;
+
+ if (net->ipv4.sysctl_tcp_ecn)
+ return true;
+
+ return dst_feature(dst, RTAX_FEATURE_ECN);
+}
+EXPORT_SYMBOL(cookie_ecn_ok);
struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb)
{
@@ -269,14 +301,16 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb)
int mss;
struct rtable *rt;
__u8 rcv_wscale;
- bool ecn_ok = false;
struct flowi4 fl4;
if (!sysctl_tcp_syncookies || !th->ack || th->rst)
goto out;
- if (tcp_synq_no_recent_overflow(sk) ||
- (mss = __cookie_v4_check(ip_hdr(skb), th, cookie)) == 0) {
+ if (tcp_synq_no_recent_overflow(sk))
+ goto out;
+
+ mss = __cookie_v4_check(ip_hdr(skb), th, cookie);
+ if (mss == 0) {
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESFAILED);
goto out;
}
@@ -287,7 +321,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb)
memset(&tcp_opt, 0, sizeof(tcp_opt));
tcp_parse_options(skb, &tcp_opt, 0, NULL);
- if (!cookie_check_timestamp(&tcp_opt, sock_net(sk), &ecn_ok))
+ if (!cookie_timestamp_decode(&tcp_opt))
goto out;
ret = NULL;
@@ -305,7 +339,6 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb)
ireq->ir_loc_addr = ip_hdr(skb)->daddr;
ireq->ir_rmt_addr = ip_hdr(skb)->saddr;
ireq->ir_mark = inet_request_mark(sk, skb);
- ireq->ecn_ok = ecn_ok;
ireq->snd_wscale = tcp_opt.snd_wscale;
ireq->sack_ok = tcp_opt.sack_ok;
ireq->wscale_ok = tcp_opt.wscale_ok;
@@ -354,6 +387,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb)
dst_metric(&rt->dst, RTAX_INITRWND));
ireq->rcv_wscale = rcv_wscale;
+ ireq->ecn_ok = cookie_ecn_ok(&tcp_opt, sock_net(sk), &rt->dst);
ret = get_cookie_sock(sk, skb, req, &rt->dst);
/* ip_queue_xmit() depends on our flow being setup
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index b3c53c8b331e..e0ee384a448f 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -496,6 +496,13 @@ static struct ctl_table ipv4_table[] = {
.proc_handler = proc_dointvec
},
{
+ .procname = "tcp_max_reordering",
+ .data = &sysctl_tcp_max_reordering,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec
+ },
+ {
.procname = "tcp_dsack",
.data = &sysctl_tcp_dsack,
.maxlen = sizeof(int),
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 39ec0c379545..3075723c729b 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -835,47 +835,29 @@ static unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now,
int large_allowed)
{
struct tcp_sock *tp = tcp_sk(sk);
- u32 xmit_size_goal, old_size_goal;
-
- xmit_size_goal = mss_now;
-
- if (large_allowed && sk_can_gso(sk)) {
- u32 gso_size, hlen;
-
- /* Maybe we should/could use sk->sk_prot->max_header here ? */
- hlen = inet_csk(sk)->icsk_af_ops->net_header_len +
- inet_csk(sk)->icsk_ext_hdr_len +
- tp->tcp_header_len;
-
- /* Goal is to send at least one packet per ms,
- * not one big TSO packet every 100 ms.
- * This preserves ACK clocking and is consistent
- * with tcp_tso_should_defer() heuristic.
- */
- gso_size = sk->sk_pacing_rate / (2 * MSEC_PER_SEC);
- gso_size = max_t(u32, gso_size,
- sysctl_tcp_min_tso_segs * mss_now);
-
- xmit_size_goal = min_t(u32, gso_size,
- sk->sk_gso_max_size - 1 - hlen);
-
- xmit_size_goal = tcp_bound_to_half_wnd(tp, xmit_size_goal);
-
- /* We try hard to avoid divides here */
- old_size_goal = tp->xmit_size_goal_segs * mss_now;
-
- if (likely(old_size_goal <= xmit_size_goal &&
- old_size_goal + mss_now > xmit_size_goal)) {
- xmit_size_goal = old_size_goal;
- } else {
- tp->xmit_size_goal_segs =
- min_t(u16, xmit_size_goal / mss_now,
- sk->sk_gso_max_segs);
- xmit_size_goal = tp->xmit_size_goal_segs * mss_now;
- }
+ u32 new_size_goal, size_goal, hlen;
+
+ if (!large_allowed || !sk_can_gso(sk))
+ return mss_now;
+
+ /* Maybe we should/could use sk->sk_prot->max_header here ? */
+ hlen = inet_csk(sk)->icsk_af_ops->net_header_len +
+ inet_csk(sk)->icsk_ext_hdr_len +
+ tp->tcp_header_len;
+
+ new_size_goal = sk->sk_gso_max_size - 1 - hlen;
+ new_size_goal = tcp_bound_to_half_wnd(tp, new_size_goal);
+
+ /* We try hard to avoid divides here */
+ size_goal = tp->gso_segs * mss_now;
+ if (unlikely(new_size_goal < size_goal ||
+ new_size_goal >= size_goal + mss_now)) {
+ tp->gso_segs = min_t(u16, new_size_goal / mss_now,
+ sk->sk_gso_max_segs);
+ size_goal = tp->gso_segs * mss_now;
}
- return max(xmit_size_goal, mss_now);
+ return max(size_goal, mss_now);
}
static int tcp_send_mss(struct sock *sk, int *size_goal, int flags)
@@ -1085,7 +1067,7 @@ static int tcp_sendmsg_fastopen(struct sock *sk, struct msghdr *msg,
int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t size)
{
- struct iovec *iov;
+ const struct iovec *iov;
struct tcp_sock *tp = tcp_sk(sk);
struct sk_buff *skb;
int iovlen, flags, err, copied = 0;
@@ -1136,8 +1118,8 @@ int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
mss_now = tcp_send_mss(sk, &size_goal, flags);
/* Ok commence sending. */
- iovlen = msg->msg_iovlen;
- iov = msg->msg_iov;
+ iovlen = msg->msg_iter.nr_segs;
+ iov = msg->msg_iter.iov;
copied = 0;
err = -EPIPE;
@@ -1349,7 +1331,7 @@ static int tcp_recv_urg(struct sock *sk, struct msghdr *msg, int len, int flags)
if (len > 0) {
if (!(flags & MSG_TRUNC))
- err = memcpy_toiovec(msg->msg_iov, &c, 1);
+ err = memcpy_to_msg(msg, &c, 1);
len = 1;
} else
msg->msg_flags |= MSG_TRUNC;
@@ -1377,7 +1359,7 @@ static int tcp_peek_sndq(struct sock *sk, struct msghdr *msg, int len)
/* XXX -- need to support SO_PEEK_OFF */
skb_queue_walk(&sk->sk_write_queue, skb) {
- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, skb->len);
+ err = skb_copy_datagram_msg(skb, 0, msg, skb->len);
if (err)
break;
@@ -1598,7 +1580,7 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
u32 urg_hole = 0;
if (unlikely(flags & MSG_ERRQUEUE))
- return ip_recv_error(sk, msg, len, addr_len);
+ return inet_recv_error(sk, msg, len, addr_len);
if (sk_can_busy_loop(sk) && skb_queue_empty(&sk->sk_receive_queue) &&
(sk->sk_state == TCP_ESTABLISHED))
@@ -1729,7 +1711,7 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
if (!user_recv && !(flags & (MSG_TRUNC | MSG_PEEK))) {
user_recv = current;
tp->ucopy.task = user_recv;
- tp->ucopy.iov = msg->msg_iov;
+ tp->ucopy.msg = msg;
}
tp->ucopy.len = len;
@@ -1833,8 +1815,7 @@ do_prequeue:
}
if (!(flags & MSG_TRUNC)) {
- err = skb_copy_datagram_iovec(skb, offset,
- msg->msg_iov, used);
+ err = skb_copy_datagram_msg(skb, offset, msg, used);
if (err) {
/* Exception. Bailout! */
if (!copied)
diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c
index b1c5970d47a1..27ead0dd16bc 100644
--- a/net/ipv4/tcp_cong.c
+++ b/net/ipv4/tcp_cong.c
@@ -1,5 +1,5 @@
/*
- * Plugable TCP congestion control support and newReno
+ * Pluggable TCP congestion control support and newReno
* congestion control.
* Based on ideas from I/O scheduler support and Web100.
*
diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c
index 20de0118c98e..6b6002416a73 100644
--- a/net/ipv4/tcp_cubic.c
+++ b/net/ipv4/tcp_cubic.c
@@ -363,16 +363,28 @@ static void hystart_update(struct sock *sk, u32 delay)
struct tcp_sock *tp = tcp_sk(sk);
struct bictcp *ca = inet_csk_ca(sk);
- if (!(ca->found & hystart_detect)) {
+ if (ca->found & hystart_detect)
+ return;
+
+ if (hystart_detect & HYSTART_ACK_TRAIN) {
u32 now = bictcp_clock();
/* first detection parameter - ack-train detection */
if ((s32)(now - ca->last_ack) <= hystart_ack_delta) {
ca->last_ack = now;
- if ((s32)(now - ca->round_start) > ca->delay_min >> 4)
+ if ((s32)(now - ca->round_start) > ca->delay_min >> 4) {
ca->found |= HYSTART_ACK_TRAIN;
+ NET_INC_STATS_BH(sock_net(sk),
+ LINUX_MIB_TCPHYSTARTTRAINDETECT);
+ NET_ADD_STATS_BH(sock_net(sk),
+ LINUX_MIB_TCPHYSTARTTRAINCWND,
+ tp->snd_cwnd);
+ tp->snd_ssthresh = tp->snd_cwnd;
+ }
}
+ }
+ if (hystart_detect & HYSTART_DELAY) {
/* obtain the minimum delay of more than sampling packets */
if (ca->sample_cnt < HYSTART_MIN_SAMPLES) {
if (ca->curr_rtt == 0 || ca->curr_rtt > delay)
@@ -381,15 +393,16 @@ static void hystart_update(struct sock *sk, u32 delay)
ca->sample_cnt++;
} else {
if (ca->curr_rtt > ca->delay_min +
- HYSTART_DELAY_THRESH(ca->delay_min>>4))
+ HYSTART_DELAY_THRESH(ca->delay_min >> 3)) {
ca->found |= HYSTART_DELAY;
+ NET_INC_STATS_BH(sock_net(sk),
+ LINUX_MIB_TCPHYSTARTDELAYDETECT);
+ NET_ADD_STATS_BH(sock_net(sk),
+ LINUX_MIB_TCPHYSTARTDELAYCWND,
+ tp->snd_cwnd);
+ tp->snd_ssthresh = tp->snd_cwnd;
+ }
}
- /*
- * Either one of two conditions are met,
- * we exit from slow start immediately.
- */
- if (ca->found & hystart_detect)
- tp->snd_ssthresh = tp->snd_cwnd;
}
}
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index a12b455928e5..075ab4d5af5e 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -81,6 +81,7 @@ int sysctl_tcp_window_scaling __read_mostly = 1;
int sysctl_tcp_sack __read_mostly = 1;
int sysctl_tcp_fack __read_mostly = 1;
int sysctl_tcp_reordering __read_mostly = TCP_FASTRETRANS_THRESH;
+int sysctl_tcp_max_reordering __read_mostly = 300;
EXPORT_SYMBOL(sysctl_tcp_reordering);
int sysctl_tcp_dsack __read_mostly = 1;
int sysctl_tcp_app_win __read_mostly = 31;
@@ -833,7 +834,7 @@ static void tcp_update_reordering(struct sock *sk, const int metric,
if (metric > tp->reordering) {
int mib_idx;
- tp->reordering = min(TCP_MAX_REORDERING, metric);
+ tp->reordering = min(sysctl_tcp_max_reordering, metric);
/* This exciting event is worth to be remembered. 8) */
if (ts)
@@ -2315,6 +2316,35 @@ static inline bool tcp_packet_delayed(const struct tcp_sock *tp)
/* Undo procedures. */
+/* We can clear retrans_stamp when there are no retransmissions in the
+ * window. It would seem that it is trivially available for us in
+ * tp->retrans_out, however, that kind of assumptions doesn't consider
+ * what will happen if errors occur when sending retransmission for the
+ * second time. ...It could the that such segment has only
+ * TCPCB_EVER_RETRANS set at the present time. It seems that checking
+ * the head skb is enough except for some reneging corner cases that
+ * are not worth the effort.
+ *
+ * Main reason for all this complexity is the fact that connection dying
+ * time now depends on the validity of the retrans_stamp, in particular,
+ * that successive retransmissions of a segment must not advance
+ * retrans_stamp under any conditions.
+ */
+static bool tcp_any_retrans_done(const struct sock *sk)
+{
+ const struct tcp_sock *tp = tcp_sk(sk);
+ struct sk_buff *skb;
+
+ if (tp->retrans_out)
+ return true;
+
+ skb = tcp_write_queue_head(sk);
+ if (unlikely(skb && TCP_SKB_CB(skb)->sacked & TCPCB_EVER_RETRANS))
+ return true;
+
+ return false;
+}
+
#if FASTRETRANS_DEBUG > 1
static void DBGUNDO(struct sock *sk, const char *msg)
{
@@ -2410,6 +2440,8 @@ static bool tcp_try_undo_recovery(struct sock *sk)
* is ACKed. For Reno it is MUST to prevent false
* fast retransmits (RFC2582). SACK TCP is safe. */
tcp_moderate_cwnd(tp);
+ if (!tcp_any_retrans_done(sk))
+ tp->retrans_stamp = 0;
return true;
}
tcp_set_ca_state(sk, TCP_CA_Open);
@@ -2430,35 +2462,6 @@ static bool tcp_try_undo_dsack(struct sock *sk)
return false;
}
-/* We can clear retrans_stamp when there are no retransmissions in the
- * window. It would seem that it is trivially available for us in
- * tp->retrans_out, however, that kind of assumptions doesn't consider
- * what will happen if errors occur when sending retransmission for the
- * second time. ...It could the that such segment has only
- * TCPCB_EVER_RETRANS set at the present time. It seems that checking
- * the head skb is enough except for some reneging corner cases that
- * are not worth the effort.
- *
- * Main reason for all this complexity is the fact that connection dying
- * time now depends on the validity of the retrans_stamp, in particular,
- * that successive retransmissions of a segment must not advance
- * retrans_stamp under any conditions.
- */
-static bool tcp_any_retrans_done(const struct sock *sk)
-{
- const struct tcp_sock *tp = tcp_sk(sk);
- struct sk_buff *skb;
-
- if (tp->retrans_out)
- return true;
-
- skb = tcp_write_queue_head(sk);
- if (unlikely(skb && TCP_SKB_CB(skb)->sacked & TCPCB_EVER_RETRANS))
- return true;
-
- return false;
-}
-
/* Undo during loss recovery after partial ACK or using F-RTO. */
static bool tcp_try_undo_loss(struct sock *sk, bool frto_undo)
{
@@ -4365,7 +4368,7 @@ int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size)
if (tcp_try_rmem_schedule(sk, skb, skb->truesize))
goto err_free;
- if (memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size))
+ if (memcpy_from_msg(skb_put(skb, size), msg, size))
goto err_free;
TCP_SKB_CB(skb)->seq = tcp_sk(sk)->rcv_nxt;
@@ -4418,7 +4421,7 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
__set_current_state(TASK_RUNNING);
local_bh_enable();
- if (!skb_copy_datagram_iovec(skb, 0, tp->ucopy.iov, chunk)) {
+ if (!skb_copy_datagram_msg(skb, 0, tp->ucopy.msg, chunk)) {
tp->ucopy.len -= chunk;
tp->copied_seq += chunk;
eaten = (chunk == skb->len);
@@ -4938,10 +4941,9 @@ static int tcp_copy_to_iovec(struct sock *sk, struct sk_buff *skb, int hlen)
local_bh_enable();
if (skb_csum_unnecessary(skb))
- err = skb_copy_datagram_iovec(skb, hlen, tp->ucopy.iov, chunk);
+ err = skb_copy_datagram_msg(skb, hlen, tp->ucopy.msg, chunk);
else
- err = skb_copy_and_csum_datagram_iovec(skb, hlen,
- tp->ucopy.iov);
+ err = skb_copy_and_csum_datagram_msg(skb, hlen, tp->ucopy.msg);
if (!err) {
tp->ucopy.len -= chunk;
@@ -5028,7 +5030,7 @@ static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
/* step 3: check security and precedence [ignored] */
/* step 4: Check for a SYN
- * RFC 5691 4.2 : Send a challenge ack
+ * RFC 5961 4.2 : Send a challenge ack
*/
if (th->syn) {
syn_challenge:
@@ -5229,7 +5231,7 @@ slow_path:
if (len < (th->doff << 2) || tcp_checksum_complete_user(sk, skb))
goto csum_error;
- if (!th->ack && !th->rst)
+ if (!th->ack && !th->rst && !th->syn)
goto discard;
/*
@@ -5648,7 +5650,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
goto discard;
}
- if (!th->ack && !th->rst)
+ if (!th->ack && !th->rst && !th->syn)
goto discard;
if (!tcp_validate_incoming(sk, skb, th, 0))
@@ -5851,12 +5853,12 @@ static inline void pr_drop_req(struct request_sock *req, __u16 port, int family)
struct inet_request_sock *ireq = inet_rsk(req);
if (family == AF_INET)
- LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("drop open request from %pI4/%u\n"),
- &ireq->ir_rmt_addr, port);
+ net_dbg_ratelimited("drop open request from %pI4/%u\n",
+ &ireq->ir_rmt_addr, port);
#if IS_ENABLED(CONFIG_IPV6)
else if (family == AF_INET6)
- LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("drop open request from %pI6/%u\n"),
- &ireq->ir_v6_rmt_addr, port);
+ net_dbg_ratelimited("drop open request from %pI6/%u\n",
+ &ireq->ir_v6_rmt_addr, port);
#endif
}
@@ -5865,7 +5867,7 @@ static inline void pr_drop_req(struct request_sock *req, __u16 port, int family)
* If we receive a SYN packet with these bits set, it means a
* network is playing bad games with TOS bits. In order to
* avoid possible false congestion notifications, we disable
- * TCP ECN negociation.
+ * TCP ECN negotiation.
*
* Exception: tcp_ca wants ECN. This is required for DCTCP
* congestion control; it requires setting ECT on all packets,
@@ -5875,20 +5877,22 @@ static inline void pr_drop_req(struct request_sock *req, __u16 port, int family)
*/
static void tcp_ecn_create_request(struct request_sock *req,
const struct sk_buff *skb,
- const struct sock *listen_sk)
+ const struct sock *listen_sk,
+ const struct dst_entry *dst)
{
const struct tcphdr *th = tcp_hdr(skb);
const struct net *net = sock_net(listen_sk);
bool th_ecn = th->ece && th->cwr;
- bool ect, need_ecn;
+ bool ect, need_ecn, ecn_ok;
if (!th_ecn)
return;
ect = !INET_ECN_is_not_ect(TCP_SKB_CB(skb)->ip_dsfield);
need_ecn = tcp_ca_needs_ecn(listen_sk);
+ ecn_ok = net->ipv4.sysctl_tcp_ecn || dst_feature(dst, RTAX_FEATURE_ECN);
- if (!ect && !need_ecn && net->ipv4.sysctl_tcp_ecn)
+ if (!ect && !need_ecn && ecn_ok)
inet_rsk(req)->ecn_ok = 1;
else if (ect && need_ecn)
inet_rsk(req)->ecn_ok = 1;
@@ -5953,13 +5957,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
if (security_inet_conn_request(sk, skb, req))
goto drop_and_free;
- if (!want_cookie || tmp_opt.tstamp_ok)
- tcp_ecn_create_request(req, skb, sk);
-
- if (want_cookie) {
- isn = cookie_init_sequence(af_ops, sk, skb, &req->mss);
- req->cookie_ts = tmp_opt.tstamp_ok;
- } else if (!isn) {
+ if (!want_cookie && !isn) {
/* VJ's idea. We save last timestamp seen
* from the destination in peer table, when entering
* state TIME-WAIT, and check against it before
@@ -6007,6 +6005,15 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops,
goto drop_and_free;
}
+ tcp_ecn_create_request(req, skb, sk, dst);
+
+ if (want_cookie) {
+ isn = cookie_init_sequence(af_ops, sk, skb, &req->mss);
+ req->cookie_ts = tmp_opt.tstamp_ok;
+ if (!tmp_opt.tstamp_ok)
+ inet_rsk(req)->ecn_ok = 0;
+ }
+
tcp_rsk(req)->snt_isn = isn;
tcp_openreq_init_rwin(req, sk, dst);
fastopen = !want_cookie &&
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 9c7d7621466b..a3f72d7fc06c 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -598,7 +598,10 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
if (th->rst)
return;
- if (skb_rtable(skb)->rt_type != RTN_LOCAL)
+ /* If sk not NULL, it means we did a successful lookup and incoming
+ * route had to be correct. prequeue might have dropped our dst.
+ */
+ if (!sk && skb_rtable(skb)->rt_type != RTN_LOCAL)
return;
/* Swap the send and the receive. */
@@ -620,6 +623,7 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
arg.iov[0].iov_base = (unsigned char *)&rep;
arg.iov[0].iov_len = sizeof(rep.th);
+ net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev);
#ifdef CONFIG_TCP_MD5SIG
hash_location = tcp_parse_md5sig_option(th);
if (!sk && hash_location) {
@@ -630,7 +634,7 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
* Incoming packet is checked with md5 hash with finding key,
* no RST generated if md5 hash doesn't match.
*/
- sk1 = __inet_lookup_listener(dev_net(skb_dst(skb)->dev),
+ sk1 = __inet_lookup_listener(net,
&tcp_hashinfo, ip_hdr(skb)->saddr,
th->source, ip_hdr(skb)->daddr,
ntohs(th->source), inet_iif(skb));
@@ -678,7 +682,6 @@ static void tcp_v4_send_reset(struct sock *sk, struct sk_buff *skb)
if (sk)
arg.bound_dev_if = sk->sk_bound_dev_if;
- net = dev_net(skb_dst(skb)->dev);
arg.tos = ip_hdr(skb)->tos;
ip_send_unicast_reply(net, skb, &TCP_SKB_CB(skb)->header.h4.opt,
ip_hdr(skb)->saddr, ip_hdr(skb)->daddr,
@@ -1429,6 +1432,7 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
struct dst_entry *dst = sk->sk_rx_dst;
sock_rps_save_rxhash(sk, skb);
+ sk_mark_napi_id(sk, skb);
if (dst) {
if (inet_sk(sk)->rx_dst_ifindex != skb->skb_iif ||
dst->ops->check(dst, 0) == NULL) {
@@ -1450,6 +1454,7 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)
if (nsk != sk) {
sock_rps_save_rxhash(nsk, skb);
+ sk_mark_napi_id(sk, skb);
if (tcp_child_process(sk, nsk, skb)) {
rsk = nsk;
goto reset;
@@ -1661,7 +1666,7 @@ process:
if (sk_filter(sk, skb))
goto discard_and_relse;
- sk_mark_napi_id(sk, skb);
+ sk_incoming_cpu_update(sk);
skb->dev = NULL;
bh_lock_sock_nested(sk);
diff --git a/net/ipv4/tcp_memcontrol.c b/net/ipv4/tcp_memcontrol.c
index 1d191357bf88..272327134a1b 100644
--- a/net/ipv4/tcp_memcontrol.c
+++ b/net/ipv4/tcp_memcontrol.c
@@ -9,13 +9,13 @@
int tcp_init_cgroup(struct mem_cgroup *memcg, struct cgroup_subsys *ss)
{
/*
- * The root cgroup does not use res_counters, but rather,
+ * The root cgroup does not use page_counters, but rather,
* rely on the data already collected by the network
* subsystem
*/
- struct res_counter *res_parent = NULL;
- struct cg_proto *cg_proto, *parent_cg;
struct mem_cgroup *parent = parent_mem_cgroup(memcg);
+ struct page_counter *counter_parent = NULL;
+ struct cg_proto *cg_proto, *parent_cg;
cg_proto = tcp_prot.proto_cgroup(memcg);
if (!cg_proto)
@@ -29,9 +29,9 @@ int tcp_init_cgroup(struct mem_cgroup *memcg, struct cgroup_subsys *ss)
parent_cg = tcp_prot.proto_cgroup(parent);
if (parent_cg)
- res_parent = &parent_cg->memory_allocated;
+ counter_parent = &parent_cg->memory_allocated;
- res_counter_init(&cg_proto->memory_allocated, res_parent);
+ page_counter_init(&cg_proto->memory_allocated, counter_parent);
percpu_counter_init(&cg_proto->sockets_allocated, 0, GFP_KERNEL);
return 0;
@@ -50,7 +50,7 @@ void tcp_destroy_cgroup(struct mem_cgroup *memcg)
}
EXPORT_SYMBOL(tcp_destroy_cgroup);
-static int tcp_update_limit(struct mem_cgroup *memcg, u64 val)
+static int tcp_update_limit(struct mem_cgroup *memcg, unsigned long nr_pages)
{
struct cg_proto *cg_proto;
int i;
@@ -60,20 +60,17 @@ static int tcp_update_limit(struct mem_cgroup *memcg, u64 val)
if (!cg_proto)
return -EINVAL;
- if (val > RES_COUNTER_MAX)
- val = RES_COUNTER_MAX;
-
- ret = res_counter_set_limit(&cg_proto->memory_allocated, val);
+ ret = page_counter_limit(&cg_proto->memory_allocated, nr_pages);
if (ret)
return ret;
for (i = 0; i < 3; i++)
- cg_proto->sysctl_mem[i] = min_t(long, val >> PAGE_SHIFT,
+ cg_proto->sysctl_mem[i] = min_t(long, nr_pages,
sysctl_tcp_mem[i]);
- if (val == RES_COUNTER_MAX)
+ if (nr_pages == PAGE_COUNTER_MAX)
clear_bit(MEMCG_SOCK_ACTIVE, &cg_proto->flags);
- else if (val != RES_COUNTER_MAX) {
+ else {
/*
* The active bit needs to be written after the static_key
* update. This is what guarantees that the socket activation
@@ -102,11 +99,20 @@ static int tcp_update_limit(struct mem_cgroup *memcg, u64 val)
return 0;
}
+enum {
+ RES_USAGE,
+ RES_LIMIT,
+ RES_MAX_USAGE,
+ RES_FAILCNT,
+};
+
+static DEFINE_MUTEX(tcp_limit_mutex);
+
static ssize_t tcp_cgroup_write(struct kernfs_open_file *of,
char *buf, size_t nbytes, loff_t off)
{
struct mem_cgroup *memcg = mem_cgroup_from_css(of_css(of));
- unsigned long long val;
+ unsigned long nr_pages;
int ret = 0;
buf = strstrip(buf);
@@ -114,10 +120,12 @@ static ssize_t tcp_cgroup_write(struct kernfs_open_file *of,
switch (of_cft(of)->private) {
case RES_LIMIT:
/* see memcontrol.c */
- ret = res_counter_memparse_write_strategy(buf, &val);
+ ret = page_counter_memparse(buf, &nr_pages);
if (ret)
break;
- ret = tcp_update_limit(memcg, val);
+ mutex_lock(&tcp_limit_mutex);
+ ret = tcp_update_limit(memcg, nr_pages);
+ mutex_unlock(&tcp_limit_mutex);
break;
default:
ret = -EINVAL;
@@ -126,43 +134,36 @@ static ssize_t tcp_cgroup_write(struct kernfs_open_file *of,
return ret ?: nbytes;
}
-static u64 tcp_read_stat(struct mem_cgroup *memcg, int type, u64 default_val)
-{
- struct cg_proto *cg_proto;
-
- cg_proto = tcp_prot.proto_cgroup(memcg);
- if (!cg_proto)
- return default_val;
-
- return res_counter_read_u64(&cg_proto->memory_allocated, type);
-}
-
-static u64 tcp_read_usage(struct mem_cgroup *memcg)
-{
- struct cg_proto *cg_proto;
-
- cg_proto = tcp_prot.proto_cgroup(memcg);
- if (!cg_proto)
- return atomic_long_read(&tcp_memory_allocated) << PAGE_SHIFT;
-
- return res_counter_read_u64(&cg_proto->memory_allocated, RES_USAGE);
-}
-
static u64 tcp_cgroup_read(struct cgroup_subsys_state *css, struct cftype *cft)
{
struct mem_cgroup *memcg = mem_cgroup_from_css(css);
+ struct cg_proto *cg_proto = tcp_prot.proto_cgroup(memcg);
u64 val;
switch (cft->private) {
case RES_LIMIT:
- val = tcp_read_stat(memcg, RES_LIMIT, RES_COUNTER_MAX);
+ if (!cg_proto)
+ return PAGE_COUNTER_MAX;
+ val = cg_proto->memory_allocated.limit;
+ val *= PAGE_SIZE;
break;
case RES_USAGE:
- val = tcp_read_usage(memcg);
+ if (!cg_proto)
+ val = atomic_long_read(&tcp_memory_allocated);
+ else
+ val = page_counter_read(&cg_proto->memory_allocated);
+ val *= PAGE_SIZE;
break;
case RES_FAILCNT:
+ if (!cg_proto)
+ return 0;
+ val = cg_proto->memory_allocated.failcnt;
+ break;
case RES_MAX_USAGE:
- val = tcp_read_stat(memcg, cft->private, 0);
+ if (!cg_proto)
+ return 0;
+ val = cg_proto->memory_allocated.watermark;
+ val *= PAGE_SIZE;
break;
default:
BUG();
@@ -183,10 +184,10 @@ static ssize_t tcp_cgroup_reset(struct kernfs_open_file *of,
switch (of_cft(of)->private) {
case RES_MAX_USAGE:
- res_counter_reset_max(&cg_proto->memory_allocated);
+ page_counter_reset_watermark(&cg_proto->memory_allocated);
break;
case RES_FAILCNT:
- res_counter_reset_failcnt(&cg_proto->memory_allocated);
+ cg_proto->memory_allocated.failcnt = 0;
break;
}
diff --git a/net/ipv4/tcp_offload.c b/net/ipv4/tcp_offload.c
index 5b90f2f447a5..9d7930ba8e0f 100644
--- a/net/ipv4/tcp_offload.c
+++ b/net/ipv4/tcp_offload.c
@@ -94,9 +94,9 @@ struct sk_buff *tcp_gso_segment(struct sk_buff *skb,
SKB_GSO_GRE_CSUM |
SKB_GSO_IPIP |
SKB_GSO_SIT |
- SKB_GSO_MPLS |
SKB_GSO_UDP_TUNNEL |
SKB_GSO_UDP_TUNNEL_CSUM |
+ SKB_GSO_TUNNEL_REMCSUM |
0) ||
!(type & (SKB_GSO_TCPV4 | SKB_GSO_TCPV6))))
goto out;
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index a3d453b94747..7f18262e2326 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -333,10 +333,19 @@ static void tcp_ecn_send_synack(struct sock *sk, struct sk_buff *skb)
static void tcp_ecn_send_syn(struct sock *sk, struct sk_buff *skb)
{
struct tcp_sock *tp = tcp_sk(sk);
+ bool use_ecn = sock_net(sk)->ipv4.sysctl_tcp_ecn == 1 ||
+ tcp_ca_needs_ecn(sk);
+
+ if (!use_ecn) {
+ const struct dst_entry *dst = __sk_dst_get(sk);
+
+ if (dst && dst_feature(dst, RTAX_FEATURE_ECN))
+ use_ecn = true;
+ }
tp->ecn_flags = 0;
- if (sock_net(sk)->ipv4.sysctl_tcp_ecn == 1 ||
- tcp_ca_needs_ecn(sk)) {
+
+ if (use_ecn) {
TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_ECE | TCPHDR_CWR;
tp->ecn_flags = TCP_ECN_OK;
if (tcp_ca_needs_ecn(sk))
@@ -1515,6 +1524,27 @@ static bool tcp_nagle_check(bool partial, const struct tcp_sock *tp,
((nonagle & TCP_NAGLE_CORK) ||
(!nonagle && tp->packets_out && tcp_minshall_check(tp)));
}
+
+/* Return how many segs we'd like on a TSO packet,
+ * to send one TSO packet per ms
+ */
+static u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now)
+{
+ u32 bytes, segs;
+
+ bytes = min(sk->sk_pacing_rate >> 10,
+ sk->sk_gso_max_size - 1 - MAX_TCP_HEADER);
+
+ /* Goal is to send at least one packet per ms,
+ * not one big TSO packet every 100 ms.
+ * This preserves ACK clocking and is consistent
+ * with tcp_tso_should_defer() heuristic.
+ */
+ segs = max_t(u32, bytes / mss_now, sysctl_tcp_min_tso_segs);
+
+ return min_t(u32, segs, sk->sk_gso_max_segs);
+}
+
/* Returns the portion of skb which can be sent right away */
static unsigned int tcp_mss_split_point(const struct sock *sk,
const struct sk_buff *skb,
@@ -1553,7 +1583,7 @@ static unsigned int tcp_mss_split_point(const struct sock *sk,
static inline unsigned int tcp_cwnd_test(const struct tcp_sock *tp,
const struct sk_buff *skb)
{
- u32 in_flight, cwnd;
+ u32 in_flight, cwnd, halfcwnd;
/* Don't be strict about the congestion window for the final FIN. */
if ((TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) &&
@@ -1562,10 +1592,14 @@ static inline unsigned int tcp_cwnd_test(const struct tcp_sock *tp,
in_flight = tcp_packets_in_flight(tp);
cwnd = tp->snd_cwnd;
- if (in_flight < cwnd)
- return (cwnd - in_flight);
+ if (in_flight >= cwnd)
+ return 0;
- return 0;
+ /* For better scheduling, ensure we have at least
+ * 2 GSO packets in flight.
+ */
+ halfcwnd = max(cwnd >> 1, 1U);
+ return min(halfcwnd, cwnd - in_flight);
}
/* Initialize TSO state of a skb.
@@ -1718,7 +1752,7 @@ static int tso_fragment(struct sock *sk, struct sk_buff *skb, unsigned int len,
* This algorithm is from John Heffner.
*/
static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb,
- bool *is_cwnd_limited)
+ bool *is_cwnd_limited, u32 max_segs)
{
struct tcp_sock *tp = tcp_sk(sk);
const struct inet_connection_sock *icsk = inet_csk(sk);
@@ -1748,8 +1782,7 @@ static bool tcp_tso_should_defer(struct sock *sk, struct sk_buff *skb,
limit = min(send_win, cong_win);
/* If a full-sized TSO skb can be sent, do it. */
- if (limit >= min_t(unsigned int, sk->sk_gso_max_size,
- tp->xmit_size_goal_segs * tp->mss_cache))
+ if (limit >= max_segs * tp->mss_cache)
goto send_now;
/* Middle in queue won't get any more data, full sendable already? */
@@ -1946,6 +1979,7 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
int cwnd_quota;
int result;
bool is_cwnd_limited = false;
+ u32 max_segs;
sent_pkts = 0;
@@ -1959,6 +1993,7 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
}
}
+ max_segs = tcp_tso_autosize(sk, mss_now);
while ((skb = tcp_send_head(sk))) {
unsigned int limit;
@@ -1991,10 +2026,23 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
break;
} else {
if (!push_one &&
- tcp_tso_should_defer(sk, skb, &is_cwnd_limited))
+ tcp_tso_should_defer(sk, skb, &is_cwnd_limited,
+ max_segs))
break;
}
+ limit = mss_now;
+ if (tso_segs > 1 && !tcp_urg_mode(tp))
+ limit = tcp_mss_split_point(sk, skb, mss_now,
+ min_t(unsigned int,
+ cwnd_quota,
+ max_segs),
+ nonagle);
+
+ if (skb->len > limit &&
+ unlikely(tso_fragment(sk, skb, limit, mss_now, gfp)))
+ break;
+
/* TCP Small Queues :
* Control number of packets in qdisc/devices to two packets / or ~1 ms.
* This allows for :
@@ -2005,8 +2053,8 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
* of queued bytes to ensure line rate.
* One example is wifi aggregation (802.11 AMPDU)
*/
- limit = max_t(unsigned int, sysctl_tcp_limit_output_bytes,
- sk->sk_pacing_rate >> 10);
+ limit = max(2 * skb->truesize, sk->sk_pacing_rate >> 10);
+ limit = min_t(u32, limit, sysctl_tcp_limit_output_bytes);
if (atomic_read(&sk->sk_wmem_alloc) > limit) {
set_bit(TSQ_THROTTLED, &tp->tsq_flags);
@@ -2019,18 +2067,6 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
break;
}
- limit = mss_now;
- if (tso_segs > 1 && !tcp_urg_mode(tp))
- limit = tcp_mss_split_point(sk, skb, mss_now,
- min_t(unsigned int,
- cwnd_quota,
- sk->sk_gso_max_segs),
- nonagle);
-
- if (skb->len > limit &&
- unlikely(tso_fragment(sk, skb, limit, mss_now, gfp)))
- break;
-
if (unlikely(tcp_transmit_skb(sk, skb, 1, gfp)))
break;
@@ -2998,9 +3034,9 @@ static int tcp_send_syn_data(struct sock *sk, struct sk_buff *syn)
{
struct tcp_sock *tp = tcp_sk(sk);
struct tcp_fastopen_request *fo = tp->fastopen_req;
- int syn_loss = 0, space, i, err = 0, iovlen = fo->data->msg_iovlen;
- struct sk_buff *syn_data = NULL, *data;
+ int syn_loss = 0, space, err = 0;
unsigned long last_syn_loss = 0;
+ struct sk_buff *syn_data;
tp->rx_opt.mss_clamp = tp->advmss; /* If MSS is not cached */
tcp_fastopen_cache_get(sk, &tp->rx_opt.mss_clamp, &fo->cookie,
@@ -3031,48 +3067,40 @@ static int tcp_send_syn_data(struct sock *sk, struct sk_buff *syn)
/* limit to order-0 allocations */
space = min_t(size_t, space, SKB_MAX_HEAD(MAX_TCP_HEADER));
- syn_data = skb_copy_expand(syn, MAX_TCP_HEADER, space,
- sk->sk_allocation);
- if (syn_data == NULL)
+ syn_data = sk_stream_alloc_skb(sk, space, sk->sk_allocation);
+ if (!syn_data)
+ goto fallback;
+ syn_data->ip_summed = CHECKSUM_PARTIAL;
+ memcpy(syn_data->cb, syn->cb, sizeof(syn->cb));
+ if (unlikely(memcpy_fromiovecend(skb_put(syn_data, space),
+ fo->data->msg_iter.iov, 0, space))) {
+ kfree_skb(syn_data);
goto fallback;
+ }
- for (i = 0; i < iovlen && syn_data->len < space; ++i) {
- struct iovec *iov = &fo->data->msg_iov[i];
- unsigned char __user *from = iov->iov_base;
- int len = iov->iov_len;
+ /* No more data pending in inet_wait_for_connect() */
+ if (space == fo->size)
+ fo->data = NULL;
+ fo->copied = space;
- if (syn_data->len + len > space)
- len = space - syn_data->len;
- else if (i + 1 == iovlen)
- /* No more data pending in inet_wait_for_connect() */
- fo->data = NULL;
+ tcp_connect_queue_skb(sk, syn_data);
- if (skb_add_data(syn_data, from, len))
- goto fallback;
- }
+ err = tcp_transmit_skb(sk, syn_data, 1, sk->sk_allocation);
- /* Queue a data-only packet after the regular SYN for retransmission */
- data = pskb_copy(syn_data, sk->sk_allocation);
- if (data == NULL)
- goto fallback;
- TCP_SKB_CB(data)->seq++;
- TCP_SKB_CB(data)->tcp_flags &= ~TCPHDR_SYN;
- TCP_SKB_CB(data)->tcp_flags = (TCPHDR_ACK|TCPHDR_PSH);
- tcp_connect_queue_skb(sk, data);
- fo->copied = data->len;
-
- /* syn_data is about to be sent, we need to take current time stamps
- * for the packets that are in write queue : SYN packet and DATA
- */
- skb_mstamp_get(&syn->skb_mstamp);
- data->skb_mstamp = syn->skb_mstamp;
+ syn->skb_mstamp = syn_data->skb_mstamp;
- if (tcp_transmit_skb(sk, syn_data, 0, sk->sk_allocation) == 0) {
+ /* Now full SYN+DATA was cloned and sent (or not),
+ * remove the SYN from the original skb (syn_data)
+ * we keep in write queue in case of a retransmit, as we
+ * also have the SYN packet (with no data) in the same queue.
+ */
+ TCP_SKB_CB(syn_data)->seq++;
+ TCP_SKB_CB(syn_data)->tcp_flags = TCPHDR_ACK | TCPHDR_PSH;
+ if (!err) {
tp->syn_data = (fo->copied > 0);
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPORIGDATASENT);
goto done;
}
- syn_data = NULL;
fallback:
/* Send a regular SYN with Fast Open cookie request option */
@@ -3081,7 +3109,6 @@ fallback:
err = tcp_transmit_skb(sk, syn, 1, sk->sk_allocation);
if (err)
tp->syn_fastopen = 0;
- kfree_skb(syn_data);
done:
fo->cookie.len = -1; /* Exclude Fast Open option for SYN retries */
return err;
@@ -3101,13 +3128,10 @@ int tcp_connect(struct sock *sk)
return 0;
}
- buff = alloc_skb_fclone(MAX_TCP_HEADER + 15, sk->sk_allocation);
- if (unlikely(buff == NULL))
+ buff = sk_stream_alloc_skb(sk, 0, sk->sk_allocation);
+ if (unlikely(!buff))
return -ENOBUFS;
- /* Reserve space for headers. */
- skb_reserve(buff, MAX_TCP_HEADER);
-
tcp_init_nondata_skb(buff, tp->write_seq++, TCPHDR_SYN);
tp->retrans_stamp = tcp_time_stamp;
tcp_connect_queue_skb(sk, buff);
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index 9b21ae8b2e31..1829c7fbc77e 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -374,17 +374,19 @@ void tcp_retransmit_timer(struct sock *sk)
*/
struct inet_sock *inet = inet_sk(sk);
if (sk->sk_family == AF_INET) {
- LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("Peer %pI4:%u/%u unexpectedly shrunk window %u:%u (repaired)\n"),
- &inet->inet_daddr,
- ntohs(inet->inet_dport), inet->inet_num,
- tp->snd_una, tp->snd_nxt);
+ net_dbg_ratelimited("Peer %pI4:%u/%u unexpectedly shrunk window %u:%u (repaired)\n",
+ &inet->inet_daddr,
+ ntohs(inet->inet_dport),
+ inet->inet_num,
+ tp->snd_una, tp->snd_nxt);
}
#if IS_ENABLED(CONFIG_IPV6)
else if (sk->sk_family == AF_INET6) {
- LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("Peer %pI6:%u/%u unexpectedly shrunk window %u:%u (repaired)\n"),
- &sk->sk_v6_daddr,
- ntohs(inet->inet_dport), inet->inet_num,
- tp->snd_una, tp->snd_nxt);
+ net_dbg_ratelimited("Peer %pI6:%u/%u unexpectedly shrunk window %u:%u (repaired)\n",
+ &sk->sk_v6_daddr,
+ ntohs(inet->inet_dport),
+ inet->inet_num,
+ tp->snd_una, tp->snd_nxt);
}
#endif
if (tcp_time_stamp - tp->rcv_tstamp > TCP_RTO_MAX) {
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index cd0db5471bb5..13b4dcf86ef6 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -144,7 +144,7 @@ static int udp_lib_lport_inuse(struct net *net, __u16 num,
struct hlist_nulls_node *node;
kuid_t uid = sock_i_uid(sk);
- sk_nulls_for_each(sk2, node, &hslot->head)
+ sk_nulls_for_each(sk2, node, &hslot->head) {
if (net_eq(sock_net(sk2), net) &&
sk2 != sk &&
(bitmap || udp_sk(sk2)->udp_port_hash == num) &&
@@ -152,14 +152,13 @@ static int udp_lib_lport_inuse(struct net *net, __u16 num,
(!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if ||
sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
(!sk2->sk_reuseport || !sk->sk_reuseport ||
- !uid_eq(uid, sock_i_uid(sk2))) &&
- (*saddr_comp)(sk, sk2)) {
- if (bitmap)
- __set_bit(udp_sk(sk2)->udp_port_hash >> log,
- bitmap);
- else
+ !uid_eq(uid, sock_i_uid(sk2))) &&
+ saddr_comp(sk, sk2)) {
+ if (!bitmap)
return 1;
+ __set_bit(udp_sk(sk2)->udp_port_hash >> log, bitmap);
}
+ }
return 0;
}
@@ -168,10 +167,10 @@ static int udp_lib_lport_inuse(struct net *net, __u16 num,
* can insert/delete a socket with local_port == num
*/
static int udp_lib_lport_inuse2(struct net *net, __u16 num,
- struct udp_hslot *hslot2,
- struct sock *sk,
- int (*saddr_comp)(const struct sock *sk1,
- const struct sock *sk2))
+ struct udp_hslot *hslot2,
+ struct sock *sk,
+ int (*saddr_comp)(const struct sock *sk1,
+ const struct sock *sk2))
{
struct sock *sk2;
struct hlist_nulls_node *node;
@@ -179,7 +178,7 @@ static int udp_lib_lport_inuse2(struct net *net, __u16 num,
int res = 0;
spin_lock(&hslot2->lock);
- udp_portaddr_for_each_entry(sk2, node, &hslot2->head)
+ udp_portaddr_for_each_entry(sk2, node, &hslot2->head) {
if (net_eq(sock_net(sk2), net) &&
sk2 != sk &&
(udp_sk(sk2)->udp_port_hash == num) &&
@@ -187,11 +186,12 @@ static int udp_lib_lport_inuse2(struct net *net, __u16 num,
(!sk2->sk_bound_dev_if || !sk->sk_bound_dev_if ||
sk2->sk_bound_dev_if == sk->sk_bound_dev_if) &&
(!sk2->sk_reuseport || !sk->sk_reuseport ||
- !uid_eq(uid, sock_i_uid(sk2))) &&
- (*saddr_comp)(sk, sk2)) {
+ !uid_eq(uid, sock_i_uid(sk2))) &&
+ saddr_comp(sk, sk2)) {
res = 1;
break;
}
+ }
spin_unlock(&hslot2->lock);
return res;
}
@@ -206,8 +206,8 @@ static int udp_lib_lport_inuse2(struct net *net, __u16 num,
* with NULL address
*/
int udp_lib_get_port(struct sock *sk, unsigned short snum,
- int (*saddr_comp)(const struct sock *sk1,
- const struct sock *sk2),
+ int (*saddr_comp)(const struct sock *sk1,
+ const struct sock *sk2),
unsigned int hash2_nulladdr)
{
struct udp_hslot *hslot, *hslot2;
@@ -336,38 +336,45 @@ int udp_v4_get_port(struct sock *sk, unsigned short snum)
return udp_lib_get_port(sk, snum, ipv4_rcv_saddr_equal, hash2_nulladdr);
}
-static inline int compute_score(struct sock *sk, struct net *net, __be32 saddr,
- unsigned short hnum,
- __be16 sport, __be32 daddr, __be16 dport, int dif)
+static inline int compute_score(struct sock *sk, struct net *net,
+ __be32 saddr, unsigned short hnum, __be16 sport,
+ __be32 daddr, __be16 dport, int dif)
{
- int score = -1;
+ int score;
+ struct inet_sock *inet;
- if (net_eq(sock_net(sk), net) && udp_sk(sk)->udp_port_hash == hnum &&
- !ipv6_only_sock(sk)) {
- struct inet_sock *inet = inet_sk(sk);
+ if (!net_eq(sock_net(sk), net) ||
+ udp_sk(sk)->udp_port_hash != hnum ||
+ ipv6_only_sock(sk))
+ return -1;
- score = (sk->sk_family == PF_INET ? 2 : 1);
- if (inet->inet_rcv_saddr) {
- if (inet->inet_rcv_saddr != daddr)
- return -1;
- score += 4;
- }
- if (inet->inet_daddr) {
- if (inet->inet_daddr != saddr)
- return -1;
- score += 4;
- }
- if (inet->inet_dport) {
- if (inet->inet_dport != sport)
- return -1;
- score += 4;
- }
- if (sk->sk_bound_dev_if) {
- if (sk->sk_bound_dev_if != dif)
- return -1;
- score += 4;
- }
+ score = (sk->sk_family == PF_INET) ? 2 : 1;
+ inet = inet_sk(sk);
+
+ if (inet->inet_rcv_saddr) {
+ if (inet->inet_rcv_saddr != daddr)
+ return -1;
+ score += 4;
+ }
+
+ if (inet->inet_daddr) {
+ if (inet->inet_daddr != saddr)
+ return -1;
+ score += 4;
+ }
+
+ if (inet->inet_dport) {
+ if (inet->inet_dport != sport)
+ return -1;
+ score += 4;
+ }
+
+ if (sk->sk_bound_dev_if) {
+ if (sk->sk_bound_dev_if != dif)
+ return -1;
+ score += 4;
}
+
return score;
}
@@ -378,33 +385,39 @@ static inline int compute_score2(struct sock *sk, struct net *net,
__be32 saddr, __be16 sport,
__be32 daddr, unsigned int hnum, int dif)
{
- int score = -1;
+ int score;
+ struct inet_sock *inet;
+
+ if (!net_eq(sock_net(sk), net) ||
+ ipv6_only_sock(sk))
+ return -1;
- if (net_eq(sock_net(sk), net) && !ipv6_only_sock(sk)) {
- struct inet_sock *inet = inet_sk(sk);
+ inet = inet_sk(sk);
- if (inet->inet_rcv_saddr != daddr)
+ if (inet->inet_rcv_saddr != daddr ||
+ inet->inet_num != hnum)
+ return -1;
+
+ score = (sk->sk_family == PF_INET) ? 2 : 1;
+
+ if (inet->inet_daddr) {
+ if (inet->inet_daddr != saddr)
return -1;
- if (inet->inet_num != hnum)
+ score += 4;
+ }
+
+ if (inet->inet_dport) {
+ if (inet->inet_dport != sport)
return -1;
+ score += 4;
+ }
- score = (sk->sk_family == PF_INET ? 2 : 1);
- if (inet->inet_daddr) {
- if (inet->inet_daddr != saddr)
- return -1;
- score += 4;
- }
- if (inet->inet_dport) {
- if (inet->inet_dport != sport)
- return -1;
- score += 4;
- }
- if (sk->sk_bound_dev_if) {
- if (sk->sk_bound_dev_if != dif)
- return -1;
- score += 4;
- }
+ if (sk->sk_bound_dev_if) {
+ if (sk->sk_bound_dev_if != dif)
+ return -1;
+ score += 4;
}
+
return score;
}
@@ -1036,7 +1049,7 @@ back_from_confirm:
/* Lockless fast path for the non-corking case. */
if (!corkreq) {
- skb = ip_make_skb(sk, fl4, getfrag, msg->msg_iov, ulen,
+ skb = ip_make_skb(sk, fl4, getfrag, msg, ulen,
sizeof(struct udphdr), &ipc, &rt,
msg->msg_flags);
err = PTR_ERR(skb);
@@ -1051,7 +1064,7 @@ back_from_confirm:
/* ... which is an evident application bug. --ANK */
release_sock(sk);
- LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("cork app bug 2\n"));
+ net_dbg_ratelimited("cork app bug 2\n");
err = -EINVAL;
goto out;
}
@@ -1067,7 +1080,7 @@ back_from_confirm:
do_append_data:
up->len += ulen;
- err = ip_append_data(sk, fl4, getfrag, msg->msg_iov, ulen,
+ err = ip_append_data(sk, fl4, getfrag, msg, ulen,
sizeof(struct udphdr), &ipc, &rt,
corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
if (err)
@@ -1133,7 +1146,7 @@ int udp_sendpage(struct sock *sk, struct page *page, int offset,
if (unlikely(!up->pending)) {
release_sock(sk);
- LIMIT_NETDEBUG(KERN_DEBUG pr_fmt("udp cork app bug 3\n"));
+ net_dbg_ratelimited("udp cork app bug 3\n");
return -EINVAL;
}
@@ -1281,12 +1294,11 @@ try_again:
}
if (skb_csum_unnecessary(skb))
- err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr),
- msg->msg_iov, copied);
+ err = skb_copy_datagram_msg(skb, sizeof(struct udphdr),
+ msg, copied);
else {
- err = skb_copy_and_csum_datagram_iovec(skb,
- sizeof(struct udphdr),
- msg->msg_iov);
+ err = skb_copy_and_csum_datagram_msg(skb, sizeof(struct udphdr),
+ msg);
if (err == -EINVAL)
goto csum_copy_err;
@@ -1445,6 +1457,7 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
if (inet_sk(sk)->inet_daddr) {
sock_rps_save_rxhash(sk, skb);
sk_mark_napi_id(sk, skb);
+ sk_incoming_cpu_update(sk);
}
rc = sock_queue_rcv_skb(sk, skb);
@@ -1546,8 +1559,8 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
* provided by the application."
*/
if (up->pcrlen == 0) { /* full coverage was set */
- LIMIT_NETDEBUG(KERN_WARNING "UDPLite: partial coverage %d while full coverage %d requested\n",
- UDP_SKB_CB(skb)->cscov, skb->len);
+ net_dbg_ratelimited("UDPLite: partial coverage %d while full coverage %d requested\n",
+ UDP_SKB_CB(skb)->cscov, skb->len);
goto drop;
}
/* The next case involves violating the min. coverage requested
@@ -1557,8 +1570,8 @@ int udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
* Therefore the above ...()->partial_cov statement is essential.
*/
if (UDP_SKB_CB(skb)->cscov < up->pcrlen) {
- LIMIT_NETDEBUG(KERN_WARNING "UDPLite: coverage %d too small, need min %d\n",
- UDP_SKB_CB(skb)->cscov, up->pcrlen);
+ net_dbg_ratelimited("UDPLite: coverage %d too small, need min %d\n",
+ UDP_SKB_CB(skb)->cscov, up->pcrlen);
goto drop;
}
}
@@ -1647,7 +1660,8 @@ static void udp_sk_rx_dst_set(struct sock *sk, struct dst_entry *dst)
static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
struct udphdr *uh,
__be32 saddr, __be32 daddr,
- struct udp_table *udptable)
+ struct udp_table *udptable,
+ int proto)
{
struct sock *sk, *stack[256 / sizeof(struct sock *)];
struct hlist_nulls_node *node;
@@ -1656,6 +1670,7 @@ static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
int dif = skb->dev->ifindex;
unsigned int count = 0, offset = offsetof(typeof(*sk), sk_nulls_node);
unsigned int hash2 = 0, hash2_any = 0, use_hash2 = (hslot->count > 10);
+ bool inner_flushed = false;
if (use_hash2) {
hash2_any = udp4_portaddr_hash(net, htonl(INADDR_ANY), hnum) &
@@ -1674,6 +1689,7 @@ start_lookup:
dif, hnum)) {
if (unlikely(count == ARRAY_SIZE(stack))) {
flush_stack(stack, count, skb, ~0);
+ inner_flushed = true;
count = 0;
}
stack[count++] = sk;
@@ -1695,7 +1711,10 @@ start_lookup:
if (count) {
flush_stack(stack, count, skb, count - 1);
} else {
- kfree_skb(skb);
+ if (!inner_flushed)
+ UDP_INC_STATS_BH(net, UDP_MIB_IGNOREDMULTI,
+ proto == IPPROTO_UDPLITE);
+ consume_skb(skb);
}
return 0;
}
@@ -1777,14 +1796,13 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
if (ret > 0)
return -ret;
return 0;
- } else {
- if (rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST))
- return __udp4_lib_mcast_deliver(net, skb, uh,
- saddr, daddr, udptable);
-
- sk = __udp4_lib_lookup_skb(skb, uh->source, uh->dest, udptable);
}
+ if (rt->rt_flags & (RTCF_BROADCAST|RTCF_MULTICAST))
+ return __udp4_lib_mcast_deliver(net, skb, uh,
+ saddr, daddr, udptable, proto);
+
+ sk = __udp4_lib_lookup_skb(skb, uh->source, uh->dest, udptable);
if (sk != NULL) {
int ret;
@@ -1822,11 +1840,11 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
return 0;
short_packet:
- LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: short packet: From %pI4:%u %d/%d to %pI4:%u\n",
- proto == IPPROTO_UDPLITE ? "Lite" : "",
- &saddr, ntohs(uh->source),
- ulen, skb->len,
- &daddr, ntohs(uh->dest));
+ net_dbg_ratelimited("UDP%s: short packet: From %pI4:%u %d/%d to %pI4:%u\n",
+ proto == IPPROTO_UDPLITE ? "Lite" : "",
+ &saddr, ntohs(uh->source),
+ ulen, skb->len,
+ &daddr, ntohs(uh->dest));
goto drop;
csum_error:
@@ -1834,10 +1852,10 @@ csum_error:
* RFC1122: OK. Discards the bad packet silently (as far as
* the network is concerned, anyway) as per 4.1.3.4 (MUST).
*/
- LIMIT_NETDEBUG(KERN_DEBUG "UDP%s: bad checksum. From %pI4:%u to %pI4:%u ulen %d\n",
- proto == IPPROTO_UDPLITE ? "Lite" : "",
- &saddr, ntohs(uh->source), &daddr, ntohs(uh->dest),
- ulen);
+ net_dbg_ratelimited("UDP%s: bad checksum. From %pI4:%u to %pI4:%u ulen %d\n",
+ proto == IPPROTO_UDPLITE ? "Lite" : "",
+ &saddr, ntohs(uh->source), &daddr, ntohs(uh->dest),
+ ulen);
UDP_INC_STATS_BH(net, UDP_MIB_CSUMERRORS, proto == IPPROTO_UDPLITE);
drop:
UDP_INC_STATS_BH(net, UDP_MIB_INERRORS, proto == IPPROTO_UDPLITE);
@@ -2027,7 +2045,7 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
} else {
up->corkflag = 0;
lock_sock(sk);
- (*push_pending_frames)(sk);
+ push_pending_frames(sk);
release_sock(sk);
}
break;
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
index 6480cea7aa53..d3e537ef6b7f 100644
--- a/net/ipv4/udp_offload.c
+++ b/net/ipv4/udp_offload.c
@@ -29,7 +29,7 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
netdev_features_t features,
struct sk_buff *(*gso_inner_segment)(struct sk_buff *skb,
netdev_features_t features),
- __be16 new_protocol)
+ __be16 new_protocol, bool is_ipv6)
{
struct sk_buff *segs = ERR_PTR(-EINVAL);
u16 mac_offset = skb->mac_header;
@@ -39,7 +39,10 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
netdev_features_t enc_features;
int udp_offset, outer_hlen;
unsigned int oldlen;
- bool need_csum;
+ bool need_csum = !!(skb_shinfo(skb)->gso_type &
+ SKB_GSO_UDP_TUNNEL_CSUM);
+ bool remcsum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_TUNNEL_REMCSUM);
+ bool offload_csum = false, dont_encap = (need_csum || remcsum);
oldlen = (u16)~skb->len;
@@ -52,10 +55,13 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
skb_set_network_header(skb, skb_inner_network_offset(skb));
skb->mac_len = skb_inner_network_offset(skb);
skb->protocol = new_protocol;
+ skb->encap_hdr_csum = need_csum;
+ skb->remcsum_offload = remcsum;
- need_csum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP_TUNNEL_CSUM);
- if (need_csum)
- skb->encap_hdr_csum = 1;
+ /* Try to offload checksum if possible */
+ offload_csum = !!(need_csum &&
+ (skb->dev->features &
+ (is_ipv6 ? NETIF_F_V6_CSUM : NETIF_F_V4_CSUM)));
/* segment inner packet. */
enc_features = skb->dev->hw_enc_features & features;
@@ -72,11 +78,21 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
do {
struct udphdr *uh;
int len;
-
- skb_reset_inner_headers(skb);
- skb->encapsulation = 1;
+ __be32 delta;
+
+ if (dont_encap) {
+ skb->encapsulation = 0;
+ skb->ip_summed = CHECKSUM_NONE;
+ } else {
+ /* Only set up inner headers if we might be offloading
+ * inner checksum.
+ */
+ skb_reset_inner_headers(skb);
+ skb->encapsulation = 1;
+ }
skb->mac_len = mac_len;
+ skb->protocol = protocol;
skb_push(skb, outer_hlen);
skb_reset_mac_header(skb);
@@ -86,19 +102,36 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb,
uh = udp_hdr(skb);
uh->len = htons(len);
- if (need_csum) {
- __be32 delta = htonl(oldlen + len);
+ if (!need_csum)
+ continue;
- uh->check = ~csum_fold((__force __wsum)
- ((__force u32)uh->check +
- (__force u32)delta));
+ delta = htonl(oldlen + len);
+
+ uh->check = ~csum_fold((__force __wsum)
+ ((__force u32)uh->check +
+ (__force u32)delta));
+ if (offload_csum) {
+ skb->ip_summed = CHECKSUM_PARTIAL;
+ skb->csum_start = skb_transport_header(skb) - skb->head;
+ skb->csum_offset = offsetof(struct udphdr, check);
+ } else if (remcsum) {
+ /* Need to calculate checksum from scratch,
+ * inner checksums are never when doing
+ * remote_checksum_offload.
+ */
+
+ skb->csum = skb_checksum(skb, udp_offset,
+ skb->len - udp_offset,
+ 0);
+ uh->check = csum_fold(skb->csum);
+ if (uh->check == 0)
+ uh->check = CSUM_MANGLED_0;
+ } else {
uh->check = gso_make_checksum(skb, ~uh->check);
if (uh->check == 0)
uh->check = CSUM_MANGLED_0;
}
-
- skb->protocol = protocol;
} while ((skb = skb->next));
out:
return segs;
@@ -134,7 +167,7 @@ struct sk_buff *skb_udp_tunnel_segment(struct sk_buff *skb,
}
segs = __skb_udp_tunnel_segment(skb, features, gso_inner_segment,
- protocol);
+ protocol, is_ipv6);
out_unlock:
rcu_read_unlock();
@@ -172,9 +205,9 @@ static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb,
if (unlikely(type & ~(SKB_GSO_UDP | SKB_GSO_DODGY |
SKB_GSO_UDP_TUNNEL |
SKB_GSO_UDP_TUNNEL_CSUM |
+ SKB_GSO_TUNNEL_REMCSUM |
SKB_GSO_IPIP |
- SKB_GSO_GRE | SKB_GSO_GRE_CSUM |
- SKB_GSO_MPLS) ||
+ SKB_GSO_GRE | SKB_GSO_GRE_CSUM) ||
!(type & (SKB_GSO_UDP))))
goto out;
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 0169ccf5aa4f..f7c8bbeb27b7 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1170,6 +1170,9 @@ enum {
IPV6_SADDR_RULE_PRIVACY,
IPV6_SADDR_RULE_ORCHID,
IPV6_SADDR_RULE_PREFIX,
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+ IPV6_SADDR_RULE_NOT_OPTIMISTIC,
+#endif
IPV6_SADDR_RULE_MAX
};
@@ -1197,6 +1200,15 @@ static inline int ipv6_saddr_preferred(int type)
return 0;
}
+static inline bool ipv6_use_optimistic_addr(struct inet6_dev *idev)
+{
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+ return idev && idev->cnf.optimistic_dad && idev->cnf.use_optimistic;
+#else
+ return false;
+#endif
+}
+
static int ipv6_get_saddr_eval(struct net *net,
struct ipv6_saddr_score *score,
struct ipv6_saddr_dst *dst,
@@ -1257,10 +1269,16 @@ static int ipv6_get_saddr_eval(struct net *net,
score->scopedist = ret;
break;
case IPV6_SADDR_RULE_PREFERRED:
+ {
/* Rule 3: Avoid deprecated and optimistic addresses */
+ u8 avoid = IFA_F_DEPRECATED;
+
+ if (!ipv6_use_optimistic_addr(score->ifa->idev))
+ avoid |= IFA_F_OPTIMISTIC;
ret = ipv6_saddr_preferred(score->addr_type) ||
- !(score->ifa->flags & (IFA_F_DEPRECATED|IFA_F_OPTIMISTIC));
+ !(score->ifa->flags & avoid);
break;
+ }
#ifdef CONFIG_IPV6_MIP6
case IPV6_SADDR_RULE_HOA:
{
@@ -1306,6 +1324,14 @@ static int ipv6_get_saddr_eval(struct net *net,
ret = score->ifa->prefix_len;
score->matchlen = ret;
break;
+#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
+ case IPV6_SADDR_RULE_NOT_OPTIMISTIC:
+ /* Optimistic addresses still have lower precedence than other
+ * preferred addresses.
+ */
+ ret = !(score->ifa->flags & IFA_F_OPTIMISTIC);
+ break;
+#endif
default:
ret = 0;
}
@@ -1385,10 +1411,8 @@ int ipv6_dev_get_saddr(struct net *net, const struct net_device *dst_dev,
if (unlikely(score->addr_type == IPV6_ADDR_ANY ||
score->addr_type & IPV6_ADDR_MULTICAST)) {
- LIMIT_NETDEBUG(KERN_DEBUG
- "ADDRCONF: unspecified / multicast address "
- "assigned as unicast address on %s",
- dev->name);
+ net_dbg_ratelimited("ADDRCONF: unspecified / multicast address assigned as unicast address on %s",
+ dev->name);
continue;
}
@@ -2315,8 +2339,8 @@ ok:
else
stored_lft = 0;
if (!update_lft && !create && stored_lft) {
- const u32 minimum_lft = min(
- stored_lft, (u32)MIN_VALID_LIFETIME);
+ const u32 minimum_lft = min_t(u32,
+ stored_lft, MIN_VALID_LIFETIME);
valid_lft = max(valid_lft, minimum_lft);
/* RFC4862 Section 5.5.3e:
@@ -2519,7 +2543,8 @@ static int inet6_addr_del(struct net *net, int ifindex, u32 ifa_flags,
if (!dev)
return -ENODEV;
- if ((idev = __in6_dev_get(dev)) == NULL)
+ idev = __in6_dev_get(dev);
+ if (idev == NULL)
return -ENXIO;
read_lock_bh(&idev->lock);
@@ -2666,7 +2691,8 @@ static void init_loopback(struct net_device *dev)
ASSERT_RTNL();
- if ((idev = ipv6_find_idev(dev)) == NULL) {
+ idev = ipv6_find_idev(dev);
+ if (idev == NULL) {
pr_debug("%s: add_dev failed\n", __func__);
return;
}
@@ -2789,7 +2815,8 @@ static void addrconf_sit_config(struct net_device *dev)
* our v4 addrs in the tunnel
*/
- if ((idev = ipv6_find_idev(dev)) == NULL) {
+ idev = ipv6_find_idev(dev);
+ if (idev == NULL) {
pr_debug("%s: add_dev failed\n", __func__);
return;
}
@@ -2813,7 +2840,8 @@ static void addrconf_gre_config(struct net_device *dev)
ASSERT_RTNL();
- if ((idev = ipv6_find_idev(dev)) == NULL) {
+ idev = ipv6_find_idev(dev);
+ if (idev == NULL) {
pr_debug("%s: add_dev failed\n", __func__);
return;
}
@@ -3222,8 +3250,15 @@ static void addrconf_dad_begin(struct inet6_ifaddr *ifp)
* Optimistic nodes can start receiving
* Frames right away
*/
- if (ifp->flags & IFA_F_OPTIMISTIC)
+ if (ifp->flags & IFA_F_OPTIMISTIC) {
ip6_ins_rt(ifp->rt);
+ if (ipv6_use_optimistic_addr(idev)) {
+ /* Because optimistic nodes can use this address,
+ * notify listeners. If DAD fails, RTM_DELADDR is sent.
+ */
+ ipv6_ifa_notify(RTM_NEWADDR, ifp);
+ }
+ }
addrconf_dad_kick(ifp);
out:
@@ -4330,6 +4365,7 @@ static inline void ipv6_store_devconf(struct ipv6_devconf *cnf,
array[DEVCONF_ACCEPT_SOURCE_ROUTE] = cnf->accept_source_route;
#ifdef CONFIG_IPV6_OPTIMISTIC_DAD
array[DEVCONF_OPTIMISTIC_DAD] = cnf->optimistic_dad;
+ array[DEVCONF_USE_OPTIMISTIC] = cnf->use_optimistic;
#endif
#ifdef CONFIG_IPV6_MROUTE
array[DEVCONF_MC_FORWARDING] = cnf->mc_forwarding;
@@ -5156,6 +5192,14 @@ static struct addrconf_sysctl_table
.proc_handler = proc_dointvec,
},
+ {
+ .procname = "use_optimistic",
+ .data = &ipv6_devconf.use_optimistic,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+
+ },
#endif
#ifdef CONFIG_IPV6_MROUTE
{
@@ -5336,10 +5380,8 @@ static void __net_exit addrconf_exit_net(struct net *net)
__addrconf_sysctl_unregister(net->ipv6.devconf_dflt);
__addrconf_sysctl_unregister(net->ipv6.devconf_all);
#endif
- if (!net_eq(net, &init_net)) {
- kfree(net->ipv6.devconf_dflt);
- kfree(net->ipv6.devconf_all);
- }
+ kfree(net->ipv6.devconf_dflt);
+ kfree(net->ipv6.devconf_all);
}
static struct pernet_operations addrconf_ops = {
diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c
index 6d16eb0e0c7f..a6727add2624 100644
--- a/net/ipv6/ah6.c
+++ b/net/ipv6/ah6.c
@@ -272,10 +272,9 @@ static int ipv6_clear_mutable_options(struct ipv6hdr *iph, int len, int dir)
ipv6_rearrange_destopt(iph, exthdr.opth);
case NEXTHDR_HOP:
if (!zero_out_mutable_opts(exthdr.opth)) {
- LIMIT_NETDEBUG(
- KERN_WARNING "overrun %sopts\n",
- nexthdr == NEXTHDR_HOP ?
- "hop" : "dest");
+ net_dbg_ratelimited("overrun %sopts\n",
+ nexthdr == NEXTHDR_HOP ?
+ "hop" : "dest");
return -EINVAL;
}
break;
@@ -354,7 +353,8 @@ static int ah6_output(struct xfrm_state *x, struct sk_buff *skb)
ahp = x->data;
ahash = ahp->ahash;
- if ((err = skb_cow_data(skb, 0, &trailer)) < 0)
+ err = skb_cow_data(skb, 0, &trailer);
+ if (err < 0)
goto out;
nfrags = err;
@@ -560,8 +560,8 @@ static int ah6_input(struct xfrm_state *x, struct sk_buff *skb)
if (!pskb_may_pull(skb, ah_hlen))
goto out;
-
- if ((err = skb_cow_data(skb, 0, &trailer)) < 0)
+ err = skb_cow_data(skb, 0, &trailer);
+ if (err < 0)
goto out;
nfrags = err;
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index 2cdc38338be3..100c589a2a6c 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -325,6 +325,16 @@ void ipv6_local_rxpmtu(struct sock *sk, struct flowi6 *fl6, u32 mtu)
kfree_skb(skb);
}
+static void ip6_datagram_prepare_pktinfo_errqueue(struct sk_buff *skb)
+{
+ int ifindex = skb->dev ? skb->dev->ifindex : -1;
+
+ if (skb->protocol == htons(ETH_P_IPV6))
+ IP6CB(skb)->iif = ifindex;
+ else
+ PKTINFO_SKB_CB(skb)->ipi_ifindex = ifindex;
+}
+
/*
* Handle MSG_ERRQUEUE
*/
@@ -351,7 +361,7 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
msg->msg_flags |= MSG_TRUNC;
copied = len;
}
- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
if (err)
goto out_free_skb;
@@ -388,8 +398,12 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
sin->sin6_family = AF_INET6;
sin->sin6_flowinfo = 0;
sin->sin6_port = 0;
- if (np->rxopt.all)
+ if (np->rxopt.all) {
+ if (serr->ee.ee_origin != SO_EE_ORIGIN_ICMP &&
+ serr->ee.ee_origin != SO_EE_ORIGIN_ICMP6)
+ ip6_datagram_prepare_pktinfo_errqueue(skb);
ip6_datagram_recv_common_ctl(sk, msg, skb);
+ }
if (skb->protocol == htons(ETH_P_IPV6)) {
sin->sin6_addr = ipv6_hdr(skb)->saddr;
if (np->rxopt.all)
@@ -445,7 +459,7 @@ int ipv6_recv_rxpmtu(struct sock *sk, struct msghdr *msg, int len,
msg->msg_flags |= MSG_TRUNC;
copied = len;
}
- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
if (err)
goto out_free_skb;
@@ -491,7 +505,10 @@ void ip6_datagram_recv_common_ctl(struct sock *sk, struct msghdr *msg,
ipv6_addr_set_v4mapped(ip_hdr(skb)->daddr,
&src_info.ipi6_addr);
}
- put_cmsg(msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info);
+
+ if (src_info.ipi6_ifindex >= 0)
+ put_cmsg(msg, SOL_IPV6, IPV6_PKTINFO,
+ sizeof(src_info), &src_info);
}
}
@@ -640,7 +657,7 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk,
int len;
int err = 0;
- for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ for_each_cmsghdr(cmsg, msg) {
int addr_type;
if (!CMSG_OK(msg, cmsg)) {
@@ -893,8 +910,8 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk,
break;
}
default:
- LIMIT_NETDEBUG(KERN_DEBUG "invalid cmsg type: %d\n",
- cmsg->cmsg_type);
+ net_dbg_ratelimited("invalid cmsg type: %d\n",
+ cmsg->cmsg_type);
err = -EINVAL;
goto exit_f;
}
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
index 83fc3a385a26..e48f2c7c5c59 100644
--- a/net/ipv6/esp6.c
+++ b/net/ipv6/esp6.c
@@ -286,8 +286,8 @@ static int esp_input_done2(struct sk_buff *skb, int err)
err = -EINVAL;
padlen = nexthdr[0];
if (padlen + 2 + alen >= elen) {
- LIMIT_NETDEBUG(KERN_WARNING "ipsec esp packet is garbage "
- "padlen=%d, elen=%d\n", padlen + 2, elen - alen);
+ net_dbg_ratelimited("ipsec esp packet is garbage padlen=%d, elen=%d\n",
+ padlen + 2, elen - alen);
goto out;
}
@@ -345,7 +345,8 @@ static int esp6_input(struct xfrm_state *x, struct sk_buff *skb)
goto out;
}
- if ((nfrags = skb_cow_data(skb, 0, &trailer)) < 0) {
+ nfrags = skb_cow_data(skb, 0, &trailer);
+ if (nfrags < 0) {
ret = -EINVAL;
goto out;
}
@@ -544,12 +545,12 @@ static int esp_init_authenc(struct xfrm_state *x)
BUG_ON(!aalg_desc);
err = -EINVAL;
- if (aalg_desc->uinfo.auth.icv_fullbits/8 !=
+ if (aalg_desc->uinfo.auth.icv_fullbits / 8 !=
crypto_aead_authsize(aead)) {
- NETDEBUG(KERN_INFO "ESP: %s digestsize %u != %hu\n",
- x->aalg->alg_name,
- crypto_aead_authsize(aead),
- aalg_desc->uinfo.auth.icv_fullbits/8);
+ pr_info("ESP: %s digestsize %u != %hu\n",
+ x->aalg->alg_name,
+ crypto_aead_authsize(aead),
+ aalg_desc->uinfo.auth.icv_fullbits / 8);
goto free_key;
}
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index bfde361b6134..a7bbbe45570b 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c
@@ -47,7 +47,7 @@
#include <net/xfrm.h>
#endif
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
/*
* Parsing tlv encoded headers.
@@ -184,7 +184,7 @@ static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)
int ret;
if (opt->dsthao) {
- LIMIT_NETDEBUG(KERN_DEBUG "hao duplicated\n");
+ net_dbg_ratelimited("hao duplicated\n");
goto discard;
}
opt->dsthao = opt->dst1;
@@ -193,14 +193,14 @@ static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)
hao = (struct ipv6_destopt_hao *)(skb_network_header(skb) + optoff);
if (hao->length != 16) {
- LIMIT_NETDEBUG(
- KERN_DEBUG "hao invalid option length = %d\n", hao->length);
+ net_dbg_ratelimited("hao invalid option length = %d\n",
+ hao->length);
goto discard;
}
if (!(ipv6_addr_type(&hao->addr) & IPV6_ADDR_UNICAST)) {
- LIMIT_NETDEBUG(
- KERN_DEBUG "hao is not an unicast addr: %pI6\n", &hao->addr);
+ net_dbg_ratelimited("hao is not an unicast addr: %pI6\n",
+ &hao->addr);
goto discard;
}
@@ -551,8 +551,8 @@ static bool ipv6_hop_ra(struct sk_buff *skb, int optoff)
memcpy(&IP6CB(skb)->ra, nh + optoff + 2, sizeof(IP6CB(skb)->ra));
return true;
}
- LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_ra: wrong RA length %d\n",
- nh[optoff + 1]);
+ net_dbg_ratelimited("ipv6_hop_ra: wrong RA length %d\n",
+ nh[optoff + 1]);
kfree_skb(skb);
return false;
}
@@ -566,8 +566,8 @@ static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
u32 pkt_len;
if (nh[optoff + 1] != 4 || (optoff & 3) != 2) {
- LIMIT_NETDEBUG(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n",
- nh[optoff+1]);
+ net_dbg_ratelimited("ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n",
+ nh[optoff+1]);
IP6_INC_STATS_BH(net, ipv6_skb_idev(skb),
IPSTATS_MIB_INHDRERRORS);
goto drop;
diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 97ae70077a4f..d674152b6ede 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -243,7 +243,8 @@ int icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6,
struct icmp6hdr *icmp6h;
int err = 0;
- if ((skb = skb_peek(&sk->sk_write_queue)) == NULL)
+ skb = skb_peek(&sk->sk_write_queue);
+ if (skb == NULL)
goto out;
icmp6h = icmp6_hdr(skb);
@@ -338,7 +339,7 @@ static struct dst_entry *icmpv6_route_lookup(struct net *net,
* anycast.
*/
if (((struct rt6_info *)dst)->rt6i_flags & RTF_ANYCAST) {
- LIMIT_NETDEBUG(KERN_DEBUG "icmp6_send: acast source\n");
+ net_dbg_ratelimited("icmp6_send: acast source\n");
dst_release(dst);
return ERR_PTR(-EINVAL);
}
@@ -452,7 +453,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
* and anycast addresses will be checked later.
*/
if ((addr_type == IPV6_ADDR_ANY) || (addr_type & IPV6_ADDR_MULTICAST)) {
- LIMIT_NETDEBUG(KERN_DEBUG "icmp6_send: addr_any/mcast source\n");
+ net_dbg_ratelimited("icmp6_send: addr_any/mcast source\n");
return;
}
@@ -460,7 +461,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
* Never answer to a ICMP packet.
*/
if (is_ineligible(skb)) {
- LIMIT_NETDEBUG(KERN_DEBUG "icmp6_send: no reply to icmp error\n");
+ net_dbg_ratelimited("icmp6_send: no reply to icmp error\n");
return;
}
@@ -509,7 +510,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
len = skb->len - msg.offset;
len = min_t(unsigned int, len, IPV6_MIN_MTU - sizeof(struct ipv6hdr) - sizeof(struct icmp6hdr));
if (len < 0) {
- LIMIT_NETDEBUG(KERN_DEBUG "icmp: len problem\n");
+ net_dbg_ratelimited("icmp: len problem\n");
goto out_dst_release;
}
@@ -679,6 +680,7 @@ static int icmpv6_rcv(struct sk_buff *skb)
const struct in6_addr *saddr, *daddr;
struct icmp6hdr *hdr;
u8 type;
+ bool success = false;
if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) {
struct sec_path *sp = skb_sec_path(skb);
@@ -706,9 +708,8 @@ static int icmpv6_rcv(struct sk_buff *skb)
daddr = &ipv6_hdr(skb)->daddr;
if (skb_checksum_validate(skb, IPPROTO_ICMPV6, ip6_compute_pseudo)) {
- LIMIT_NETDEBUG(KERN_DEBUG
- "ICMPv6 checksum failed [%pI6c > %pI6c]\n",
- saddr, daddr);
+ net_dbg_ratelimited("ICMPv6 checksum failed [%pI6c > %pI6c]\n",
+ saddr, daddr);
goto csum_error;
}
@@ -727,7 +728,7 @@ static int icmpv6_rcv(struct sk_buff *skb)
break;
case ICMPV6_ECHO_REPLY:
- ping_rcv(skb);
+ success = ping_rcv(skb);
break;
case ICMPV6_PKT_TOOBIG:
@@ -781,7 +782,7 @@ static int icmpv6_rcv(struct sk_buff *skb)
if (type & ICMPV6_INFOMSG_MASK)
break;
- LIMIT_NETDEBUG(KERN_DEBUG "icmpv6: msg of unknown type\n");
+ net_dbg_ratelimited("icmpv6: msg of unknown type\n");
/*
* error of unknown type.
@@ -791,7 +792,14 @@ static int icmpv6_rcv(struct sk_buff *skb)
icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu);
}
- kfree_skb(skb);
+ /* until the v6 path can be better sorted assume failure and
+ * preserve the status quo behaviour for the rest of the paths to here
+ */
+ if (success)
+ consume_skb(skb);
+ else
+ kfree_skb(skb);
+
return 0;
csum_error:
@@ -1009,4 +1017,3 @@ struct ctl_table * __net_init ipv6_icmp_sysctl_init(struct net *net)
return table;
}
#endif
-
diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c
index 3dd7d4ebd7cd..2f780cba6e12 100644
--- a/net/ipv6/ip6_flowlabel.c
+++ b/net/ipv6/ip6_flowlabel.c
@@ -654,7 +654,11 @@ release:
goto done;
err = -ENOMEM;
- if (sfl1 == NULL || (err = mem_check(sk)) != 0)
+ if (sfl1 == NULL)
+ goto done;
+
+ err = mem_check(sk);
+ if (err != 0)
goto done;
fl1 = fl_intern(net, fl, freq.flr_label);
@@ -769,10 +773,9 @@ static void ip6fl_seq_stop(struct seq_file *seq, void *v)
static int ip6fl_seq_show(struct seq_file *seq, void *v)
{
struct ip6fl_iter_state *state = ip6fl_seq_private(seq);
- if (v == SEQ_START_TOKEN)
- seq_printf(seq, "%-5s %-1s %-6s %-6s %-6s %-8s %-32s %s\n",
- "Label", "S", "Owner", "Users", "Linger", "Expires", "Dst", "Opt");
- else {
+ if (v == SEQ_START_TOKEN) {
+ seq_puts(seq, "Label S Owner Users Linger Expires Dst Opt\n");
+ } else {
struct ip6_flowlabel *fl = v;
seq_printf(seq,
"%05X %-1d %-6d %-6d %-6ld %-8ld %pi6 %-4d\n",
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index 12c3c8ef3849..13cda4c6313b 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -502,11 +502,11 @@ static int ip6gre_rcv(struct sk_buff *skb)
skb->protocol = gre_proto;
/* WCCP version 1 and 2 protocol decoding.
- * - Change protocol to IP
+ * - Change protocol to IPv6
* - When dealing with WCCPv2, Skip extra 4 bytes in GRE header
*/
if (flags == 0 && gre_proto == htons(ETH_P_WCCP)) {
- skb->protocol = htons(ETH_P_IP);
+ skb->protocol = htons(ETH_P_IPV6);
if ((*(h + offset) & 0xF0) != 0x40)
offset += 4;
}
@@ -902,7 +902,7 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb,
struct net_device_stats *stats = &t->dev->stats;
int ret;
- if (!ip6_tnl_xmit_ctl(t))
+ if (!ip6_tnl_xmit_ctl(t, &t->parms.laddr, &t->parms.raddr))
goto tx_err;
switch (skb->protocol) {
@@ -961,8 +961,6 @@ static void ip6gre_tnl_link_config(struct ip6_tnl *t, int set_mtu)
else
dev->flags &= ~IFF_POINTOPOINT;
- dev->iflink = p->link;
-
/* Precalculate GRE options length */
if (t->parms.o_flags&(GRE_CSUM|GRE_KEY|GRE_SEQ)) {
if (t->parms.o_flags&GRE_CSUM)
@@ -1272,6 +1270,7 @@ static int ip6gre_tunnel_init(struct net_device *dev)
u64_stats_init(&ip6gre_tunnel_stats->syncp);
}
+ dev->iflink = tunnel->parms.link;
return 0;
}
@@ -1481,6 +1480,8 @@ static int ip6gre_tap_init(struct net_device *dev)
if (!dev->tstats)
return -ENOMEM;
+ dev->iflink = tunnel->parms.link;
+
return 0;
}
diff --git a/net/ipv6/ip6_input.c b/net/ipv6/ip6_input.c
index a3084ab5df6c..aacdcb4dc762 100644
--- a/net/ipv6/ip6_input.c
+++ b/net/ipv6/ip6_input.c
@@ -220,7 +220,8 @@ resubmit:
nexthdr = skb_network_header(skb)[nhoff];
raw = raw6_local_deliver(skb, nexthdr);
- if ((ipprot = rcu_dereference(inet6_protos[nexthdr])) != NULL) {
+ ipprot = rcu_dereference(inet6_protos[nexthdr]);
+ if (ipprot != NULL) {
int ret;
if (ipprot->flags & INET6_PROTO_FINAL) {
diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c
index a071563a7e6e..46d452a56d3e 100644
--- a/net/ipv6/ip6_offload.c
+++ b/net/ipv6/ip6_offload.c
@@ -69,7 +69,8 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
int nhoff;
if (unlikely(skb_shinfo(skb)->gso_type &
- ~(SKB_GSO_UDP |
+ ~(SKB_GSO_TCPV4 |
+ SKB_GSO_UDP |
SKB_GSO_DODGY |
SKB_GSO_TCP_ECN |
SKB_GSO_GRE |
@@ -78,7 +79,7 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb,
SKB_GSO_SIT |
SKB_GSO_UDP_TUNNEL |
SKB_GSO_UDP_TUNNEL_CSUM |
- SKB_GSO_MPLS |
+ SKB_GSO_TUNNEL_REMCSUM |
SKB_GSO_TCPV6 |
0)))
goto out;
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 8e950c250ada..ce69a12ae48c 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -747,13 +747,11 @@ slow_path:
if (len < left) {
len &= ~7;
}
- /*
- * Allocate buffer.
- */
- if ((frag = alloc_skb(len + hlen + sizeof(struct frag_hdr) +
- hroom + troom, GFP_ATOMIC)) == NULL) {
- NETDEBUG(KERN_INFO "IPv6: frag: no memory for new fragment!\n");
+ /* Allocate buffer */
+ frag = alloc_skb(len + hlen + sizeof(struct frag_hdr) +
+ hroom + troom, GFP_ATOMIC);
+ if (!frag) {
IP6_INC_STATS(net, ip6_dst_idev(skb_dst(skb)),
IPSTATS_MIB_FRAGFAILS);
err = -ENOMEM;
@@ -900,7 +898,8 @@ static int ip6_dst_lookup_tail(struct sock *sk,
if (*dst == NULL)
*dst = ip6_route_output(net, sk, fl6);
- if ((err = (*dst)->error))
+ err = (*dst)->error;
+ if (err)
goto out_err_release;
if (ipv6_addr_any(&fl6->saddr)) {
@@ -948,7 +947,8 @@ static int ip6_dst_lookup_tail(struct sock *sk,
memcpy(&fl_gw6, fl6, sizeof(struct flowi6));
memset(&fl_gw6.daddr, 0, sizeof(struct in6_addr));
*dst = ip6_route_output(net, sk, &fl_gw6);
- if ((err = (*dst)->error))
+ err = (*dst)->error;
+ if (err)
goto out_err_release;
}
}
@@ -1056,7 +1056,8 @@ static inline int ip6_ufo_append_data(struct sock *sk,
* device, so create one single skb packet containing complete
* udp datagram
*/
- if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) {
+ skb = skb_peek_tail(&sk->sk_write_queue);
+ if (skb == NULL) {
skb = sock_alloc_send_skb(sk,
hh_len + fragheaderlen + transhdrlen + 20,
(flags & MSG_DONTWAIT), &err);
@@ -1536,7 +1537,8 @@ int ip6_push_pending_frames(struct sock *sk)
unsigned char proto = fl6->flowi6_proto;
int err = 0;
- if ((skb = __skb_dequeue(&sk->sk_write_queue)) == NULL)
+ skb = __skb_dequeue(&sk->sk_write_queue);
+ if (skb == NULL)
goto out;
tail_skb = &(skb_shinfo(skb)->frag_list);
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 9409887fb664..92b3da571980 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -183,6 +183,7 @@ ip6_tnl_lookup(struct net *net, const struct in6_addr *remote, const struct in6_
unsigned int hash = HASH(remote, local);
struct ip6_tnl *t;
struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
+ struct in6_addr any;
for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
if (ipv6_addr_equal(local, &t->parms.laddr) &&
@@ -190,6 +191,22 @@ ip6_tnl_lookup(struct net *net, const struct in6_addr *remote, const struct in6_
(t->dev->flags & IFF_UP))
return t;
}
+
+ memset(&any, 0, sizeof(any));
+ hash = HASH(&any, local);
+ for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
+ if (ipv6_addr_equal(local, &t->parms.laddr) &&
+ (t->dev->flags & IFF_UP))
+ return t;
+ }
+
+ hash = HASH(remote, &any);
+ for_each_ip6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
+ if (ipv6_addr_equal(remote, &t->parms.raddr) &&
+ (t->dev->flags & IFF_UP))
+ return t;
+ }
+
t = rcu_dereference(ip6n->tnls_wc[0]);
if (t && (t->dev->flags & IFF_UP))
return t;
@@ -272,9 +289,6 @@ static int ip6_tnl_create2(struct net_device *dev)
int err;
t = netdev_priv(dev);
- err = ip6_tnl_dev_init(dev);
- if (err < 0)
- goto out;
err = register_netdevice(dev);
if (err < 0)
@@ -477,6 +491,7 @@ ip6_tnl_err(struct sk_buff *skb, __u8 ipproto, struct inet6_skb_parm *opt,
int rel_msg = 0;
u8 rel_type = ICMPV6_DEST_UNREACH;
u8 rel_code = ICMPV6_ADDR_UNREACH;
+ u8 tproto;
__u32 rel_info = 0;
__u16 len;
int err = -ENOENT;
@@ -486,11 +501,12 @@ ip6_tnl_err(struct sk_buff *skb, __u8 ipproto, struct inet6_skb_parm *opt,
processing of the error. */
rcu_read_lock();
- if ((t = ip6_tnl_lookup(dev_net(skb->dev), &ipv6h->daddr,
- &ipv6h->saddr)) == NULL)
+ t = ip6_tnl_lookup(dev_net(skb->dev), &ipv6h->daddr, &ipv6h->saddr);
+ if (t == NULL)
goto out;
- if (t->parms.proto != ipproto && t->parms.proto != 0)
+ tproto = ACCESS_ONCE(t->parms.proto);
+ if (tproto != ipproto && tproto != 0)
goto out;
err = 0;
@@ -534,7 +550,8 @@ ip6_tnl_err(struct sk_buff *skb, __u8 ipproto, struct inet6_skb_parm *opt,
mtu = IPV6_MIN_MTU;
t->dev->mtu = mtu;
- if ((len = sizeof(*ipv6h) + ntohs(ipv6h->payload_len)) > mtu) {
+ len = sizeof(*ipv6h) + ntohs(ipv6h->payload_len);
+ if (len > mtu) {
rel_type = ICMPV6_PKT_TOOBIG;
rel_code = 0;
rel_info = mtu;
@@ -791,15 +808,16 @@ static int ip6_tnl_rcv(struct sk_buff *skb, __u16 protocol,
{
struct ip6_tnl *t;
const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
+ u8 tproto;
int err;
rcu_read_lock();
-
- if ((t = ip6_tnl_lookup(dev_net(skb->dev), &ipv6h->saddr,
- &ipv6h->daddr)) != NULL) {
+ t = ip6_tnl_lookup(dev_net(skb->dev), &ipv6h->saddr, &ipv6h->daddr);
+ if (t != NULL) {
struct pcpu_sw_netstats *tstats;
- if (t->parms.proto != ipproto && t->parms.proto != 0) {
+ tproto = ACCESS_ONCE(t->parms.proto);
+ if (tproto != ipproto && tproto != 0) {
rcu_read_unlock();
goto discard;
}
@@ -905,24 +923,28 @@ ip6_tnl_addr_conflict(const struct ip6_tnl *t, const struct ipv6hdr *hdr)
return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr);
}
-int ip6_tnl_xmit_ctl(struct ip6_tnl *t)
+int ip6_tnl_xmit_ctl(struct ip6_tnl *t,
+ const struct in6_addr *laddr,
+ const struct in6_addr *raddr)
{
struct __ip6_tnl_parm *p = &t->parms;
int ret = 0;
struct net *net = t->net;
- if (p->flags & IP6_TNL_F_CAP_XMIT) {
+ if ((p->flags & IP6_TNL_F_CAP_XMIT) ||
+ ((p->flags & IP6_TNL_F_CAP_PER_PACKET) &&
+ (ip6_tnl_get_cap(t, laddr, raddr) & IP6_TNL_F_CAP_XMIT))) {
struct net_device *ldev = NULL;
rcu_read_lock();
if (p->link)
ldev = dev_get_by_index_rcu(net, p->link);
- if (unlikely(!ipv6_chk_addr(net, &p->laddr, ldev, 0)))
+ if (unlikely(!ipv6_chk_addr(net, laddr, ldev, 0)))
pr_warn("%s xmit: Local address not yet configured!\n",
p->name);
- else if (!ipv6_addr_is_multicast(&p->raddr) &&
- unlikely(ipv6_chk_addr(net, &p->raddr, NULL, 0)))
+ else if (!ipv6_addr_is_multicast(raddr) &&
+ unlikely(ipv6_chk_addr(net, raddr, NULL, 0)))
pr_warn("%s xmit: Routing loop! Remote address found on this node!\n",
p->name);
else
@@ -971,8 +993,34 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,
u8 proto;
int err = -1;
- if (!fl6->flowi6_mark)
+ /* NBMA tunnel */
+ if (ipv6_addr_any(&t->parms.raddr)) {
+ struct in6_addr *addr6;
+ struct neighbour *neigh;
+ int addr_type;
+
+ if (!skb_dst(skb))
+ goto tx_err_link_failure;
+
+ neigh = dst_neigh_lookup(skb_dst(skb),
+ &ipv6_hdr(skb)->daddr);
+ if (!neigh)
+ goto tx_err_link_failure;
+
+ addr6 = (struct in6_addr *)&neigh->primary_key;
+ addr_type = ipv6_addr_type(addr6);
+
+ if (addr_type == IPV6_ADDR_ANY)
+ addr6 = &ipv6_hdr(skb)->daddr;
+
+ memcpy(&fl6->daddr, addr6, sizeof(fl6->daddr));
+ neigh_release(neigh);
+ } else if (!fl6->flowi6_mark)
dst = ip6_tnl_dst_check(t);
+
+ if (!ip6_tnl_xmit_ctl(t, &fl6->saddr, &fl6->daddr))
+ goto tx_err_link_failure;
+
if (!dst) {
ndst = ip6_route_output(net, NULL, fl6);
@@ -1021,7 +1069,8 @@ static int ip6_tnl_xmit2(struct sk_buff *skb,
(skb_cloned(skb) && !skb_clone_writable(skb, 0))) {
struct sk_buff *new_skb;
- if (!(new_skb = skb_realloc_headroom(skb, max_headroom)))
+ new_skb = skb_realloc_headroom(skb, max_headroom);
+ if (!new_skb)
goto tx_err_dst_release;
if (skb->sk)
@@ -1078,10 +1127,11 @@ ip4ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
struct flowi6 fl6;
__u8 dsfield;
__u32 mtu;
+ u8 tproto;
int err;
- if ((t->parms.proto != IPPROTO_IPIP && t->parms.proto != 0) ||
- !ip6_tnl_xmit_ctl(t))
+ tproto = ACCESS_ONCE(t->parms.proto);
+ if (tproto != IPPROTO_IPIP && tproto != 0)
return -1;
if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT))
@@ -1120,10 +1170,12 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
struct flowi6 fl6;
__u8 dsfield;
__u32 mtu;
+ u8 tproto;
int err;
- if ((t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) ||
- !ip6_tnl_xmit_ctl(t) || ip6_tnl_addr_conflict(t, ipv6h))
+ tproto = ACCESS_ONCE(t->parms.proto);
+ if ((tproto != IPPROTO_IPV6 && tproto != 0) ||
+ ip6_tnl_addr_conflict(t, ipv6h))
return -1;
offset = ip6_tnl_parse_tlv_enc_lim(skb, skb_network_header(skb));
@@ -1285,6 +1337,14 @@ static int ip6_tnl_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p)
return err;
}
+static int ip6_tnl0_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p)
+{
+ /* for default tnl0 device allow to change only the proto */
+ t->parms.proto = p->proto;
+ netdev_state_change(t->dev);
+ return 0;
+}
+
static void
ip6_tnl_parm_from_user(struct __ip6_tnl_parm *p, const struct ip6_tnl_parm *u)
{
@@ -1384,7 +1444,7 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
ip6_tnl_parm_from_user(&p1, &p);
t = ip6_tnl_locate(net, &p1, cmd == SIOCADDTUNNEL);
- if (dev != ip6n->fb_tnl_dev && cmd == SIOCCHGTUNNEL) {
+ if (cmd == SIOCCHGTUNNEL) {
if (t != NULL) {
if (t->dev != dev) {
err = -EEXIST;
@@ -1392,8 +1452,10 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
}
} else
t = netdev_priv(dev);
-
- err = ip6_tnl_update(t, &p1);
+ if (dev == ip6n->fb_tnl_dev)
+ err = ip6_tnl0_update(t, &p1);
+ else
+ err = ip6_tnl_update(t, &p1);
}
if (t) {
err = 0;
@@ -1462,6 +1524,7 @@ ip6_tnl_change_mtu(struct net_device *dev, int new_mtu)
static const struct net_device_ops ip6_tnl_netdev_ops = {
+ .ndo_init = ip6_tnl_dev_init,
.ndo_uninit = ip6_tnl_dev_uninit,
.ndo_start_xmit = ip6_tnl_xmit,
.ndo_do_ioctl = ip6_tnl_ioctl,
@@ -1546,16 +1609,10 @@ static int __net_init ip6_fb_tnl_dev_init(struct net_device *dev)
struct ip6_tnl *t = netdev_priv(dev);
struct net *net = dev_net(dev);
struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
- int err = ip6_tnl_dev_init_gen(dev);
-
- if (err)
- return err;
t->parms.proto = IPPROTO_IPV6;
dev_hold(dev);
- ip6_tnl_link_config(t);
-
rcu_assign_pointer(ip6n->tnls_wc[0], t);
return 0;
}
diff --git a/net/ipv6/ip6_udp_tunnel.c b/net/ipv6/ip6_udp_tunnel.c
index b04ed72c4542..8db6c98fe218 100644
--- a/net/ipv6/ip6_udp_tunnel.c
+++ b/net/ipv6/ip6_udp_tunnel.c
@@ -79,15 +79,13 @@ int udp_tunnel6_xmit_skb(struct socket *sock, struct dst_entry *dst,
uh->source = src_port;
uh->len = htons(skb->len);
- uh->check = 0;
memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED
| IPSKB_REROUTED);
skb_dst_set(skb, dst);
- udp6_set_csum(udp_get_no_check6_tx(sk), skb, &inet6_sk(sk)->saddr,
- &sk->sk_v6_daddr, skb->len);
+ udp6_set_csum(udp_get_no_check6_tx(sk), skb, saddr, daddr, skb->len);
__skb_push(skb, sizeof(*ip6h));
skb_reset_network_header(skb);
diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c
index d440bb585524..ace10d0b3aac 100644
--- a/net/ipv6/ip6_vti.c
+++ b/net/ipv6/ip6_vti.c
@@ -95,6 +95,7 @@ vti6_tnl_lookup(struct net *net, const struct in6_addr *remote,
unsigned int hash = HASH(remote, local);
struct ip6_tnl *t;
struct vti6_net *ip6n = net_generic(net, vti6_net_id);
+ struct in6_addr any;
for_each_vti6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
if (ipv6_addr_equal(local, &t->parms.laddr) &&
@@ -102,6 +103,22 @@ vti6_tnl_lookup(struct net *net, const struct in6_addr *remote,
(t->dev->flags & IFF_UP))
return t;
}
+
+ memset(&any, 0, sizeof(any));
+ hash = HASH(&any, local);
+ for_each_vti6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
+ if (ipv6_addr_equal(local, &t->parms.laddr) &&
+ (t->dev->flags & IFF_UP))
+ return t;
+ }
+
+ hash = HASH(remote, &any);
+ for_each_vti6_tunnel_rcu(ip6n->tnls_r_l[hash]) {
+ if (ipv6_addr_equal(remote, &t->parms.raddr) &&
+ (t->dev->flags & IFF_UP))
+ return t;
+ }
+
t = rcu_dereference(ip6n->tnls_wc[0]);
if (t && (t->dev->flags & IFF_UP))
return t;
@@ -172,10 +189,6 @@ static int vti6_tnl_create2(struct net_device *dev)
struct vti6_net *ip6n = net_generic(net, vti6_net_id);
int err;
- err = vti6_dev_init(dev);
- if (err < 0)
- goto out;
-
err = register_netdevice(dev);
if (err < 0)
goto out;
@@ -291,8 +304,8 @@ static int vti6_rcv(struct sk_buff *skb)
const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
rcu_read_lock();
- if ((t = vti6_tnl_lookup(dev_net(skb->dev), &ipv6h->saddr,
- &ipv6h->daddr)) != NULL) {
+ t = vti6_tnl_lookup(dev_net(skb->dev), &ipv6h->saddr, &ipv6h->daddr);
+ if (t != NULL) {
if (t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) {
rcu_read_unlock();
goto discard;
@@ -416,6 +429,7 @@ vti6_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl)
struct net_device_stats *stats = &t->dev->stats;
struct dst_entry *dst = skb_dst(skb);
struct net_device *tdev;
+ struct xfrm_state *x;
int err = -1;
if (!dst)
@@ -429,7 +443,12 @@ vti6_xmit(struct sk_buff *skb, struct net_device *dev, struct flowi *fl)
goto tx_err_link_failure;
}
- if (!vti6_state_check(dst->xfrm, &t->parms.raddr, &t->parms.laddr))
+ x = dst->xfrm;
+ if (!vti6_state_check(x, &t->parms.raddr, &t->parms.laddr))
+ goto tx_err_link_failure;
+
+ if (!ip6_tnl_xmit_ctl(t, (const struct in6_addr *)&x->props.saddr,
+ (const struct in6_addr *)&x->id.daddr))
goto tx_err_link_failure;
tdev = dst->dev;
@@ -484,7 +503,7 @@ vti6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
ipv6h = ipv6_hdr(skb);
if ((t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) ||
- !ip6_tnl_xmit_ctl(t) || vti6_addr_conflict(t, ipv6h))
+ vti6_addr_conflict(t, ipv6h))
goto tx_err;
xfrm_decode_session(skb, &fl, AF_INET6);
@@ -783,6 +802,7 @@ static int vti6_change_mtu(struct net_device *dev, int new_mtu)
}
static const struct net_device_ops vti6_netdev_ops = {
+ .ndo_init = vti6_dev_init,
.ndo_uninit = vti6_dev_uninit,
.ndo_start_xmit = vti6_tnl_xmit,
.ndo_do_ioctl = vti6_ioctl,
@@ -852,16 +872,10 @@ static int __net_init vti6_fb_tnl_dev_init(struct net_device *dev)
struct ip6_tnl *t = netdev_priv(dev);
struct net *net = dev_net(dev);
struct vti6_net *ip6n = net_generic(net, vti6_net_id);
- int err = vti6_dev_init_gen(dev);
-
- if (err)
- return err;
t->parms.proto = IPPROTO_IPV6;
dev_hold(dev);
- vti6_link_config(t);
-
rcu_assign_pointer(ip6n->tnls_wc[0], t);
return 0;
}
@@ -914,6 +928,15 @@ static int vti6_newlink(struct net *src_net, struct net_device *dev,
return vti6_tnl_create2(dev);
}
+static void vti6_dellink(struct net_device *dev, struct list_head *head)
+{
+ struct net *net = dev_net(dev);
+ struct vti6_net *ip6n = net_generic(net, vti6_net_id);
+
+ if (dev != ip6n->fb_tnl_dev)
+ unregister_netdevice_queue(dev, head);
+}
+
static int vti6_changelink(struct net_device *dev, struct nlattr *tb[],
struct nlattr *data[])
{
@@ -989,6 +1012,7 @@ static struct rtnl_link_ops vti6_link_ops __read_mostly = {
.setup = vti6_dev_setup,
.validate = vti6_validate,
.newlink = vti6_newlink,
+ .dellink = vti6_dellink,
.changelink = vti6_changelink,
.get_size = vti6_get_size,
.fill_info = vti6_fill_info,
@@ -1029,6 +1053,7 @@ static int __net_init vti6_init_net(struct net *net)
if (!ip6n->fb_tnl_dev)
goto err_alloc_dev;
dev_net_set(ip6n->fb_tnl_dev, net);
+ ip6n->fb_tnl_dev->rtnl_link_ops = &vti6_link_ops;
err = vti6_fb_tnl_dev_init(ip6n->fb_tnl_dev);
if (err < 0)
diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c
index 0171f08325c3..722669754bbf 100644
--- a/net/ipv6/ip6mr.c
+++ b/net/ipv6/ip6mr.c
@@ -1439,6 +1439,10 @@ reg_pernet_fail:
void ip6_mr_cleanup(void)
{
+ rtnl_unregister(RTNL_FAMILY_IP6MR, RTM_GETROUTE);
+#ifdef CONFIG_IPV6_PIMSM_V2
+ inet6_del_protocol(&pim6_protocol, IPPROTO_PIM);
+#endif
unregister_netdevice_notifier(&ip6_mr_notifier);
unregister_pernet_subsys(&ip6mr_net_ops);
kmem_cache_destroy(mrt_cachep);
@@ -2090,7 +2094,7 @@ static void ip6_mr_forward(struct net *net, struct mr6_table *mrt,
if (ipv6_addr_any(&cache->mf6c_origin) && true_vifi >= 0) {
struct mfc6_cache *cache_proxy;
- /* For an (*,G) entry, we only check that the incomming
+ /* For an (*,G) entry, we only check that the incoming
* interface is part of the static tree.
*/
cache_proxy = ip6mr_cache_find_any_parent(mrt, vif);
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index e1a9583bb419..66980d8d98d1 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -110,12 +110,8 @@ struct ipv6_txoptions *ipv6_update_options(struct sock *sk,
icsk->icsk_ext_hdr_len = opt->opt_flen + opt->opt_nflen;
icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);
}
- opt = xchg(&inet6_sk(sk)->opt, opt);
- } else {
- spin_lock(&sk->sk_dst_lock);
- opt = xchg(&inet6_sk(sk)->opt, opt);
- spin_unlock(&sk->sk_dst_lock);
}
+ opt = xchg(&inet6_sk(sk)->opt, opt);
sk_dst_reset(sk);
return opt;
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c
index 9648de2b6745..5ce107c8aab3 100644
--- a/net/ipv6/mcast.c
+++ b/net/ipv6/mcast.c
@@ -1550,7 +1550,7 @@ static void ip6_mc_hdr(struct sock *sk, struct sk_buff *skb,
hdr->daddr = *daddr;
}
-static struct sk_buff *mld_newpack(struct inet6_dev *idev, int size)
+static struct sk_buff *mld_newpack(struct inet6_dev *idev, unsigned int mtu)
{
struct net_device *dev = idev->dev;
struct net *net = dev_net(dev);
@@ -1561,13 +1561,13 @@ static struct sk_buff *mld_newpack(struct inet6_dev *idev, int size)
const struct in6_addr *saddr;
int hlen = LL_RESERVED_SPACE(dev);
int tlen = dev->needed_tailroom;
+ unsigned int size = mtu + hlen + tlen;
int err;
u8 ra[8] = { IPPROTO_ICMPV6, 0,
IPV6_TLV_ROUTERALERT, 2, 0, 0,
IPV6_TLV_PADN, 0 };
/* we assume size > sizeof(ra) here */
- size += hlen + tlen;
/* limit our allocations to order-0 page */
size = min_t(int, size, SKB_MAX_ORDER(0, 0));
skb = sock_alloc_send_skb(sk, size, 1, &err);
@@ -1576,6 +1576,8 @@ static struct sk_buff *mld_newpack(struct inet6_dev *idev, int size)
return NULL;
skb->priority = TC_PRIO_CONTROL;
+ skb->reserved_tailroom = skb_end_offset(skb) -
+ min(mtu, skb_end_offset(skb));
skb_reserve(skb, hlen);
if (__ipv6_get_lladdr(idev, &addr_buf, IFA_F_TENTATIVE)) {
@@ -1690,8 +1692,7 @@ static struct sk_buff *add_grhead(struct sk_buff *skb, struct ifmcaddr6 *pmc,
return skb;
}
-#define AVAILABLE(skb) ((skb) ? ((skb)->dev ? (skb)->dev->mtu - (skb)->len : \
- skb_tailroom(skb)) : 0)
+#define AVAILABLE(skb) ((skb) ? skb_availroom(skb) : 0)
static struct sk_buff *add_grec(struct sk_buff *skb, struct ifmcaddr6 *pmc,
int type, int gdeleted, int sdeleted, int crsend)
@@ -2823,11 +2824,7 @@ static int igmp6_mcf_seq_show(struct seq_file *seq, void *v)
struct igmp6_mcf_iter_state *state = igmp6_mcf_seq_private(seq);
if (v == SEQ_START_TOKEN) {
- seq_printf(seq,
- "%3s %6s "
- "%32s %32s %6s %6s\n", "Idx",
- "Device", "Multicast Address",
- "Source Address", "INC", "EXC");
+ seq_puts(seq, "Idx Device Multicast Address Source Address INC EXC\n");
} else {
seq_printf(seq,
"%3d %6.6s %pi6 %pi6 %6lu %6lu\n",
diff --git a/net/ipv6/mip6.c b/net/ipv6/mip6.c
index f61429d391d3..b9779d441b12 100644
--- a/net/ipv6/mip6.c
+++ b/net/ipv6/mip6.c
@@ -97,16 +97,17 @@ static int mip6_mh_filter(struct sock *sk, struct sk_buff *skb)
return -1;
if (mh->ip6mh_hdrlen < mip6_mh_len(mh->ip6mh_type)) {
- LIMIT_NETDEBUG(KERN_DEBUG "mip6: MH message too short: %d vs >=%d\n",
- mh->ip6mh_hdrlen, mip6_mh_len(mh->ip6mh_type));
+ net_dbg_ratelimited("mip6: MH message too short: %d vs >=%d\n",
+ mh->ip6mh_hdrlen,
+ mip6_mh_len(mh->ip6mh_type));
mip6_param_prob(skb, 0, offsetof(struct ip6_mh, ip6mh_hdrlen) +
skb_network_header_len(skb));
return -1;
}
if (mh->ip6mh_proto != IPPROTO_NONE) {
- LIMIT_NETDEBUG(KERN_DEBUG "mip6: MH invalid payload proto = %d\n",
- mh->ip6mh_proto);
+ net_dbg_ratelimited("mip6: MH invalid payload proto = %d\n",
+ mh->ip6mh_proto);
mip6_param_prob(skb, 0, offsetof(struct ip6_mh, ip6mh_proto) +
skb_network_header_len(skb));
return -1;
@@ -288,7 +289,7 @@ static int mip6_destopt_offset(struct xfrm_state *x, struct sk_buff *skb,
* XXX: packet if HAO exists.
*/
if (ipv6_find_tlv(skb, offset, IPV6_TLV_HAO) >= 0) {
- LIMIT_NETDEBUG(KERN_WARNING "mip6: hao exists already, override\n");
+ net_dbg_ratelimited("mip6: hao exists already, override\n");
return offset;
}
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 4cb45c1079a2..682866777d53 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -162,7 +162,8 @@ static void ndisc_fill_addr_option(struct sk_buff *skb, int type, void *data)
memcpy(opt+2, data, data_len);
data_len += 2;
opt += data_len;
- if ((space -= data_len) > 0)
+ space -= data_len;
+ if (space > 0)
memset(opt, 0, space);
}
@@ -656,8 +657,8 @@ static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb)
if (skb && ipv6_chk_addr(dev_net(dev), &ipv6_hdr(skb)->saddr, dev, 1))
saddr = &ipv6_hdr(skb)->saddr;
-
- if ((probes -= NEIGH_VAR(neigh->parms, UCAST_PROBES)) < 0) {
+ probes -= NEIGH_VAR(neigh->parms, UCAST_PROBES);
+ if (probes < 0) {
if (!(neigh->nud_state & NUD_VALID)) {
ND_PRINTK(1, dbg,
"%s: trying to ucast probe in NUD_INVALID: %pI6\n",
@@ -1763,7 +1764,7 @@ int __init ndisc_init(void)
/*
* Initialize the neighbour table
*/
- neigh_table_init(&nd_tbl);
+ neigh_table_init(NEIGH_ND_TABLE, &nd_tbl);
#ifdef CONFIG_SYSCTL
err = neigh_sysctl_register(NULL, &nd_tbl.parms,
@@ -1796,6 +1797,6 @@ void ndisc_cleanup(void)
#ifdef CONFIG_SYSCTL
neigh_sysctl_unregister(&nd_tbl.parms);
#endif
- neigh_table_clear(&nd_tbl);
+ neigh_table_clear(NEIGH_ND_TABLE, &nd_tbl);
unregister_pernet_subsys(&ndisc_net_ops);
}
diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c
index d38e6a8d8b9f..398377a9d018 100644
--- a/net/ipv6/netfilter.c
+++ b/net/ipv6/netfilter.c
@@ -36,7 +36,7 @@ int ip6_route_me_harder(struct sk_buff *skb)
err = dst->error;
if (err) {
IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
- LIMIT_NETDEBUG(KERN_DEBUG "ip6_route_me_harder: No more route.\n");
+ net_dbg_ratelimited("ip6_route_me_harder: No more route\n");
dst_release(dst);
return err;
}
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig
index 6af874fc187f..a069822936e6 100644
--- a/net/ipv6/netfilter/Kconfig
+++ b/net/ipv6/netfilter/Kconfig
@@ -91,6 +91,15 @@ config NFT_MASQ_IPV6
This is the expression that provides IPv4 masquerading support for
nf_tables.
+config NFT_REDIR_IPV6
+ tristate "IPv6 redirect support for nf_tables"
+ depends on NF_TABLES_IPV6
+ depends on NFT_REDIR
+ select NF_NAT_REDIRECT
+ help
+ This is the expression that provides IPv4 redirect support for
+ nf_tables.
+
endif # NF_NAT_IPV6
config IP6_NF_IPTABLES
diff --git a/net/ipv6/netfilter/Makefile b/net/ipv6/netfilter/Makefile
index fbb25f01143c..c36e0a5490de 100644
--- a/net/ipv6/netfilter/Makefile
+++ b/net/ipv6/netfilter/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_NFT_CHAIN_ROUTE_IPV6) += nft_chain_route_ipv6.o
obj-$(CONFIG_NFT_CHAIN_NAT_IPV6) += nft_chain_nat_ipv6.o
obj-$(CONFIG_NFT_REJECT_IPV6) += nft_reject_ipv6.o
obj-$(CONFIG_NFT_MASQ_IPV6) += nft_masq_ipv6.o
+obj-$(CONFIG_NFT_REDIR_IPV6) += nft_redir_ipv6.o
# matches
obj-$(CONFIG_IP6_NF_MATCH_AH) += ip6t_ah.o
diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
index 4cbc6b290dd5..b68d0e59c1f8 100644
--- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
+++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c
@@ -60,11 +60,11 @@ static bool ipv6_invert_tuple(struct nf_conntrack_tuple *tuple,
return true;
}
-static int ipv6_print_tuple(struct seq_file *s,
+static void ipv6_print_tuple(struct seq_file *s,
const struct nf_conntrack_tuple *tuple)
{
- return seq_printf(s, "src=%pI6 dst=%pI6 ",
- tuple->src.u3.ip6, tuple->dst.u3.ip6);
+ seq_printf(s, "src=%pI6 dst=%pI6 ",
+ tuple->src.u3.ip6, tuple->dst.u3.ip6);
}
static int ipv6_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
index b3807c5cb888..90388d606483 100644
--- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
+++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c
@@ -84,13 +84,13 @@ static bool icmpv6_invert_tuple(struct nf_conntrack_tuple *tuple,
}
/* Print out the per-protocol part of the tuple. */
-static int icmpv6_print_tuple(struct seq_file *s,
+static void icmpv6_print_tuple(struct seq_file *s,
const struct nf_conntrack_tuple *tuple)
{
- return seq_printf(s, "type=%u code=%u id=%u ",
- tuple->dst.u.icmp.type,
- tuple->dst.u.icmp.code,
- ntohs(tuple->src.u.icmp.id));
+ seq_printf(s, "type=%u code=%u id=%u ",
+ tuple->dst.u.icmp.type,
+ tuple->dst.u.icmp.code,
+ ntohs(tuple->src.u.icmp.id));
}
static unsigned int *icmpv6_get_timeouts(struct net *net)
diff --git a/net/ipv6/netfilter/nf_log_ipv6.c b/net/ipv6/netfilter/nf_log_ipv6.c
index 7b17a0be93e7..ddf07e6f59d7 100644
--- a/net/ipv6/netfilter/nf_log_ipv6.c
+++ b/net/ipv6/netfilter/nf_log_ipv6.c
@@ -5,6 +5,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/module.h>
#include <linux/spinlock.h>
@@ -398,8 +399,17 @@ static int __init nf_log_ipv6_init(void)
if (ret < 0)
return ret;
- nf_log_register(NFPROTO_IPV6, &nf_ip6_logger);
+ ret = nf_log_register(NFPROTO_IPV6, &nf_ip6_logger);
+ if (ret < 0) {
+ pr_err("failed to register logger\n");
+ goto err1;
+ }
+
return 0;
+
+err1:
+ unregister_pernet_subsys(&nf_log_ipv6_net_ops);
+ return ret;
}
static void __exit nf_log_ipv6_exit(void)
@@ -412,6 +422,6 @@ module_init(nf_log_ipv6_init);
module_exit(nf_log_ipv6_exit);
MODULE_AUTHOR("Netfilter Core Team <coreteam@netfilter.org>");
-MODULE_DESCRIPTION("Netfilter IPv4 packet logging");
+MODULE_DESCRIPTION("Netfilter IPv6 packet logging");
MODULE_LICENSE("GPL");
MODULE_ALIAS_NF_LOGGER(AF_INET6, 0);
diff --git a/net/ipv6/netfilter/nf_reject_ipv6.c b/net/ipv6/netfilter/nf_reject_ipv6.c
index 015eb8a80766..d05b36440e8b 100644
--- a/net/ipv6/netfilter/nf_reject_ipv6.c
+++ b/net/ipv6/netfilter/nf_reject_ipv6.c
@@ -11,6 +11,7 @@
#include <net/ip6_route.h>
#include <net/ip6_fib.h>
#include <net/ip6_checksum.h>
+#include <net/netfilter/ipv6/nf_reject.h>
#include <linux/netfilter_ipv6.h>
#include <net/netfilter/ipv6/nf_reject.h>
diff --git a/net/ipv6/netfilter/nft_masq_ipv6.c b/net/ipv6/netfilter/nft_masq_ipv6.c
index 8a7ac685076d..529c119cbb14 100644
--- a/net/ipv6/netfilter/nft_masq_ipv6.c
+++ b/net/ipv6/netfilter/nft_masq_ipv6.c
@@ -25,6 +25,7 @@ static void nft_masq_ipv6_eval(const struct nft_expr *expr,
struct nf_nat_range range;
unsigned int verdict;
+ memset(&range, 0, sizeof(range));
range.flags = priv->flags;
verdict = nf_nat_masquerade_ipv6(pkt->skb, &range, pkt->out);
diff --git a/net/ipv6/netfilter/nft_redir_ipv6.c b/net/ipv6/netfilter/nft_redir_ipv6.c
new file mode 100644
index 000000000000..2433a6bfb191
--- /dev/null
+++ b/net/ipv6/netfilter/nft_redir_ipv6.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2014 Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nf_nat.h>
+#include <net/netfilter/nft_redir.h>
+#include <net/netfilter/nf_nat_redirect.h>
+
+static void nft_redir_ipv6_eval(const struct nft_expr *expr,
+ struct nft_data data[NFT_REG_MAX + 1],
+ const struct nft_pktinfo *pkt)
+{
+ struct nft_redir *priv = nft_expr_priv(expr);
+ struct nf_nat_range range;
+ unsigned int verdict;
+
+ memset(&range, 0, sizeof(range));
+ if (priv->sreg_proto_min) {
+ range.min_proto.all = (__force __be16)
+ data[priv->sreg_proto_min].data[0];
+ range.max_proto.all = (__force __be16)
+ data[priv->sreg_proto_max].data[0];
+ range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
+ }
+
+ range.flags |= priv->flags;
+
+ verdict = nf_nat_redirect_ipv6(pkt->skb, &range, pkt->ops->hooknum);
+ data[NFT_REG_VERDICT].verdict = verdict;
+}
+
+static struct nft_expr_type nft_redir_ipv6_type;
+static const struct nft_expr_ops nft_redir_ipv6_ops = {
+ .type = &nft_redir_ipv6_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_redir)),
+ .eval = nft_redir_ipv6_eval,
+ .init = nft_redir_init,
+ .dump = nft_redir_dump,
+ .validate = nft_redir_validate,
+};
+
+static struct nft_expr_type nft_redir_ipv6_type __read_mostly = {
+ .family = NFPROTO_IPV6,
+ .name = "redir",
+ .ops = &nft_redir_ipv6_ops,
+ .policy = nft_redir_policy,
+ .maxattr = NFTA_REDIR_MAX,
+ .owner = THIS_MODULE,
+};
+
+static int __init nft_redir_ipv6_module_init(void)
+{
+ return nft_register_expr(&nft_redir_ipv6_type);
+}
+
+static void __exit nft_redir_ipv6_module_exit(void)
+{
+ nft_unregister_expr(&nft_redir_ipv6_type);
+}
+
+module_init(nft_redir_ipv6_module_init);
+module_exit(nft_redir_ipv6_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>");
+MODULE_ALIAS_NFT_AF_EXPR(AF_INET6, "redir");
diff --git a/net/ipv6/netfilter/nft_reject_ipv6.c b/net/ipv6/netfilter/nft_reject_ipv6.c
index 0bc19fa87821..f73285924144 100644
--- a/net/ipv6/netfilter/nft_reject_ipv6.c
+++ b/net/ipv6/netfilter/nft_reject_ipv6.c
@@ -19,9 +19,9 @@
#include <net/netfilter/nft_reject.h>
#include <net/netfilter/ipv6/nf_reject.h>
-void nft_reject_ipv6_eval(const struct nft_expr *expr,
- struct nft_data data[NFT_REG_MAX + 1],
- const struct nft_pktinfo *pkt)
+static void nft_reject_ipv6_eval(const struct nft_expr *expr,
+ struct nft_data data[NFT_REG_MAX + 1],
+ const struct nft_pktinfo *pkt)
{
struct nft_reject *priv = nft_expr_priv(expr);
struct net *net = dev_net((pkt->in != NULL) ? pkt->in : pkt->out);
@@ -38,7 +38,6 @@ void nft_reject_ipv6_eval(const struct nft_expr *expr,
data[NFT_REG_VERDICT].verdict = NF_DROP;
}
-EXPORT_SYMBOL_GPL(nft_reject_ipv6_eval);
static struct nft_expr_type nft_reject_ipv6_type;
static const struct nft_expr_ops nft_reject_ipv6_ops = {
diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c
index 5b7a1ed2aba9..2d3148378a1f 100644
--- a/net/ipv6/ping.c
+++ b/net/ipv6/ping.c
@@ -163,7 +163,8 @@ int ping_v6_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
pfh.icmph.checksum = 0;
pfh.icmph.un.echo.id = inet->inet_sport;
pfh.icmph.un.echo.sequence = user_icmph.icmp6_sequence;
- pfh.iov = msg->msg_iov;
+ /* XXX: stripping const */
+ pfh.iov = (struct iovec *)msg->msg_iter.iov;
pfh.wcheck = 0;
pfh.family = AF_INET6;
diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c
index 1752cd0b4882..679253d0af84 100644
--- a/net/ipv6/proc.c
+++ b/net/ipv6/proc.c
@@ -136,6 +136,7 @@ static const struct snmp_mib snmp6_udp6_list[] = {
SNMP_MIB_ITEM("Udp6RcvbufErrors", UDP_MIB_RCVBUFERRORS),
SNMP_MIB_ITEM("Udp6SndbufErrors", UDP_MIB_SNDBUFERRORS),
SNMP_MIB_ITEM("Udp6InCsumErrors", UDP_MIB_CSUMERRORS),
+ SNMP_MIB_ITEM("Udp6IgnoredMulti", UDP_MIB_IGNOREDMULTI),
SNMP_MIB_SENTINEL
};
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index 896af8807979..ee25631f8c29 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -486,13 +486,13 @@ static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk,
}
if (skb_csum_unnecessary(skb)) {
- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
} else if (msg->msg_flags&MSG_TRUNC) {
if (__skb_checksum_complete(skb))
goto csum_copy_err;
- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
} else {
- err = skb_copy_and_csum_datagram_iovec(skb, 0, msg->msg_iov);
+ err = skb_copy_and_csum_datagram_msg(skb, 0, msg);
if (err == -EINVAL)
goto csum_copy_err;
}
@@ -548,7 +548,8 @@ static int rawv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6,
if (!rp->checksum)
goto send;
- if ((skb = skb_peek(&sk->sk_write_queue)) == NULL)
+ skb = skb_peek(&sk->sk_write_queue);
+ if (!skb)
goto out;
offset = rp->offset;
@@ -671,65 +672,62 @@ error:
return err;
}
-static int rawv6_probe_proto_opt(struct flowi6 *fl6, struct msghdr *msg)
+struct raw6_frag_vec {
+ struct msghdr *msg;
+ int hlen;
+ char c[4];
+};
+
+static int rawv6_probe_proto_opt(struct raw6_frag_vec *rfv, struct flowi6 *fl6)
{
- struct iovec *iov;
- u8 __user *type = NULL;
- u8 __user *code = NULL;
- u8 len = 0;
- int probed = 0;
- int i;
-
- if (!msg->msg_iov)
- return 0;
+ int err = 0;
+ switch (fl6->flowi6_proto) {
+ case IPPROTO_ICMPV6:
+ rfv->hlen = 2;
+ err = memcpy_from_msg(rfv->c, rfv->msg, rfv->hlen);
+ if (!err) {
+ fl6->fl6_icmp_type = rfv->c[0];
+ fl6->fl6_icmp_code = rfv->c[1];
+ }
+ break;
+ case IPPROTO_MH:
+ rfv->hlen = 4;
+ err = memcpy_from_msg(rfv->c, rfv->msg, rfv->hlen);
+ if (!err)
+ fl6->fl6_mh_type = rfv->c[2];
+ }
+ return err;
+}
- for (i = 0; i < msg->msg_iovlen; i++) {
- iov = &msg->msg_iov[i];
- if (!iov)
- continue;
+static int raw6_getfrag(void *from, char *to, int offset, int len, int odd,
+ struct sk_buff *skb)
+{
+ struct raw6_frag_vec *rfv = from;
- switch (fl6->flowi6_proto) {
- case IPPROTO_ICMPV6:
- /* check if one-byte field is readable or not. */
- if (iov->iov_base && iov->iov_len < 1)
- break;
-
- if (!type) {
- type = iov->iov_base;
- /* check if code field is readable or not. */
- if (iov->iov_len > 1)
- code = type + 1;
- } else if (!code)
- code = iov->iov_base;
-
- if (type && code) {
- if (get_user(fl6->fl6_icmp_type, type) ||
- get_user(fl6->fl6_icmp_code, code))
- return -EFAULT;
- probed = 1;
- }
- break;
- case IPPROTO_MH:
- if (iov->iov_base && iov->iov_len < 1)
- break;
- /* check if type field is readable or not. */
- if (iov->iov_len > 2 - len) {
- u8 __user *p = iov->iov_base;
- if (get_user(fl6->fl6_mh_type, &p[2 - len]))
- return -EFAULT;
- probed = 1;
- } else
- len += iov->iov_len;
+ if (offset < rfv->hlen) {
+ int copy = min(rfv->hlen - offset, len);
- break;
- default:
- probed = 1;
- break;
- }
- if (probed)
- break;
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ memcpy(to, rfv->c + offset, copy);
+ else
+ skb->csum = csum_block_add(
+ skb->csum,
+ csum_partial_copy_nocheck(rfv->c + offset,
+ to, copy, 0),
+ odd);
+
+ odd = 0;
+ offset += copy;
+ to += copy;
+ len -= copy;
+
+ if (!len)
+ return 0;
}
- return 0;
+
+ offset -= rfv->hlen;
+
+ return ip_generic_getfrag(rfv->msg, to, offset, len, odd, skb);
}
static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
@@ -744,6 +742,7 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
struct ipv6_txoptions *opt = NULL;
struct ip6_flowlabel *flowlabel = NULL;
struct dst_entry *dst = NULL;
+ struct raw6_frag_vec rfv;
struct flowi6 fl6;
int addr_len = msg->msg_namelen;
int hlimit = -1;
@@ -847,7 +846,9 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
opt = ipv6_fixup_options(&opt_space, opt);
fl6.flowi6_proto = proto;
- err = rawv6_probe_proto_opt(&fl6, msg);
+ rfv.msg = msg;
+ rfv.hlen = 0;
+ err = rawv6_probe_proto_opt(&rfv, &fl6);
if (err)
goto out;
@@ -885,10 +886,11 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk,
back_from_confirm:
if (inet->hdrincl)
- err = rawv6_send_hdrinc(sk, msg->msg_iov, len, &fl6, &dst, msg->msg_flags);
+ /* XXX: stripping const */
+ err = rawv6_send_hdrinc(sk, (struct iovec *)msg->msg_iter.iov, len, &fl6, &dst, msg->msg_flags);
else {
lock_sock(sk);
- err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov,
+ err = ip6_append_data(sk, raw6_getfrag, &rfv,
len, 0, hlimit, tclass, opt, &fl6, (struct rt6_info *)dst,
msg->msg_flags, dontfrag);
diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
index 1a157ca2ebc1..d7d70e69973b 100644
--- a/net/ipv6/reassembly.c
+++ b/net/ipv6/reassembly.c
@@ -69,7 +69,7 @@ struct ip6frag_skb_cb {
#define FRAG6_CB(skb) ((struct ip6frag_skb_cb *)((skb)->cb))
-static inline u8 ip6_frag_ecn(const struct ipv6hdr *ipv6h)
+static u8 ip6_frag_ecn(const struct ipv6hdr *ipv6h)
{
return 1 << (ipv6_get_dsfield(ipv6h) & INET_ECN_MASK);
}
@@ -178,7 +178,7 @@ static void ip6_frag_expire(unsigned long data)
ip6_expire_frag_queue(net, fq, &ip6_frags);
}
-static __inline__ struct frag_queue *
+static struct frag_queue *
fq_find(struct net *net, __be32 id, const struct in6_addr *src,
const struct in6_addr *dst, u8 ecn)
{
@@ -429,7 +429,8 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
struct sk_buff *clone;
int i, plen = 0;
- if ((clone = alloc_skb(0, GFP_ATOMIC)) == NULL)
+ clone = alloc_skb(0, GFP_ATOMIC);
+ if (clone == NULL)
goto out_oom;
clone->next = head->next;
head->next = clone;
@@ -684,21 +685,21 @@ static void ip6_frags_sysctl_unregister(void)
unregister_net_sysctl_table(ip6_ctl_header);
}
#else
-static inline int ip6_frags_ns_sysctl_register(struct net *net)
+static int ip6_frags_ns_sysctl_register(struct net *net)
{
return 0;
}
-static inline void ip6_frags_ns_sysctl_unregister(struct net *net)
+static void ip6_frags_ns_sysctl_unregister(struct net *net)
{
}
-static inline int ip6_frags_sysctl_register(void)
+static int ip6_frags_sysctl_register(void)
{
return 0;
}
-static inline void ip6_frags_sysctl_unregister(void)
+static void ip6_frags_sysctl_unregister(void)
{
}
#endif
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index a318dd89b6d9..c91083156edb 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -772,23 +772,22 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len,
}
#endif
-#define BACKTRACK(__net, saddr) \
-do { \
- if (rt == __net->ipv6.ip6_null_entry) { \
- struct fib6_node *pn; \
- while (1) { \
- if (fn->fn_flags & RTN_TL_ROOT) \
- goto out; \
- pn = fn->parent; \
- if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn) \
- fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr); \
- else \
- fn = pn; \
- if (fn->fn_flags & RTN_RTINFO) \
- goto restart; \
- } \
- } \
-} while (0)
+static struct fib6_node* fib6_backtrack(struct fib6_node *fn,
+ struct in6_addr *saddr)
+{
+ struct fib6_node *pn;
+ while (1) {
+ if (fn->fn_flags & RTN_TL_ROOT)
+ return NULL;
+ pn = fn->parent;
+ if (FIB6_SUBTREE(pn) && FIB6_SUBTREE(pn) != fn)
+ fn = fib6_lookup(FIB6_SUBTREE(pn), NULL, saddr);
+ else
+ fn = pn;
+ if (fn->fn_flags & RTN_RTINFO)
+ return fn;
+ }
+}
static struct rt6_info *ip6_pol_route_lookup(struct net *net,
struct fib6_table *table,
@@ -804,8 +803,11 @@ restart:
rt = rt6_device_match(net, rt, &fl6->saddr, fl6->flowi6_oif, flags);
if (rt->rt6i_nsiblings && fl6->flowi6_oif == 0)
rt = rt6_multipath_select(rt, fl6, fl6->flowi6_oif, flags);
- BACKTRACK(net, &fl6->saddr);
-out:
+ if (rt == net->ipv6.ip6_null_entry) {
+ fn = fib6_backtrack(fn, &fl6->saddr);
+ if (fn)
+ goto restart;
+ }
dst_use(&rt->dst, jiffies);
read_unlock_bh(&table->tb6_lock);
return rt;
@@ -915,33 +917,48 @@ static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort,
static struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table, int oif,
struct flowi6 *fl6, int flags)
{
- struct fib6_node *fn;
+ struct fib6_node *fn, *saved_fn;
struct rt6_info *rt, *nrt;
int strict = 0;
int attempts = 3;
int err;
- int reachable = net->ipv6.devconf_all->forwarding ? 0 : RT6_LOOKUP_F_REACHABLE;
strict |= flags & RT6_LOOKUP_F_IFACE;
+ if (net->ipv6.devconf_all->forwarding == 0)
+ strict |= RT6_LOOKUP_F_REACHABLE;
-relookup:
+redo_fib6_lookup_lock:
read_lock_bh(&table->tb6_lock);
-restart_2:
fn = fib6_lookup(&table->tb6_root, &fl6->daddr, &fl6->saddr);
+ saved_fn = fn;
-restart:
- rt = rt6_select(fn, oif, strict | reachable);
+redo_rt6_select:
+ rt = rt6_select(fn, oif, strict);
if (rt->rt6i_nsiblings)
- rt = rt6_multipath_select(rt, fl6, oif, strict | reachable);
- BACKTRACK(net, &fl6->saddr);
- if (rt == net->ipv6.ip6_null_entry ||
- rt->rt6i_flags & RTF_CACHE)
- goto out;
+ rt = rt6_multipath_select(rt, fl6, oif, strict);
+ if (rt == net->ipv6.ip6_null_entry) {
+ fn = fib6_backtrack(fn, &fl6->saddr);
+ if (fn)
+ goto redo_rt6_select;
+ else if (strict & RT6_LOOKUP_F_REACHABLE) {
+ /* also consider unreachable route */
+ strict &= ~RT6_LOOKUP_F_REACHABLE;
+ fn = saved_fn;
+ goto redo_rt6_select;
+ } else {
+ dst_hold(&rt->dst);
+ read_unlock_bh(&table->tb6_lock);
+ goto out2;
+ }
+ }
dst_hold(&rt->dst);
read_unlock_bh(&table->tb6_lock);
+ if (rt->rt6i_flags & RTF_CACHE)
+ goto out2;
+
if (!(rt->rt6i_flags & (RTF_NONEXTHOP | RTF_GATEWAY)))
nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr);
else if (!(rt->dst.flags & DST_HOST))
@@ -967,15 +984,8 @@ restart:
* released someone could insert this route. Relookup.
*/
ip6_rt_put(rt);
- goto relookup;
+ goto redo_fib6_lookup_lock;
-out:
- if (reachable) {
- reachable = 0;
- goto restart_2;
- }
- dst_hold(&rt->dst);
- read_unlock_bh(&table->tb6_lock);
out2:
rt->dst.lastuse = jiffies;
rt->dst.__use++;
@@ -1235,10 +1245,12 @@ restart:
rt = net->ipv6.ip6_null_entry;
else if (rt->dst.error) {
rt = net->ipv6.ip6_null_entry;
- goto out;
+ } else if (rt == net->ipv6.ip6_null_entry) {
+ fn = fib6_backtrack(fn, &fl6->saddr);
+ if (fn)
+ goto restart;
}
- BACKTRACK(net, &fl6->saddr);
-out:
+
dst_hold(&rt->dst);
read_unlock_bh(&table->tb6_lock);
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index 58e5b4710127..213546bd6d5d 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -195,10 +195,8 @@ static int ipip6_tunnel_create(struct net_device *dev)
struct sit_net *sitn = net_generic(net, sit_net_id);
int err;
- err = ipip6_tunnel_init(dev);
- if (err < 0)
- goto out;
- ipip6_tunnel_clone_6rd(dev, sitn);
+ memcpy(dev->dev_addr, &t->parms.iph.saddr, 4);
+ memcpy(dev->broadcast, &t->parms.iph.daddr, 4);
if ((__force u16)t->parms.i_flags & SIT_ISATAP)
dev->priv_flags |= IFF_ISATAP;
@@ -207,7 +205,8 @@ static int ipip6_tunnel_create(struct net_device *dev)
if (err < 0)
goto out;
- strcpy(t->parms.name, dev->name);
+ ipip6_tunnel_clone_6rd(dev, sitn);
+
dev->rtnl_link_ops = &sit_link_ops;
dev_hold(dev);
@@ -1242,7 +1241,8 @@ ipip6_tunnel_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof(p)))
goto done;
err = -ENOENT;
- if ((t = ipip6_tunnel_locate(net, &p, 0)) == NULL)
+ t = ipip6_tunnel_locate(net, &p, 0);
+ if (t == NULL)
goto done;
err = -EPERM;
if (t == netdev_priv(sitn->fb_tunnel_dev))
@@ -1330,6 +1330,7 @@ static int ipip6_tunnel_change_mtu(struct net_device *dev, int new_mtu)
}
static const struct net_device_ops ipip6_netdev_ops = {
+ .ndo_init = ipip6_tunnel_init,
.ndo_uninit = ipip6_tunnel_uninit,
.ndo_start_xmit = sit_tunnel_xmit,
.ndo_do_ioctl = ipip6_tunnel_ioctl,
@@ -1378,9 +1379,7 @@ static int ipip6_tunnel_init(struct net_device *dev)
tunnel->dev = dev;
tunnel->net = dev_net(dev);
-
- memcpy(dev->dev_addr, &tunnel->parms.iph.saddr, 4);
- memcpy(dev->broadcast, &tunnel->parms.iph.daddr, 4);
+ strcpy(tunnel->parms.name, dev->name);
ipip6_tunnel_bind_dev(dev);
dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
@@ -1405,7 +1404,6 @@ static int __net_init ipip6_fb_tunnel_init(struct net_device *dev)
tunnel->dev = dev;
tunnel->net = dev_net(dev);
- strcpy(tunnel->parms.name, dev->name);
iph->version = 4;
iph->protocol = IPPROTO_IPV6;
@@ -1714,7 +1712,7 @@ static int ipip6_fill_info(struct sk_buff *skb, const struct net_device *dev)
nla_put_u16(skb, IFLA_IPTUN_ENCAP_DPORT,
tunnel->encap.dport) ||
nla_put_u16(skb, IFLA_IPTUN_ENCAP_FLAGS,
- tunnel->encap.dport))
+ tunnel->encap.flags))
goto nla_put_failure;
return 0;
@@ -1839,8 +1837,8 @@ static int __net_init sit_init_net(struct net *net)
goto err_dev_free;
ipip6_tunnel_clone_6rd(sitn->fb_tunnel_dev, sitn);
-
- if ((err = register_netdev(sitn->fb_tunnel_dev)))
+ err = register_netdev(sitn->fb_tunnel_dev);
+ if (err)
goto err_reg_dev;
t = netdev_priv(sitn->fb_tunnel_dev);
diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c
index 2f25cb6347ca..7337fc7947e2 100644
--- a/net/ipv6/syncookies.c
+++ b/net/ipv6/syncookies.c
@@ -166,13 +166,15 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
int mss;
struct dst_entry *dst;
__u8 rcv_wscale;
- bool ecn_ok = false;
if (!sysctl_tcp_syncookies || !th->ack || th->rst)
goto out;
- if (tcp_synq_no_recent_overflow(sk) ||
- (mss = __cookie_v6_check(ipv6_hdr(skb), th, cookie)) == 0) {
+ if (tcp_synq_no_recent_overflow(sk))
+ goto out;
+
+ mss = __cookie_v6_check(ipv6_hdr(skb), th, cookie);
+ if (mss == 0) {
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SYNCOOKIESFAILED);
goto out;
}
@@ -183,7 +185,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
memset(&tcp_opt, 0, sizeof(tcp_opt));
tcp_parse_options(skb, &tcp_opt, 0, NULL);
- if (!cookie_check_timestamp(&tcp_opt, sock_net(sk), &ecn_ok))
+ if (!cookie_timestamp_decode(&tcp_opt))
goto out;
ret = NULL;
@@ -220,7 +222,6 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
req->expires = 0UL;
req->num_retrans = 0;
- ireq->ecn_ok = ecn_ok;
ireq->snd_wscale = tcp_opt.snd_wscale;
ireq->sack_ok = tcp_opt.sack_ok;
ireq->wscale_ok = tcp_opt.wscale_ok;
@@ -261,6 +262,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
dst_metric(dst, RTAX_INITRWND));
ireq->rcv_wscale = rcv_wscale;
+ ireq->ecn_ok = cookie_ecn_ok(&tcp_opt, sock_net(sk), dst);
ret = get_cookie_sock(sk, skb, req, dst);
out:
@@ -269,4 +271,3 @@ out_free:
reqsk_free(req);
return NULL;
}
-
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index ace29b60813c..5ff87805258e 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -787,16 +787,16 @@ static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = {
.queue_hash_add = inet6_csk_reqsk_queue_hash_add,
};
-static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win,
- u32 tsval, u32 tsecr, int oif,
- struct tcp_md5sig_key *key, int rst, u8 tclass,
- u32 label)
+static void tcp_v6_send_response(struct sock *sk, struct sk_buff *skb, u32 seq,
+ u32 ack, u32 win, u32 tsval, u32 tsecr,
+ int oif, struct tcp_md5sig_key *key, int rst,
+ u8 tclass, u32 label)
{
const struct tcphdr *th = tcp_hdr(skb);
struct tcphdr *t1;
struct sk_buff *buff;
struct flowi6 fl6;
- struct net *net = dev_net(skb_dst(skb)->dev);
+ struct net *net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev);
struct sock *ctl_sk = net->ipv6.tcp_sk;
unsigned int tot_len = sizeof(struct tcphdr);
struct dst_entry *dst;
@@ -903,7 +903,10 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb)
if (th->rst)
return;
- if (!ipv6_unicast_destination(skb))
+ /* If sk not NULL, it means we did a successful lookup and incoming
+ * route had to be correct. prequeue might have dropped our dst.
+ */
+ if (!sk && !ipv6_unicast_destination(skb))
return;
#ifdef CONFIG_TCP_MD5SIG
@@ -943,7 +946,7 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb)
(th->doff << 2);
oif = sk ? sk->sk_bound_dev_if : 0;
- tcp_v6_send_response(skb, seq, ack_seq, 0, 0, 0, oif, key, 1, 0, 0);
+ tcp_v6_send_response(sk, skb, seq, ack_seq, 0, 0, 0, oif, key, 1, 0, 0);
#ifdef CONFIG_TCP_MD5SIG
release_sk1:
@@ -954,13 +957,13 @@ release_sk1:
#endif
}
-static void tcp_v6_send_ack(struct sk_buff *skb, u32 seq, u32 ack,
- u32 win, u32 tsval, u32 tsecr, int oif,
+static void tcp_v6_send_ack(struct sock *sk, struct sk_buff *skb, u32 seq,
+ u32 ack, u32 win, u32 tsval, u32 tsecr, int oif,
struct tcp_md5sig_key *key, u8 tclass,
u32 label)
{
- tcp_v6_send_response(skb, seq, ack, win, tsval, tsecr, oif, key, 0, tclass,
- label);
+ tcp_v6_send_response(sk, skb, seq, ack, win, tsval, tsecr, oif, key, 0,
+ tclass, label);
}
static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb)
@@ -968,7 +971,7 @@ static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb)
struct inet_timewait_sock *tw = inet_twsk(sk);
struct tcp_timewait_sock *tcptw = tcp_twsk(sk);
- tcp_v6_send_ack(skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,
+ tcp_v6_send_ack(sk, skb, tcptw->tw_snd_nxt, tcptw->tw_rcv_nxt,
tcptw->tw_rcv_wnd >> tw->tw_rcv_wscale,
tcp_time_stamp + tcptw->tw_ts_offset,
tcptw->tw_ts_recent, tw->tw_bound_dev_if, tcp_twsk_md5_key(tcptw),
@@ -983,10 +986,10 @@ static void tcp_v6_reqsk_send_ack(struct sock *sk, struct sk_buff *skb,
/* sk->sk_state == TCP_LISTEN -> for regular TCP_SYN_RECV
* sk->sk_state == TCP_SYN_RECV -> for Fast Open.
*/
- tcp_v6_send_ack(skb, (sk->sk_state == TCP_LISTEN) ?
+ tcp_v6_send_ack(sk, skb, (sk->sk_state == TCP_LISTEN) ?
tcp_rsk(req)->snt_isn + 1 : tcp_sk(sk)->snd_nxt,
- tcp_rsk(req)->rcv_nxt,
- req->rcv_wnd, tcp_time_stamp, req->ts_recent, sk->sk_bound_dev_if,
+ tcp_rsk(req)->rcv_nxt, req->rcv_wnd,
+ tcp_time_stamp, req->ts_recent, sk->sk_bound_dev_if,
tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->daddr),
0, 0);
}
@@ -1293,6 +1296,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
struct dst_entry *dst = sk->sk_rx_dst;
sock_rps_save_rxhash(sk, skb);
+ sk_mark_napi_id(sk, skb);
if (dst) {
if (inet_sk(sk)->rx_dst_ifindex != skb->skb_iif ||
dst->ops->check(dst, np->rx_dst_cookie) == NULL) {
@@ -1322,6 +1326,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
*/
if (nsk != sk) {
sock_rps_save_rxhash(nsk, skb);
+ sk_mark_napi_id(sk, skb);
if (tcp_child_process(sk, nsk, skb))
goto reset;
if (opt_skb)
@@ -1454,7 +1459,7 @@ process:
if (sk_filter(sk, skb))
goto discard_and_relse;
- sk_mark_napi_id(sk, skb);
+ sk_incoming_cpu_update(sk);
skb->dev = NULL;
bh_lock_sock_nested(sk);
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index f6ba535b6feb..189dc4ae3eca 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -148,72 +148,85 @@ static inline int compute_score(struct sock *sk, struct net *net,
const struct in6_addr *daddr, __be16 dport,
int dif)
{
- int score = -1;
+ int score;
+ struct inet_sock *inet;
- if (net_eq(sock_net(sk), net) && udp_sk(sk)->udp_port_hash == hnum &&
- sk->sk_family == PF_INET6) {
- struct inet_sock *inet = inet_sk(sk);
+ if (!net_eq(sock_net(sk), net) ||
+ udp_sk(sk)->udp_port_hash != hnum ||
+ sk->sk_family != PF_INET6)
+ return -1;
- score = 0;
- if (inet->inet_dport) {
- if (inet->inet_dport != sport)
- return -1;
- score++;
- }
- if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) {
- if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr))
- return -1;
- score++;
- }
- if (!ipv6_addr_any(&sk->sk_v6_daddr)) {
- if (!ipv6_addr_equal(&sk->sk_v6_daddr, saddr))
- return -1;
- score++;
- }
- if (sk->sk_bound_dev_if) {
- if (sk->sk_bound_dev_if != dif)
- return -1;
- score++;
- }
+ score = 0;
+ inet = inet_sk(sk);
+
+ if (inet->inet_dport) {
+ if (inet->inet_dport != sport)
+ return -1;
+ score++;
+ }
+
+ if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) {
+ if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr))
+ return -1;
+ score++;
+ }
+
+ if (!ipv6_addr_any(&sk->sk_v6_daddr)) {
+ if (!ipv6_addr_equal(&sk->sk_v6_daddr, saddr))
+ return -1;
+ score++;
}
+
+ if (sk->sk_bound_dev_if) {
+ if (sk->sk_bound_dev_if != dif)
+ return -1;
+ score++;
+ }
+
return score;
}
#define SCORE2_MAX (1 + 1 + 1)
static inline int compute_score2(struct sock *sk, struct net *net,
- const struct in6_addr *saddr, __be16 sport,
- const struct in6_addr *daddr, unsigned short hnum,
- int dif)
+ const struct in6_addr *saddr, __be16 sport,
+ const struct in6_addr *daddr,
+ unsigned short hnum, int dif)
{
- int score = -1;
+ int score;
+ struct inet_sock *inet;
- if (net_eq(sock_net(sk), net) && udp_sk(sk)->udp_port_hash == hnum &&
- sk->sk_family == PF_INET6) {
- struct inet_sock *inet = inet_sk(sk);
+ if (!net_eq(sock_net(sk), net) ||
+ udp_sk(sk)->udp_port_hash != hnum ||
+ sk->sk_family != PF_INET6)
+ return -1;
- if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr))
+ if (!ipv6_addr_equal(&sk->sk_v6_rcv_saddr, daddr))
+ return -1;
+
+ score = 0;
+ inet = inet_sk(sk);
+
+ if (inet->inet_dport) {
+ if (inet->inet_dport != sport)
return -1;
- score = 0;
- if (inet->inet_dport) {
- if (inet->inet_dport != sport)
- return -1;
- score++;
- }
- if (!ipv6_addr_any(&sk->sk_v6_daddr)) {
- if (!ipv6_addr_equal(&sk->sk_v6_daddr, saddr))
- return -1;
- score++;
- }
- if (sk->sk_bound_dev_if) {
- if (sk->sk_bound_dev_if != dif)
- return -1;
- score++;
- }
+ score++;
}
+
+ if (!ipv6_addr_any(&sk->sk_v6_daddr)) {
+ if (!ipv6_addr_equal(&sk->sk_v6_daddr, saddr))
+ return -1;
+ score++;
+ }
+
+ if (sk->sk_bound_dev_if) {
+ if (sk->sk_bound_dev_if != dif)
+ return -1;
+ score++;
+ }
+
return score;
}
-
/* called with read_rcu_lock() */
static struct sock *udp6_lib_lookup2(struct net *net,
const struct in6_addr *saddr, __be16 sport,
@@ -357,7 +370,8 @@ static struct sock *__udp6_lib_lookup_skb(struct sk_buff *skb,
struct sock *sk;
const struct ipv6hdr *iph = ipv6_hdr(skb);
- if (unlikely(sk = skb_steal_sock(skb)))
+ sk = skb_steal_sock(skb);
+ if (unlikely(sk))
return sk;
return __udp6_lib_lookup(dev_net(skb_dst(skb)->dev), &iph->saddr, sport,
&iph->daddr, dport, inet6_iif(skb),
@@ -424,10 +438,10 @@ try_again:
}
if (skb_csum_unnecessary(skb))
- err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr),
- msg->msg_iov, copied);
+ err = skb_copy_datagram_msg(skb, sizeof(struct udphdr),
+ msg, copied);
else {
- err = skb_copy_and_csum_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov);
+ err = skb_copy_and_csum_datagram_msg(skb, sizeof(struct udphdr), msg);
if (err == -EINVAL)
goto csum_copy_err;
}
@@ -577,6 +591,7 @@ static int __udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
if (!ipv6_addr_any(&sk->sk_v6_daddr)) {
sock_rps_save_rxhash(sk, skb);
sk_mark_napi_id(sk, skb);
+ sk_incoming_cpu_update(sk);
}
rc = sock_queue_rcv_skb(sk, skb);
@@ -659,15 +674,13 @@ int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
if ((is_udplite & UDPLITE_RECV_CC) && UDP_SKB_CB(skb)->partial_cov) {
if (up->pcrlen == 0) { /* full coverage was set */
- LIMIT_NETDEBUG(KERN_WARNING "UDPLITE6: partial coverage"
- " %d while full coverage %d requested\n",
- UDP_SKB_CB(skb)->cscov, skb->len);
+ net_dbg_ratelimited("UDPLITE6: partial coverage %d while full coverage %d requested\n",
+ UDP_SKB_CB(skb)->cscov, skb->len);
goto drop;
}
if (UDP_SKB_CB(skb)->cscov < up->pcrlen) {
- LIMIT_NETDEBUG(KERN_WARNING "UDPLITE6: coverage %d "
- "too small, need min %d\n",
- UDP_SKB_CB(skb)->cscov, up->pcrlen);
+ net_dbg_ratelimited("UDPLITE6: coverage %d too small, need min %d\n",
+ UDP_SKB_CB(skb)->cscov, up->pcrlen);
goto drop;
}
}
@@ -760,9 +773,9 @@ static void udp6_csum_zero_error(struct sk_buff *skb)
/* RFC 2460 section 8.1 says that we SHOULD log
* this error. Well, it is reasonable.
*/
- LIMIT_NETDEBUG(KERN_INFO "IPv6: udp checksum is 0 for [%pI6c]:%u->[%pI6c]:%u\n",
- &ipv6_hdr(skb)->saddr, ntohs(udp_hdr(skb)->source),
- &ipv6_hdr(skb)->daddr, ntohs(udp_hdr(skb)->dest));
+ net_dbg_ratelimited("IPv6: udp checksum is 0 for [%pI6c]:%u->[%pI6c]:%u\n",
+ &ipv6_hdr(skb)->saddr, ntohs(udp_hdr(skb)->source),
+ &ipv6_hdr(skb)->daddr, ntohs(udp_hdr(skb)->dest));
}
/*
@@ -771,7 +784,7 @@ static void udp6_csum_zero_error(struct sk_buff *skb)
*/
static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
const struct in6_addr *saddr, const struct in6_addr *daddr,
- struct udp_table *udptable)
+ struct udp_table *udptable, int proto)
{
struct sock *sk, *stack[256 / sizeof(struct sock *)];
const struct udphdr *uh = udp_hdr(skb);
@@ -781,6 +794,7 @@ static int __udp6_lib_mcast_deliver(struct net *net, struct sk_buff *skb,
int dif = inet6_iif(skb);
unsigned int count = 0, offset = offsetof(typeof(*sk), sk_nulls_node);
unsigned int hash2 = 0, hash2_any = 0, use_hash2 = (hslot->count > 10);
+ bool inner_flushed = false;
if (use_hash2) {
hash2_any = udp6_portaddr_hash(net, &in6addr_any, hnum) &
@@ -803,6 +817,7 @@ start_lookup:
(uh->check || udp_sk(sk)->no_check6_rx)) {
if (unlikely(count == ARRAY_SIZE(stack))) {
flush_stack(stack, count, skb, ~0);
+ inner_flushed = true;
count = 0;
}
stack[count++] = sk;
@@ -821,7 +836,10 @@ start_lookup:
if (count) {
flush_stack(stack, count, skb, count - 1);
} else {
- kfree_skb(skb);
+ if (!inner_flushed)
+ UDP_INC_STATS_BH(net, UDP_MIB_IGNOREDMULTI,
+ proto == IPPROTO_UDPLITE);
+ consume_skb(skb);
}
return 0;
}
@@ -873,7 +891,7 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
*/
if (ipv6_addr_is_multicast(daddr))
return __udp6_lib_mcast_deliver(net, skb,
- saddr, daddr, udptable);
+ saddr, daddr, udptable, proto);
/* Unicast */
@@ -925,14 +943,11 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
return 0;
short_packet:
- LIMIT_NETDEBUG(KERN_DEBUG "UDP%sv6: short packet: From [%pI6c]:%u %d/%d to [%pI6c]:%u\n",
- proto == IPPROTO_UDPLITE ? "-Lite" : "",
- saddr,
- ntohs(uh->source),
- ulen,
- skb->len,
- daddr,
- ntohs(uh->dest));
+ net_dbg_ratelimited("UDP%sv6: short packet: From [%pI6c]:%u %d/%d to [%pI6c]:%u\n",
+ proto == IPPROTO_UDPLITE ? "-Lite" : "",
+ saddr, ntohs(uh->source),
+ ulen, skb->len,
+ daddr, ntohs(uh->dest));
goto discard;
csum_error:
UDP6_INC_STATS_BH(net, UDP_MIB_CSUMERRORS, proto == IPPROTO_UDPLITE);
@@ -1025,7 +1040,8 @@ static int udp_v6_push_pending_frames(struct sock *sk)
fl6 = &inet->cork.fl.u.ip6;
/* Grab the skbuff where UDP header space exists. */
- if ((skb = skb_peek(&sk->sk_write_queue)) == NULL)
+ skb = skb_peek(&sk->sk_write_queue);
+ if (skb == NULL)
goto out;
/*
@@ -1284,7 +1300,7 @@ back_from_confirm:
/* ... which is an evident application bug. --ANK */
release_sock(sk);
- LIMIT_NETDEBUG(KERN_DEBUG "udp cork app bug 2\n");
+ net_dbg_ratelimited("udp cork app bug 2\n");
err = -EINVAL;
goto out;
}
@@ -1296,7 +1312,7 @@ do_append_data:
dontfrag = np->dontfrag;
up->len += ulen;
getfrag = is_udplite ? udplite_getfrag : ip_generic_getfrag;
- err = ip6_append_data(sk, getfrag, msg->msg_iov, ulen,
+ err = ip6_append_data(sk, getfrag, msg, ulen,
sizeof(struct udphdr), hlimit, tclass, opt, &fl6,
(struct rt6_info *)dst,
corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags, dontfrag);
diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c
index 6b8f543f6ac6..b6aa8ed18257 100644
--- a/net/ipv6/udp_offload.c
+++ b/net/ipv6/udp_offload.c
@@ -42,11 +42,11 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb,
SKB_GSO_DODGY |
SKB_GSO_UDP_TUNNEL |
SKB_GSO_UDP_TUNNEL_CSUM |
+ SKB_GSO_TUNNEL_REMCSUM |
SKB_GSO_GRE |
SKB_GSO_GRE_CSUM |
SKB_GSO_IPIP |
- SKB_GSO_SIT |
- SKB_GSO_MPLS) ||
+ SKB_GSO_SIT) ||
!(type & (SKB_GSO_UDP))))
goto out;
diff --git a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c
index 91729b807c7d..f11ad1d95e0e 100644
--- a/net/ipx/af_ipx.c
+++ b/net/ipx/af_ipx.c
@@ -306,7 +306,7 @@ void ipxitf_down(struct ipx_interface *intrfc)
spin_unlock_bh(&ipx_interfaces_lock);
}
-static __inline__ void __ipxitf_put(struct ipx_interface *intrfc)
+static void __ipxitf_put(struct ipx_interface *intrfc)
{
if (atomic_dec_and_test(&intrfc->refcnt))
__ipxitf_down(intrfc);
@@ -1745,8 +1745,7 @@ static int ipx_sendmsg(struct kiocb *iocb, struct socket *sock,
memcpy(usipx->sipx_node, ipxs->dest_addr.node, IPX_NODE_LEN);
}
- rc = ipxrtr_route_packet(sk, usipx, msg->msg_iov, len,
- flags & MSG_DONTWAIT);
+ rc = ipxrtr_route_packet(sk, usipx, msg, len, flags & MSG_DONTWAIT);
if (rc >= 0)
rc = len;
out:
@@ -1764,6 +1763,7 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock,
struct ipxhdr *ipx = NULL;
struct sk_buff *skb;
int copied, rc;
+ bool locked = true;
lock_sock(sk);
/* put the autobinding in */
@@ -1790,6 +1790,8 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock,
if (sock_flag(sk, SOCK_ZAPPED))
goto out;
+ release_sock(sk);
+ locked = false;
skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
flags & MSG_DONTWAIT, &rc);
if (!skb) {
@@ -1805,8 +1807,7 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock,
msg->msg_flags |= MSG_TRUNC;
}
- rc = skb_copy_datagram_iovec(skb, sizeof(struct ipxhdr), msg->msg_iov,
- copied);
+ rc = skb_copy_datagram_msg(skb, sizeof(struct ipxhdr), msg, copied);
if (rc)
goto out_free;
if (skb->tstamp.tv64)
@@ -1826,7 +1827,8 @@ static int ipx_recvmsg(struct kiocb *iocb, struct socket *sock,
out_free:
skb_free_datagram(sk, skb);
out:
- release_sock(sk);
+ if (locked)
+ release_sock(sk);
return rc;
}
diff --git a/net/ipx/ipx_proc.c b/net/ipx/ipx_proc.c
index e15c16a517e7..c1d247ebe916 100644
--- a/net/ipx/ipx_proc.c
+++ b/net/ipx/ipx_proc.c
@@ -45,7 +45,7 @@ static int ipx_seq_interface_show(struct seq_file *seq, void *v)
}
i = list_entry(v, struct ipx_interface, node);
- seq_printf(seq, "%08lX ", (unsigned long int)ntohl(i->if_netnum));
+ seq_printf(seq, "%08X ", ntohl(i->if_netnum));
seq_printf(seq, "%02X%02X%02X%02X%02X%02X ",
i->if_node[0], i->if_node[1], i->if_node[2],
i->if_node[3], i->if_node[4], i->if_node[5]);
@@ -87,10 +87,10 @@ static int ipx_seq_route_show(struct seq_file *seq, void *v)
rt = list_entry(v, struct ipx_route, node);
- seq_printf(seq, "%08lX ", (unsigned long int)ntohl(rt->ir_net));
+ seq_printf(seq, "%08X ", ntohl(rt->ir_net));
if (rt->ir_routed)
- seq_printf(seq, "%08lX %02X%02X%02X%02X%02X%02X\n",
- (long unsigned int)ntohl(rt->ir_intrfc->if_netnum),
+ seq_printf(seq, "%08X %02X%02X%02X%02X%02X%02X\n",
+ ntohl(rt->ir_intrfc->if_netnum),
rt->ir_router_node[0], rt->ir_router_node[1],
rt->ir_router_node[2], rt->ir_router_node[3],
rt->ir_router_node[4], rt->ir_router_node[5]);
@@ -194,19 +194,19 @@ static int ipx_seq_socket_show(struct seq_file *seq, void *v)
s = v;
ipxs = ipx_sk(s);
#ifdef CONFIG_IPX_INTERN
- seq_printf(seq, "%08lX:%02X%02X%02X%02X%02X%02X:%04X ",
- (unsigned long)ntohl(ipxs->intrfc->if_netnum),
+ seq_printf(seq, "%08X:%02X%02X%02X%02X%02X%02X:%04X ",
+ ntohl(ipxs->intrfc->if_netnum),
ipxs->node[0], ipxs->node[1], ipxs->node[2], ipxs->node[3],
ipxs->node[4], ipxs->node[5], ntohs(ipxs->port));
#else
- seq_printf(seq, "%08lX:%04X ", (unsigned long) ntohl(ipxs->intrfc->if_netnum),
+ seq_printf(seq, "%08X:%04X ", ntohl(ipxs->intrfc->if_netnum),
ntohs(ipxs->port));
#endif /* CONFIG_IPX_INTERN */
if (s->sk_state != TCP_ESTABLISHED)
seq_printf(seq, "%-28s", "Not_Connected");
else {
- seq_printf(seq, "%08lX:%02X%02X%02X%02X%02X%02X:%04X ",
- (unsigned long)ntohl(ipxs->dest_addr.net),
+ seq_printf(seq, "%08X:%02X%02X%02X%02X%02X%02X:%04X ",
+ ntohl(ipxs->dest_addr.net),
ipxs->dest_addr.node[0], ipxs->dest_addr.node[1],
ipxs->dest_addr.node[2], ipxs->dest_addr.node[3],
ipxs->dest_addr.node[4], ipxs->dest_addr.node[5],
diff --git a/net/ipx/ipx_route.c b/net/ipx/ipx_route.c
index 67e7ad3d46b1..3e2a32a9f3bd 100644
--- a/net/ipx/ipx_route.c
+++ b/net/ipx/ipx_route.c
@@ -165,7 +165,7 @@ int ipxrtr_route_skb(struct sk_buff *skb)
* Route an outgoing frame from a socket.
*/
int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx,
- struct iovec *iov, size_t len, int noblock)
+ struct msghdr *msg, size_t len, int noblock)
{
struct sk_buff *skb;
struct ipx_sock *ipxs = ipx_sk(sk);
@@ -229,7 +229,7 @@ int ipxrtr_route_packet(struct sock *sk, struct sockaddr_ipx *usipx,
memcpy(ipx->ipx_dest.node, usipx->sipx_node, IPX_NODE_LEN);
ipx->ipx_dest.sock = usipx->sipx_port;
- rc = memcpy_fromiovec(skb_put(skb, len), iov, len);
+ rc = memcpy_from_msg(skb_put(skb, len), msg, len);
if (rc) {
kfree_skb(skb);
goto out_put;
diff --git a/net/ipx/sysctl_net_ipx.c b/net/ipx/sysctl_net_ipx.c
index ad7c03dedaab..0dafcc561ed6 100644
--- a/net/ipx/sysctl_net_ipx.c
+++ b/net/ipx/sysctl_net_ipx.c
@@ -9,14 +9,12 @@
#include <linux/mm.h>
#include <linux/sysctl.h>
#include <net/net_namespace.h>
+#include <net/ipx.h>
#ifndef CONFIG_SYSCTL
#error This file should not be compiled without CONFIG_SYSCTL defined
#endif
-/* From af_ipx.c */
-extern int sysctl_ipx_pprop_broadcasting;
-
static struct ctl_table ipx_table[] = {
{
.procname = "ipx_pprop_broadcasting",
diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c
index 3f3a6cbdceb7..568edc72d737 100644
--- a/net/irda/af_irda.c
+++ b/net/irda/af_irda.c
@@ -84,14 +84,12 @@ static int irda_data_indication(void *instance, void *sap, struct sk_buff *skb)
struct sock *sk;
int err;
- IRDA_DEBUG(3, "%s()\n", __func__);
-
self = instance;
sk = instance;
err = sock_queue_rcv_skb(sk, skb);
if (err) {
- IRDA_DEBUG(1, "%s(), error: no more mem!\n", __func__);
+ pr_debug("%s(), error: no more mem!\n", __func__);
self->rx_flow = FLOW_STOP;
/* When we return error, TTP will need to requeue the skb */
@@ -115,7 +113,7 @@ static void irda_disconnect_indication(void *instance, void *sap,
self = instance;
- IRDA_DEBUG(2, "%s(%p)\n", __func__, self);
+ pr_debug("%s(%p)\n", __func__, self);
/* Don't care about it, but let's not leak it */
if(skb)
@@ -123,8 +121,8 @@ static void irda_disconnect_indication(void *instance, void *sap,
sk = instance;
if (sk == NULL) {
- IRDA_DEBUG(0, "%s(%p) : BUG : sk is NULL\n",
- __func__, self);
+ pr_debug("%s(%p) : BUG : sk is NULL\n",
+ __func__, self);
return;
}
@@ -180,7 +178,7 @@ static void irda_connect_confirm(void *instance, void *sap,
self = instance;
- IRDA_DEBUG(2, "%s(%p)\n", __func__, self);
+ pr_debug("%s(%p)\n", __func__, self);
sk = instance;
if (sk == NULL) {
@@ -201,16 +199,16 @@ static void irda_connect_confirm(void *instance, void *sap,
switch (sk->sk_type) {
case SOCK_STREAM:
if (max_sdu_size != 0) {
- IRDA_ERROR("%s: max_sdu_size must be 0\n",
- __func__);
+ net_err_ratelimited("%s: max_sdu_size must be 0\n",
+ __func__);
return;
}
self->max_data_size = irttp_get_max_seg_size(self->tsap);
break;
case SOCK_SEQPACKET:
if (max_sdu_size == 0) {
- IRDA_ERROR("%s: max_sdu_size cannot be 0\n",
- __func__);
+ net_err_ratelimited("%s: max_sdu_size cannot be 0\n",
+ __func__);
return;
}
self->max_data_size = max_sdu_size;
@@ -219,8 +217,8 @@ static void irda_connect_confirm(void *instance, void *sap,
self->max_data_size = irttp_get_max_seg_size(self->tsap);
}
- IRDA_DEBUG(2, "%s(), max_data_size=%d\n", __func__,
- self->max_data_size);
+ pr_debug("%s(), max_data_size=%d\n", __func__,
+ self->max_data_size);
memcpy(&self->qos_tx, qos, sizeof(struct qos_info));
@@ -244,7 +242,7 @@ static void irda_connect_indication(void *instance, void *sap,
self = instance;
- IRDA_DEBUG(2, "%s(%p)\n", __func__, self);
+ pr_debug("%s(%p)\n", __func__, self);
sk = instance;
if (sk == NULL) {
@@ -262,8 +260,8 @@ static void irda_connect_indication(void *instance, void *sap,
switch (sk->sk_type) {
case SOCK_STREAM:
if (max_sdu_size != 0) {
- IRDA_ERROR("%s: max_sdu_size must be 0\n",
- __func__);
+ net_err_ratelimited("%s: max_sdu_size must be 0\n",
+ __func__);
kfree_skb(skb);
return;
}
@@ -271,8 +269,8 @@ static void irda_connect_indication(void *instance, void *sap,
break;
case SOCK_SEQPACKET:
if (max_sdu_size == 0) {
- IRDA_ERROR("%s: max_sdu_size cannot be 0\n",
- __func__);
+ net_err_ratelimited("%s: max_sdu_size cannot be 0\n",
+ __func__);
kfree_skb(skb);
return;
}
@@ -282,8 +280,8 @@ static void irda_connect_indication(void *instance, void *sap,
self->max_data_size = irttp_get_max_seg_size(self->tsap);
}
- IRDA_DEBUG(2, "%s(), max_data_size=%d\n", __func__,
- self->max_data_size);
+ pr_debug("%s(), max_data_size=%d\n", __func__,
+ self->max_data_size);
memcpy(&self->qos_tx, qos, sizeof(struct qos_info));
@@ -301,12 +299,10 @@ static void irda_connect_response(struct irda_sock *self)
{
struct sk_buff *skb;
- IRDA_DEBUG(2, "%s()\n", __func__);
-
skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER, GFP_KERNEL);
if (skb == NULL) {
- IRDA_DEBUG(0, "%s() Unable to allocate sk_buff!\n",
- __func__);
+ pr_debug("%s() Unable to allocate sk_buff!\n",
+ __func__);
return;
}
@@ -327,26 +323,24 @@ static void irda_flow_indication(void *instance, void *sap, LOCAL_FLOW flow)
struct irda_sock *self;
struct sock *sk;
- IRDA_DEBUG(2, "%s()\n", __func__);
-
self = instance;
sk = instance;
BUG_ON(sk == NULL);
switch (flow) {
case FLOW_STOP:
- IRDA_DEBUG(1, "%s(), IrTTP wants us to slow down\n",
- __func__);
+ pr_debug("%s(), IrTTP wants us to slow down\n",
+ __func__);
self->tx_flow = flow;
break;
case FLOW_START:
self->tx_flow = flow;
- IRDA_DEBUG(1, "%s(), IrTTP wants us to start again\n",
- __func__);
+ pr_debug("%s(), IrTTP wants us to start again\n",
+ __func__);
wake_up_interruptible(sk_sleep(sk));
break;
default:
- IRDA_DEBUG(0, "%s(), Unknown flow command!\n", __func__);
+ pr_debug("%s(), Unknown flow command!\n", __func__);
/* Unknown flow command, better stop */
self->tx_flow = flow;
break;
@@ -368,11 +362,11 @@ static void irda_getvalue_confirm(int result, __u16 obj_id,
self = priv;
if (!self) {
- IRDA_WARNING("%s: lost myself!\n", __func__);
+ net_warn_ratelimited("%s: lost myself!\n", __func__);
return;
}
- IRDA_DEBUG(2, "%s(%p)\n", __func__, self);
+ pr_debug("%s(%p)\n", __func__, self);
/* We probably don't need to make any more queries */
iriap_close(self->iriap);
@@ -380,8 +374,8 @@ static void irda_getvalue_confirm(int result, __u16 obj_id,
/* Check if request succeeded */
if (result != IAS_SUCCESS) {
- IRDA_DEBUG(1, "%s(), IAS query failed! (%d)\n", __func__,
- result);
+ pr_debug("%s(), IAS query failed! (%d)\n", __func__,
+ result);
self->errno = result; /* We really need it later */
@@ -413,11 +407,9 @@ static void irda_selective_discovery_indication(discinfo_t *discovery,
{
struct irda_sock *self;
- IRDA_DEBUG(2, "%s()\n", __func__);
-
self = priv;
if (!self) {
- IRDA_WARNING("%s: lost myself!\n", __func__);
+ net_warn_ratelimited("%s: lost myself!\n", __func__);
return;
}
@@ -440,8 +432,6 @@ static void irda_discovery_timeout(u_long priv)
{
struct irda_sock *self;
- IRDA_DEBUG(2, "%s()\n", __func__);
-
self = (struct irda_sock *) priv;
BUG_ON(self == NULL);
@@ -465,7 +455,7 @@ static int irda_open_tsap(struct irda_sock *self, __u8 tsap_sel, char *name)
notify_t notify;
if (self->tsap) {
- IRDA_DEBUG(0, "%s: busy!\n", __func__);
+ pr_debug("%s: busy!\n", __func__);
return -EBUSY;
}
@@ -483,8 +473,8 @@ static int irda_open_tsap(struct irda_sock *self, __u8 tsap_sel, char *name)
self->tsap = irttp_open_tsap(tsap_sel, DEFAULT_INITIAL_CREDIT,
&notify);
if (self->tsap == NULL) {
- IRDA_DEBUG(0, "%s(), Unable to allocate TSAP!\n",
- __func__);
+ pr_debug("%s(), Unable to allocate TSAP!\n",
+ __func__);
return -ENOMEM;
}
/* Remember which TSAP selector we actually got */
@@ -505,7 +495,7 @@ static int irda_open_lsap(struct irda_sock *self, int pid)
notify_t notify;
if (self->lsap) {
- IRDA_WARNING("%s(), busy!\n", __func__);
+ net_warn_ratelimited("%s(), busy!\n", __func__);
return -EBUSY;
}
@@ -517,7 +507,7 @@ static int irda_open_lsap(struct irda_sock *self, int pid)
self->lsap = irlmp_open_lsap(LSAP_CONNLESS, &notify, pid);
if (self->lsap == NULL) {
- IRDA_DEBUG( 0, "%s(), Unable to allocate LSAP!\n", __func__);
+ pr_debug("%s(), Unable to allocate LSAP!\n", __func__);
return -ENOMEM;
}
@@ -538,11 +528,11 @@ static int irda_open_lsap(struct irda_sock *self, int pid)
*/
static int irda_find_lsap_sel(struct irda_sock *self, char *name)
{
- IRDA_DEBUG(2, "%s(%p, %s)\n", __func__, self, name);
+ pr_debug("%s(%p, %s)\n", __func__, self, name);
if (self->iriap) {
- IRDA_WARNING("%s(): busy with a previous query\n",
- __func__);
+ net_warn_ratelimited("%s(): busy with a previous query\n",
+ __func__);
return -EBUSY;
}
@@ -577,8 +567,8 @@ static int irda_find_lsap_sel(struct irda_sock *self, char *name)
/* Get the remote TSAP selector */
switch (self->ias_result->type) {
case IAS_INTEGER:
- IRDA_DEBUG(4, "%s() int=%d\n",
- __func__, self->ias_result->t.integer);
+ pr_debug("%s() int=%d\n",
+ __func__, self->ias_result->t.integer);
if (self->ias_result->t.integer != -1)
self->dtsap_sel = self->ias_result->t.integer;
@@ -587,7 +577,7 @@ static int irda_find_lsap_sel(struct irda_sock *self, char *name)
break;
default:
self->dtsap_sel = 0;
- IRDA_DEBUG(0, "%s(), bad type!\n", __func__);
+ pr_debug("%s(), bad type!\n", __func__);
break;
}
if (self->ias_result)
@@ -625,7 +615,7 @@ static int irda_discover_daddr_and_lsap_sel(struct irda_sock *self, char *name)
__u32 daddr = DEV_ADDR_ANY; /* Address we found the service on */
__u8 dtsap_sel = 0x0; /* TSAP associated with it */
- IRDA_DEBUG(2, "%s(), name=%s\n", __func__, name);
+ pr_debug("%s(), name=%s\n", __func__, name);
/* Ask lmp for the current discovery log
* Note : we have to use irlmp_get_discoveries(), as opposed
@@ -646,8 +636,8 @@ static int irda_discover_daddr_and_lsap_sel(struct irda_sock *self, char *name)
/* Try the address in the log */
self->daddr = discoveries[i].daddr;
self->saddr = 0x0;
- IRDA_DEBUG(1, "%s(), trying daddr = %08x\n",
- __func__, self->daddr);
+ pr_debug("%s(), trying daddr = %08x\n",
+ __func__, self->daddr);
/* Query remote LM-IAS for this service */
err = irda_find_lsap_sel(self, name);
@@ -655,8 +645,8 @@ static int irda_discover_daddr_and_lsap_sel(struct irda_sock *self, char *name)
case 0:
/* We found the requested service */
if(daddr != DEV_ADDR_ANY) {
- IRDA_DEBUG(1, "%s(), discovered service ''%s'' in two different devices !!!\n",
- __func__, name);
+ pr_debug("%s(), discovered service ''%s'' in two different devices !!!\n",
+ __func__, name);
self->daddr = DEV_ADDR_ANY;
kfree(discoveries);
return -ENOTUNIQ;
@@ -670,7 +660,8 @@ static int irda_discover_daddr_and_lsap_sel(struct irda_sock *self, char *name)
break;
default:
/* Something bad did happen :-( */
- IRDA_DEBUG(0, "%s(), unexpected IAS query failure\n", __func__);
+ pr_debug("%s(), unexpected IAS query failure\n",
+ __func__);
self->daddr = DEV_ADDR_ANY;
kfree(discoveries);
return -EHOSTUNREACH;
@@ -681,8 +672,8 @@ static int irda_discover_daddr_and_lsap_sel(struct irda_sock *self, char *name)
/* Check out what we found */
if(daddr == DEV_ADDR_ANY) {
- IRDA_DEBUG(1, "%s(), cannot discover service ''%s'' in any device !!!\n",
- __func__, name);
+ pr_debug("%s(), cannot discover service ''%s'' in any device !!!\n",
+ __func__, name);
self->daddr = DEV_ADDR_ANY;
return -EADDRNOTAVAIL;
}
@@ -692,8 +683,8 @@ static int irda_discover_daddr_and_lsap_sel(struct irda_sock *self, char *name)
self->saddr = 0x0;
self->dtsap_sel = dtsap_sel;
- IRDA_DEBUG(1, "%s(), discovered requested service ''%s'' at address %08x\n",
- __func__, name, self->daddr);
+ pr_debug("%s(), discovered requested service ''%s'' at address %08x\n",
+ __func__, name, self->daddr);
return 0;
}
@@ -725,8 +716,8 @@ static int irda_getname(struct socket *sock, struct sockaddr *uaddr,
saddr.sir_addr = self->saddr;
}
- IRDA_DEBUG(1, "%s(), tsap_sel = %#x\n", __func__, saddr.sir_lsap_sel);
- IRDA_DEBUG(1, "%s(), addr = %08x\n", __func__, saddr.sir_addr);
+ pr_debug("%s(), tsap_sel = %#x\n", __func__, saddr.sir_lsap_sel);
+ pr_debug("%s(), addr = %08x\n", __func__, saddr.sir_addr);
/* uaddr_len come to us uninitialised */
*uaddr_len = sizeof (struct sockaddr_irda);
@@ -746,8 +737,6 @@ static int irda_listen(struct socket *sock, int backlog)
struct sock *sk = sock->sk;
int err = -EOPNOTSUPP;
- IRDA_DEBUG(2, "%s()\n", __func__);
-
lock_sock(sk);
if ((sk->sk_type != SOCK_STREAM) && (sk->sk_type != SOCK_SEQPACKET) &&
@@ -779,7 +768,7 @@ static int irda_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
struct irda_sock *self = irda_sk(sk);
int err;
- IRDA_DEBUG(2, "%s(%p)\n", __func__, self);
+ pr_debug("%s(%p)\n", __func__, self);
if (addr_len != sizeof(struct sockaddr_irda))
return -EINVAL;
@@ -792,7 +781,8 @@ static int irda_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
self->pid = addr->sir_lsap_sel;
err = -EOPNOTSUPP;
if (self->pid & 0x80) {
- IRDA_DEBUG(0, "%s(), extension in PID not supp!\n", __func__);
+ pr_debug("%s(), extension in PID not supp!\n",
+ __func__);
goto out;
}
err = irda_open_lsap(self, self->pid);
@@ -845,8 +835,6 @@ static int irda_accept(struct socket *sock, struct socket *newsock, int flags)
struct sk_buff *skb;
int err;
- IRDA_DEBUG(2, "%s()\n", __func__);
-
err = irda_create(sock_net(sk), newsock, sk->sk_protocol, 0);
if (err)
return err;
@@ -911,7 +899,7 @@ static int irda_accept(struct socket *sock, struct socket *newsock, int flags)
new->tsap = irttp_dup(self->tsap, new);
err = -EPERM; /* value does not seem to make sense. -arnd */
if (!new->tsap) {
- IRDA_DEBUG(0, "%s(), dup failed!\n", __func__);
+ pr_debug("%s(), dup failed!\n", __func__);
kfree_skb(skb);
goto out;
}
@@ -971,7 +959,7 @@ static int irda_connect(struct socket *sock, struct sockaddr *uaddr,
struct irda_sock *self = irda_sk(sk);
int err;
- IRDA_DEBUG(2, "%s(%p)\n", __func__, self);
+ pr_debug("%s(%p)\n", __func__, self);
lock_sock(sk);
/* Don't allow connect for Ultra sockets */
@@ -1007,13 +995,13 @@ static int irda_connect(struct socket *sock, struct sockaddr *uaddr,
/* Try to find one suitable */
err = irda_discover_daddr_and_lsap_sel(self, addr->sir_name);
if (err) {
- IRDA_DEBUG(0, "%s(), auto-connect failed!\n", __func__);
+ pr_debug("%s(), auto-connect failed!\n", __func__);
goto out;
}
} else {
/* Use the one provided by the user */
self->daddr = addr->sir_addr;
- IRDA_DEBUG(1, "%s(), daddr = %08x\n", __func__, self->daddr);
+ pr_debug("%s(), daddr = %08x\n", __func__, self->daddr);
/* If we don't have a valid service name, we assume the
* user want to connect on a specific LSAP. Prevent
@@ -1023,7 +1011,7 @@ static int irda_connect(struct socket *sock, struct sockaddr *uaddr,
/* Query remote LM-IAS using service name */
err = irda_find_lsap_sel(self, addr->sir_name);
if (err) {
- IRDA_DEBUG(0, "%s(), connect failed!\n", __func__);
+ pr_debug("%s(), connect failed!\n", __func__);
goto out;
}
} else {
@@ -1048,7 +1036,7 @@ static int irda_connect(struct socket *sock, struct sockaddr *uaddr,
self->saddr, self->daddr, NULL,
self->max_sdu_size_rx, NULL);
if (err) {
- IRDA_DEBUG(0, "%s(), connect failed!\n", __func__);
+ pr_debug("%s(), connect failed!\n", __func__);
goto out;
}
@@ -1098,8 +1086,6 @@ static int irda_create(struct net *net, struct socket *sock, int protocol,
struct sock *sk;
struct irda_sock *self;
- IRDA_DEBUG(2, "%s()\n", __func__);
-
if (net != &init_net)
return -EAFNOSUPPORT;
@@ -1119,7 +1105,7 @@ static int irda_create(struct net *net, struct socket *sock, int protocol,
return -ENOMEM;
self = irda_sk(sk);
- IRDA_DEBUG(2, "%s() : self is %p\n", __func__, self);
+ pr_debug("%s() : self is %p\n", __func__, self);
init_waitqueue_head(&self->query_wait);
@@ -1181,7 +1167,7 @@ static int irda_create(struct net *net, struct socket *sock, int protocol,
*/
static void irda_destroy_socket(struct irda_sock *self)
{
- IRDA_DEBUG(2, "%s(%p)\n", __func__, self);
+ pr_debug("%s(%p)\n", __func__, self);
/* Unregister with IrLMP */
irlmp_unregister_client(self->ckey);
@@ -1218,8 +1204,6 @@ static int irda_release(struct socket *sock)
{
struct sock *sk = sock->sk;
- IRDA_DEBUG(2, "%s()\n", __func__);
-
if (sk == NULL)
return 0;
@@ -1286,7 +1270,7 @@ static int irda_sendmsg(struct kiocb *iocb, struct socket *sock,
struct sk_buff *skb;
int err = -EPIPE;
- IRDA_DEBUG(4, "%s(), len=%zd\n", __func__, len);
+ pr_debug("%s(), len=%zd\n", __func__, len);
/* Note : socket.c set MSG_EOR on SEQPACKET sockets */
if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_EOR | MSG_CMSG_COMPAT |
@@ -1322,8 +1306,8 @@ static int irda_sendmsg(struct kiocb *iocb, struct socket *sock,
/* Check that we don't send out too big frames */
if (len > self->max_data_size) {
- IRDA_DEBUG(2, "%s(), Chopping frame from %zd to %d bytes!\n",
- __func__, len, self->max_data_size);
+ pr_debug("%s(), Chopping frame from %zd to %d bytes!\n",
+ __func__, len, self->max_data_size);
len = self->max_data_size;
}
@@ -1335,7 +1319,7 @@ static int irda_sendmsg(struct kiocb *iocb, struct socket *sock,
skb_reserve(skb, self->max_header_size + 16);
skb_reset_transport_header(skb);
skb_put(skb, len);
- err = memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len);
+ err = memcpy_from_msg(skb_transport_header(skb), msg, len);
if (err) {
kfree_skb(skb);
goto out_err;
@@ -1347,7 +1331,7 @@ static int irda_sendmsg(struct kiocb *iocb, struct socket *sock,
*/
err = irttp_data_request(self->tsap, skb);
if (err) {
- IRDA_DEBUG(0, "%s(), err=%d\n", __func__, err);
+ pr_debug("%s(), err=%d\n", __func__, err);
goto out_err;
}
@@ -1378,8 +1362,6 @@ static int irda_recvmsg_dgram(struct kiocb *iocb, struct socket *sock,
size_t copied;
int err;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
flags & MSG_DONTWAIT, &err);
if (!skb)
@@ -1389,12 +1371,12 @@ static int irda_recvmsg_dgram(struct kiocb *iocb, struct socket *sock,
copied = skb->len;
if (copied > size) {
- IRDA_DEBUG(2, "%s(), Received truncated frame (%zd < %zd)!\n",
- __func__, copied, size);
+ pr_debug("%s(), Received truncated frame (%zd < %zd)!\n",
+ __func__, copied, size);
copied = size;
msg->msg_flags |= MSG_TRUNC;
}
- skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ skb_copy_datagram_msg(skb, 0, msg, copied);
skb_free_datagram(sk, skb);
@@ -1406,7 +1388,7 @@ static int irda_recvmsg_dgram(struct kiocb *iocb, struct socket *sock,
*/
if (self->rx_flow == FLOW_STOP) {
if ((atomic_read(&sk->sk_rmem_alloc) << 2) <= sk->sk_rcvbuf) {
- IRDA_DEBUG(2, "%s(), Starting IrTTP\n", __func__);
+ pr_debug("%s(), Starting IrTTP\n", __func__);
self->rx_flow = FLOW_START;
irttp_flow_request(self->tsap, FLOW_START);
}
@@ -1428,8 +1410,6 @@ static int irda_recvmsg_stream(struct kiocb *iocb, struct socket *sock,
int target, err;
long timeo;
- IRDA_DEBUG(3, "%s()\n", __func__);
-
if ((err = sock_error(sk)) < 0)
return err;
@@ -1486,7 +1466,7 @@ static int irda_recvmsg_stream(struct kiocb *iocb, struct socket *sock,
}
chunk = min_t(unsigned int, skb->len, size);
- if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) {
+ if (memcpy_to_msg(msg, skb->data, chunk)) {
skb_queue_head(&sk->sk_receive_queue, skb);
if (copied == 0)
copied = -EFAULT;
@@ -1501,15 +1481,15 @@ static int irda_recvmsg_stream(struct kiocb *iocb, struct socket *sock,
/* put the skb back if we didn't use it up.. */
if (skb->len) {
- IRDA_DEBUG(1, "%s(), back on q!\n",
- __func__);
+ pr_debug("%s(), back on q!\n",
+ __func__);
skb_queue_head(&sk->sk_receive_queue, skb);
break;
}
kfree_skb(skb);
} else {
- IRDA_DEBUG(0, "%s() questionable!?\n", __func__);
+ pr_debug("%s() questionable!?\n", __func__);
/* put message back and return */
skb_queue_head(&sk->sk_receive_queue, skb);
@@ -1525,7 +1505,7 @@ static int irda_recvmsg_stream(struct kiocb *iocb, struct socket *sock,
*/
if (self->rx_flow == FLOW_STOP) {
if ((atomic_read(&sk->sk_rmem_alloc) << 2) <= sk->sk_rcvbuf) {
- IRDA_DEBUG(2, "%s(), Starting IrTTP\n", __func__);
+ pr_debug("%s(), Starting IrTTP\n", __func__);
self->rx_flow = FLOW_START;
irttp_flow_request(self->tsap, FLOW_START);
}
@@ -1549,7 +1529,7 @@ static int irda_sendmsg_dgram(struct kiocb *iocb, struct socket *sock,
struct sk_buff *skb;
int err;
- IRDA_DEBUG(4, "%s(), len=%zd\n", __func__, len);
+ pr_debug("%s(), len=%zd\n", __func__, len);
if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT))
return -EINVAL;
@@ -1573,9 +1553,8 @@ static int irda_sendmsg_dgram(struct kiocb *iocb, struct socket *sock,
* service, so we have no fragmentation and no coalescence
*/
if (len > self->max_data_size) {
- IRDA_DEBUG(0, "%s(), Warning to much data! "
- "Chopping frame from %zd to %d bytes!\n",
- __func__, len, self->max_data_size);
+ pr_debug("%s(), Warning too much data! Chopping frame from %zd to %d bytes!\n",
+ __func__, len, self->max_data_size);
len = self->max_data_size;
}
@@ -1588,9 +1567,9 @@ static int irda_sendmsg_dgram(struct kiocb *iocb, struct socket *sock,
skb_reserve(skb, self->max_header_size);
skb_reset_transport_header(skb);
- IRDA_DEBUG(4, "%s(), appending user data\n", __func__);
+ pr_debug("%s(), appending user data\n", __func__);
skb_put(skb, len);
- err = memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len);
+ err = memcpy_from_msg(skb_transport_header(skb), msg, len);
if (err) {
kfree_skb(skb);
goto out;
@@ -1602,7 +1581,7 @@ static int irda_sendmsg_dgram(struct kiocb *iocb, struct socket *sock,
*/
err = irttp_udata_request(self->tsap, skb);
if (err) {
- IRDA_DEBUG(0, "%s(), err=%d\n", __func__, err);
+ pr_debug("%s(), err=%d\n", __func__, err);
goto out;
}
@@ -1631,7 +1610,7 @@ static int irda_sendmsg_ultra(struct kiocb *iocb, struct socket *sock,
struct sk_buff *skb;
int err;
- IRDA_DEBUG(4, "%s(), len=%zd\n", __func__, len);
+ pr_debug("%s(), len=%zd\n", __func__, len);
err = -EINVAL;
if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT))
@@ -1659,7 +1638,8 @@ static int irda_sendmsg_ultra(struct kiocb *iocb, struct socket *sock,
pid = addr->sir_lsap_sel;
if (pid & 0x80) {
- IRDA_DEBUG(0, "%s(), extension in PID not supp!\n", __func__);
+ pr_debug("%s(), extension in PID not supp!\n",
+ __func__);
err = -EOPNOTSUPP;
goto out;
}
@@ -1668,8 +1648,8 @@ static int irda_sendmsg_ultra(struct kiocb *iocb, struct socket *sock,
* port. Jean II */
if ((self->lsap == NULL) ||
(sk->sk_state != TCP_ESTABLISHED)) {
- IRDA_DEBUG(0, "%s(), socket not bound to Ultra PID.\n",
- __func__);
+ pr_debug("%s(), socket not bound to Ultra PID.\n",
+ __func__);
err = -ENOTCONN;
goto out;
}
@@ -1682,9 +1662,8 @@ static int irda_sendmsg_ultra(struct kiocb *iocb, struct socket *sock,
* service, so we have no fragmentation and no coalescence
*/
if (len > self->max_data_size) {
- IRDA_DEBUG(0, "%s(), Warning to much data! "
- "Chopping frame from %zd to %d bytes!\n",
- __func__, len, self->max_data_size);
+ pr_debug("%s(), Warning too much data! Chopping frame from %zd to %d bytes!\n",
+ __func__, len, self->max_data_size);
len = self->max_data_size;
}
@@ -1697,9 +1676,9 @@ static int irda_sendmsg_ultra(struct kiocb *iocb, struct socket *sock,
skb_reserve(skb, self->max_header_size);
skb_reset_transport_header(skb);
- IRDA_DEBUG(4, "%s(), appending user data\n", __func__);
+ pr_debug("%s(), appending user data\n", __func__);
skb_put(skb, len);
- err = memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len);
+ err = memcpy_from_msg(skb_transport_header(skb), msg, len);
if (err) {
kfree_skb(skb);
goto out;
@@ -1708,7 +1687,7 @@ static int irda_sendmsg_ultra(struct kiocb *iocb, struct socket *sock,
err = irlmp_connless_data_request((bound ? self->lsap : NULL),
skb, pid);
if (err)
- IRDA_DEBUG(0, "%s(), err=%d\n", __func__, err);
+ pr_debug("%s(), err=%d\n", __func__, err);
out:
release_sock(sk);
return err ? : len;
@@ -1723,7 +1702,7 @@ static int irda_shutdown(struct socket *sock, int how)
struct sock *sk = sock->sk;
struct irda_sock *self = irda_sk(sk);
- IRDA_DEBUG(1, "%s(%p)\n", __func__, self);
+ pr_debug("%s(%p)\n", __func__, self);
lock_sock(sk);
@@ -1762,8 +1741,6 @@ static unsigned int irda_poll(struct file * file, struct socket *sock,
struct irda_sock *self = irda_sk(sk);
unsigned int mask;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
poll_wait(file, sk_sleep(sk), wait);
mask = 0;
@@ -1771,13 +1748,13 @@ static unsigned int irda_poll(struct file * file, struct socket *sock,
if (sk->sk_err)
mask |= POLLERR;
if (sk->sk_shutdown & RCV_SHUTDOWN) {
- IRDA_DEBUG(0, "%s(), POLLHUP\n", __func__);
+ pr_debug("%s(), POLLHUP\n", __func__);
mask |= POLLHUP;
}
/* Readable? */
if (!skb_queue_empty(&sk->sk_receive_queue)) {
- IRDA_DEBUG(4, "Socket is readable\n");
+ pr_debug("Socket is readable\n");
mask |= POLLIN | POLLRDNORM;
}
@@ -1785,7 +1762,7 @@ static unsigned int irda_poll(struct file * file, struct socket *sock,
switch (sk->sk_type) {
case SOCK_STREAM:
if (sk->sk_state == TCP_CLOSE) {
- IRDA_DEBUG(0, "%s(), POLLHUP\n", __func__);
+ pr_debug("%s(), POLLHUP\n", __func__);
mask |= POLLHUP;
}
@@ -1823,7 +1800,7 @@ static int irda_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
struct sock *sk = sock->sk;
int err;
- IRDA_DEBUG(4, "%s(), cmd=%#x\n", __func__, cmd);
+ pr_debug("%s(), cmd=%#x\n", __func__, cmd);
err = -EINVAL;
switch (cmd) {
@@ -1864,7 +1841,7 @@ static int irda_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
case SIOCSIFMETRIC:
break;
default:
- IRDA_DEBUG(1, "%s(), doing device ioctl!\n", __func__);
+ pr_debug("%s(), doing device ioctl!\n", __func__);
err = -ENOIOCTLCMD;
}
@@ -1900,7 +1877,7 @@ static int irda_setsockopt(struct socket *sock, int level, int optname,
struct ias_attrib * ias_attr; /* Attribute in IAS object */
int opt, free_ias = 0, err = 0;
- IRDA_DEBUG(2, "%s(%p)\n", __func__, self);
+ pr_debug("%s(%p)\n", __func__, self);
if (level != SOL_IRLMP)
return -ENOPROTOOPT;
@@ -2100,7 +2077,8 @@ static int irda_setsockopt(struct socket *sock, int level, int optname,
/* Check is the user space own the object */
if(ias_attr->value->owner != IAS_USER_ATTR) {
- IRDA_DEBUG(1, "%s(), attempting to delete a kernel attribute\n", __func__);
+ pr_debug("%s(), attempting to delete a kernel attribute\n",
+ __func__);
kfree(ias_opt);
err = -EPERM;
goto out;
@@ -2123,12 +2101,12 @@ static int irda_setsockopt(struct socket *sock, int level, int optname,
/* Only possible for a seqpacket service (TTP with SAR) */
if (sk->sk_type != SOCK_SEQPACKET) {
- IRDA_DEBUG(2, "%s(), setting max_sdu_size = %d\n",
- __func__, opt);
+ pr_debug("%s(), setting max_sdu_size = %d\n",
+ __func__, opt);
self->max_sdu_size_rx = opt;
} else {
- IRDA_WARNING("%s: not allowed to set MAXSDUSIZE for this socket type!\n",
- __func__);
+ net_warn_ratelimited("%s: not allowed to set MAXSDUSIZE for this socket type!\n",
+ __func__);
err = -ENOPROTOOPT;
goto out;
}
@@ -2256,7 +2234,7 @@ static int irda_getsockopt(struct socket *sock, int level, int optname,
int err = 0;
int offset, total;
- IRDA_DEBUG(2, "%s(%p)\n", __func__, self);
+ pr_debug("%s(%p)\n", __func__, self);
if (level != SOL_IRLMP)
return -ENOPROTOOPT;
@@ -2439,8 +2417,8 @@ bed:
/* Check that we can proceed with IAP */
if (self->iriap) {
- IRDA_WARNING("%s: busy with a previous query\n",
- __func__);
+ net_warn_ratelimited("%s: busy with a previous query\n",
+ __func__);
kfree(ias_opt);
err = -EBUSY;
goto out;
@@ -2544,7 +2522,8 @@ bed:
/* Wait until a node is discovered */
if (!self->cachedaddr) {
- IRDA_DEBUG(1, "%s(), nothing discovered yet, going to sleep...\n", __func__);
+ pr_debug("%s(), nothing discovered yet, going to sleep...\n",
+ __func__);
/* Set watchdog timer to expire in <val> ms. */
self->errno = 0;
@@ -2560,14 +2539,14 @@ bed:
/* If watchdog is still activated, kill it! */
del_timer(&(self->watchdog));
- IRDA_DEBUG(1, "%s(), ...waking up !\n", __func__);
+ pr_debug("%s(), ...waking up !\n", __func__);
if (err != 0)
goto out;
}
else
- IRDA_DEBUG(1, "%s(), found immediately !\n",
- __func__);
+ pr_debug("%s(), found immediately !\n",
+ __func__);
/* Tell IrLMP that we have been notified */
irlmp_update_client(self->ckey, self->mask.word,
diff --git a/net/irda/discovery.c b/net/irda/discovery.c
index 6786e7f193d2..364d70aed068 100644
--- a/net/irda/discovery.c
+++ b/net/irda/discovery.c
@@ -112,8 +112,6 @@ void irlmp_add_discovery_log(hashbin_t *cachelog, hashbin_t *log)
{
discovery_t *discovery;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
/*
* If log is missing this means that IrLAP was unable to perform the
* discovery, so restart discovery again with just the half timeout
@@ -159,8 +157,6 @@ void irlmp_expire_discoveries(hashbin_t *log, __u32 saddr, int force)
int i = 0; /* How many we expired */
IRDA_ASSERT(log != NULL, return;);
- IRDA_DEBUG(4, "%s()\n", __func__);
-
spin_lock_irqsave(&log->hb_spinlock, flags);
discovery = (discovery_t *) hashbin_get_first(log);
@@ -232,10 +228,10 @@ void irlmp_dump_discoveries(hashbin_t *log)
discovery = (discovery_t *) hashbin_get_first(log);
while (discovery != NULL) {
- IRDA_DEBUG(0, "Discovery:\n");
- IRDA_DEBUG(0, " daddr=%08x\n", discovery->data.daddr);
- IRDA_DEBUG(0, " saddr=%08x\n", discovery->data.saddr);
- IRDA_DEBUG(0, " nickname=%s\n", discovery->data.info);
+ pr_debug("Discovery:\n");
+ pr_debug(" daddr=%08x\n", discovery->data.daddr);
+ pr_debug(" saddr=%08x\n", discovery->data.saddr);
+ pr_debug(" nickname=%s\n", discovery->data.info);
discovery = (discovery_t *) hashbin_get_next(log);
}
diff --git a/net/irda/ircomm/ircomm_core.c b/net/irda/ircomm/ircomm_core.c
index 4490a675b1bb..3af219545f6d 100644
--- a/net/irda/ircomm/ircomm_core.c
+++ b/net/irda/ircomm/ircomm_core.c
@@ -69,7 +69,8 @@ static int __init ircomm_init(void)
{
ircomm = hashbin_new(HB_LOCK);
if (ircomm == NULL) {
- IRDA_ERROR("%s(), can't allocate hashbin!\n", __func__);
+ net_err_ratelimited("%s(), can't allocate hashbin!\n",
+ __func__);
return -ENOMEM;
}
@@ -83,15 +84,13 @@ static int __init ircomm_init(void)
}
#endif /* CONFIG_PROC_FS */
- IRDA_MESSAGE("IrCOMM protocol (Dag Brattli)\n");
+ net_info_ratelimited("IrCOMM protocol (Dag Brattli)\n");
return 0;
}
static void __exit ircomm_cleanup(void)
{
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
hashbin_delete(ircomm, (FREE_FUNC) __ircomm_close);
#ifdef CONFIG_PROC_FS
@@ -110,8 +109,8 @@ struct ircomm_cb *ircomm_open(notify_t *notify, __u8 service_type, int line)
struct ircomm_cb *self = NULL;
int ret;
- IRDA_DEBUG(2, "%s(), service_type=0x%02x\n", __func__ ,
- service_type);
+ pr_debug("%s(), service_type=0x%02x\n", __func__ ,
+ service_type);
IRDA_ASSERT(ircomm != NULL, return NULL;);
@@ -154,8 +153,6 @@ EXPORT_SYMBOL(ircomm_open);
*/
static int __ircomm_close(struct ircomm_cb *self)
{
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
/* Disconnect link if any */
ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, NULL, NULL);
@@ -190,8 +187,6 @@ int ircomm_close(struct ircomm_cb *self)
IRDA_ASSERT(self != NULL, return -EIO;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EIO;);
- IRDA_DEBUG(0, "%s()\n", __func__ );
-
entry = hashbin_remove(ircomm, self->line, NULL);
IRDA_ASSERT(entry == self, return -1;);
@@ -215,8 +210,6 @@ int ircomm_connect_request(struct ircomm_cb *self, __u8 dlsap_sel,
struct ircomm_info info;
int ret;
- IRDA_DEBUG(2 , "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
@@ -242,8 +235,6 @@ EXPORT_SYMBOL(ircomm_connect_request);
void ircomm_connect_indication(struct ircomm_cb *self, struct sk_buff *skb,
struct ircomm_info *info)
{
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
/*
* If there are any data hiding in the control channel, we must
* deliver it first. The side effect is that the control channel
@@ -254,7 +245,7 @@ void ircomm_connect_indication(struct ircomm_cb *self, struct sk_buff *skb,
info->qos, info->max_data_size,
info->max_header_size, skb);
else {
- IRDA_DEBUG(0, "%s(), missing handler\n", __func__ );
+ pr_debug("%s(), missing handler\n", __func__);
}
}
@@ -271,8 +262,6 @@ int ircomm_connect_response(struct ircomm_cb *self, struct sk_buff *userdata)
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
ret = ircomm_do_event(self, IRCOMM_CONNECT_RESPONSE, userdata, NULL);
return ret;
@@ -289,15 +278,13 @@ EXPORT_SYMBOL(ircomm_connect_response);
void ircomm_connect_confirm(struct ircomm_cb *self, struct sk_buff *skb,
struct ircomm_info *info)
{
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
if (self->notify.connect_confirm )
self->notify.connect_confirm(self->notify.instance,
self, info->qos,
info->max_data_size,
info->max_header_size, skb);
else {
- IRDA_DEBUG(0, "%s(), missing handler\n", __func__ );
+ pr_debug("%s(), missing handler\n", __func__);
}
}
@@ -311,8 +298,6 @@ int ircomm_data_request(struct ircomm_cb *self, struct sk_buff *skb)
{
int ret;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return -EFAULT;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;);
IRDA_ASSERT(skb != NULL, return -EFAULT;);
@@ -332,14 +317,12 @@ EXPORT_SYMBOL(ircomm_data_request);
*/
void ircomm_data_indication(struct ircomm_cb *self, struct sk_buff *skb)
{
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(skb->len > 0, return;);
if (self->notify.data_indication)
self->notify.data_indication(self->notify.instance, self, skb);
else {
- IRDA_DEBUG(0, "%s(), missing handler\n", __func__ );
+ pr_debug("%s(), missing handler\n", __func__);
}
}
@@ -364,8 +347,8 @@ void ircomm_process_data(struct ircomm_cb *self, struct sk_buff *skb)
* fine
*/
if (unlikely(skb->len < (clen + 1))) {
- IRDA_DEBUG(2, "%s() throwing away illegal frame\n",
- __func__ );
+ pr_debug("%s() throwing away illegal frame\n",
+ __func__);
return;
}
@@ -383,8 +366,8 @@ void ircomm_process_data(struct ircomm_cb *self, struct sk_buff *skb)
if (skb->len)
ircomm_data_indication(self, skb);
else {
- IRDA_DEBUG(4, "%s(), data was control info only!\n",
- __func__ );
+ pr_debug("%s(), data was control info only!\n",
+ __func__);
}
}
@@ -398,8 +381,6 @@ int ircomm_control_request(struct ircomm_cb *self, struct sk_buff *skb)
{
int ret;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return -EFAULT;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;);
IRDA_ASSERT(skb != NULL, return -EFAULT;);
@@ -420,8 +401,6 @@ EXPORT_SYMBOL(ircomm_control_request);
static void ircomm_control_indication(struct ircomm_cb *self,
struct sk_buff *skb, int clen)
{
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
/* Use udata for delivering data on the control channel */
if (self->notify.udata_indication) {
struct sk_buff *ctrl_skb;
@@ -441,7 +420,7 @@ static void ircomm_control_indication(struct ircomm_cb *self,
* see ircomm_tty_control_indication(). */
dev_kfree_skb(ctrl_skb);
} else {
- IRDA_DEBUG(0, "%s(), missing handler\n", __func__ );
+ pr_debug("%s(), missing handler\n", __func__);
}
}
@@ -456,8 +435,6 @@ int ircomm_disconnect_request(struct ircomm_cb *self, struct sk_buff *userdata)
struct ircomm_info info;
int ret;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
@@ -477,15 +454,13 @@ EXPORT_SYMBOL(ircomm_disconnect_request);
void ircomm_disconnect_indication(struct ircomm_cb *self, struct sk_buff *skb,
struct ircomm_info *info)
{
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
IRDA_ASSERT(info != NULL, return;);
if (self->notify.disconnect_indication) {
self->notify.disconnect_indication(self->notify.instance, self,
info->reason, skb);
} else {
- IRDA_DEBUG(0, "%s(), missing handler\n", __func__ );
+ pr_debug("%s(), missing handler\n", __func__);
}
}
@@ -497,8 +472,6 @@ void ircomm_disconnect_indication(struct ircomm_cb *self, struct sk_buff *skb,
*/
void ircomm_flow_request(struct ircomm_cb *self, LOCAL_FLOW flow)
{
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
diff --git a/net/irda/ircomm/ircomm_event.c b/net/irda/ircomm/ircomm_event.c
index b172c6522328..b0730ac9f388 100644
--- a/net/irda/ircomm/ircomm_event.c
+++ b/net/irda/ircomm/ircomm_event.c
@@ -54,8 +54,7 @@ const char *const ircomm_state[] = {
"IRCOMM_CONN",
};
-#ifdef CONFIG_IRDA_DEBUG
-static const char *const ircomm_event[] = {
+static const char *const ircomm_event[] __maybe_unused = {
"IRCOMM_CONNECT_REQUEST",
"IRCOMM_CONNECT_RESPONSE",
"IRCOMM_TTP_CONNECT_INDICATION",
@@ -73,7 +72,6 @@ static const char *const ircomm_event[] = {
"IRCOMM_CONTROL_REQUEST",
"IRCOMM_CONTROL_INDICATION",
};
-#endif /* CONFIG_IRDA_DEBUG */
static int (*state[])(struct ircomm_cb *self, IRCOMM_EVENT event,
struct sk_buff *skb, struct ircomm_info *info) =
@@ -106,8 +104,8 @@ static int ircomm_state_idle(struct ircomm_cb *self, IRCOMM_EVENT event,
ircomm_connect_indication(self, skb, info);
break;
default:
- IRDA_DEBUG(4, "%s(), unknown event: %s\n", __func__ ,
- ircomm_event[event]);
+ pr_debug("%s(), unknown event: %s\n", __func__ ,
+ ircomm_event[event]);
ret = -EINVAL;
}
return ret;
@@ -136,8 +134,8 @@ static int ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event,
ircomm_disconnect_indication(self, skb, info);
break;
default:
- IRDA_DEBUG(0, "%s(), unknown event: %s\n", __func__ ,
- ircomm_event[event]);
+ pr_debug("%s(), unknown event: %s\n", __func__ ,
+ ircomm_event[event]);
ret = -EINVAL;
}
return ret;
@@ -169,8 +167,8 @@ static int ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event,
ircomm_disconnect_indication(self, skb, info);
break;
default:
- IRDA_DEBUG(0, "%s(), unknown event = %s\n", __func__ ,
- ircomm_event[event]);
+ pr_debug("%s(), unknown event = %s\n", __func__ ,
+ ircomm_event[event]);
ret = -EINVAL;
}
return ret;
@@ -211,8 +209,8 @@ static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event,
ret = self->issue.disconnect_request(self, skb, info);
break;
default:
- IRDA_DEBUG(0, "%s(), unknown event = %s\n", __func__ ,
- ircomm_event[event]);
+ pr_debug("%s(), unknown event = %s\n", __func__ ,
+ ircomm_event[event]);
ret = -EINVAL;
}
return ret;
@@ -227,8 +225,8 @@ static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event,
int ircomm_do_event(struct ircomm_cb *self, IRCOMM_EVENT event,
struct sk_buff *skb, struct ircomm_info *info)
{
- IRDA_DEBUG(4, "%s: state=%s, event=%s\n", __func__ ,
- ircomm_state[self->state], ircomm_event[event]);
+ pr_debug("%s: state=%s, event=%s\n", __func__ ,
+ ircomm_state[self->state], ircomm_event[event]);
return (*state[self->state])(self, event, skb, info);
}
@@ -243,6 +241,6 @@ void ircomm_next_state(struct ircomm_cb *self, IRCOMM_STATE state)
{
self->state = state;
- IRDA_DEBUG(4, "%s: next state=%s, service type=%d\n", __func__ ,
- ircomm_state[self->state], self->service_type);
+ pr_debug("%s: next state=%s, service type=%d\n", __func__ ,
+ ircomm_state[self->state], self->service_type);
}
diff --git a/net/irda/ircomm/ircomm_lmp.c b/net/irda/ircomm/ircomm_lmp.c
index 6536114adf37..e4cc847bb933 100644
--- a/net/irda/ircomm/ircomm_lmp.c
+++ b/net/irda/ircomm/ircomm_lmp.c
@@ -52,8 +52,6 @@ static int ircomm_lmp_connect_request(struct ircomm_cb *self,
{
int ret = 0;
- IRDA_DEBUG(0, "%s()\n", __func__ );
-
/* Don't forget to refcount it - should be NULL anyway */
if(userdata)
skb_get(userdata);
@@ -74,8 +72,6 @@ static int ircomm_lmp_connect_response(struct ircomm_cb *self,
{
struct sk_buff *tx_skb;
- IRDA_DEBUG(0, "%s()\n", __func__ );
-
/* Any userdata supplied? */
if (userdata == NULL) {
tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
@@ -107,8 +103,6 @@ static int ircomm_lmp_disconnect_request(struct ircomm_cb *self,
struct sk_buff *tx_skb;
int ret;
- IRDA_DEBUG(0, "%s()\n", __func__ );
-
if (!userdata) {
tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
if (!tx_skb)
@@ -144,13 +138,11 @@ static void ircomm_lmp_flow_control(struct sk_buff *skb)
cb = (struct irda_skb_cb *) skb->cb;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
line = cb->line;
self = (struct ircomm_cb *) hashbin_lock_find(ircomm, line, NULL);
if (!self) {
- IRDA_DEBUG(2, "%s(), didn't find myself\n", __func__ );
+ pr_debug("%s(), didn't find myself\n", __func__);
return;
}
@@ -160,7 +152,7 @@ static void ircomm_lmp_flow_control(struct sk_buff *skb)
self->pkt_count--;
if ((self->pkt_count < 2) && (self->flow_status == FLOW_STOP)) {
- IRDA_DEBUG(2, "%s(), asking TTY to start again!\n", __func__ );
+ pr_debug("%s(), asking TTY to start again!\n", __func__);
self->flow_status = FLOW_START;
if (self->notify.flow_indication)
self->notify.flow_indication(self->notify.instance,
@@ -187,7 +179,7 @@ static int ircomm_lmp_data_request(struct ircomm_cb *self,
cb->line = self->line;
- IRDA_DEBUG(4, "%s(), sending frame\n", __func__ );
+ pr_debug("%s(), sending frame\n", __func__);
/* Don't forget to refcount it - see ircomm_tty_do_softint() */
skb_get(skb);
@@ -196,7 +188,7 @@ static int ircomm_lmp_data_request(struct ircomm_cb *self,
skb->destructor = ircomm_lmp_flow_control;
if ((self->pkt_count++ > 7) && (self->flow_status == FLOW_START)) {
- IRDA_DEBUG(2, "%s(), asking TTY to slow down!\n", __func__ );
+ pr_debug("%s(), asking TTY to slow down!\n", __func__);
self->flow_status = FLOW_STOP;
if (self->notify.flow_indication)
self->notify.flow_indication(self->notify.instance,
@@ -204,7 +196,7 @@ static int ircomm_lmp_data_request(struct ircomm_cb *self,
}
ret = irlmp_data_request(self->lsap, skb);
if (ret) {
- IRDA_ERROR("%s(), failed\n", __func__);
+ net_err_ratelimited("%s(), failed\n", __func__);
/* irlmp_data_request already free the packet */
}
@@ -222,8 +214,6 @@ static int ircomm_lmp_data_indication(void *instance, void *sap,
{
struct ircomm_cb *self = (struct ircomm_cb *) instance;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
IRDA_ASSERT(skb != NULL, return -1;);
@@ -252,8 +242,6 @@ static void ircomm_lmp_connect_confirm(void *instance, void *sap,
struct ircomm_cb *self = (struct ircomm_cb *) instance;
struct ircomm_info info;
- IRDA_DEBUG(0, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
IRDA_ASSERT(skb != NULL, return;);
@@ -285,8 +273,6 @@ static void ircomm_lmp_connect_indication(void *instance, void *sap,
struct ircomm_cb *self = (struct ircomm_cb *)instance;
struct ircomm_info info;
- IRDA_DEBUG(0, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
IRDA_ASSERT(skb != NULL, return;);
@@ -315,8 +301,6 @@ static void ircomm_lmp_disconnect_indication(void *instance, void *sap,
struct ircomm_cb *self = (struct ircomm_cb *) instance;
struct ircomm_info info;
- IRDA_DEBUG(0, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
@@ -338,8 +322,6 @@ int ircomm_open_lsap(struct ircomm_cb *self)
{
notify_t notify;
- IRDA_DEBUG(0, "%s()\n", __func__ );
-
/* Register callbacks */
irda_notify_init(&notify);
notify.data_indication = ircomm_lmp_data_indication;
@@ -351,7 +333,7 @@ int ircomm_open_lsap(struct ircomm_cb *self)
self->lsap = irlmp_open_lsap(LSAP_ANY, &notify, 0);
if (!self->lsap) {
- IRDA_DEBUG(0,"%sfailed to allocate tsap\n", __func__ );
+ pr_debug("%sfailed to allocate tsap\n", __func__);
return -1;
}
self->slsap_sel = self->lsap->slsap_sel;
diff --git a/net/irda/ircomm/ircomm_param.c b/net/irda/ircomm/ircomm_param.c
index f80b1a6a244b..3c4caa60c926 100644
--- a/net/irda/ircomm/ircomm_param.c
+++ b/net/irda/ircomm/ircomm_param.c
@@ -61,12 +61,12 @@ static int ircomm_param_dte(void *instance, irda_param_t *param, int get);
static int ircomm_param_dce(void *instance, irda_param_t *param, int get);
static int ircomm_param_poll(void *instance, irda_param_t *param, int get);
-static pi_minor_info_t pi_minor_call_table_common[] = {
+static const pi_minor_info_t pi_minor_call_table_common[] = {
{ ircomm_param_service_type, PV_INT_8_BITS },
{ ircomm_param_port_type, PV_INT_8_BITS },
{ ircomm_param_port_name, PV_STRING }
};
-static pi_minor_info_t pi_minor_call_table_non_raw[] = {
+static const pi_minor_info_t pi_minor_call_table_non_raw[] = {
{ ircomm_param_data_rate, PV_INT_32_BITS | PV_BIG_ENDIAN },
{ ircomm_param_data_format, PV_INT_8_BITS },
{ ircomm_param_flow_control, PV_INT_8_BITS },
@@ -74,13 +74,13 @@ static pi_minor_info_t pi_minor_call_table_non_raw[] = {
{ ircomm_param_enq_ack, PV_INT_16_BITS },
{ ircomm_param_line_status, PV_INT_8_BITS }
};
-static pi_minor_info_t pi_minor_call_table_9_wire[] = {
+static const pi_minor_info_t pi_minor_call_table_9_wire[] = {
{ ircomm_param_dte, PV_INT_8_BITS },
{ ircomm_param_dce, PV_INT_8_BITS },
{ ircomm_param_poll, PV_NO_VALUE },
};
-static pi_major_info_t pi_major_call_table[] = {
+static const pi_major_info_t pi_major_call_table[] = {
{ pi_minor_call_table_common, 3 },
{ pi_minor_call_table_non_raw, 6 },
{ pi_minor_call_table_9_wire, 3 }
@@ -101,8 +101,6 @@ int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush)
struct sk_buff *skb;
int count;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
@@ -130,7 +128,8 @@ int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush)
count = irda_param_insert(self, pi, skb_tail_pointer(skb),
skb_tailroom(skb), &ircomm_param_info);
if (count < 0) {
- IRDA_WARNING("%s(), no room for parameter!\n", __func__);
+ net_warn_ratelimited("%s(), no room for parameter!\n",
+ __func__);
spin_unlock_irqrestore(&self->spinlock, flags);
return -1;
}
@@ -138,7 +137,7 @@ int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush)
spin_unlock_irqrestore(&self->spinlock, flags);
- IRDA_DEBUG(2, "%s(), skb->len=%d\n", __func__ , skb->len);
+ pr_debug("%s(), skb->len=%d\n", __func__ , skb->len);
if (flush) {
/* ircomm_tty_do_softint will take care of the rest */
@@ -172,12 +171,11 @@ static int ircomm_param_service_type(void *instance, irda_param_t *param,
/* Find all common service types */
service_type &= self->service_type;
if (!service_type) {
- IRDA_DEBUG(2,
- "%s(), No common service type to use!\n", __func__ );
+ pr_debug("%s(), No common service type to use!\n", __func__);
return -1;
}
- IRDA_DEBUG(0, "%s(), services in common=%02x\n", __func__ ,
- service_type);
+ pr_debug("%s(), services in common=%02x\n", __func__ ,
+ service_type);
/*
* Now choose a preferred service type of those available
@@ -191,8 +189,8 @@ static int ircomm_param_service_type(void *instance, irda_param_t *param,
else if (service_type & IRCOMM_3_WIRE_RAW)
self->settings.service_type = IRCOMM_3_WIRE_RAW;
- IRDA_DEBUG(0, "%s(), resulting service type=0x%02x\n", __func__ ,
- self->settings.service_type);
+ pr_debug("%s(), resulting service type=0x%02x\n", __func__ ,
+ self->settings.service_type);
/*
* Now the line is ready for some communication. Check if we are a
@@ -234,8 +232,8 @@ static int ircomm_param_port_type(void *instance, irda_param_t *param, int get)
else {
self->settings.port_type = (__u8) param->pv.i;
- IRDA_DEBUG(0, "%s(), port type=%d\n", __func__ ,
- self->settings.port_type);
+ pr_debug("%s(), port type=%d\n", __func__ ,
+ self->settings.port_type);
}
return 0;
}
@@ -254,9 +252,9 @@ static int ircomm_param_port_name(void *instance, irda_param_t *param, int get)
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
if (get) {
- IRDA_DEBUG(0, "%s(), not imp!\n", __func__ );
+ pr_debug("%s(), not imp!\n", __func__);
} else {
- IRDA_DEBUG(0, "%s(), port-name=%s\n", __func__ , param->pv.c);
+ pr_debug("%s(), port-name=%s\n", __func__ , param->pv.c);
strncpy(self->settings.port_name, param->pv.c, 32);
}
@@ -281,7 +279,7 @@ static int ircomm_param_data_rate(void *instance, irda_param_t *param, int get)
else
self->settings.data_rate = param->pv.i;
- IRDA_DEBUG(2, "%s(), data rate = %d\n", __func__ , param->pv.i);
+ pr_debug("%s(), data rate = %d\n", __func__ , param->pv.i);
return 0;
}
@@ -327,7 +325,7 @@ static int ircomm_param_flow_control(void *instance, irda_param_t *param,
else
self->settings.flow_control = (__u8) param->pv.i;
- IRDA_DEBUG(1, "%s(), flow control = 0x%02x\n", __func__ , (__u8) param->pv.i);
+ pr_debug("%s(), flow control = 0x%02x\n", __func__ , (__u8)param->pv.i);
return 0;
}
@@ -353,8 +351,8 @@ static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get)
self->settings.xonxoff[1] = (__u16) param->pv.i >> 8;
}
- IRDA_DEBUG(0, "%s(), XON/XOFF = 0x%02x,0x%02x\n", __func__ ,
- param->pv.i & 0xff, param->pv.i >> 8);
+ pr_debug("%s(), XON/XOFF = 0x%02x,0x%02x\n", __func__ ,
+ param->pv.i & 0xff, param->pv.i >> 8);
return 0;
}
@@ -380,8 +378,8 @@ static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get)
self->settings.enqack[1] = (__u16) param->pv.i >> 8;
}
- IRDA_DEBUG(0, "%s(), ENQ/ACK = 0x%02x,0x%02x\n", __func__ ,
- param->pv.i & 0xff, param->pv.i >> 8);
+ pr_debug("%s(), ENQ/ACK = 0x%02x,0x%02x\n", __func__ ,
+ param->pv.i & 0xff, param->pv.i >> 8);
return 0;
}
@@ -395,7 +393,7 @@ static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get)
static int ircomm_param_line_status(void *instance, irda_param_t *param,
int get)
{
- IRDA_DEBUG(2, "%s(), not impl.\n", __func__ );
+ pr_debug("%s(), not impl.\n", __func__);
return 0;
}
@@ -456,7 +454,7 @@ static int ircomm_param_dce(void *instance, irda_param_t *param, int get)
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
__u8 dce;
- IRDA_DEBUG(1, "%s(), dce = 0x%02x\n", __func__ , (__u8) param->pv.i);
+ pr_debug("%s(), dce = 0x%02x\n", __func__ , (__u8)param->pv.i);
dce = (__u8) param->pv.i;
@@ -468,7 +466,7 @@ static int ircomm_param_dce(void *instance, irda_param_t *param, int get)
/* Check if any of the settings have changed */
if (dce & 0x0f) {
if (dce & IRCOMM_DELTA_CTS) {
- IRDA_DEBUG(2, "%s(), CTS\n", __func__ );
+ pr_debug("%s(), CTS\n", __func__);
}
}
diff --git a/net/irda/ircomm/ircomm_ttp.c b/net/irda/ircomm/ircomm_ttp.c
index d362d711b79c..4b81e0934770 100644
--- a/net/irda/ircomm/ircomm_ttp.c
+++ b/net/irda/ircomm/ircomm_ttp.c
@@ -76,8 +76,6 @@ int ircomm_open_tsap(struct ircomm_cb *self)
{
notify_t notify;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
/* Register callbacks */
irda_notify_init(&notify);
notify.data_indication = ircomm_ttp_data_indication;
@@ -91,7 +89,7 @@ int ircomm_open_tsap(struct ircomm_cb *self)
self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT,
&notify);
if (!self->tsap) {
- IRDA_DEBUG(0, "%sfailed to allocate tsap\n", __func__ );
+ pr_debug("%sfailed to allocate tsap\n", __func__);
return -1;
}
self->slsap_sel = self->tsap->stsap_sel;
@@ -119,8 +117,6 @@ static int ircomm_ttp_connect_request(struct ircomm_cb *self,
{
int ret = 0;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
/* Don't forget to refcount it - should be NULL anyway */
if(userdata)
skb_get(userdata);
@@ -143,8 +139,6 @@ static int ircomm_ttp_connect_response(struct ircomm_cb *self,
{
int ret;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
/* Don't forget to refcount it - should be NULL anyway */
if(userdata)
skb_get(userdata);
@@ -171,7 +165,7 @@ static int ircomm_ttp_data_request(struct ircomm_cb *self,
IRDA_ASSERT(skb != NULL, return -1;);
- IRDA_DEBUG(2, "%s(), clen=%d\n", __func__ , clen);
+ pr_debug("%s(), clen=%d\n", __func__ , clen);
/*
* Insert clen field, currently we either send data only, or control
@@ -188,7 +182,7 @@ static int ircomm_ttp_data_request(struct ircomm_cb *self,
ret = irttp_data_request(self->tsap, skb);
if (ret) {
- IRDA_ERROR("%s(), failed\n", __func__);
+ net_err_ratelimited("%s(), failed\n", __func__);
/* irttp_data_request already free the packet */
}
@@ -206,8 +200,6 @@ static int ircomm_ttp_data_indication(void *instance, void *sap,
{
struct ircomm_cb *self = (struct ircomm_cb *) instance;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;);
IRDA_ASSERT(skb != NULL, return -1;);
@@ -229,16 +221,14 @@ static void ircomm_ttp_connect_confirm(void *instance, void *sap,
struct ircomm_cb *self = (struct ircomm_cb *) instance;
struct ircomm_info info;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
IRDA_ASSERT(skb != NULL, return;);
IRDA_ASSERT(qos != NULL, goto out;);
if (max_sdu_size != TTP_SAR_DISABLE) {
- IRDA_ERROR("%s(), SAR not allowed for IrCOMM!\n",
- __func__);
+ net_err_ratelimited("%s(), SAR not allowed for IrCOMM!\n",
+ __func__);
goto out;
}
@@ -270,16 +260,14 @@ static void ircomm_ttp_connect_indication(void *instance, void *sap,
struct ircomm_cb *self = (struct ircomm_cb *)instance;
struct ircomm_info info;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
IRDA_ASSERT(skb != NULL, return;);
IRDA_ASSERT(qos != NULL, goto out;);
if (max_sdu_size != TTP_SAR_DISABLE) {
- IRDA_ERROR("%s(), SAR not allowed for IrCOMM!\n",
- __func__);
+ net_err_ratelimited("%s(), SAR not allowed for IrCOMM!\n",
+ __func__);
goto out;
}
@@ -329,8 +317,6 @@ static void ircomm_ttp_disconnect_indication(void *instance, void *sap,
struct ircomm_cb *self = (struct ircomm_cb *) instance;
struct ircomm_info info;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
@@ -354,8 +340,6 @@ static void ircomm_ttp_flow_indication(void *instance, void *sap,
{
struct ircomm_cb *self = (struct ircomm_cb *) instance;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;);
diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c
index 61ceb4cdb4a2..40695b9751c1 100644
--- a/net/irda/ircomm/ircomm_tty.c
+++ b/net/irda/ircomm/ircomm_tty.c
@@ -147,7 +147,8 @@ static int __init ircomm_tty_init(void)
return -ENOMEM;
ircomm_tty = hashbin_new(HB_LOCK);
if (ircomm_tty == NULL) {
- IRDA_ERROR("%s(), can't allocate hashbin!\n", __func__);
+ net_err_ratelimited("%s(), can't allocate hashbin!\n",
+ __func__);
put_tty_driver(driver);
return -ENOMEM;
}
@@ -163,8 +164,8 @@ static int __init ircomm_tty_init(void)
driver->flags = TTY_DRIVER_REAL_RAW;
tty_set_operations(driver, &ops);
if (tty_register_driver(driver)) {
- IRDA_ERROR("%s(): Couldn't register serial driver\n",
- __func__);
+ net_err_ratelimited("%s(): Couldn't register serial driver\n",
+ __func__);
put_tty_driver(driver);
return -1;
}
@@ -173,8 +174,6 @@ static int __init ircomm_tty_init(void)
static void __exit __ircomm_tty_cleanup(struct ircomm_tty_cb *self)
{
- IRDA_DEBUG(0, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
@@ -195,12 +194,10 @@ static void __exit ircomm_tty_cleanup(void)
{
int ret;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
ret = tty_unregister_driver(driver);
if (ret) {
- IRDA_ERROR("%s(), failed to unregister driver\n",
- __func__);
+ net_err_ratelimited("%s(), failed to unregister driver\n",
+ __func__);
return;
}
@@ -219,14 +216,12 @@ static int ircomm_tty_startup(struct ircomm_tty_cb *self)
notify_t notify;
int ret = -ENODEV;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
/* Check if already open */
if (test_and_set_bit(ASYNCB_INITIALIZED, &self->port.flags)) {
- IRDA_DEBUG(2, "%s(), already open so break out!\n", __func__ );
+ pr_debug("%s(), already open so break out!\n", __func__);
return 0;
}
@@ -256,7 +251,7 @@ static int ircomm_tty_startup(struct ircomm_tty_cb *self)
/* Connect IrCOMM link with remote device */
ret = ircomm_tty_attach_cable(self);
if (ret < 0) {
- IRDA_ERROR("%s(), error attaching cable!\n", __func__);
+ net_err_ratelimited("%s(), error attaching cable!\n", __func__);
goto err;
}
@@ -281,8 +276,6 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self,
int do_clocal = 0;
unsigned long flags;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
/*
* If non-blocking mode is set, or the port is not enabled,
* then make the check up front and then exit.
@@ -297,12 +290,12 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self,
if (tty->termios.c_cflag & CBAUD)
tty_port_raise_dtr_rts(port);
port->flags |= ASYNC_NORMAL_ACTIVE;
- IRDA_DEBUG(1, "%s(), O_NONBLOCK requested!\n", __func__ );
+ pr_debug("%s(), O_NONBLOCK requested!\n", __func__);
return 0;
}
if (tty->termios.c_cflag & CLOCAL) {
- IRDA_DEBUG(1, "%s(), doing CLOCAL!\n", __func__ );
+ pr_debug("%s(), doing CLOCAL!\n", __func__);
do_clocal = 1;
}
@@ -316,8 +309,8 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self,
retval = 0;
add_wait_queue(&port->open_wait, &wait);
- IRDA_DEBUG(2, "%s(%d):block_til_ready before block on %s open_count=%d\n",
- __FILE__, __LINE__, tty->driver->name, port->count);
+ pr_debug("%s(%d):block_til_ready before block on %s open_count=%d\n",
+ __FILE__, __LINE__, tty->driver->name, port->count);
spin_lock_irqsave(&port->lock, flags);
port->count--;
@@ -354,8 +347,8 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self,
break;
}
- IRDA_DEBUG(1, "%s(%d):block_til_ready blocking on %s open_count=%d\n",
- __FILE__, __LINE__, tty->driver->name, port->count);
+ pr_debug("%s(%d):block_til_ready blocking on %s open_count=%d\n",
+ __FILE__, __LINE__, tty->driver->name, port->count);
schedule();
}
@@ -369,8 +362,8 @@ static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self,
port->blocked_open--;
spin_unlock_irqrestore(&port->lock, flags);
- IRDA_DEBUG(1, "%s(%d):block_til_ready after blocking on %s open_count=%d\n",
- __FILE__, __LINE__, tty->driver->name, port->count);
+ pr_debug("%s(%d):block_til_ready after blocking on %s open_count=%d\n",
+ __FILE__, __LINE__, tty->driver->name, port->count);
if (!retval)
port->flags |= ASYNC_NORMAL_ACTIVE;
@@ -389,10 +382,8 @@ static int ircomm_tty_install(struct tty_driver *driver, struct tty_struct *tty)
if (!self) {
/* No, so make new instance */
self = kzalloc(sizeof(struct ircomm_tty_cb), GFP_KERNEL);
- if (self == NULL) {
- IRDA_ERROR("%s(), kmalloc failed!\n", __func__);
+ if (self == NULL)
return -ENOMEM;
- }
tty_port_init(&self->port);
self->port.ops = &ircomm_port_ops;
@@ -440,16 +431,14 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp)
unsigned long flags;
int ret;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
/* ++ is not atomic, so this should be protected - Jean II */
spin_lock_irqsave(&self->port.lock, flags);
self->port.count++;
spin_unlock_irqrestore(&self->port.lock, flags);
tty_port_tty_set(&self->port, tty);
- IRDA_DEBUG(1, "%s(), %s%d, count = %d\n", __func__ , tty->driver->name,
- self->line, self->port.count);
+ pr_debug("%s(), %s%d, count = %d\n", __func__ , tty->driver->name,
+ self->line, self->port.count);
/* Not really used by us, but lets do it anyway */
self->port.low_latency = (self->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0;
@@ -469,8 +458,8 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp)
if (wait_event_interruptible(self->port.close_wait,
!test_bit(ASYNCB_CLOSING, &self->port.flags))) {
- IRDA_WARNING("%s - got signal while blocking on ASYNC_CLOSING!\n",
- __func__);
+ net_warn_ratelimited("%s - got signal while blocking on ASYNC_CLOSING!\n",
+ __func__);
return -ERESTARTSYS;
}
@@ -488,9 +477,9 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp)
self->settings.service_type = IRCOMM_9_WIRE; /* 9 wire as default */
/* Jan Kiszka -> add DSR/RI -> Conform to IrCOMM spec */
self->settings.dce = IRCOMM_CTS | IRCOMM_CD | IRCOMM_DSR | IRCOMM_RI; /* Default line settings */
- IRDA_DEBUG(2, "%s(), IrCOMM device\n", __func__ );
+ pr_debug("%s(), IrCOMM device\n", __func__);
} else {
- IRDA_DEBUG(2, "%s(), IrLPT device\n", __func__ );
+ pr_debug("%s(), IrLPT device\n", __func__);
self->service_type = IRCOMM_3_WIRE_RAW;
self->settings.service_type = IRCOMM_3_WIRE_RAW; /* Default */
}
@@ -501,9 +490,8 @@ static int ircomm_tty_open(struct tty_struct *tty, struct file *filp)
ret = ircomm_tty_block_til_ready(self, tty, filp);
if (ret) {
- IRDA_DEBUG(2,
- "%s(), returning after block_til_ready with %d\n", __func__ ,
- ret);
+ pr_debug("%s(), returning after block_til_ready with %d\n",
+ __func__, ret);
return ret;
}
@@ -521,8 +509,6 @@ static void ircomm_tty_close(struct tty_struct *tty, struct file *filp)
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
struct tty_port *port = &self->port;
- IRDA_DEBUG(0, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
@@ -572,8 +558,6 @@ static void ircomm_tty_do_softint(struct work_struct *work)
unsigned long flags;
struct sk_buff *skb, *ctrl_skb;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
if (!self || self->magic != IRCOMM_TTY_MAGIC)
return;
@@ -639,8 +623,8 @@ static int ircomm_tty_write(struct tty_struct *tty,
int len = 0;
int size;
- IRDA_DEBUG(2, "%s(), count=%d, hw_stopped=%d\n", __func__ , count,
- tty->hw_stopped);
+ pr_debug("%s(), count=%d, hw_stopped=%d\n", __func__ , count,
+ tty->hw_stopped);
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
@@ -662,7 +646,7 @@ static int ircomm_tty_write(struct tty_struct *tty,
* we don't mess up the original "safe skb" (see tx_data_size).
* Jean II */
if (self->max_header_size == IRCOMM_TTY_HDR_UNINITIALISED) {
- IRDA_DEBUG(1, "%s() : not initialised\n", __func__);
+ pr_debug("%s() : not initialised\n", __func__);
#ifdef IRCOMM_NO_TX_BEFORE_INIT
/* We didn't consume anything, TTY will retry */
return 0;
@@ -791,7 +775,7 @@ static int ircomm_tty_write_room(struct tty_struct *tty)
ret = self->max_data_size;
spin_unlock_irqrestore(&self->spinlock, flags);
}
- IRDA_DEBUG(2, "%s(), ret=%d\n", __func__ , ret);
+ pr_debug("%s(), ret=%d\n", __func__ , ret);
return ret;
}
@@ -808,8 +792,6 @@ static void ircomm_tty_wait_until_sent(struct tty_struct *tty, int timeout)
unsigned long orig_jiffies, poll_time;
unsigned long flags;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
@@ -843,8 +825,6 @@ static void ircomm_tty_throttle(struct tty_struct *tty)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
@@ -874,8 +854,6 @@ static void ircomm_tty_unthrottle(struct tty_struct *tty)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
@@ -889,7 +867,7 @@ static void ircomm_tty_unthrottle(struct tty_struct *tty)
self->settings.dte |= (IRCOMM_RTS|IRCOMM_DELTA_RTS);
ircomm_param_request(self, IRCOMM_DTE, TRUE);
- IRDA_DEBUG(1, "%s(), FLOW_START\n", __func__ );
+ pr_debug("%s(), FLOW_START\n", __func__);
}
ircomm_flow_request(self->ircomm, FLOW_START);
}
@@ -926,8 +904,6 @@ static void ircomm_tty_shutdown(struct ircomm_tty_cb *self)
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
- IRDA_DEBUG(0, "%s()\n", __func__ );
-
if (!test_and_clear_bit(ASYNCB_INITIALIZED, &self->port.flags))
return;
@@ -970,8 +946,6 @@ static void ircomm_tty_hangup(struct tty_struct *tty)
struct tty_port *port = &self->port;
unsigned long flags;
- IRDA_DEBUG(0, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
@@ -999,7 +973,7 @@ static void ircomm_tty_hangup(struct tty_struct *tty)
*/
static void ircomm_tty_send_xchar(struct tty_struct *tty, char ch)
{
- IRDA_DEBUG(0, "%s(), not impl\n", __func__ );
+ pr_debug("%s(), not impl\n", __func__);
}
/*
@@ -1043,8 +1017,6 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self)
struct tty_struct *tty;
int status;
- IRDA_DEBUG(0, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
@@ -1056,15 +1028,13 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self)
/*wake_up_interruptible(&self->delta_msr_wait);*/
}
if ((self->port.flags & ASYNC_CHECK_CD) && (status & IRCOMM_DELTA_CD)) {
- IRDA_DEBUG(2,
- "%s(), ircomm%d CD now %s...\n", __func__ , self->line,
- (status & IRCOMM_CD) ? "on" : "off");
+ pr_debug("%s(), ircomm%d CD now %s...\n", __func__ , self->line,
+ (status & IRCOMM_CD) ? "on" : "off");
if (status & IRCOMM_CD) {
wake_up_interruptible(&self->port.open_wait);
} else {
- IRDA_DEBUG(2,
- "%s(), Doing serial hangup..\n", __func__ );
+ pr_debug("%s(), Doing serial hangup..\n", __func__);
if (tty)
tty_hangup(tty);
@@ -1075,8 +1045,7 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self)
if (tty && tty_port_cts_enabled(&self->port)) {
if (tty->hw_stopped) {
if (status & IRCOMM_CTS) {
- IRDA_DEBUG(2,
- "%s(), CTS tx start...\n", __func__ );
+ pr_debug("%s(), CTS tx start...\n", __func__);
tty->hw_stopped = 0;
/* Wake up processes blocked on open */
@@ -1087,8 +1056,7 @@ void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self)
}
} else {
if (!(status & IRCOMM_CTS)) {
- IRDA_DEBUG(2,
- "%s(), CTS tx stop...\n", __func__ );
+ pr_debug("%s(), CTS tx stop...\n", __func__);
tty->hw_stopped = 1;
}
}
@@ -1109,15 +1077,13 @@ static int ircomm_tty_data_indication(void *instance, void *sap,
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
struct tty_struct *tty;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
IRDA_ASSERT(skb != NULL, return -1;);
tty = tty_port_tty_get(&self->port);
if (!tty) {
- IRDA_DEBUG(0, "%s(), no tty!\n", __func__ );
+ pr_debug("%s(), no tty!\n", __func__);
return 0;
}
@@ -1128,7 +1094,7 @@ static int ircomm_tty_data_indication(void *instance, void *sap,
* params, we can just as well declare the hardware for running.
*/
if (tty->hw_stopped && (self->flow == FLOW_START)) {
- IRDA_DEBUG(0, "%s(), polling for line settings!\n", __func__ );
+ pr_debug("%s(), polling for line settings!\n", __func__);
ircomm_param_request(self, IRCOMM_POLL, TRUE);
/* We can just as well declare the hardware for running */
@@ -1161,8 +1127,6 @@ static int ircomm_tty_control_indication(void *instance, void *sap,
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
int clen;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
IRDA_ASSERT(skb != NULL, return -1;);
@@ -1197,7 +1161,7 @@ static void ircomm_tty_flow_indication(void *instance, void *sap,
switch (cmd) {
case FLOW_START:
- IRDA_DEBUG(2, "%s(), hw start!\n", __func__ );
+ pr_debug("%s(), hw start!\n", __func__);
if (tty)
tty->hw_stopped = 0;
@@ -1206,7 +1170,7 @@ static void ircomm_tty_flow_indication(void *instance, void *sap,
break;
default: /* If we get here, something is very wrong, better stop */
case FLOW_STOP:
- IRDA_DEBUG(2, "%s(), hw stopped!\n", __func__ );
+ pr_debug("%s(), hw stopped!\n", __func__);
if (tty)
tty->hw_stopped = 1;
break;
diff --git a/net/irda/ircomm/ircomm_tty_attach.c b/net/irda/ircomm/ircomm_tty_attach.c
index 2ee87bf387cc..61137f8b5293 100644
--- a/net/irda/ircomm/ircomm_tty_attach.c
+++ b/net/irda/ircomm/ircomm_tty_attach.c
@@ -89,8 +89,7 @@ const char *const ircomm_tty_state[] = {
"*** ERROR *** ",
};
-#ifdef CONFIG_IRDA_DEBUG
-static const char *const ircomm_tty_event[] = {
+static const char *const ircomm_tty_event[] __maybe_unused = {
"IRCOMM_TTY_ATTACH_CABLE",
"IRCOMM_TTY_DETACH_CABLE",
"IRCOMM_TTY_DATA_REQUEST",
@@ -106,7 +105,6 @@ static const char *const ircomm_tty_event[] = {
"IRCOMM_TTY_GOT_LSAPSEL",
"*** ERROR ****",
};
-#endif /* CONFIG_IRDA_DEBUG */
static int (*state[])(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event,
struct sk_buff *skb, struct ircomm_tty_info *info) =
@@ -130,14 +128,12 @@ int ircomm_tty_attach_cable(struct ircomm_tty_cb *self)
{
struct tty_struct *tty;
- IRDA_DEBUG(0, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
/* Check if somebody has already connected to us */
if (ircomm_is_connected(self->ircomm)) {
- IRDA_DEBUG(0, "%s(), already connected!\n", __func__ );
+ pr_debug("%s(), already connected!\n", __func__);
return 0;
}
@@ -163,8 +159,6 @@ int ircomm_tty_attach_cable(struct ircomm_tty_cb *self)
*/
void ircomm_tty_detach_cable(struct ircomm_tty_cb *self)
{
- IRDA_DEBUG(0, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
@@ -212,8 +206,6 @@ static void ircomm_tty_ias_register(struct ircomm_tty_cb *self)
__u8 oct_seq[6];
__u16 hints;
- IRDA_DEBUG(0, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
@@ -313,17 +305,17 @@ int ircomm_tty_send_initial_parameters(struct ircomm_tty_cb *self)
* Set default values, but only if the application for some reason
* haven't set them already
*/
- IRDA_DEBUG(2, "%s(), data-rate = %d\n", __func__ ,
- self->settings.data_rate);
+ pr_debug("%s(), data-rate = %d\n", __func__ ,
+ self->settings.data_rate);
if (!self->settings.data_rate)
self->settings.data_rate = 9600;
- IRDA_DEBUG(2, "%s(), data-format = %d\n", __func__ ,
- self->settings.data_format);
+ pr_debug("%s(), data-format = %d\n", __func__ ,
+ self->settings.data_format);
if (!self->settings.data_format)
self->settings.data_format = IRCOMM_WSIZE_8; /* 8N1 */
- IRDA_DEBUG(2, "%s(), flow-control = %d\n", __func__ ,
- self->settings.flow_control);
+ pr_debug("%s(), flow-control = %d\n", __func__ ,
+ self->settings.flow_control);
/*self->settings.flow_control = IRCOMM_RTS_CTS_IN|IRCOMM_RTS_CTS_OUT;*/
/* Do not set delta values for the initial parameters */
@@ -367,8 +359,6 @@ static void ircomm_tty_discovery_indication(discinfo_t *discovery,
struct ircomm_tty_cb *self;
struct ircomm_tty_info info;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
/* Important note :
* We need to drop all passive discoveries.
* The LSAP management of IrComm is deficient and doesn't deal
@@ -404,8 +394,6 @@ void ircomm_tty_disconnect_indication(void *instance, void *sap,
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
struct tty_struct *tty;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
@@ -436,8 +424,6 @@ static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id,
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) priv;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
@@ -447,13 +433,13 @@ static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id,
/* Check if request succeeded */
if (result != IAS_SUCCESS) {
- IRDA_DEBUG(4, "%s(), got NULL value!\n", __func__ );
+ pr_debug("%s(), got NULL value!\n", __func__);
return;
}
switch (value->type) {
case IAS_OCT_SEQ:
- IRDA_DEBUG(2, "%s(), got octet sequence\n", __func__ );
+ pr_debug("%s(), got octet sequence\n", __func__);
irda_param_extract_all(self, value->t.oct_seq, value->len,
&ircomm_param_info);
@@ -463,21 +449,21 @@ static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id,
break;
case IAS_INTEGER:
/* Got LSAP selector */
- IRDA_DEBUG(2, "%s(), got lsapsel = %d\n", __func__ ,
- value->t.integer);
+ pr_debug("%s(), got lsapsel = %d\n", __func__ ,
+ value->t.integer);
if (value->t.integer == -1) {
- IRDA_DEBUG(0, "%s(), invalid value!\n", __func__ );
+ pr_debug("%s(), invalid value!\n", __func__);
} else
self->dlsap_sel = value->t.integer;
ircomm_tty_do_event(self, IRCOMM_TTY_GOT_LSAPSEL, NULL, NULL);
break;
case IAS_MISSING:
- IRDA_DEBUG(0, "%s(), got IAS_MISSING\n", __func__ );
+ pr_debug("%s(), got IAS_MISSING\n", __func__);
break;
default:
- IRDA_DEBUG(0, "%s(), got unknown type!\n", __func__ );
+ pr_debug("%s(), got unknown type!\n", __func__);
break;
}
irias_delete_value(value);
@@ -497,8 +483,6 @@ void ircomm_tty_connect_confirm(void *instance, void *sap,
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
@@ -528,8 +512,6 @@ void ircomm_tty_connect_indication(void *instance, void *sap,
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance;
int clen;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
@@ -559,8 +541,6 @@ void ircomm_tty_link_established(struct ircomm_tty_cb *self)
{
struct tty_struct *tty;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
@@ -578,10 +558,10 @@ void ircomm_tty_link_established(struct ircomm_tty_cb *self)
*/
if (tty_port_cts_enabled(&self->port) &&
((self->settings.dce & IRCOMM_CTS) == 0)) {
- IRDA_DEBUG(0, "%s(), waiting for CTS ...\n", __func__ );
+ pr_debug("%s(), waiting for CTS ...\n", __func__);
goto put;
} else {
- IRDA_DEBUG(1, "%s(), starting hardware!\n", __func__ );
+ pr_debug("%s(), starting hardware!\n", __func__);
tty->hw_stopped = 0;
@@ -621,8 +601,6 @@ static void ircomm_tty_watchdog_timer_expired(void *data)
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) data;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
@@ -642,8 +620,8 @@ int ircomm_tty_do_event(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event,
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;);
- IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __func__ ,
- ircomm_tty_state[self->state], ircomm_tty_event[event]);
+ pr_debug("%s: state=%s, event=%s\n", __func__ ,
+ ircomm_tty_state[self->state], ircomm_tty_event[event]);
return (*state[self->state])(self, event, skb, info);
}
@@ -660,8 +638,8 @@ static inline void ircomm_tty_next_state(struct ircomm_tty_cb *self, IRCOMM_TTY_
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;);
- IRDA_DEBUG(2, "%s: next state=%s, service type=%d\n", __func__ ,
- ircomm_tty_state[self->state], self->service_type);
+ pr_debug("%s: next state=%s, service type=%d\n", __func__ ,
+ ircomm_tty_state[self->state], self->service_type);
*/
self->state = state;
}
@@ -679,8 +657,8 @@ static int ircomm_tty_state_idle(struct ircomm_tty_cb *self,
{
int ret = 0;
- IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __func__ ,
- ircomm_tty_state[self->state], ircomm_tty_event[event]);
+ pr_debug("%s: state=%s, event=%s\n", __func__ ,
+ ircomm_tty_state[self->state], ircomm_tty_event[event]);
switch (event) {
case IRCOMM_TTY_ATTACH_CABLE:
/* Try to discover any remote devices */
@@ -694,8 +672,8 @@ static int ircomm_tty_state_idle(struct ircomm_tty_cb *self,
self->saddr = info->saddr;
if (self->iriap) {
- IRDA_WARNING("%s(), busy with a previous query\n",
- __func__);
+ net_warn_ratelimited("%s(), busy with a previous query\n",
+ __func__);
return -EBUSY;
}
@@ -723,8 +701,8 @@ static int ircomm_tty_state_idle(struct ircomm_tty_cb *self,
ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
break;
default:
- IRDA_DEBUG(2, "%s(), unknown event: %s\n", __func__ ,
- ircomm_tty_event[event]);
+ pr_debug("%s(), unknown event: %s\n", __func__ ,
+ ircomm_tty_event[event]);
ret = -EINVAL;
}
return ret;
@@ -743,8 +721,8 @@ static int ircomm_tty_state_search(struct ircomm_tty_cb *self,
{
int ret = 0;
- IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __func__ ,
- ircomm_tty_state[self->state], ircomm_tty_event[event]);
+ pr_debug("%s: state=%s, event=%s\n", __func__ ,
+ ircomm_tty_state[self->state], ircomm_tty_event[event]);
switch (event) {
case IRCOMM_TTY_DISCOVERY_INDICATION:
@@ -752,8 +730,8 @@ static int ircomm_tty_state_search(struct ircomm_tty_cb *self,
self->saddr = info->saddr;
if (self->iriap) {
- IRDA_WARNING("%s(), busy with a previous query\n",
- __func__);
+ net_warn_ratelimited("%s(), busy with a previous query\n",
+ __func__);
return -EBUSY;
}
@@ -796,8 +774,8 @@ static int ircomm_tty_state_search(struct ircomm_tty_cb *self,
ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
break;
default:
- IRDA_DEBUG(2, "%s(), unknown event: %s\n", __func__ ,
- ircomm_tty_event[event]);
+ pr_debug("%s(), unknown event: %s\n", __func__ ,
+ ircomm_tty_event[event]);
ret = -EINVAL;
}
return ret;
@@ -816,14 +794,14 @@ static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self,
{
int ret = 0;
- IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __func__ ,
- ircomm_tty_state[self->state], ircomm_tty_event[event]);
+ pr_debug("%s: state=%s, event=%s\n", __func__ ,
+ ircomm_tty_state[self->state], ircomm_tty_event[event]);
switch (event) {
case IRCOMM_TTY_GOT_PARAMETERS:
if (self->iriap) {
- IRDA_WARNING("%s(), busy with a previous query\n",
- __func__);
+ net_warn_ratelimited("%s(), busy with a previous query\n",
+ __func__);
return -EBUSY;
}
@@ -854,8 +832,8 @@ static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self,
ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
break;
default:
- IRDA_DEBUG(2, "%s(), unknown event: %s\n", __func__ ,
- ircomm_tty_event[event]);
+ pr_debug("%s(), unknown event: %s\n", __func__ ,
+ ircomm_tty_event[event]);
ret = -EINVAL;
}
return ret;
@@ -874,8 +852,8 @@ static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self,
{
int ret = 0;
- IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __func__ ,
- ircomm_tty_state[self->state], ircomm_tty_event[event]);
+ pr_debug("%s: state=%s, event=%s\n", __func__ ,
+ ircomm_tty_state[self->state], ircomm_tty_event[event]);
switch (event) {
case IRCOMM_TTY_GOT_LSAPSEL:
@@ -903,8 +881,8 @@ static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self,
ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
break;
default:
- IRDA_DEBUG(2, "%s(), unknown event: %s\n", __func__ ,
- ircomm_tty_event[event]);
+ pr_debug("%s(), unknown event: %s\n", __func__ ,
+ ircomm_tty_event[event]);
ret = -EINVAL;
}
return ret;
@@ -923,8 +901,8 @@ static int ircomm_tty_state_setup(struct ircomm_tty_cb *self,
{
int ret = 0;
- IRDA_DEBUG(2, "%s: state=%s, event=%s\n", __func__ ,
- ircomm_tty_state[self->state], ircomm_tty_event[event]);
+ pr_debug("%s: state=%s, event=%s\n", __func__ ,
+ ircomm_tty_state[self->state], ircomm_tty_event[event]);
switch (event) {
case IRCOMM_TTY_CONNECT_CONFIRM:
@@ -957,8 +935,8 @@ static int ircomm_tty_state_setup(struct ircomm_tty_cb *self,
ircomm_tty_next_state(self, IRCOMM_TTY_IDLE);
break;
default:
- IRDA_DEBUG(2, "%s(), unknown event: %s\n", __func__ ,
- ircomm_tty_event[event]);
+ pr_debug("%s(), unknown event: %s\n", __func__ ,
+ ircomm_tty_event[event]);
ret = -EINVAL;
}
return ret;
@@ -995,13 +973,13 @@ static int ircomm_tty_state_ready(struct ircomm_tty_cb *self,
self->settings.dce = IRCOMM_DELTA_CD;
ircomm_tty_check_modem_status(self);
} else {
- IRDA_DEBUG(0, "%s(), hanging up!\n", __func__ );
+ pr_debug("%s(), hanging up!\n", __func__);
tty_port_tty_hangup(&self->port, false);
}
break;
default:
- IRDA_DEBUG(2, "%s(), unknown event: %s\n", __func__ ,
- ircomm_tty_event[event]);
+ pr_debug("%s(), unknown event: %s\n", __func__ ,
+ ircomm_tty_event[event]);
ret = -EINVAL;
}
return ret;
diff --git a/net/irda/ircomm/ircomm_tty_ioctl.c b/net/irda/ircomm/ircomm_tty_ioctl.c
index ce943853c38d..75ccdbd0728e 100644
--- a/net/irda/ircomm/ircomm_tty_ioctl.c
+++ b/net/irda/ircomm/ircomm_tty_ioctl.c
@@ -56,8 +56,6 @@ static void ircomm_tty_change_speed(struct ircomm_tty_cb *self,
unsigned int cflag, cval;
int baud;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
if (!self->ircomm)
return;
@@ -93,7 +91,8 @@ static void ircomm_tty_change_speed(struct ircomm_tty_cb *self,
self->settings.flow_control |= IRCOMM_RTS_CTS_IN;
/* This got me. Bummer. Jean II */
if (self->service_type == IRCOMM_3_WIRE_RAW)
- IRDA_WARNING("%s(), enabling RTS/CTS on link that doesn't support it (3-wire-raw)\n", __func__);
+ net_warn_ratelimited("%s(), enabling RTS/CTS on link that doesn't support it (3-wire-raw)\n",
+ __func__);
} else {
self->port.flags &= ~ASYNC_CTS_FLOW;
self->settings.flow_control &= ~IRCOMM_RTS_CTS_IN;
@@ -149,8 +148,6 @@ void ircomm_tty_set_termios(struct tty_struct *tty,
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
unsigned int cflag = tty->termios.c_cflag;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
if ((cflag == old_termios->c_cflag) &&
(RELEVANT_IFLAG(tty->termios.c_iflag) ==
RELEVANT_IFLAG(old_termios->c_iflag)))
@@ -198,8 +195,6 @@ int ircomm_tty_tiocmget(struct tty_struct *tty)
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
unsigned int result;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
if (tty->flags & (1 << TTY_IO_ERROR))
return -EIO;
@@ -223,8 +218,6 @@ int ircomm_tty_tiocmset(struct tty_struct *tty,
{
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
if (tty->flags & (1 << TTY_IO_ERROR))
return -EIO;
@@ -265,8 +258,6 @@ static int ircomm_tty_get_serial_info(struct ircomm_tty_cb *self,
if (!retinfo)
return -EFAULT;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
memset(&info, 0, sizeof(info));
info.line = self->line;
info.flags = self->port.flags;
@@ -301,8 +292,6 @@ static int ircomm_tty_set_serial_info(struct ircomm_tty_cb *self,
struct serial_struct new_serial;
struct ircomm_tty_cb old_state, *state;
- IRDA_DEBUG(0, "%s()\n", __func__ );
-
if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
return -EFAULT;
@@ -375,8 +364,6 @@ int ircomm_tty_ioctl(struct tty_struct *tty,
struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data;
int ret = 0;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
(cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
(cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
@@ -392,11 +379,11 @@ int ircomm_tty_ioctl(struct tty_struct *tty,
ret = ircomm_tty_set_serial_info(self, (struct serial_struct __user *) arg);
break;
case TIOCMIWAIT:
- IRDA_DEBUG(0, "(), TIOCMIWAIT, not impl!\n");
+ pr_debug("(), TIOCMIWAIT, not impl!\n");
break;
case TIOCGICOUNT:
- IRDA_DEBUG(0, "%s(), TIOCGICOUNT not impl!\n", __func__ );
+ pr_debug("%s(), TIOCGICOUNT not impl!\n", __func__);
#if 0
save_flags(flags); cli();
cnow = driver->icount;
diff --git a/net/irda/irda_device.c b/net/irda/irda_device.c
index 9e0d909390fd..856736656a30 100644
--- a/net/irda/irda_device.c
+++ b/net/irda/irda_device.c
@@ -63,14 +63,14 @@ int __init irda_device_init( void)
{
dongles = hashbin_new(HB_NOLOCK);
if (dongles == NULL) {
- IRDA_WARNING("IrDA: Can't allocate dongles hashbin!\n");
+ net_warn_ratelimited("IrDA: Can't allocate dongles hashbin!\n");
return -ENOMEM;
}
spin_lock_init(&dongles->hb_spinlock);
tasks = hashbin_new(HB_LOCK);
if (tasks == NULL) {
- IRDA_WARNING("IrDA: Can't allocate tasks hashbin!\n");
+ net_warn_ratelimited("IrDA: Can't allocate tasks hashbin!\n");
hashbin_delete(dongles, NULL);
return -ENOMEM;
}
@@ -84,14 +84,12 @@ int __init irda_device_init( void)
static void leftover_dongle(void *arg)
{
struct dongle_reg *reg = arg;
- IRDA_WARNING("IrDA: Dongle type %x not unregistered\n",
- reg->type);
+ net_warn_ratelimited("IrDA: Dongle type %x not unregistered\n",
+ reg->type);
}
void irda_device_cleanup(void)
{
- IRDA_DEBUG(4, "%s()\n", __func__);
-
hashbin_delete(tasks, (FREE_FUNC) __irda_task_delete);
hashbin_delete(dongles, leftover_dongle);
@@ -107,7 +105,7 @@ void irda_device_set_media_busy(struct net_device *dev, int status)
{
struct irlap_cb *self;
- IRDA_DEBUG(4, "%s(%s)\n", __func__, status ? "TRUE" : "FALSE");
+ pr_debug("%s(%s)\n", __func__, status ? "TRUE" : "FALSE");
self = (struct irlap_cb *) dev->atalk_ptr;
@@ -127,7 +125,7 @@ void irda_device_set_media_busy(struct net_device *dev, int status)
irlap_start_mbusy_timer(self, SMALLBUSY_TIMEOUT);
else
irlap_start_mbusy_timer(self, MEDIABUSY_TIMEOUT);
- IRDA_DEBUG( 4, "Media busy!\n");
+ pr_debug("Media busy!\n");
} else {
self->media_busy = FALSE;
irlap_stop_mbusy_timer(self);
@@ -147,11 +145,9 @@ int irda_device_is_receiving(struct net_device *dev)
struct if_irda_req req;
int ret;
- IRDA_DEBUG(2, "%s()\n", __func__);
-
if (!dev->netdev_ops->ndo_do_ioctl) {
- IRDA_ERROR("%s: do_ioctl not impl. by device driver\n",
- __func__);
+ net_err_ratelimited("%s: do_ioctl not impl. by device driver\n",
+ __func__);
return -1;
}
@@ -192,8 +188,6 @@ static int irda_task_kick(struct irda_task *task)
int count = 0;
int timeout;
- IRDA_DEBUG(2, "%s()\n", __func__);
-
IRDA_ASSERT(task != NULL, return -1;);
IRDA_ASSERT(task->magic == IRDA_TASK_MAGIC, return -1;);
@@ -201,15 +195,15 @@ static int irda_task_kick(struct irda_task *task)
do {
timeout = task->function(task);
if (count++ > 100) {
- IRDA_ERROR("%s: error in task handler!\n",
- __func__);
+ net_err_ratelimited("%s: error in task handler!\n",
+ __func__);
irda_task_delete(task);
return TRUE;
}
} while ((timeout == 0) && (task->state != IRDA_TASK_DONE));
if (timeout < 0) {
- IRDA_ERROR("%s: Error executing task!\n", __func__);
+ net_err_ratelimited("%s: Error executing task!\n", __func__);
irda_task_delete(task);
return TRUE;
}
@@ -241,8 +235,8 @@ static int irda_task_kick(struct irda_task *task)
irda_task_timer_expired);
finished = FALSE;
} else {
- IRDA_DEBUG(0, "%s(), not finished, and no timeout!\n",
- __func__);
+ pr_debug("%s(), not finished, and no timeout!\n",
+ __func__);
finished = FALSE;
}
@@ -259,8 +253,6 @@ static void irda_task_timer_expired(void *data)
{
struct irda_task *task;
- IRDA_DEBUG(2, "%s()\n", __func__);
-
task = data;
irda_task_kick(task);
diff --git a/net/irda/iriap.c b/net/irda/iriap.c
index e1b37f5a2691..4a7ae32afa09 100644
--- a/net/irda/iriap.c
+++ b/net/irda/iriap.c
@@ -43,9 +43,8 @@
#include <net/irda/iriap_event.h>
#include <net/irda/iriap.h>
-#ifdef CONFIG_IRDA_DEBUG
/* FIXME: This one should go in irlmp.c */
-static const char *const ias_charset_types[] = {
+static const char *const ias_charset_types[] __maybe_unused = {
"CS_ASCII",
"CS_ISO_8859_1",
"CS_ISO_8859_2",
@@ -58,7 +57,6 @@ static const char *const ias_charset_types[] = {
"CS_ISO_8859_9",
"CS_UNICODE"
};
-#endif /* CONFIG_IRDA_DEBUG */
static hashbin_t *iriap = NULL;
static void *service_handle;
@@ -110,8 +108,8 @@ int __init iriap_init(void)
/* Object repository - defined in irias_object.c */
irias_objects = hashbin_new(HB_LOCK);
if (!irias_objects) {
- IRDA_WARNING("%s: Can't allocate irias_objects hashbin!\n",
- __func__);
+ net_warn_ratelimited("%s: Can't allocate irias_objects hashbin!\n",
+ __func__);
hashbin_delete(iriap, NULL);
return -ENOMEM;
}
@@ -145,7 +143,7 @@ int __init iriap_init(void)
*/
server = iriap_open(LSAP_IAS, IAS_SERVER, NULL, NULL);
if (!server) {
- IRDA_DEBUG(0, "%s(), unable to open server\n", __func__);
+ pr_debug("%s(), unable to open server\n", __func__);
return -1;
}
iriap_register_lsap(server, LSAP_IAS, IAS_SERVER);
@@ -177,13 +175,9 @@ struct iriap_cb *iriap_open(__u8 slsap_sel, int mode, void *priv,
{
struct iriap_cb *self;
- IRDA_DEBUG(2, "%s()\n", __func__);
-
self = kzalloc(sizeof(*self), GFP_ATOMIC);
- if (!self) {
- IRDA_WARNING("%s: Unable to kmalloc!\n", __func__);
+ if (!self)
return NULL;
- }
/*
* Initialize instance
@@ -223,8 +217,6 @@ EXPORT_SYMBOL(iriap_open);
*/
static void __iriap_close(struct iriap_cb *self)
{
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
@@ -247,8 +239,6 @@ void iriap_close(struct iriap_cb *self)
{
struct iriap_cb *entry;
- IRDA_DEBUG(2, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
@@ -268,8 +258,6 @@ static int iriap_register_lsap(struct iriap_cb *self, __u8 slsap_sel, int mode)
{
notify_t notify;
- IRDA_DEBUG(2, "%s()\n", __func__);
-
irda_notify_init(&notify);
notify.connect_confirm = iriap_connect_confirm;
notify.connect_indication = iriap_connect_indication;
@@ -283,7 +271,8 @@ static int iriap_register_lsap(struct iriap_cb *self, __u8 slsap_sel, int mode)
self->lsap = irlmp_open_lsap(slsap_sel, &notify, 0);
if (self->lsap == NULL) {
- IRDA_ERROR("%s: Unable to allocated LSAP!\n", __func__);
+ net_err_ratelimited("%s: Unable to allocated LSAP!\n",
+ __func__);
return -1;
}
self->slsap_sel = self->lsap->slsap_sel;
@@ -303,8 +292,8 @@ static void iriap_disconnect_indication(void *instance, void *sap,
{
struct iriap_cb *self;
- IRDA_DEBUG(4, "%s(), reason=%s [%d]\n", __func__,
- irlmp_reason_str(reason), reason);
+ pr_debug("%s(), reason=%s [%d]\n", __func__,
+ irlmp_reason_str(reason), reason);
self = instance;
@@ -320,7 +309,7 @@ static void iriap_disconnect_indication(void *instance, void *sap,
dev_kfree_skb(skb);
if (self->mode == IAS_CLIENT) {
- IRDA_DEBUG(4, "%s(), disconnect as client\n", __func__);
+ pr_debug("%s(), disconnect as client\n", __func__);
iriap_do_client_event(self, IAP_LM_DISCONNECT_INDICATION,
@@ -333,7 +322,7 @@ static void iriap_disconnect_indication(void *instance, void *sap,
if (self->confirm)
self->confirm(IAS_DISCONNECT, 0, NULL, self->priv);
} else {
- IRDA_DEBUG(4, "%s(), disconnect as server\n", __func__);
+ pr_debug("%s(), disconnect as server\n", __func__);
iriap_do_server_event(self, IAP_LM_DISCONNECT_INDICATION,
NULL);
iriap_close(self);
@@ -347,16 +336,13 @@ static void iriap_disconnect_request(struct iriap_cb *self)
{
struct sk_buff *tx_skb;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
if (tx_skb == NULL) {
- IRDA_DEBUG(0,
- "%s(), Could not allocate an sk_buff of length %d\n",
- __func__, LMP_MAX_HEADER);
+ pr_debug("%s(), Could not allocate an sk_buff of length %d\n",
+ __func__, LMP_MAX_HEADER);
return;
}
@@ -461,14 +447,14 @@ static void iriap_getvaluebyclass_confirm(struct iriap_cb *self,
len = get_unaligned_be16(fp + n);
n += 2;
- IRDA_DEBUG(4, "%s(), len=%d\n", __func__, len);
+ pr_debug("%s(), len=%d\n", __func__, len);
/* Get object ID, MSB first */
obj_id = get_unaligned_be16(fp + n);
n += 2;
type = fp[n++];
- IRDA_DEBUG(4, "%s(), Value type = %d\n", __func__, type);
+ pr_debug("%s(), Value type = %d\n", __func__, type);
switch (type) {
case IAS_INTEGER:
@@ -477,7 +463,7 @@ static void iriap_getvaluebyclass_confirm(struct iriap_cb *self,
value = irias_new_integer_value(tmp_cpu32);
/* Legal values restricted to 0x01-0x6f, page 15 irttp */
- IRDA_DEBUG(4, "%s(), lsap=%d\n", __func__, value->t.integer);
+ pr_debug("%s(), lsap=%d\n", __func__, value->t.integer);
break;
case IAS_STRING:
charset = fp[n++];
@@ -496,11 +482,11 @@ static void iriap_getvaluebyclass_confirm(struct iriap_cb *self,
/* case CS_ISO_8859_9: */
/* case CS_UNICODE: */
default:
- IRDA_DEBUG(0, "%s(), charset [%d] %s, not supported\n",
- __func__, charset,
- charset < ARRAY_SIZE(ias_charset_types) ?
- ias_charset_types[charset] :
- "(unknown)");
+ pr_debug("%s(), charset [%d] %s, not supported\n",
+ __func__, charset,
+ charset < ARRAY_SIZE(ias_charset_types) ?
+ ias_charset_types[charset] :
+ "(unknown)");
/* Aborting, close connection! */
iriap_disconnect_request(self);
@@ -508,12 +494,12 @@ static void iriap_getvaluebyclass_confirm(struct iriap_cb *self,
/* break; */
}
value_len = fp[n++];
- IRDA_DEBUG(4, "%s(), strlen=%d\n", __func__, value_len);
+ pr_debug("%s(), strlen=%d\n", __func__, value_len);
/* Make sure the string is null-terminated */
if (n + value_len < skb->len)
fp[n + value_len] = 0x00;
- IRDA_DEBUG(4, "Got string %s\n", fp+n);
+ pr_debug("Got string %s\n", fp+n);
/* Will truncate to IAS_MAX_STRING bytes */
value = irias_new_string_value(fp+n);
@@ -539,7 +525,7 @@ static void iriap_getvaluebyclass_confirm(struct iriap_cb *self,
if (self->confirm)
self->confirm(IAS_SUCCESS, obj_id, value, self->priv);
else {
- IRDA_DEBUG(0, "%s(), missing handler!\n", __func__);
+ pr_debug("%s(), missing handler!\n", __func__);
irias_delete_value(value);
}
}
@@ -561,8 +547,6 @@ static void iriap_getvaluebyclass_response(struct iriap_cb *self,
__be16 tmp_be16;
__u8 *fp;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
IRDA_ASSERT(value != NULL, return;);
@@ -623,12 +607,12 @@ static void iriap_getvaluebyclass_response(struct iriap_cb *self,
memcpy(fp+n, value->t.oct_seq, value->len); n+=value->len;
break;
case IAS_MISSING:
- IRDA_DEBUG( 3, "%s: sending IAS_MISSING\n", __func__);
+ pr_debug("%s: sending IAS_MISSING\n", __func__);
skb_put(tx_skb, 1);
fp[n++] = value->type;
break;
default:
- IRDA_DEBUG(0, "%s(), type not implemented!\n", __func__);
+ pr_debug("%s(), type not implemented!\n", __func__);
break;
}
iriap_do_r_connect_event(self, IAP_CALL_RESPONSE, tx_skb);
@@ -655,8 +639,6 @@ static void iriap_getvaluebyclass_indication(struct iriap_cb *self,
__u8 *fp;
int n;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
IRDA_ASSERT(skb != NULL, return;);
@@ -678,20 +660,20 @@ static void iriap_getvaluebyclass_indication(struct iriap_cb *self,
memcpy(attr, fp+n, attr_len); n+=attr_len;
attr[attr_len] = '\0';
- IRDA_DEBUG(4, "LM-IAS: Looking up %s: %s\n", name, attr);
+ pr_debug("LM-IAS: Looking up %s: %s\n", name, attr);
obj = irias_find_object(name);
if (obj == NULL) {
- IRDA_DEBUG(2, "LM-IAS: Object %s not found\n", name);
+ pr_debug("LM-IAS: Object %s not found\n", name);
iriap_getvaluebyclass_response(self, 0x1235, IAS_CLASS_UNKNOWN,
&irias_missing);
return;
}
- IRDA_DEBUG(4, "LM-IAS: found %s, id=%d\n", obj->name, obj->id);
+ pr_debug("LM-IAS: found %s, id=%d\n", obj->name, obj->id);
attrib = irias_find_attrib(obj, attr);
if (attrib == NULL) {
- IRDA_DEBUG(2, "LM-IAS: Attribute %s not found\n", attr);
+ pr_debug("LM-IAS: Attribute %s not found\n", attr);
iriap_getvaluebyclass_response(self, obj->id,
IAS_ATTRIB_UNKNOWN,
&irias_missing);
@@ -714,8 +696,6 @@ void iriap_send_ack(struct iriap_cb *self)
struct sk_buff *tx_skb;
__u8 *frame;
- IRDA_DEBUG(2, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
@@ -745,7 +725,7 @@ void iriap_connect_request(struct iriap_cb *self)
self->saddr, self->daddr,
NULL, NULL);
if (ret < 0) {
- IRDA_DEBUG(0, "%s(), connect failed!\n", __func__);
+ pr_debug("%s(), connect failed!\n", __func__);
self->confirm(IAS_DISCONNECT, 0, NULL, self->priv);
}
}
@@ -793,8 +773,6 @@ static void iriap_connect_indication(void *instance, void *sap,
{
struct iriap_cb *self, *new;
- IRDA_DEBUG(1, "%s()\n", __func__);
-
self = instance;
IRDA_ASSERT(skb != NULL, return;);
@@ -804,14 +782,14 @@ static void iriap_connect_indication(void *instance, void *sap,
/* Start new server */
new = iriap_open(LSAP_IAS, IAS_SERVER, NULL, NULL);
if (!new) {
- IRDA_DEBUG(0, "%s(), open failed\n", __func__);
+ pr_debug("%s(), open failed\n", __func__);
goto out;
}
/* Now attach up the new "socket" */
new->lsap = irlmp_dup(self->lsap, new);
if (!new->lsap) {
- IRDA_DEBUG(0, "%s(), dup failed!\n", __func__);
+ pr_debug("%s(), dup failed!\n", __func__);
goto out;
}
@@ -841,8 +819,6 @@ static int iriap_data_indication(void *instance, void *sap,
__u8 *frame;
__u8 opcode;
- IRDA_DEBUG(3, "%s()\n", __func__);
-
self = instance;
IRDA_ASSERT(skb != NULL, return 0;);
@@ -853,21 +829,20 @@ static int iriap_data_indication(void *instance, void *sap,
if (self->mode == IAS_SERVER) {
/* Call server */
- IRDA_DEBUG(4, "%s(), Calling server!\n", __func__);
+ pr_debug("%s(), Calling server!\n", __func__);
iriap_do_r_connect_event(self, IAP_RECV_F_LST, skb);
goto out;
}
opcode = frame[0];
if (~opcode & IAP_LST) {
- IRDA_WARNING("%s:, IrIAS multiframe commands or "
- "results is not implemented yet!\n",
- __func__);
+ net_warn_ratelimited("%s:, IrIAS multiframe commands or results is not implemented yet!\n",
+ __func__);
goto out;
}
/* Check for ack frames since they don't contain any data */
if (opcode & IAP_ACK) {
- IRDA_DEBUG(0, "%s() Got ack frame!\n", __func__);
+ pr_debug("%s() Got ack frame!\n", __func__);
goto out;
}
@@ -875,7 +850,7 @@ static int iriap_data_indication(void *instance, void *sap,
switch (opcode) {
case GET_INFO_BASE:
- IRDA_DEBUG(0, "IrLMP GetInfoBaseDetails not implemented!\n");
+ pr_debug("IrLMP GetInfoBaseDetails not implemented!\n");
break;
case GET_VALUE_BY_CLASS:
iriap_do_call_event(self, IAP_RECV_F_LST, NULL);
@@ -885,7 +860,7 @@ static int iriap_data_indication(void *instance, void *sap,
iriap_getvaluebyclass_confirm(self, skb);
break;
case IAS_CLASS_UNKNOWN:
- IRDA_DEBUG(1, "%s(), No such class!\n", __func__);
+ pr_debug("%s(), No such class!\n", __func__);
/* Finished, close connection! */
iriap_disconnect_request(self);
@@ -898,7 +873,7 @@ static int iriap_data_indication(void *instance, void *sap,
self->priv);
break;
case IAS_ATTRIB_UNKNOWN:
- IRDA_DEBUG(1, "%s(), No such attribute!\n", __func__);
+ pr_debug("%s(), No such attribute!\n", __func__);
/* Finished, close connection! */
iriap_disconnect_request(self);
@@ -913,8 +888,8 @@ static int iriap_data_indication(void *instance, void *sap,
}
break;
default:
- IRDA_DEBUG(0, "%s(), Unknown op-code: %02x\n", __func__,
- opcode);
+ pr_debug("%s(), Unknown op-code: %02x\n", __func__,
+ opcode);
break;
}
@@ -935,8 +910,6 @@ void iriap_call_indication(struct iriap_cb *self, struct sk_buff *skb)
__u8 *fp;
__u8 opcode;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
IRDA_ASSERT(skb != NULL, return;);
@@ -945,16 +918,16 @@ void iriap_call_indication(struct iriap_cb *self, struct sk_buff *skb)
opcode = fp[0];
if (~opcode & 0x80) {
- IRDA_WARNING("%s: IrIAS multiframe commands or results "
- "is not implemented yet!\n", __func__);
+ net_warn_ratelimited("%s: IrIAS multiframe commands or results is not implemented yet!\n",
+ __func__);
return;
}
opcode &= 0x7f; /* Mask away LST bit */
switch (opcode) {
case GET_INFO_BASE:
- IRDA_WARNING("%s: GetInfoBaseDetails not implemented yet!\n",
- __func__);
+ net_warn_ratelimited("%s: GetInfoBaseDetails not implemented yet!\n",
+ __func__);
break;
case GET_VALUE_BY_CLASS:
iriap_getvaluebyclass_indication(self, skb);
diff --git a/net/irda/iriap_event.c b/net/irda/iriap_event.c
index 703774e29e32..e6098b2e048a 100644
--- a/net/irda/iriap_event.c
+++ b/net/irda/iriap_event.c
@@ -187,7 +187,7 @@ static void state_s_disconnect(struct iriap_cb *self, IRIAP_EVENT event,
case IAP_LM_DISCONNECT_INDICATION:
break;
default:
- IRDA_DEBUG(0, "%s(), Unknown event %d\n", __func__, event);
+ pr_debug("%s(), Unknown event %d\n", __func__, event);
break;
}
}
@@ -219,7 +219,7 @@ static void state_s_connecting(struct iriap_cb *self, IRIAP_EVENT event,
iriap_next_client_state(self, S_DISCONNECT);
break;
default:
- IRDA_DEBUG(0, "%s(), Unknown event %d\n", __func__, event);
+ pr_debug("%s(), Unknown event %d\n", __func__, event);
break;
}
}
@@ -243,7 +243,7 @@ static void state_s_call(struct iriap_cb *self, IRIAP_EVENT event,
iriap_next_client_state(self, S_DISCONNECT);
break;
default:
- IRDA_DEBUG(0, "state_s_call: Unknown event %d\n", event);
+ pr_debug("state_s_call: Unknown event %d\n", event);
break;
}
}
@@ -271,7 +271,7 @@ static void state_s_make_call(struct iriap_cb *self, IRIAP_EVENT event,
iriap_next_call_state(self, S_OUTSTANDING);
break;
default:
- IRDA_DEBUG(0, "%s(), Unknown event %d\n", __func__, event);
+ pr_debug("%s(), Unknown event %d\n", __func__, event);
break;
}
}
@@ -285,7 +285,7 @@ static void state_s_make_call(struct iriap_cb *self, IRIAP_EVENT event,
static void state_s_calling(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
- IRDA_DEBUG(0, "%s(), Not implemented\n", __func__);
+ pr_debug("%s(), Not implemented\n", __func__);
}
/*
@@ -307,7 +307,7 @@ static void state_s_outstanding(struct iriap_cb *self, IRIAP_EVENT event,
iriap_next_call_state(self, S_WAIT_FOR_CALL);
break;
default:
- IRDA_DEBUG(0, "%s(), Unknown event %d\n", __func__, event);
+ pr_debug("%s(), Unknown event %d\n", __func__, event);
break;
}
}
@@ -320,7 +320,7 @@ static void state_s_outstanding(struct iriap_cb *self, IRIAP_EVENT event,
static void state_s_replying(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
- IRDA_DEBUG(0, "%s(), Not implemented\n", __func__);
+ pr_debug("%s(), Not implemented\n", __func__);
}
/*
@@ -332,7 +332,7 @@ static void state_s_replying(struct iriap_cb *self, IRIAP_EVENT event,
static void state_s_wait_for_call(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
- IRDA_DEBUG(0, "%s(), Not implemented\n", __func__);
+ pr_debug("%s(), Not implemented\n", __func__);
}
@@ -345,7 +345,7 @@ static void state_s_wait_for_call(struct iriap_cb *self, IRIAP_EVENT event,
static void state_s_wait_active(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
- IRDA_DEBUG(0, "%s(), Not implemented\n", __func__);
+ pr_debug("%s(), Not implemented\n", __func__);
}
/**************************************************************************
@@ -368,10 +368,8 @@ static void state_r_disconnect(struct iriap_cb *self, IRIAP_EVENT event,
switch (event) {
case IAP_LM_CONNECT_INDICATION:
tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC);
- if (tx_skb == NULL) {
- IRDA_WARNING("%s: unable to malloc!\n", __func__);
+ if (tx_skb == NULL)
return;
- }
/* Reserve space for MUX_CONTROL and LAP header */
skb_reserve(tx_skb, LMP_MAX_HEADER);
@@ -388,7 +386,7 @@ static void state_r_disconnect(struct iriap_cb *self, IRIAP_EVENT event,
iriap_next_r_connect_state(self, R_RECEIVING);
break;
default:
- IRDA_DEBUG(0, "%s(), unknown event %d\n", __func__, event);
+ pr_debug("%s(), unknown event %d\n", __func__, event);
break;
}
}
@@ -399,8 +397,6 @@ static void state_r_disconnect(struct iriap_cb *self, IRIAP_EVENT event,
static void state_r_call(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
- IRDA_DEBUG(4, "%s()\n", __func__);
-
switch (event) {
case IAP_LM_DISCONNECT_INDICATION:
/* Abort call */
@@ -408,7 +404,7 @@ static void state_r_call(struct iriap_cb *self, IRIAP_EVENT event,
iriap_next_r_connect_state(self, R_WAITING);
break;
default:
- IRDA_DEBUG(0, "%s(), unknown event!\n", __func__);
+ pr_debug("%s(), unknown event!\n", __func__);
break;
}
}
@@ -423,13 +419,13 @@ static void state_r_call(struct iriap_cb *self, IRIAP_EVENT event,
static void state_r_waiting(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
- IRDA_DEBUG(0, "%s(), Not implemented\n", __func__);
+ pr_debug("%s(), Not implemented\n", __func__);
}
static void state_r_wait_active(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
- IRDA_DEBUG(0, "%s(), Not implemented\n", __func__);
+ pr_debug("%s(), Not implemented\n", __func__);
}
/*
@@ -441,8 +437,6 @@ static void state_r_wait_active(struct iriap_cb *self, IRIAP_EVENT event,
static void state_r_receiving(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
- IRDA_DEBUG(4, "%s()\n", __func__);
-
switch (event) {
case IAP_RECV_F_LST:
iriap_next_r_connect_state(self, R_EXECUTE);
@@ -450,7 +444,7 @@ static void state_r_receiving(struct iriap_cb *self, IRIAP_EVENT event,
iriap_call_indication(self, skb);
break;
default:
- IRDA_DEBUG(0, "%s(), unknown event!\n", __func__);
+ pr_debug("%s(), unknown event!\n", __func__);
break;
}
}
@@ -464,8 +458,6 @@ static void state_r_receiving(struct iriap_cb *self, IRIAP_EVENT event,
static void state_r_execute(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(skb != NULL, return;);
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IAS_MAGIC, return;);
@@ -485,7 +477,7 @@ static void state_r_execute(struct iriap_cb *self, IRIAP_EVENT event,
irlmp_data_request(self->lsap, skb);
break;
default:
- IRDA_DEBUG(0, "%s(), unknown event!\n", __func__);
+ pr_debug("%s(), unknown event!\n", __func__);
break;
}
}
@@ -493,7 +485,7 @@ static void state_r_execute(struct iriap_cb *self, IRIAP_EVENT event,
static void state_r_returning(struct iriap_cb *self, IRIAP_EVENT event,
struct sk_buff *skb)
{
- IRDA_DEBUG(0, "%s(), event=%d\n", __func__, event);
+ pr_debug("%s(), event=%d\n", __func__, event);
switch (event) {
case IAP_RECV_F_LST:
diff --git a/net/irda/irias_object.c b/net/irda/irias_object.c
index f07ed9fd5792..53b86d0e1630 100644
--- a/net/irda/irias_object.c
+++ b/net/irda/irias_object.c
@@ -48,20 +48,18 @@ struct ias_object *irias_new_object( char *name, int id)
{
struct ias_object *obj;
- IRDA_DEBUG( 4, "%s()\n", __func__);
-
obj = kzalloc(sizeof(struct ias_object), GFP_ATOMIC);
if (obj == NULL) {
- IRDA_WARNING("%s(), Unable to allocate object!\n",
- __func__);
+ net_warn_ratelimited("%s(), Unable to allocate object!\n",
+ __func__);
return NULL;
}
obj->magic = IAS_OBJECT_MAGIC;
obj->name = kstrndup(name, IAS_MAX_CLASSNAME, GFP_ATOMIC);
if (!obj->name) {
- IRDA_WARNING("%s(), Unable to allocate name!\n",
- __func__);
+ net_warn_ratelimited("%s(), Unable to allocate name!\n",
+ __func__);
kfree(obj);
return NULL;
}
@@ -73,8 +71,8 @@ struct ias_object *irias_new_object( char *name, int id)
obj->attribs = hashbin_new(HB_LOCK);
if (obj->attribs == NULL) {
- IRDA_WARNING("%s(), Unable to allocate attribs!\n",
- __func__);
+ net_warn_ratelimited("%s(), Unable to allocate attribs!\n",
+ __func__);
kfree(obj->name);
kfree(obj);
return NULL;
@@ -134,8 +132,8 @@ int irias_delete_object(struct ias_object *obj)
/* Remove from list */
node = hashbin_remove_this(irias_objects, (irda_queue_t *) obj);
if (!node)
- IRDA_DEBUG( 0, "%s(), object already removed!\n",
- __func__);
+ pr_debug("%s(), object already removed!\n",
+ __func__);
/* Destroy */
__irias_delete_object(obj);
@@ -269,8 +267,8 @@ int irias_object_change_attribute(char *obj_name, char *attrib_name,
/* Find object */
obj = hashbin_lock_find(irias_objects, 0, obj_name);
if (obj == NULL) {
- IRDA_WARNING("%s: Unable to find object: %s\n", __func__,
- obj_name);
+ net_warn_ratelimited("%s: Unable to find object: %s\n",
+ __func__, obj_name);
return -1;
}
@@ -280,15 +278,15 @@ int irias_object_change_attribute(char *obj_name, char *attrib_name,
/* Find attribute */
attrib = hashbin_find(obj->attribs, 0, attrib_name);
if (attrib == NULL) {
- IRDA_WARNING("%s: Unable to find attribute: %s\n",
- __func__, attrib_name);
+ net_warn_ratelimited("%s: Unable to find attribute: %s\n",
+ __func__, attrib_name);
spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags);
return -1;
}
if ( attrib->value->type != new_value->type) {
- IRDA_DEBUG( 0, "%s(), changing value type not allowed!\n",
- __func__);
+ pr_debug("%s(), changing value type not allowed!\n",
+ __func__);
spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags);
return -1;
}
@@ -322,8 +320,8 @@ void irias_add_integer_attrib(struct ias_object *obj, char *name, int value,
attrib = kzalloc(sizeof(struct ias_attrib), GFP_ATOMIC);
if (attrib == NULL) {
- IRDA_WARNING("%s: Unable to allocate attribute!\n",
- __func__);
+ net_warn_ratelimited("%s: Unable to allocate attribute!\n",
+ __func__);
return;
}
@@ -333,8 +331,8 @@ void irias_add_integer_attrib(struct ias_object *obj, char *name, int value,
/* Insert value */
attrib->value = irias_new_integer_value(value);
if (!attrib->name || !attrib->value) {
- IRDA_WARNING("%s: Unable to allocate attribute!\n",
- __func__);
+ net_warn_ratelimited("%s: Unable to allocate attribute!\n",
+ __func__);
if (attrib->value)
irias_delete_value(attrib->value);
kfree(attrib->name);
@@ -366,8 +364,8 @@ void irias_add_octseq_attrib(struct ias_object *obj, char *name, __u8 *octets,
attrib = kzalloc(sizeof(struct ias_attrib), GFP_ATOMIC);
if (attrib == NULL) {
- IRDA_WARNING("%s: Unable to allocate attribute!\n",
- __func__);
+ net_warn_ratelimited("%s: Unable to allocate attribute!\n",
+ __func__);
return;
}
@@ -376,8 +374,8 @@ void irias_add_octseq_attrib(struct ias_object *obj, char *name, __u8 *octets,
attrib->value = irias_new_octseq_value( octets, len);
if (!attrib->name || !attrib->value) {
- IRDA_WARNING("%s: Unable to allocate attribute!\n",
- __func__);
+ net_warn_ratelimited("%s: Unable to allocate attribute!\n",
+ __func__);
if (attrib->value)
irias_delete_value(attrib->value);
kfree(attrib->name);
@@ -408,8 +406,8 @@ void irias_add_string_attrib(struct ias_object *obj, char *name, char *value,
attrib = kzalloc(sizeof( struct ias_attrib), GFP_ATOMIC);
if (attrib == NULL) {
- IRDA_WARNING("%s: Unable to allocate attribute!\n",
- __func__);
+ net_warn_ratelimited("%s: Unable to allocate attribute!\n",
+ __func__);
return;
}
@@ -418,8 +416,8 @@ void irias_add_string_attrib(struct ias_object *obj, char *name, char *value,
attrib->value = irias_new_string_value(value);
if (!attrib->name || !attrib->value) {
- IRDA_WARNING("%s: Unable to allocate attribute!\n",
- __func__);
+ net_warn_ratelimited("%s: Unable to allocate attribute!\n",
+ __func__);
if (attrib->value)
irias_delete_value(attrib->value);
kfree(attrib->name);
@@ -442,10 +440,8 @@ struct ias_value *irias_new_integer_value(int integer)
struct ias_value *value;
value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC);
- if (value == NULL) {
- IRDA_WARNING("%s: Unable to kmalloc!\n", __func__);
+ if (value == NULL)
return NULL;
- }
value->type = IAS_INTEGER;
value->len = 4;
@@ -467,16 +463,14 @@ struct ias_value *irias_new_string_value(char *string)
struct ias_value *value;
value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC);
- if (value == NULL) {
- IRDA_WARNING("%s: Unable to kmalloc!\n", __func__);
+ if (value == NULL)
return NULL;
- }
value->type = IAS_STRING;
value->charset = CS_ASCII;
value->t.string = kstrndup(string, IAS_MAX_STRING, GFP_ATOMIC);
if (!value->t.string) {
- IRDA_WARNING("%s: Unable to kmalloc!\n", __func__);
+ net_warn_ratelimited("%s: Unable to kmalloc!\n", __func__);
kfree(value);
return NULL;
}
@@ -498,10 +492,8 @@ struct ias_value *irias_new_octseq_value(__u8 *octseq , int len)
struct ias_value *value;
value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC);
- if (value == NULL) {
- IRDA_WARNING("%s: Unable to kmalloc!\n", __func__);
+ if (value == NULL)
return NULL;
- }
value->type = IAS_OCT_SEQ;
/* Check length */
@@ -511,7 +503,7 @@ struct ias_value *irias_new_octseq_value(__u8 *octseq , int len)
value->t.oct_seq = kmemdup(octseq, len, GFP_ATOMIC);
if (value->t.oct_seq == NULL){
- IRDA_WARNING("%s: Unable to kmalloc!\n", __func__);
+ net_warn_ratelimited("%s: Unable to kmalloc!\n", __func__);
kfree(value);
return NULL;
}
@@ -523,10 +515,8 @@ struct ias_value *irias_new_missing_value(void)
struct ias_value *value;
value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC);
- if (value == NULL) {
- IRDA_WARNING("%s: Unable to kmalloc!\n", __func__);
+ if (value == NULL)
return NULL;
- }
value->type = IAS_MISSING;
@@ -541,8 +531,6 @@ struct ias_value *irias_new_missing_value(void)
*/
void irias_delete_value(struct ias_value *value)
{
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(value != NULL, return;);
switch (value->type) {
@@ -559,7 +547,7 @@ void irias_delete_value(struct ias_value *value)
kfree(value->t.oct_seq);
break;
default:
- IRDA_DEBUG(0, "%s(), Unknown value type!\n", __func__);
+ pr_debug("%s(), Unknown value type!\n", __func__);
break;
}
kfree(value);
diff --git a/net/irda/irlan/irlan_client.c b/net/irda/irlan/irlan_client.c
index 42cf1390ce9c..c5837a40c78e 100644
--- a/net/irda/irlan/irlan_client.c
+++ b/net/irda/irlan/irlan_client.c
@@ -72,8 +72,6 @@ static void irlan_client_kick_timer_expired(void *data)
{
struct irlan_cb *self = (struct irlan_cb *) data;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
@@ -91,8 +89,6 @@ static void irlan_client_kick_timer_expired(void *data)
static void irlan_client_start_kick_timer(struct irlan_cb *self, int timeout)
{
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
irda_start_timer(&self->client.kick_timer, timeout, (void *) self,
irlan_client_kick_timer_expired);
}
@@ -105,8 +101,6 @@ static void irlan_client_start_kick_timer(struct irlan_cb *self, int timeout)
*/
void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr)
{
- IRDA_DEBUG(1, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
@@ -117,7 +111,7 @@ void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr)
if ((self->client.state != IRLAN_IDLE) ||
(self->provider.access_type == ACCESS_DIRECT))
{
- IRDA_DEBUG(0, "%s(), already awake!\n", __func__ );
+ pr_debug("%s(), already awake!\n", __func__);
return;
}
@@ -126,7 +120,7 @@ void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr)
self->daddr = daddr;
if (self->disconnect_reason == LM_USER_REQUEST) {
- IRDA_DEBUG(0, "%s(), still stopped by user\n", __func__ );
+ pr_debug("%s(), still stopped by user\n", __func__);
return;
}
@@ -153,8 +147,6 @@ void irlan_client_discovery_indication(discinfo_t *discovery,
struct irlan_cb *self;
__u32 saddr, daddr;
- IRDA_DEBUG(1, "%s()\n", __func__ );
-
IRDA_ASSERT(discovery != NULL, return;);
/*
@@ -175,8 +167,8 @@ void irlan_client_discovery_indication(discinfo_t *discovery,
if (self) {
IRDA_ASSERT(self->magic == IRLAN_MAGIC, goto out;);
- IRDA_DEBUG(1, "%s(), Found instance (%08x)!\n", __func__ ,
- daddr);
+ pr_debug("%s(), Found instance (%08x)!\n", __func__ ,
+ daddr);
irlan_client_wakeup(self, saddr, daddr);
}
@@ -195,8 +187,6 @@ static int irlan_client_ctrl_data_indication(void *instance, void *sap,
{
struct irlan_cb *self;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
self = instance;
IRDA_ASSERT(self != NULL, return -1;);
@@ -206,7 +196,7 @@ static int irlan_client_ctrl_data_indication(void *instance, void *sap,
irlan_do_client_event(self, IRLAN_DATA_INDICATION, skb);
/* Ready for a new command */
- IRDA_DEBUG(2, "%s(), clearing tx_busy\n", __func__ );
+ pr_debug("%s(), clearing tx_busy\n", __func__);
self->client.tx_busy = FALSE;
/* Check if we have some queued commands waiting to be sent */
@@ -223,7 +213,7 @@ static void irlan_client_ctrl_disconnect_indication(void *instance, void *sap,
struct tsap_cb *tsap;
struct sk_buff *skb;
- IRDA_DEBUG(4, "%s(), reason=%d\n", __func__ , reason);
+ pr_debug("%s(), reason=%d\n", __func__ , reason);
self = instance;
tsap = sap;
@@ -255,8 +245,6 @@ static void irlan_client_open_ctrl_tsap(struct irlan_cb *self)
struct tsap_cb *tsap;
notify_t notify;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
@@ -275,7 +263,7 @@ static void irlan_client_open_ctrl_tsap(struct irlan_cb *self)
tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, &notify);
if (!tsap) {
- IRDA_DEBUG(2, "%s(), Got no tsap!\n", __func__ );
+ pr_debug("%s(), Got no tsap!\n", __func__);
return;
}
self->client.tsap_ctrl = tsap;
@@ -295,8 +283,6 @@ static void irlan_client_ctrl_connect_confirm(void *instance, void *sap,
{
struct irlan_cb *self;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
self = instance;
IRDA_ASSERT(self != NULL, return;);
@@ -323,34 +309,34 @@ static void print_ret_code(__u8 code)
printk(KERN_INFO "Success\n");
break;
case 1:
- IRDA_WARNING("IrLAN: Insufficient resources\n");
+ net_warn_ratelimited("IrLAN: Insufficient resources\n");
break;
case 2:
- IRDA_WARNING("IrLAN: Invalid command format\n");
+ net_warn_ratelimited("IrLAN: Invalid command format\n");
break;
case 3:
- IRDA_WARNING("IrLAN: Command not supported\n");
+ net_warn_ratelimited("IrLAN: Command not supported\n");
break;
case 4:
- IRDA_WARNING("IrLAN: Parameter not supported\n");
+ net_warn_ratelimited("IrLAN: Parameter not supported\n");
break;
case 5:
- IRDA_WARNING("IrLAN: Value not supported\n");
+ net_warn_ratelimited("IrLAN: Value not supported\n");
break;
case 6:
- IRDA_WARNING("IrLAN: Not open\n");
+ net_warn_ratelimited("IrLAN: Not open\n");
break;
case 7:
- IRDA_WARNING("IrLAN: Authentication required\n");
+ net_warn_ratelimited("IrLAN: Authentication required\n");
break;
case 8:
- IRDA_WARNING("IrLAN: Invalid password\n");
+ net_warn_ratelimited("IrLAN: Invalid password\n");
break;
case 9:
- IRDA_WARNING("IrLAN: Protocol error\n");
+ net_warn_ratelimited("IrLAN: Protocol error\n");
break;
case 255:
- IRDA_WARNING("IrLAN: Asynchronous status\n");
+ net_warn_ratelimited("IrLAN: Asynchronous status\n");
break;
}
}
@@ -374,13 +360,13 @@ void irlan_client_parse_response(struct irlan_cb *self, struct sk_buff *skb)
IRDA_ASSERT(skb != NULL, return;);
- IRDA_DEBUG(4, "%s() skb->len=%d\n", __func__ , (int) skb->len);
+ pr_debug("%s() skb->len=%d\n", __func__ , (int)skb->len);
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
if (!skb) {
- IRDA_ERROR("%s(), Got NULL skb!\n", __func__);
+ net_err_ratelimited("%s(), Got NULL skb!\n", __func__);
return;
}
frame = skb->data;
@@ -405,7 +391,7 @@ void irlan_client_parse_response(struct irlan_cb *self, struct sk_buff *skb)
/* How many parameters? */
count = frame[1];
- IRDA_DEBUG(4, "%s(), got %d parameters\n", __func__ , count);
+ pr_debug("%s(), got %d parameters\n", __func__ , count);
ptr = frame+2;
@@ -413,7 +399,7 @@ void irlan_client_parse_response(struct irlan_cb *self, struct sk_buff *skb)
for (i=0; i<count;i++) {
ret = irlan_extract_param(ptr, name, value, &val_len);
if (ret < 0) {
- IRDA_DEBUG(2, "%s(), IrLAN, Error!\n", __func__ );
+ pr_debug("%s(), IrLAN, Error!\n", __func__);
break;
}
ptr += ret;
@@ -437,7 +423,7 @@ static void irlan_check_response_param(struct irlan_cb *self, char *param,
__u8 *bytes;
int i;
- IRDA_DEBUG(4, "%s(), parm=%s\n", __func__ , param);
+ pr_debug("%s(), parm=%s\n", __func__ , param);
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
@@ -475,13 +461,13 @@ static void irlan_check_response_param(struct irlan_cb *self, char *param,
else if (strcmp(value, "HOSTED") == 0)
self->client.access_type = ACCESS_HOSTED;
else {
- IRDA_DEBUG(2, "%s(), unknown access type!\n", __func__ );
+ pr_debug("%s(), unknown access type!\n", __func__);
}
}
/* IRLAN version */
if (strcmp(param, "IRLAN_VER") == 0) {
- IRDA_DEBUG(4, "IrLAN version %d.%d\n", (__u8) value[0],
- (__u8) value[1]);
+ pr_debug("IrLAN version %d.%d\n", (__u8)value[0],
+ (__u8)value[1]);
self->version[0] = value[0];
self->version[1] = value[1];
@@ -490,37 +476,37 @@ static void irlan_check_response_param(struct irlan_cb *self, char *param,
/* Which remote TSAP to use for data channel */
if (strcmp(param, "DATA_CHAN") == 0) {
self->dtsap_sel_data = value[0];
- IRDA_DEBUG(4, "Data TSAP = %02x\n", self->dtsap_sel_data);
+ pr_debug("Data TSAP = %02x\n", self->dtsap_sel_data);
return;
}
if (strcmp(param, "CON_ARB") == 0) {
memcpy(&tmp_cpu, value, 2); /* Align value */
le16_to_cpus(&tmp_cpu); /* Convert to host order */
self->client.recv_arb_val = tmp_cpu;
- IRDA_DEBUG(2, "%s(), receive arb val=%d\n", __func__ ,
- self->client.recv_arb_val);
+ pr_debug("%s(), receive arb val=%d\n", __func__ ,
+ self->client.recv_arb_val);
}
if (strcmp(param, "MAX_FRAME") == 0) {
memcpy(&tmp_cpu, value, 2); /* Align value */
le16_to_cpus(&tmp_cpu); /* Convert to host order */
self->client.max_frame = tmp_cpu;
- IRDA_DEBUG(4, "%s(), max frame=%d\n", __func__ ,
- self->client.max_frame);
+ pr_debug("%s(), max frame=%d\n", __func__ ,
+ self->client.max_frame);
}
/* RECONNECT_KEY, in case the link goes down! */
if (strcmp(param, "RECONNECT_KEY") == 0) {
- IRDA_DEBUG(4, "Got reconnect key: ");
+ pr_debug("Got reconnect key: ");
/* for (i = 0; i < val_len; i++) */
/* printk("%02x", value[i]); */
memcpy(self->client.reconnect_key, value, val_len);
self->client.key_len = val_len;
- IRDA_DEBUG(4, "\n");
+ pr_debug("\n");
}
/* FILTER_ENTRY, have we got an ethernet address? */
if (strcmp(param, "FILTER_ENTRY") == 0) {
bytes = value;
- IRDA_DEBUG(4, "Ethernet address = %pM\n", bytes);
+ pr_debug("Ethernet address = %pM\n", bytes);
for (i = 0; i < 6; i++)
self->dev->dev_addr[i] = bytes[i];
}
@@ -537,8 +523,6 @@ void irlan_client_get_value_confirm(int result, __u16 obj_id,
{
struct irlan_cb *self;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(priv != NULL, return;);
self = priv;
@@ -550,7 +534,7 @@ void irlan_client_get_value_confirm(int result, __u16 obj_id,
/* Check if request succeeded */
if (result != IAS_SUCCESS) {
- IRDA_DEBUG(2, "%s(), got NULL value!\n", __func__ );
+ pr_debug("%s(), got NULL value!\n", __func__);
irlan_do_client_event(self, IRLAN_IAS_PROVIDER_NOT_AVAIL,
NULL);
return;
@@ -568,7 +552,7 @@ void irlan_client_get_value_confirm(int result, __u16 obj_id,
irias_delete_value(value);
break;
default:
- IRDA_DEBUG(2, "%s(), unknown type!\n", __func__ );
+ pr_debug("%s(), unknown type!\n", __func__);
break;
}
irlan_do_client_event(self, IRLAN_IAS_PROVIDER_NOT_AVAIL, NULL);
diff --git a/net/irda/irlan/irlan_client_event.c b/net/irda/irlan/irlan_client_event.c
index 8d5a8ebc444f..cc93fabbbb19 100644
--- a/net/irda/irlan/irlan_client_event.c
+++ b/net/irda/irlan/irlan_client_event.c
@@ -92,16 +92,14 @@ void irlan_do_client_event(struct irlan_cb *self, IRLAN_EVENT event,
static int irlan_client_state_idle(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
switch (event) {
case IRLAN_DISCOVERY_INDICATION:
if (self->client.iriap) {
- IRDA_WARNING("%s(), busy with a previous query\n",
- __func__);
+ net_warn_ratelimited("%s(), busy with a previous query\n",
+ __func__);
return -EBUSY;
}
@@ -114,10 +112,10 @@ static int irlan_client_state_idle(struct irlan_cb *self, IRLAN_EVENT event,
"IrLAN", "IrDA:TinyTP:LsapSel");
break;
case IRLAN_WATCHDOG_TIMEOUT:
- IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__ );
+ pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
break;
default:
- IRDA_DEBUG(4, "%s(), Unknown event %d\n", __func__ , event);
+ pr_debug("%s(), Unknown event %d\n", __func__ , event);
break;
}
if (skb)
@@ -136,8 +134,6 @@ static int irlan_client_state_idle(struct irlan_cb *self, IRLAN_EVENT event,
static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
@@ -154,7 +150,7 @@ static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event,
irlan_next_client_state(self, IRLAN_CONN);
break;
case IRLAN_IAS_PROVIDER_NOT_AVAIL:
- IRDA_DEBUG(2, "%s(), IAS_PROVIDER_NOT_AVAIL\n", __func__ );
+ pr_debug("%s(), IAS_PROVIDER_NOT_AVAIL\n", __func__);
irlan_next_client_state(self, IRLAN_IDLE);
/* Give the client a kick! */
@@ -167,10 +163,10 @@ static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event,
irlan_next_client_state(self, IRLAN_IDLE);
break;
case IRLAN_WATCHDOG_TIMEOUT:
- IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__ );
+ pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
break;
default:
- IRDA_DEBUG(2, "%s(), Unknown event %d\n", __func__ , event);
+ pr_debug("%s(), Unknown event %d\n", __func__ , event);
break;
}
if (skb)
@@ -189,8 +185,6 @@ static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event,
static int irlan_client_state_conn(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return -1;);
switch (event) {
@@ -204,10 +198,10 @@ static int irlan_client_state_conn(struct irlan_cb *self, IRLAN_EVENT event,
irlan_next_client_state(self, IRLAN_IDLE);
break;
case IRLAN_WATCHDOG_TIMEOUT:
- IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__ );
+ pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
break;
default:
- IRDA_DEBUG(2, "%s(), Unknown event %d\n", __func__ , event);
+ pr_debug("%s(), Unknown event %d\n", __func__ , event);
break;
}
if (skb)
@@ -224,8 +218,6 @@ static int irlan_client_state_conn(struct irlan_cb *self, IRLAN_EVENT event,
static int irlan_client_state_info(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return -1;);
switch (event) {
@@ -244,10 +236,10 @@ static int irlan_client_state_info(struct irlan_cb *self, IRLAN_EVENT event,
irlan_next_client_state(self, IRLAN_IDLE);
break;
case IRLAN_WATCHDOG_TIMEOUT:
- IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__ );
+ pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
break;
default:
- IRDA_DEBUG(2, "%s(), Unknown event %d\n", __func__ , event);
+ pr_debug("%s(), Unknown event %d\n", __func__ , event);
break;
}
if (skb)
@@ -266,8 +258,6 @@ static int irlan_client_state_info(struct irlan_cb *self, IRLAN_EVENT event,
static int irlan_client_state_media(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return -1;);
switch(event) {
@@ -281,10 +271,10 @@ static int irlan_client_state_media(struct irlan_cb *self, IRLAN_EVENT event,
irlan_next_client_state(self, IRLAN_IDLE);
break;
case IRLAN_WATCHDOG_TIMEOUT:
- IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__ );
+ pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
break;
default:
- IRDA_DEBUG(2, "%s(), Unknown event %d\n", __func__ , event);
+ pr_debug("%s(), Unknown event %d\n", __func__ , event);
break;
}
if (skb)
@@ -305,8 +295,6 @@ static int irlan_client_state_open(struct irlan_cb *self, IRLAN_EVENT event,
{
struct qos_info qos;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return -1;);
switch(event) {
@@ -344,7 +332,7 @@ static int irlan_client_state_open(struct irlan_cb *self, IRLAN_EVENT event,
irlan_next_client_state(self, IRLAN_DATA);
break;
default:
- IRDA_DEBUG(2, "%s(), unknown access type!\n", __func__ );
+ pr_debug("%s(), unknown access type!\n", __func__);
break;
}
break;
@@ -353,10 +341,10 @@ static int irlan_client_state_open(struct irlan_cb *self, IRLAN_EVENT event,
irlan_next_client_state(self, IRLAN_IDLE);
break;
case IRLAN_WATCHDOG_TIMEOUT:
- IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__ );
+ pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
break;
default:
- IRDA_DEBUG(2, "%s(), Unknown event %d\n", __func__ , event);
+ pr_debug("%s(), Unknown event %d\n", __func__ , event);
break;
}
@@ -376,8 +364,6 @@ static int irlan_client_state_open(struct irlan_cb *self, IRLAN_EVENT event,
static int irlan_client_state_wait(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return -1;);
switch(event) {
@@ -390,10 +376,10 @@ static int irlan_client_state_wait(struct irlan_cb *self, IRLAN_EVENT event,
irlan_next_client_state(self, IRLAN_IDLE);
break;
case IRLAN_WATCHDOG_TIMEOUT:
- IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__ );
+ pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
break;
default:
- IRDA_DEBUG(2, "%s(), Unknown event %d\n", __func__ , event);
+ pr_debug("%s(), Unknown event %d\n", __func__ , event);
break;
}
if (skb)
@@ -407,8 +393,6 @@ static int irlan_client_state_arb(struct irlan_cb *self, IRLAN_EVENT event,
{
struct qos_info qos;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return -1;);
switch(event) {
@@ -429,7 +413,7 @@ static int irlan_client_state_arb(struct irlan_cb *self, IRLAN_EVENT event,
} else if (self->client.recv_arb_val >
self->provider.send_arb_val)
{
- IRDA_DEBUG(2, "%s(), lost the battle :-(\n", __func__ );
+ pr_debug("%s(), lost the battle :-(\n", __func__);
}
break;
case IRLAN_DATA_CONNECT_INDICATION:
@@ -440,10 +424,10 @@ static int irlan_client_state_arb(struct irlan_cb *self, IRLAN_EVENT event,
irlan_next_client_state(self, IRLAN_IDLE);
break;
case IRLAN_WATCHDOG_TIMEOUT:
- IRDA_DEBUG(2, "%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__ );
+ pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__);
break;
default:
- IRDA_DEBUG(2, "%s(), Unknown event %d\n", __func__ , event);
+ pr_debug("%s(), Unknown event %d\n", __func__ , event);
break;
}
if (skb)
@@ -462,8 +446,6 @@ static int irlan_client_state_arb(struct irlan_cb *self, IRLAN_EVENT event,
static int irlan_client_state_data(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
@@ -476,7 +458,7 @@ static int irlan_client_state_data(struct irlan_cb *self, IRLAN_EVENT event,
irlan_next_client_state(self, IRLAN_IDLE);
break;
default:
- IRDA_DEBUG(2, "%s(), Unknown event %d\n", __func__ , event);
+ pr_debug("%s(), Unknown event %d\n", __func__ , event);
break;
}
if (skb)
@@ -494,8 +476,6 @@ static int irlan_client_state_data(struct irlan_cb *self, IRLAN_EVENT event,
static int irlan_client_state_close(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
if (skb)
dev_kfree_skb(skb);
@@ -511,8 +491,6 @@ static int irlan_client_state_close(struct irlan_cb *self, IRLAN_EVENT event,
static int irlan_client_state_sync(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
if (skb)
dev_kfree_skb(skb);
diff --git a/net/irda/irlan/irlan_common.c b/net/irda/irlan/irlan_common.c
index 5a2d0a695529..481bbc2a4349 100644
--- a/net/irda/irlan/irlan_common.c
+++ b/net/irda/irlan/irlan_common.c
@@ -118,8 +118,6 @@ static int __init irlan_init(void)
struct irlan_cb *new;
__u16 hints;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
#ifdef CONFIG_PROC_FS
{ struct proc_dir_entry *proc;
proc = proc_create("irlan", 0, proc_irda, &irlan_fops);
@@ -130,7 +128,6 @@ static int __init irlan_init(void)
}
#endif /* CONFIG_PROC_FS */
- IRDA_DEBUG(4, "%s()\n", __func__ );
hints = irlmp_service_to_hint(S_LAN);
/* Register with IrLMP as a client */
@@ -173,8 +170,6 @@ static void __exit irlan_cleanup(void)
{
struct irlan_cb *self, *next;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
irlmp_unregister_client(ckey);
irlmp_unregister_service(skey);
@@ -201,8 +196,6 @@ static struct irlan_cb __init *irlan_open(__u32 saddr, __u32 daddr)
struct net_device *dev;
struct irlan_cb *self;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
/* Create network device with irlan */
dev = alloc_irlandev(eth ? "eth%d" : "irlan%d");
if (!dev)
@@ -245,8 +238,8 @@ static struct irlan_cb __init *irlan_open(__u32 saddr, __u32 daddr)
irlan_next_provider_state(self, IRLAN_IDLE);
if (register_netdev(dev)) {
- IRDA_DEBUG(2, "%s(), register_netdev() failed!\n",
- __func__ );
+ pr_debug("%s(), register_netdev() failed!\n",
+ __func__);
self = NULL;
free_netdev(dev);
} else {
@@ -266,8 +259,6 @@ static struct irlan_cb __init *irlan_open(__u32 saddr, __u32 daddr)
*/
static void __irlan_close(struct irlan_cb *self)
{
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
ASSERT_RTNL();
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
@@ -314,8 +305,6 @@ static void irlan_connect_indication(void *instance, void *sap,
struct irlan_cb *self;
struct tsap_cb *tsap;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
self = instance;
tsap = sap;
@@ -326,7 +315,7 @@ static void irlan_connect_indication(void *instance, void *sap,
self->max_sdu_size = max_sdu_size;
self->max_header_size = max_header_size;
- IRDA_DEBUG(0, "%s: We are now connected!\n", __func__);
+ pr_debug("%s: We are now connected!\n", __func__);
del_timer(&self->watchdog_timer);
@@ -370,7 +359,7 @@ static void irlan_connect_confirm(void *instance, void *sap,
/* TODO: we could set the MTU depending on the max_sdu_size */
- IRDA_DEBUG(0, "%s: We are now connected!\n", __func__);
+ pr_debug("%s: We are now connected!\n", __func__);
del_timer(&self->watchdog_timer);
/*
@@ -403,7 +392,7 @@ static void irlan_disconnect_indication(void *instance,
struct irlan_cb *self;
struct tsap_cb *tsap;
- IRDA_DEBUG(0, "%s(), reason=%d\n", __func__ , reason);
+ pr_debug("%s(), reason=%d\n", __func__ , reason);
self = instance;
tsap = sap;
@@ -415,29 +404,30 @@ static void irlan_disconnect_indication(void *instance,
IRDA_ASSERT(tsap == self->tsap_data, return;);
- IRDA_DEBUG(2, "IrLAN, data channel disconnected by peer!\n");
+ pr_debug("IrLAN, data channel disconnected by peer!\n");
/* Save reason so we know if we should try to reconnect or not */
self->disconnect_reason = reason;
switch (reason) {
case LM_USER_REQUEST: /* User request */
- IRDA_DEBUG(2, "%s(), User requested\n", __func__ );
+ pr_debug("%s(), User requested\n", __func__);
break;
case LM_LAP_DISCONNECT: /* Unexpected IrLAP disconnect */
- IRDA_DEBUG(2, "%s(), Unexpected IrLAP disconnect\n", __func__ );
+ pr_debug("%s(), Unexpected IrLAP disconnect\n", __func__);
break;
case LM_CONNECT_FAILURE: /* Failed to establish IrLAP connection */
- IRDA_DEBUG(2, "%s(), IrLAP connect failed\n", __func__ );
+ pr_debug("%s(), IrLAP connect failed\n", __func__);
break;
case LM_LAP_RESET: /* IrLAP reset */
- IRDA_DEBUG(2, "%s(), IrLAP reset\n", __func__ );
+ pr_debug("%s(), IrLAP reset\n", __func__);
break;
case LM_INIT_DISCONNECT:
- IRDA_DEBUG(2, "%s(), IrLMP connect failed\n", __func__ );
+ pr_debug("%s(), IrLMP connect failed\n", __func__);
break;
default:
- IRDA_ERROR("%s(), Unknown disconnect reason\n", __func__);
+ net_err_ratelimited("%s(), Unknown disconnect reason\n",
+ __func__);
break;
}
@@ -459,8 +449,6 @@ void irlan_open_data_tsap(struct irlan_cb *self)
struct tsap_cb *tsap;
notify_t notify;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
@@ -481,7 +469,7 @@ void irlan_open_data_tsap(struct irlan_cb *self)
tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, &notify);
if (!tsap) {
- IRDA_DEBUG(2, "%s(), Got no tsap!\n", __func__ );
+ pr_debug("%s(), Got no tsap!\n", __func__);
return;
}
self->tsap_data = tsap;
@@ -495,8 +483,6 @@ void irlan_open_data_tsap(struct irlan_cb *self)
void irlan_close_tsaps(struct irlan_cb *self)
{
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
@@ -585,8 +571,6 @@ int irlan_run_ctrl_tx_queue(struct irlan_cb *self)
{
struct sk_buff *skb;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
if (irda_lock(&self->client.tx_busy) == FALSE)
return -EBUSY;
@@ -604,7 +588,7 @@ int irlan_run_ctrl_tx_queue(struct irlan_cb *self)
dev_kfree_skb(skb);
return -1;
}
- IRDA_DEBUG(2, "%s(), sending ...\n", __func__ );
+ pr_debug("%s(), sending ...\n", __func__);
return irttp_data_request(self->client.tsap_ctrl, skb);
}
@@ -617,8 +601,6 @@ int irlan_run_ctrl_tx_queue(struct irlan_cb *self)
*/
static void irlan_ctrl_data_request(struct irlan_cb *self, struct sk_buff *skb)
{
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
/* Queue command */
skb_queue_tail(&self->client.txq, skb);
@@ -637,8 +619,6 @@ void irlan_get_provider_info(struct irlan_cb *self)
struct sk_buff *skb;
__u8 *frame;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
@@ -670,8 +650,6 @@ void irlan_open_data_channel(struct irlan_cb *self)
struct sk_buff *skb;
__u8 *frame;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
@@ -705,8 +683,6 @@ void irlan_close_data_channel(struct irlan_cb *self)
struct sk_buff *skb;
__u8 *frame;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
@@ -746,8 +722,6 @@ static void irlan_open_unicast_addr(struct irlan_cb *self)
struct sk_buff *skb;
__u8 *frame;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
@@ -788,8 +762,6 @@ void irlan_set_broadcast_filter(struct irlan_cb *self, int status)
struct sk_buff *skb;
__u8 *frame;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
@@ -832,8 +804,6 @@ void irlan_set_multicast_filter(struct irlan_cb *self, int status)
struct sk_buff *skb;
__u8 *frame;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
@@ -877,8 +847,6 @@ static void irlan_get_unicast_addr(struct irlan_cb *self)
struct sk_buff *skb;
__u8 *frame;
- IRDA_DEBUG(2, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
@@ -917,8 +885,6 @@ void irlan_get_media_char(struct irlan_cb *self)
struct sk_buff *skb;
__u8 *frame;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
@@ -1005,7 +971,7 @@ static int __irlan_insert_param(struct sk_buff *skb, char *param, int type,
int n=0;
if (skb == NULL) {
- IRDA_DEBUG(2, "%s(), Got NULL skb\n", __func__ );
+ pr_debug("%s(), Got NULL skb\n", __func__);
return 0;
}
@@ -1022,7 +988,7 @@ static int __irlan_insert_param(struct sk_buff *skb, char *param, int type,
IRDA_ASSERT(value_len > 0, return 0;);
break;
default:
- IRDA_DEBUG(2, "%s(), Unknown parameter type!\n", __func__ );
+ pr_debug("%s(), Unknown parameter type!\n", __func__);
return 0;
}
@@ -1031,7 +997,7 @@ static int __irlan_insert_param(struct sk_buff *skb, char *param, int type,
/* Make space for data */
if (skb_tailroom(skb) < (param_len+value_len+3)) {
- IRDA_DEBUG(2, "%s(), No more space at end of skb\n", __func__ );
+ pr_debug("%s(), No more space at end of skb\n", __func__);
return 0;
}
skb_put(skb, param_len+value_len+3);
@@ -1078,13 +1044,11 @@ int irlan_extract_param(__u8 *buf, char *name, char *value, __u16 *len)
__u16 val_len;
int n=0;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
/* get length of parameter name (1 byte) */
name_len = buf[n++];
if (name_len > 254) {
- IRDA_DEBUG(2, "%s(), name_len > 254\n", __func__ );
+ pr_debug("%s(), name_len > 254\n", __func__);
return -RSP_INVALID_COMMAND_FORMAT;
}
@@ -1101,7 +1065,7 @@ int irlan_extract_param(__u8 *buf, char *name, char *value, __u16 *len)
le16_to_cpus(&val_len); n+=2;
if (val_len >= 1016) {
- IRDA_DEBUG(2, "%s(), parameter length to long\n", __func__ );
+ pr_debug("%s(), parameter length to long\n", __func__);
return -RSP_INVALID_COMMAND_FORMAT;
}
*len = val_len;
@@ -1111,8 +1075,8 @@ int irlan_extract_param(__u8 *buf, char *name, char *value, __u16 *len)
value[val_len] = '\0';
n+=val_len;
- IRDA_DEBUG(4, "Parameter: %s ", name);
- IRDA_DEBUG(4, "Value: %s\n", value);
+ pr_debug("Parameter: %s ", name);
+ pr_debug("Value: %s\n", value);
return n;
}
diff --git a/net/irda/irlan/irlan_eth.c b/net/irda/irlan/irlan_eth.c
index dc13f1a45f2f..fcfbe579434a 100644
--- a/net/irda/irlan/irlan_eth.c
+++ b/net/irda/irlan/irlan_eth.c
@@ -110,8 +110,6 @@ static int irlan_eth_open(struct net_device *dev)
{
struct irlan_cb *self = netdev_priv(dev);
- IRDA_DEBUG(2, "%s()\n", __func__);
-
/* Ready to play! */
netif_stop_queue(dev); /* Wait until data link is ready */
@@ -137,8 +135,6 @@ static int irlan_eth_close(struct net_device *dev)
{
struct irlan_cb *self = netdev_priv(dev);
- IRDA_DEBUG(2, "%s()\n", __func__);
-
/* Stop device */
netif_stop_queue(dev);
@@ -231,8 +227,8 @@ int irlan_eth_receive(void *instance, void *sap, struct sk_buff *skb)
return 0;
}
if (skb->len < ETH_HLEN) {
- IRDA_DEBUG(0, "%s() : IrLAN frame too short (%d)\n",
- __func__, skb->len);
+ pr_debug("%s() : IrLAN frame too short (%d)\n",
+ __func__, skb->len);
dev->stats.rx_dropped++;
dev_kfree_skb(skb);
return 0;
@@ -281,9 +277,9 @@ void irlan_eth_flow_indication(void *instance, void *sap, LOCAL_FLOW flow)
IRDA_ASSERT(dev != NULL, return;);
- IRDA_DEBUG(0, "%s() : flow %s ; running %d\n", __func__,
- flow == FLOW_STOP ? "FLOW_STOP" : "FLOW_START",
- netif_running(dev));
+ pr_debug("%s() : flow %s ; running %d\n", __func__,
+ flow == FLOW_STOP ? "FLOW_STOP" : "FLOW_START",
+ netif_running(dev));
switch (flow) {
case FLOW_STOP:
@@ -310,32 +306,30 @@ static void irlan_eth_set_multicast_list(struct net_device *dev)
{
struct irlan_cb *self = netdev_priv(dev);
- IRDA_DEBUG(2, "%s()\n", __func__);
-
/* Check if data channel has been connected yet */
if (self->client.state != IRLAN_DATA) {
- IRDA_DEBUG(1, "%s(), delaying!\n", __func__);
+ pr_debug("%s(), delaying!\n", __func__);
return;
}
if (dev->flags & IFF_PROMISC) {
/* Enable promiscuous mode */
- IRDA_WARNING("Promiscuous mode not implemented by IrLAN!\n");
+ net_warn_ratelimited("Promiscuous mode not implemented by IrLAN!\n");
} else if ((dev->flags & IFF_ALLMULTI) ||
netdev_mc_count(dev) > HW_MAX_ADDRS) {
/* Disable promiscuous mode, use normal mode. */
- IRDA_DEBUG(4, "%s(), Setting multicast filter\n", __func__);
+ pr_debug("%s(), Setting multicast filter\n", __func__);
/* hardware_set_filter(NULL); */
irlan_set_multicast_filter(self, TRUE);
} else if (!netdev_mc_empty(dev)) {
- IRDA_DEBUG(4, "%s(), Setting multicast filter\n", __func__);
+ pr_debug("%s(), Setting multicast filter\n", __func__);
/* Walk the address list, and load the filter */
/* hardware_set_filter(dev->mc_list); */
irlan_set_multicast_filter(self, TRUE);
} else {
- IRDA_DEBUG(4, "%s(), Clearing multicast filter\n", __func__);
+ pr_debug("%s(), Clearing multicast filter\n", __func__);
irlan_set_multicast_filter(self, FALSE);
}
diff --git a/net/irda/irlan/irlan_event.c b/net/irda/irlan/irlan_event.c
index 43f16040a6fe..9a1cc11c16f6 100644
--- a/net/irda/irlan/irlan_event.c
+++ b/net/irda/irlan/irlan_event.c
@@ -40,7 +40,7 @@ const char * const irlan_state[] = {
void irlan_next_client_state(struct irlan_cb *self, IRLAN_STATE state)
{
- IRDA_DEBUG(2, "%s(), %s\n", __func__ , irlan_state[state]);
+ pr_debug("%s(), %s\n", __func__ , irlan_state[state]);
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
@@ -50,7 +50,7 @@ void irlan_next_client_state(struct irlan_cb *self, IRLAN_STATE state)
void irlan_next_provider_state(struct irlan_cb *self, IRLAN_STATE state)
{
- IRDA_DEBUG(2, "%s(), %s\n", __func__ , irlan_state[state]);
+ pr_debug("%s(), %s\n", __func__ , irlan_state[state]);
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
diff --git a/net/irda/irlan/irlan_filter.c b/net/irda/irlan/irlan_filter.c
index 7977be7caf0f..e755e90b2f26 100644
--- a/net/irda/irlan/irlan_filter.c
+++ b/net/irda/irlan/irlan_filter.c
@@ -43,7 +43,7 @@ void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb)
if ((self->provider.filter_type == IRLAN_DIRECTED) &&
(self->provider.filter_operation == DYNAMIC))
{
- IRDA_DEBUG(0, "Giving peer a dynamic Ethernet address\n");
+ pr_debug("Giving peer a dynamic Ethernet address\n");
self->provider.mac_address[0] = 0x40;
self->provider.mac_address[1] = 0x00;
self->provider.mac_address[2] = 0x00;
@@ -73,7 +73,7 @@ void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb)
if ((self->provider.filter_type == IRLAN_DIRECTED) &&
(self->provider.filter_mode == FILTER))
{
- IRDA_DEBUG(0, "Directed filter on\n");
+ pr_debug("Directed filter on\n");
skb->data[0] = 0x00; /* Success */
skb->data[1] = 0x00;
return;
@@ -81,7 +81,7 @@ void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb)
if ((self->provider.filter_type == IRLAN_DIRECTED) &&
(self->provider.filter_mode == NONE))
{
- IRDA_DEBUG(0, "Directed filter off\n");
+ pr_debug("Directed filter off\n");
skb->data[0] = 0x00; /* Success */
skb->data[1] = 0x00;
return;
@@ -90,7 +90,7 @@ void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb)
if ((self->provider.filter_type == IRLAN_BROADCAST) &&
(self->provider.filter_mode == FILTER))
{
- IRDA_DEBUG(0, "Broadcast filter on\n");
+ pr_debug("Broadcast filter on\n");
skb->data[0] = 0x00; /* Success */
skb->data[1] = 0x00;
return;
@@ -98,7 +98,7 @@ void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb)
if ((self->provider.filter_type == IRLAN_BROADCAST) &&
(self->provider.filter_mode == NONE))
{
- IRDA_DEBUG(0, "Broadcast filter off\n");
+ pr_debug("Broadcast filter off\n");
skb->data[0] = 0x00; /* Success */
skb->data[1] = 0x00;
return;
@@ -106,7 +106,7 @@ void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb)
if ((self->provider.filter_type == IRLAN_MULTICAST) &&
(self->provider.filter_mode == FILTER))
{
- IRDA_DEBUG(0, "Multicast filter on\n");
+ pr_debug("Multicast filter on\n");
skb->data[0] = 0x00; /* Success */
skb->data[1] = 0x00;
return;
@@ -114,7 +114,7 @@ void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb)
if ((self->provider.filter_type == IRLAN_MULTICAST) &&
(self->provider.filter_mode == NONE))
{
- IRDA_DEBUG(0, "Multicast filter off\n");
+ pr_debug("Multicast filter off\n");
skb->data[0] = 0x00; /* Success */
skb->data[1] = 0x00;
return;
@@ -122,7 +122,7 @@ void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb)
if ((self->provider.filter_type == IRLAN_MULTICAST) &&
(self->provider.filter_operation == GET))
{
- IRDA_DEBUG(0, "Multicast filter get\n");
+ pr_debug("Multicast filter get\n");
skb->data[0] = 0x00; /* Success? */
skb->data[1] = 0x02;
irlan_insert_string_param(skb, "FILTER_MODE", "NONE");
@@ -132,7 +132,7 @@ void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb)
skb->data[0] = 0x00; /* Command not supported */
skb->data[1] = 0x00;
- IRDA_DEBUG(0, "Not implemented!\n");
+ pr_debug("Not implemented!\n");
}
/*
@@ -143,18 +143,15 @@ void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb)
*/
void irlan_check_command_param(struct irlan_cb *self, char *param, char *value)
{
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
- IRDA_DEBUG(4, "%s, %s\n", param, value);
+ pr_debug("%s, %s\n", param, value);
/*
* This is experimental!! DB.
*/
if (strcmp(param, "MODE") == 0) {
- IRDA_DEBUG(0, "%s()\n", __func__ );
self->use_udata = TRUE;
return;
}
diff --git a/net/irda/irlan/irlan_provider.c b/net/irda/irlan/irlan_provider.c
index 4664855222f4..15c292cf2644 100644
--- a/net/irda/irlan/irlan_provider.c
+++ b/net/irda/irlan/irlan_provider.c
@@ -70,8 +70,6 @@ static int irlan_provider_data_indication(void *instance, void *sap,
struct irlan_cb *self;
__u8 code;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
self = instance;
IRDA_ASSERT(self != NULL, return -1;);
@@ -82,32 +80,32 @@ static int irlan_provider_data_indication(void *instance, void *sap,
code = skb->data[0];
switch(code) {
case CMD_GET_PROVIDER_INFO:
- IRDA_DEBUG(4, "Got GET_PROVIDER_INFO command!\n");
+ pr_debug("Got GET_PROVIDER_INFO command!\n");
irlan_do_provider_event(self, IRLAN_GET_INFO_CMD, skb);
break;
case CMD_GET_MEDIA_CHAR:
- IRDA_DEBUG(4, "Got GET_MEDIA_CHAR command!\n");
+ pr_debug("Got GET_MEDIA_CHAR command!\n");
irlan_do_provider_event(self, IRLAN_GET_MEDIA_CMD, skb);
break;
case CMD_OPEN_DATA_CHANNEL:
- IRDA_DEBUG(4, "Got OPEN_DATA_CHANNEL command!\n");
+ pr_debug("Got OPEN_DATA_CHANNEL command!\n");
irlan_do_provider_event(self, IRLAN_OPEN_DATA_CMD, skb);
break;
case CMD_FILTER_OPERATION:
- IRDA_DEBUG(4, "Got FILTER_OPERATION command!\n");
+ pr_debug("Got FILTER_OPERATION command!\n");
irlan_do_provider_event(self, IRLAN_FILTER_CONFIG_CMD, skb);
break;
case CMD_RECONNECT_DATA_CHAN:
- IRDA_DEBUG(2, "%s(), Got RECONNECT_DATA_CHAN command\n", __func__ );
- IRDA_DEBUG(2, "%s(), NOT IMPLEMENTED\n", __func__ );
+ pr_debug("%s(), Got RECONNECT_DATA_CHAN command\n", __func__);
+ pr_debug("%s(), NOT IMPLEMENTED\n", __func__);
break;
case CMD_CLOSE_DATA_CHAN:
- IRDA_DEBUG(2, "Got CLOSE_DATA_CHAN command!\n");
- IRDA_DEBUG(2, "%s(), NOT IMPLEMENTED\n", __func__ );
+ pr_debug("Got CLOSE_DATA_CHAN command!\n");
+ pr_debug("%s(), NOT IMPLEMENTED\n", __func__);
break;
default:
- IRDA_DEBUG(2, "%s(), Unknown command!\n", __func__ );
+ pr_debug("%s(), Unknown command!\n", __func__);
break;
}
return 0;
@@ -128,8 +126,6 @@ static void irlan_provider_connect_indication(void *instance, void *sap,
struct irlan_cb *self;
struct tsap_cb *tsap;
- IRDA_DEBUG(0, "%s()\n", __func__ );
-
self = instance;
tsap = sap;
@@ -179,7 +175,7 @@ static void irlan_provider_disconnect_indication(void *instance, void *sap,
struct irlan_cb *self;
struct tsap_cb *tsap;
- IRDA_DEBUG(4, "%s(), reason=%d\n", __func__ , reason);
+ pr_debug("%s(), reason=%d\n", __func__ , reason);
self = instance;
tsap = sap;
@@ -233,7 +229,7 @@ int irlan_provider_parse_command(struct irlan_cb *self, int cmd,
IRDA_ASSERT(skb != NULL, return -RSP_PROTOCOL_ERROR;);
- IRDA_DEBUG(4, "%s(), skb->len=%d\n", __func__ , (int)skb->len);
+ pr_debug("%s(), skb->len=%d\n", __func__ , (int)skb->len);
IRDA_ASSERT(self != NULL, return -RSP_PROTOCOL_ERROR;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -RSP_PROTOCOL_ERROR;);
@@ -255,7 +251,7 @@ int irlan_provider_parse_command(struct irlan_cb *self, int cmd,
/* How many parameters? */
count = frame[1];
- IRDA_DEBUG(4, "Got %d parameters\n", count);
+ pr_debug("Got %d parameters\n", count);
ptr = frame+2;
@@ -263,7 +259,7 @@ int irlan_provider_parse_command(struct irlan_cb *self, int cmd,
for (i=0; i<count;i++) {
ret = irlan_extract_param(ptr, name, value, &val_len);
if (ret < 0) {
- IRDA_DEBUG(2, "%s(), IrLAN, Error!\n", __func__ );
+ pr_debug("%s(), IrLAN, Error!\n", __func__);
break;
}
ptr+=ret;
@@ -288,8 +284,6 @@ void irlan_provider_send_reply(struct irlan_cb *self, int command,
{
struct sk_buff *skb;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;);
@@ -320,7 +314,7 @@ void irlan_provider_send_reply(struct irlan_cb *self, int command,
irlan_insert_string_param(skb, "MEDIA", "802.5");
break;
default:
- IRDA_DEBUG(2, "%s(), unknown media type!\n", __func__ );
+ pr_debug("%s(), unknown media type!\n", __func__);
break;
}
irlan_insert_short_param(skb, "IRLAN_VER", 0x0101);
@@ -344,7 +338,7 @@ void irlan_provider_send_reply(struct irlan_cb *self, int command,
irlan_insert_string_param(skb, "ACCESS_TYPE", "HOSTED");
break;
default:
- IRDA_DEBUG(2, "%s(), Unknown access type\n", __func__ );
+ pr_debug("%s(), Unknown access type\n", __func__);
break;
}
irlan_insert_short_param(skb, "MAX_FRAME", 0x05ee);
@@ -364,7 +358,7 @@ void irlan_provider_send_reply(struct irlan_cb *self, int command,
irlan_filter_request(self, skb);
break;
default:
- IRDA_DEBUG(2, "%s(), Unknown command!\n", __func__ );
+ pr_debug("%s(), Unknown command!\n", __func__);
break;
}
@@ -382,8 +376,6 @@ int irlan_provider_open_ctrl_tsap(struct irlan_cb *self)
struct tsap_cb *tsap;
notify_t notify;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
@@ -403,7 +395,7 @@ int irlan_provider_open_ctrl_tsap(struct irlan_cb *self)
tsap = irttp_open_tsap(LSAP_ANY, 1, &notify);
if (!tsap) {
- IRDA_DEBUG(2, "%s(), Got no tsap!\n", __func__ );
+ pr_debug("%s(), Got no tsap!\n", __func__);
return -1;
}
self->provider.tsap_ctrl = tsap;
diff --git a/net/irda/irlan/irlan_provider_event.c b/net/irda/irlan/irlan_provider_event.c
index 01a9d7c993ee..9c4f7f51d6b5 100644
--- a/net/irda/irlan/irlan_provider_event.c
+++ b/net/irda/irlan/irlan_provider_event.c
@@ -72,8 +72,6 @@ void irlan_do_provider_event(struct irlan_cb *self, IRLAN_EVENT event,
static int irlan_provider_state_idle(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return -1;);
switch(event) {
@@ -82,7 +80,7 @@ static int irlan_provider_state_idle(struct irlan_cb *self, IRLAN_EVENT event,
irlan_next_provider_state( self, IRLAN_INFO);
break;
default:
- IRDA_DEBUG(4, "%s(), Unknown event %d\n", __func__ , event);
+ pr_debug("%s(), Unknown event %d\n", __func__ , event);
break;
}
if (skb)
@@ -101,8 +99,6 @@ static int irlan_provider_state_info(struct irlan_cb *self, IRLAN_EVENT event,
{
int ret;
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return -1;);
switch(event) {
@@ -147,7 +143,7 @@ static int irlan_provider_state_info(struct irlan_cb *self, IRLAN_EVENT event,
irlan_next_provider_state(self, IRLAN_IDLE);
break;
default:
- IRDA_DEBUG( 0, "%s(), Unknown event %d\n", __func__ , event);
+ pr_debug("%s(), Unknown event %d\n", __func__ , event);
break;
}
if (skb)
@@ -166,8 +162,6 @@ static int irlan_provider_state_info(struct irlan_cb *self, IRLAN_EVENT event,
static int irlan_provider_state_open(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return -1;);
switch(event) {
@@ -186,7 +180,7 @@ static int irlan_provider_state_open(struct irlan_cb *self, IRLAN_EVENT event,
irlan_next_provider_state(self, IRLAN_IDLE);
break;
default:
- IRDA_DEBUG(2, "%s(), Unknown event %d\n", __func__ , event);
+ pr_debug("%s(), Unknown event %d\n", __func__ , event);
break;
}
if (skb)
@@ -205,8 +199,6 @@ static int irlan_provider_state_open(struct irlan_cb *self, IRLAN_EVENT event,
static int irlan_provider_state_data(struct irlan_cb *self, IRLAN_EVENT event,
struct sk_buff *skb)
{
- IRDA_DEBUG(4, "%s()\n", __func__ );
-
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;);
@@ -221,7 +213,7 @@ static int irlan_provider_state_data(struct irlan_cb *self, IRLAN_EVENT event,
irlan_next_provider_state(self, IRLAN_IDLE);
break;
default:
- IRDA_DEBUG( 0, "%s(), Unknown event %d\n", __func__ , event);
+ pr_debug("%s(), Unknown event %d\n", __func__ , event);
break;
}
if (skb)
diff --git a/net/irda/irlap.c b/net/irda/irlap.c
index a778df55f5d6..7f2cafddfb6e 100644
--- a/net/irda/irlap.c
+++ b/net/irda/irlap.c
@@ -60,8 +60,7 @@ static void __irlap_close(struct irlap_cb *self);
static void irlap_init_qos_capabilities(struct irlap_cb *self,
struct qos_info *qos_user);
-#ifdef CONFIG_IRDA_DEBUG
-static const char *const lap_reasons[] = {
+static const char *const lap_reasons[] __maybe_unused = {
"ERROR, NOT USED",
"LAP_DISC_INDICATION",
"LAP_NO_RESPONSE",
@@ -71,7 +70,6 @@ static const char *const lap_reasons[] = {
"LAP_PRIMARY_CONFLICT",
"ERROR, NOT USED",
};
-#endif /* CONFIG_IRDA_DEBUG */
int __init irlap_init(void)
{
@@ -85,8 +83,8 @@ int __init irlap_init(void)
/* Allocate master array */
irlap = hashbin_new(HB_LOCK);
if (irlap == NULL) {
- IRDA_ERROR("%s: can't allocate irlap hashbin!\n",
- __func__);
+ net_err_ratelimited("%s: can't allocate irlap hashbin!\n",
+ __func__);
return -ENOMEM;
}
@@ -111,8 +109,6 @@ struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos,
{
struct irlap_cb *self;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
/* Initialize the irlap structure. */
self = kzalloc(sizeof(struct irlap_cb), GFP_KERNEL);
if (self == NULL)
@@ -213,8 +209,6 @@ void irlap_close(struct irlap_cb *self)
{
struct irlap_cb *lap;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
@@ -229,7 +223,7 @@ void irlap_close(struct irlap_cb *self)
/* Be sure that we manage to remove ourself from the hash */
lap = hashbin_remove(irlap, self->saddr, NULL);
if (!lap) {
- IRDA_DEBUG(1, "%s(), Didn't find myself!\n", __func__);
+ pr_debug("%s(), Didn't find myself!\n", __func__);
return;
}
__irlap_close(lap);
@@ -244,8 +238,6 @@ EXPORT_SYMBOL(irlap_close);
*/
void irlap_connect_indication(struct irlap_cb *self, struct sk_buff *skb)
{
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
@@ -263,8 +255,6 @@ void irlap_connect_indication(struct irlap_cb *self, struct sk_buff *skb)
*/
void irlap_connect_response(struct irlap_cb *self, struct sk_buff *userdata)
{
- IRDA_DEBUG(4, "%s()\n", __func__);
-
irlap_do_event(self, CONNECT_RESPONSE, userdata, NULL);
}
@@ -278,7 +268,7 @@ void irlap_connect_response(struct irlap_cb *self, struct sk_buff *userdata)
void irlap_connect_request(struct irlap_cb *self, __u32 daddr,
struct qos_info *qos_user, int sniff)
{
- IRDA_DEBUG(3, "%s(), daddr=0x%08x\n", __func__, daddr);
+ pr_debug("%s(), daddr=0x%08x\n", __func__, daddr);
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
@@ -305,8 +295,6 @@ void irlap_connect_request(struct irlap_cb *self, __u32 daddr,
*/
void irlap_connect_confirm(struct irlap_cb *self, struct sk_buff *skb)
{
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
@@ -342,8 +330,6 @@ void irlap_data_request(struct irlap_cb *self, struct sk_buff *skb,
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
- IRDA_DEBUG(3, "%s()\n", __func__);
-
IRDA_ASSERT(skb_headroom(skb) >= (LAP_ADDR_HEADER+LAP_CTRL_HEADER),
return;);
skb_push(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER);
@@ -389,8 +375,6 @@ void irlap_unitdata_request(struct irlap_cb *self, struct sk_buff *skb)
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
- IRDA_DEBUG(3, "%s()\n", __func__);
-
IRDA_ASSERT(skb_headroom(skb) >= (LAP_ADDR_HEADER+LAP_CTRL_HEADER),
return;);
skb_push(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER);
@@ -415,8 +399,6 @@ void irlap_unitdata_request(struct irlap_cb *self, struct sk_buff *skb)
#ifdef CONFIG_IRDA_ULTRA
void irlap_unitdata_indication(struct irlap_cb *self, struct sk_buff *skb)
{
- IRDA_DEBUG(1, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
IRDA_ASSERT(skb != NULL, return;);
@@ -435,8 +417,6 @@ void irlap_unitdata_indication(struct irlap_cb *self, struct sk_buff *skb)
*/
void irlap_disconnect_request(struct irlap_cb *self)
{
- IRDA_DEBUG(3, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
@@ -456,7 +436,7 @@ void irlap_disconnect_request(struct irlap_cb *self)
irlap_do_event(self, DISCONNECT_REQUEST, NULL, NULL);
break;
default:
- IRDA_DEBUG(2, "%s(), disconnect pending!\n", __func__);
+ pr_debug("%s(), disconnect pending!\n", __func__);
self->disconnect_pending = TRUE;
break;
}
@@ -470,7 +450,7 @@ void irlap_disconnect_request(struct irlap_cb *self)
*/
void irlap_disconnect_indication(struct irlap_cb *self, LAP_REASON reason)
{
- IRDA_DEBUG(1, "%s(), reason=%s\n", __func__, lap_reasons[reason]);
+ pr_debug("%s(), reason=%s\n", __func__, lap_reasons[reason]);
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
@@ -480,7 +460,7 @@ void irlap_disconnect_indication(struct irlap_cb *self, LAP_REASON reason)
switch (reason) {
case LAP_RESET_INDICATION:
- IRDA_DEBUG(1, "%s(), Sending reset request!\n", __func__);
+ pr_debug("%s(), Sending reset request!\n", __func__);
irlap_do_event(self, RESET_REQUEST, NULL, NULL);
break;
case LAP_NO_RESPONSE: /* FALLTHROUGH */
@@ -491,7 +471,8 @@ void irlap_disconnect_indication(struct irlap_cb *self, LAP_REASON reason)
reason, NULL);
break;
default:
- IRDA_ERROR("%s: Unknown reason %d\n", __func__, reason);
+ net_err_ratelimited("%s: Unknown reason %d\n",
+ __func__, reason);
}
}
@@ -509,7 +490,7 @@ void irlap_discovery_request(struct irlap_cb *self, discovery_t *discovery)
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
IRDA_ASSERT(discovery != NULL, return;);
- IRDA_DEBUG(4, "%s(), nslots = %d\n", __func__, discovery->nslots);
+ pr_debug("%s(), nslots = %d\n", __func__, discovery->nslots);
IRDA_ASSERT((discovery->nslots == 1) || (discovery->nslots == 6) ||
(discovery->nslots == 8) || (discovery->nslots == 16),
@@ -517,8 +498,8 @@ void irlap_discovery_request(struct irlap_cb *self, discovery_t *discovery)
/* Discovery is only possible in NDM mode */
if (self->state != LAP_NDM) {
- IRDA_DEBUG(4, "%s(), discovery only possible in NDM mode\n",
- __func__);
+ pr_debug("%s(), discovery only possible in NDM mode\n",
+ __func__);
irlap_discovery_confirm(self, NULL);
/* Note : in theory, if we are not in NDM, we could postpone
* the discovery like we do for connection request.
@@ -540,8 +521,8 @@ void irlap_discovery_request(struct irlap_cb *self, discovery_t *discovery)
self->discovery_log = hashbin_new(HB_NOLOCK);
if (self->discovery_log == NULL) {
- IRDA_WARNING("%s(), Unable to allocate discovery log!\n",
- __func__);
+ net_warn_ratelimited("%s(), Unable to allocate discovery log!\n",
+ __func__);
return;
}
@@ -596,8 +577,6 @@ void irlap_discovery_confirm(struct irlap_cb *self, hashbin_t *discovery_log)
*/
void irlap_discovery_indication(struct irlap_cb *self, discovery_t *discovery)
{
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
IRDA_ASSERT(discovery != NULL, return;);
@@ -625,10 +604,10 @@ void irlap_status_indication(struct irlap_cb *self, int quality_of_link)
{
switch (quality_of_link) {
case STATUS_NO_ACTIVITY:
- IRDA_MESSAGE("IrLAP, no activity on link!\n");
+ net_info_ratelimited("IrLAP, no activity on link!\n");
break;
case STATUS_NOISY:
- IRDA_MESSAGE("IrLAP, noisy link!\n");
+ net_info_ratelimited("IrLAP, noisy link!\n");
break;
default:
break;
@@ -642,8 +621,6 @@ void irlap_status_indication(struct irlap_cb *self, int quality_of_link)
*/
void irlap_reset_indication(struct irlap_cb *self)
{
- IRDA_DEBUG(1, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
@@ -658,7 +635,6 @@ void irlap_reset_indication(struct irlap_cb *self)
*/
void irlap_reset_confirm(void)
{
- IRDA_DEBUG(1, "%s()\n", __func__);
}
/*
@@ -758,7 +734,7 @@ int irlap_validate_nr_received(struct irlap_cb *self, int nr)
{
/* nr as expected? */
if (nr == self->vs) {
- IRDA_DEBUG(4, "%s(), expected!\n", __func__);
+ pr_debug("%s(), expected!\n", __func__);
return NR_EXPECTED;
}
@@ -786,8 +762,6 @@ int irlap_validate_nr_received(struct irlap_cb *self, int nr)
*/
void irlap_initiate_connection_state(struct irlap_cb *self)
{
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
@@ -869,7 +843,7 @@ static void irlap_change_speed(struct irlap_cb *self, __u32 speed, int now)
{
struct sk_buff *skb;
- IRDA_DEBUG(0, "%s(), setting speed to %d\n", __func__, speed);
+ pr_debug("%s(), setting speed to %d\n", __func__, speed);
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
@@ -912,7 +886,7 @@ static void irlap_init_qos_capabilities(struct irlap_cb *self,
* user may not have set all of them.
*/
if (qos_user) {
- IRDA_DEBUG(1, "%s(), Found user specified QoS!\n", __func__);
+ pr_debug("%s(), Found user specified QoS!\n", __func__);
if (qos_user->baud_rate.bits)
self->qos_rx.baud_rate.bits &= qos_user->baud_rate.bits;
@@ -942,8 +916,6 @@ static void irlap_init_qos_capabilities(struct irlap_cb *self,
*/
void irlap_apply_default_connection_parameters(struct irlap_cb *self)
{
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
@@ -1005,8 +977,6 @@ void irlap_apply_default_connection_parameters(struct irlap_cb *self)
*/
void irlap_apply_connection_parameters(struct irlap_cb *self, int now)
{
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
@@ -1085,12 +1055,12 @@ void irlap_apply_connection_parameters(struct irlap_cb *self, int now)
self->N1 = sysctl_warn_noreply_time * 1000 /
self->qos_rx.max_turn_time.value;
- IRDA_DEBUG(4, "Setting N1 = %d\n", self->N1);
+ pr_debug("Setting N1 = %d\n", self->N1);
/* Set N2 to match our own disconnect time */
self->N2 = self->qos_tx.link_disc_time.value * 1000 /
self->qos_rx.max_turn_time.value;
- IRDA_DEBUG(4, "Setting N2 = %d\n", self->N2);
+ pr_debug("Setting N2 = %d\n", self->N2);
}
#ifdef CONFIG_PROC_FS
diff --git a/net/irda/irlap_event.c b/net/irda/irlap_event.c
index ccd214f9d196..0e1b4d79f745 100644
--- a/net/irda/irlap_event.c
+++ b/net/irda/irlap_event.c
@@ -78,8 +78,7 @@ static int irlap_state_sclose (struct irlap_cb *self, IRLAP_EVENT event,
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 *const irlap_event[] = {
+static const char *const irlap_event[] __maybe_unused = {
"DISCOVERY_REQUEST",
"CONNECT_REQUEST",
"CONNECT_RESPONSE",
@@ -119,7 +118,6 @@ static const char *const irlap_event[] = {
"BACKOFF_TIMER_EXPIRED",
"MEDIA_BUSY_TIMER_EXPIRED",
};
-#endif /* CONFIG_IRDA_DEBUG */
const char *const irlap_state[] = {
"LAP_NDM",
@@ -218,7 +216,7 @@ static void irlap_start_poll_timer(struct irlap_cb *self, int timeout)
} else
self->fast_RR = FALSE;
- IRDA_DEBUG(3, "%s(), timeout=%d (%ld)\n", __func__, timeout, jiffies);
+ pr_debug("%s(), timeout=%d (%ld)\n", __func__, timeout, jiffies);
#endif /* CONFIG_IRDA_FAST_RR */
if (timeout == 0)
@@ -242,8 +240,8 @@ void irlap_do_event(struct irlap_cb *self, IRLAP_EVENT event,
if (!self || self->magic != LAP_MAGIC)
return;
- IRDA_DEBUG(3, "%s(), event = %s, state = %s\n", __func__,
- irlap_event[event], irlap_state[self->state]);
+ pr_debug("%s(), event = %s, state = %s\n", __func__,
+ irlap_event[event], irlap_state[self->state]);
ret = (*state[self->state])(self, event, skb, info);
@@ -260,8 +258,8 @@ void irlap_do_event(struct irlap_cb *self, IRLAP_EVENT event,
* try to disconnect link if we send any data frames, since
* that will change the state away form XMIT
*/
- IRDA_DEBUG(2, "%s() : queue len = %d\n", __func__,
- skb_queue_len(&self->txq));
+ pr_debug("%s() : queue len = %d\n", __func__,
+ skb_queue_len(&self->txq));
if (!skb_queue_empty(&self->txq)) {
/* Prevent race conditions with irlap_data_request() */
@@ -340,8 +338,8 @@ static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event,
/* Note : this will never happen, because we test
* media busy in irlap_connect_request() and
* postpone the event... - Jean II */
- IRDA_DEBUG(0, "%s(), CONNECT_REQUEST: media busy!\n",
- __func__);
+ pr_debug("%s(), CONNECT_REQUEST: media busy!\n",
+ __func__);
/* Always switch state before calling upper layers */
irlap_next_state(self, LAP_NDM);
@@ -367,16 +365,16 @@ static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event,
irlap_connect_indication(self, skb);
} else {
- IRDA_DEBUG(0, "%s(), SNRM frame does not "
- "contain an I field!\n", __func__);
+ pr_debug("%s(), SNRM frame does not contain an I field!\n",
+ __func__);
}
break;
case DISCOVERY_REQUEST:
IRDA_ASSERT(info != NULL, return -1;);
if (self->media_busy) {
- IRDA_DEBUG(1, "%s(), DISCOVERY_REQUEST: media busy!\n",
- __func__);
+ pr_debug("%s(), DISCOVERY_REQUEST: media busy!\n",
+ __func__);
/* irlap->log.condition = MEDIA_BUSY; */
/* This will make IrLMP try again */
@@ -442,7 +440,8 @@ static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event,
* those cases...
* Jean II
*/
- IRDA_DEBUG(1, "%s(), Receiving final discovery request, missed the discovery slots :-(\n", __func__);
+ pr_debug("%s(), Receiving final discovery request, missed the discovery slots :-(\n",
+ __func__);
/* Last discovery request -> in the log */
irlap_discovery_indication(self, info->discovery);
@@ -520,8 +519,8 @@ static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event,
case RECV_UI_FRAME:
/* Only accept broadcast frames in NDM mode */
if (info->caddr != CBROADCAST) {
- IRDA_DEBUG(0, "%s(), not a broadcast frame!\n",
- __func__);
+ pr_debug("%s(), not a broadcast frame!\n",
+ __func__);
} else
irlap_unitdata_indication(self, skb);
break;
@@ -537,11 +536,11 @@ static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event,
irlap_send_test_frame(self, CBROADCAST, info->daddr, skb);
break;
case RECV_TEST_RSP:
- IRDA_DEBUG(0, "%s() not implemented!\n", __func__);
+ pr_debug("%s() not implemented!\n", __func__);
break;
default:
- IRDA_DEBUG(2, "%s(), Unknown event %s\n", __func__,
- irlap_event[event]);
+ pr_debug("%s(), Unknown event %s\n", __func__,
+ irlap_event[event]);
ret = -1;
break;
@@ -568,13 +567,12 @@ static int irlap_state_query(struct irlap_cb *self, IRLAP_EVENT event,
IRDA_ASSERT(info != NULL, return -1;);
IRDA_ASSERT(info->discovery != NULL, return -1;);
- IRDA_DEBUG(4, "%s(), daddr=%08x\n", __func__,
- info->discovery->data.daddr);
+ pr_debug("%s(), daddr=%08x\n", __func__,
+ info->discovery->data.daddr);
if (!self->discovery_log) {
- IRDA_WARNING("%s: discovery log is gone! "
- "maybe the discovery timeout has been set"
- " too short?\n", __func__);
+ net_warn_ratelimited("%s: discovery log is gone! maybe the discovery timeout has been set too short?\n",
+ __func__);
break;
}
hashbin_insert(self->discovery_log,
@@ -599,7 +597,8 @@ static int irlap_state_query(struct irlap_cb *self, IRLAP_EVENT event,
IRDA_ASSERT(info != NULL, return -1;);
- IRDA_DEBUG(1, "%s(), Receiving discovery request (s = %d) while performing discovery :-(\n", __func__, info->s);
+ pr_debug("%s(), Receiving discovery request (s = %d) while performing discovery :-(\n",
+ __func__, info->s);
/* Last discovery request ? */
if (info->s == 0xff)
@@ -613,8 +612,8 @@ static int irlap_state_query(struct irlap_cb *self, IRLAP_EVENT event,
* timing requirements.
*/
if (irda_device_is_receiving(self->netdev) && !self->add_wait) {
- IRDA_DEBUG(2, "%s(), device is slow to answer, "
- "waiting some more!\n", __func__);
+ pr_debug("%s(), device is slow to answer, waiting some more!\n",
+ __func__);
irlap_start_slot_timer(self, msecs_to_jiffies(10));
self->add_wait = TRUE;
return ret;
@@ -650,8 +649,8 @@ static int irlap_state_query(struct irlap_cb *self, IRLAP_EVENT event,
}
break;
default:
- IRDA_DEBUG(2, "%s(), Unknown event %s\n", __func__,
- irlap_event[event]);
+ pr_debug("%s(), Unknown event %s\n", __func__,
+ irlap_event[event]);
ret = -1;
break;
@@ -672,15 +671,13 @@ static int irlap_state_reply(struct irlap_cb *self, IRLAP_EVENT event,
discovery_t *discovery_rsp;
int ret=0;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
switch (event) {
case QUERY_TIMER_EXPIRED:
- IRDA_DEBUG(0, "%s(), QUERY_TIMER_EXPIRED <%ld>\n",
- __func__, jiffies);
+ pr_debug("%s(), QUERY_TIMER_EXPIRED <%ld>\n",
+ __func__, jiffies);
irlap_next_state(self, LAP_NDM);
break;
case RECV_DISCOVERY_XID_CMD:
@@ -718,8 +715,8 @@ static int irlap_state_reply(struct irlap_cb *self, IRLAP_EVENT event,
}
break;
default:
- IRDA_DEBUG(1, "%s(), Unknown event %d, %s\n", __func__,
- event, irlap_event[event]);
+ pr_debug("%s(), Unknown event %d, %s\n", __func__,
+ event, irlap_event[event]);
ret = -1;
break;
@@ -739,7 +736,7 @@ static int irlap_state_conn(struct irlap_cb *self, IRLAP_EVENT event,
{
int ret = 0;
- IRDA_DEBUG(4, "%s(), event=%s\n", __func__, irlap_event[ event]);
+ pr_debug("%s(), event=%s\n", __func__, irlap_event[event]);
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
@@ -799,20 +796,20 @@ static int irlap_state_conn(struct irlap_cb *self, IRLAP_EVENT event,
break;
case RECV_DISCOVERY_XID_CMD:
- IRDA_DEBUG(3, "%s(), event RECV_DISCOVER_XID_CMD!\n",
- __func__);
+ pr_debug("%s(), event RECV_DISCOVER_XID_CMD!\n",
+ __func__);
irlap_next_state(self, LAP_NDM);
break;
case DISCONNECT_REQUEST:
- IRDA_DEBUG(0, "%s(), Disconnect request!\n", __func__);
+ pr_debug("%s(), Disconnect request!\n", __func__);
irlap_send_dm_frame(self);
irlap_next_state( self, LAP_NDM);
irlap_disconnect_indication(self, LAP_DISC_INDICATION);
break;
default:
- IRDA_DEBUG(1, "%s(), Unknown event %d, %s\n", __func__,
- event, irlap_event[event]);
+ pr_debug("%s(), Unknown event %d, %s\n", __func__,
+ event, irlap_event[event]);
ret = -1;
break;
@@ -833,8 +830,6 @@ static int irlap_state_setup(struct irlap_cb *self, IRLAP_EVENT event,
{
int ret = 0;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
@@ -862,7 +857,7 @@ static int irlap_state_setup(struct irlap_cb *self, IRLAP_EVENT event,
self->retry_count++;
break;
case RECV_SNRM_CMD:
- IRDA_DEBUG(4, "%s(), SNRM battle!\n", __func__);
+ pr_debug("%s(), SNRM battle!\n", __func__);
IRDA_ASSERT(skb != NULL, return 0;);
IRDA_ASSERT(info != NULL, return 0;);
@@ -949,8 +944,8 @@ static int irlap_state_setup(struct irlap_cb *self, IRLAP_EVENT event,
irlap_disconnect_indication(self, LAP_DISC_INDICATION);
break;
default:
- IRDA_DEBUG(1, "%s(), Unknown event %d, %s\n", __func__,
- event, irlap_event[event]);
+ pr_debug("%s(), Unknown event %d, %s\n", __func__,
+ event, irlap_event[event]);
ret = -1;
break;
@@ -967,7 +962,7 @@ static int irlap_state_setup(struct irlap_cb *self, IRLAP_EVENT event,
static int irlap_state_offline(struct irlap_cb *self, IRLAP_EVENT event,
struct sk_buff *skb, struct irlap_info *info)
{
- IRDA_DEBUG( 0, "%s(), Unknown event\n", __func__);
+ pr_debug("%s(), Unknown event\n", __func__);
return -1;
}
@@ -1030,8 +1025,8 @@ static int irlap_state_xmit_p(struct irlap_cb *self, IRLAP_EVENT event,
* speed and turn-around-time.
*/
if((!nextfit) && (skb->len > self->bytes_left)) {
- IRDA_DEBUG(0, "%s(), Not allowed to transmit"
- " more bytes!\n", __func__);
+ pr_debug("%s(), Not allowed to transmit more bytes!\n",
+ __func__);
/* Requeue the skb */
skb_queue_head(&self->txq, skb_get(skb));
/*
@@ -1082,8 +1077,8 @@ static int irlap_state_xmit_p(struct irlap_cb *self, IRLAP_EVENT event,
self->fast_RR = FALSE;
#endif /* CONFIG_IRDA_FAST_RR */
} else {
- IRDA_DEBUG(4, "%s(), Unable to send! remote busy?\n",
- __func__);
+ pr_debug("%s(), Unable to send! remote busy?\n",
+ __func__);
skb_queue_head(&self->txq, skb_get(skb));
/*
@@ -1094,8 +1089,8 @@ static int irlap_state_xmit_p(struct irlap_cb *self, IRLAP_EVENT event,
}
break;
case POLL_TIMER_EXPIRED:
- IRDA_DEBUG(3, "%s(), POLL_TIMER_EXPIRED <%ld>\n",
- __func__, jiffies);
+ pr_debug("%s(), POLL_TIMER_EXPIRED <%ld>\n",
+ __func__, jiffies);
irlap_send_rr_frame(self, CMD_FRAME);
/* Return to NRM properly - Jean II */
self->window = self->window_size;
@@ -1120,8 +1115,8 @@ static int irlap_state_xmit_p(struct irlap_cb *self, IRLAP_EVENT event,
* when we return... - Jean II */
break;
default:
- IRDA_DEBUG(0, "%s(), Unknown event %s\n",
- __func__, irlap_event[event]);
+ pr_debug("%s(), Unknown event %s\n",
+ __func__, irlap_event[event]);
ret = -EINVAL;
break;
@@ -1139,8 +1134,6 @@ static int irlap_state_pclose(struct irlap_cb *self, IRLAP_EVENT event,
{
int ret = 0;
- IRDA_DEBUG(1, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
@@ -1174,7 +1167,7 @@ static int irlap_state_pclose(struct irlap_cb *self, IRLAP_EVENT event,
}
break;
default:
- IRDA_DEBUG(1, "%s(), Unknown event %d\n", __func__, event);
+ pr_debug("%s(), Unknown event %d\n", __func__, event);
ret = -1;
break;
@@ -1296,9 +1289,8 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
/* Keep state */
irlap_next_state(self, LAP_NRM_P);
} else {
- IRDA_DEBUG(4,
- "%s(), missing or duplicate frame!\n",
- __func__);
+ pr_debug("%s(), missing or duplicate frame!\n",
+ __func__);
/* Update Nr received */
irlap_update_nr_received(self, info->nr);
@@ -1367,8 +1359,8 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
if ((ns_status == NS_UNEXPECTED) &&
(nr_status == NR_UNEXPECTED))
{
- IRDA_DEBUG(4, "%s(), unexpected nr and ns!\n",
- __func__);
+ pr_debug("%s(), unexpected nr and ns!\n",
+ __func__);
if (info->pf) {
/* Resend rejected frames */
irlap_resend_rejected_frames(self, CMD_FRAME);
@@ -1408,9 +1400,9 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
}
break;
}
- IRDA_DEBUG(1, "%s(), Not implemented!\n", __func__);
- IRDA_DEBUG(1, "%s(), event=%s, ns_status=%d, nr_status=%d\n",
- __func__, irlap_event[event], ns_status, nr_status);
+ pr_debug("%s(), Not implemented!\n", __func__);
+ pr_debug("%s(), event=%s, ns_status=%d, nr_status=%d\n",
+ __func__, irlap_event[event], ns_status, nr_status);
break;
case RECV_UI_FRAME:
/* Poll bit cleared? */
@@ -1421,7 +1413,8 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
del_timer(&self->final_timer);
irlap_data_indication(self, skb, TRUE);
irlap_next_state(self, LAP_XMIT_P);
- IRDA_DEBUG(1, "%s: RECV_UI_FRAME: next state %s\n", __func__, irlap_state[self->state]);
+ pr_debug("%s: RECV_UI_FRAME: next state %s\n",
+ __func__, irlap_state[self->state]);
irlap_start_poll_timer(self, self->poll_timeout);
}
break;
@@ -1464,10 +1457,9 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
/* Update Nr received */
irlap_update_nr_received(self, info->nr);
- IRDA_DEBUG(4, "RECV_RR_FRAME: Retrans:%d, nr=%d, va=%d, "
- "vs=%d, vr=%d\n",
- self->retry_count, info->nr, self->va,
- self->vs, self->vr);
+ pr_debug("RECV_RR_FRAME: Retrans:%d, nr=%d, va=%d, vs=%d, vr=%d\n",
+ self->retry_count, info->nr, self->va,
+ self->vs, self->vr);
/* Resend rejected frames */
irlap_resend_rejected_frames(self, CMD_FRAME);
@@ -1475,8 +1467,8 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
irlap_next_state(self, LAP_NRM_P);
} else if (ret == NR_INVALID) {
- IRDA_DEBUG(1, "%s(), Received RR with "
- "invalid nr !\n", __func__);
+ pr_debug("%s(), Received RR with invalid nr !\n",
+ __func__);
irlap_next_state(self, LAP_RESET_WAIT);
@@ -1512,8 +1504,7 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
* we only do this once for each frame.
*/
if (irda_device_is_receiving(self->netdev) && !self->add_wait) {
- IRDA_DEBUG(1, "FINAL_TIMER_EXPIRED when receiving a "
- "frame! Waiting a little bit more!\n");
+ pr_debug("FINAL_TIMER_EXPIRED when receiving a frame! Waiting a little bit more!\n");
irlap_start_final_timer(self, msecs_to_jiffies(300));
/*
@@ -1530,18 +1521,18 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
if (self->retry_count < self->N2) {
if (skb_peek(&self->wx_list) == NULL) {
/* Retry sending the pf bit to the secondary */
- IRDA_DEBUG(4, "nrm_p: resending rr");
+ pr_debug("nrm_p: resending rr");
irlap_wait_min_turn_around(self, &self->qos_tx);
irlap_send_rr_frame(self, CMD_FRAME);
} else {
- IRDA_DEBUG(4, "nrm_p: resend frames");
+ pr_debug("nrm_p: resend frames");
irlap_resend_rejected_frames(self, CMD_FRAME);
}
irlap_start_final_timer(self, self->final_timeout);
self->retry_count++;
- IRDA_DEBUG(4, "irlap_state_nrm_p: FINAL_TIMER_EXPIRED:"
- " retry_count=%d\n", self->retry_count);
+ pr_debug("irlap_state_nrm_p: FINAL_TIMER_EXPIRED: retry_count=%d\n",
+ self->retry_count);
/* Early warning event. I'm using a pretty liberal
* interpretation of the spec and generate an event
@@ -1581,7 +1572,7 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
irlap_start_final_timer(self, 2 * self->final_timeout);
break;
case RECV_RD_RSP:
- IRDA_DEBUG(1, "%s(), RECV_RD_RSP\n", __func__);
+ pr_debug("%s(), RECV_RD_RSP\n", __func__);
irlap_flush_all_queues(self);
irlap_next_state(self, LAP_XMIT_P);
@@ -1589,8 +1580,8 @@ static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event,
irlap_disconnect_request(self);
break;
default:
- IRDA_DEBUG(1, "%s(), Unknown event %s\n",
- __func__, irlap_event[event]);
+ pr_debug("%s(), Unknown event %s\n",
+ __func__, irlap_event[event]);
ret = -1;
break;
@@ -1610,7 +1601,7 @@ static int irlap_state_reset_wait(struct irlap_cb *self, IRLAP_EVENT event,
{
int ret = 0;
- IRDA_DEBUG(3, "%s(), event = %s\n", __func__, irlap_event[event]);
+ pr_debug("%s(), event = %s\n", __func__, irlap_event[event]);
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
@@ -1636,8 +1627,8 @@ static int irlap_state_reset_wait(struct irlap_cb *self, IRLAP_EVENT event,
irlap_next_state( self, LAP_PCLOSE);
break;
default:
- IRDA_DEBUG(2, "%s(), Unknown event %s\n", __func__,
- irlap_event[event]);
+ pr_debug("%s(), Unknown event %s\n", __func__,
+ irlap_event[event]);
ret = -1;
break;
@@ -1657,7 +1648,7 @@ static int irlap_state_reset(struct irlap_cb *self, IRLAP_EVENT event,
{
int ret = 0;
- IRDA_DEBUG(3, "%s(), event = %s\n", __func__, irlap_event[event]);
+ pr_debug("%s(), event = %s\n", __func__, irlap_event[event]);
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
@@ -1715,7 +1706,7 @@ static int irlap_state_reset(struct irlap_cb *self, IRLAP_EVENT event,
* state
*/
if (!info) {
- IRDA_DEBUG(3, "%s(), RECV_SNRM_CMD\n", __func__);
+ pr_debug("%s(), RECV_SNRM_CMD\n", __func__);
irlap_initiate_connection_state(self);
irlap_wait_min_turn_around(self, &self->qos_tx);
irlap_send_ua_response_frame(self, &self->qos_rx);
@@ -1723,14 +1714,13 @@ static int irlap_state_reset(struct irlap_cb *self, IRLAP_EVENT event,
irlap_start_wd_timer(self, self->wd_timeout);
irlap_next_state(self, LAP_NDM);
} else {
- IRDA_DEBUG(0,
- "%s(), SNRM frame contained an I field!\n",
- __func__);
+ pr_debug("%s(), SNRM frame contained an I field!\n",
+ __func__);
}
break;
default:
- IRDA_DEBUG(1, "%s(), Unknown event %s\n",
- __func__, irlap_event[event]);
+ pr_debug("%s(), Unknown event %s\n",
+ __func__, irlap_event[event]);
ret = -1;
break;
@@ -1750,7 +1740,7 @@ static int irlap_state_xmit_s(struct irlap_cb *self, IRLAP_EVENT event,
{
int ret = 0;
- IRDA_DEBUG(4, "%s(), event=%s\n", __func__, irlap_event[event]);
+ pr_debug("%s(), event=%s\n", __func__, irlap_event[event]);
IRDA_ASSERT(self != NULL, return -ENODEV;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return -EBADR;);
@@ -1786,8 +1776,8 @@ static int irlap_state_xmit_s(struct irlap_cb *self, IRLAP_EVENT event,
* speed and turn-around-time.
*/
if((!nextfit) && (skb->len > self->bytes_left)) {
- IRDA_DEBUG(0, "%s(), Not allowed to transmit"
- " more bytes!\n", __func__);
+ pr_debug("%s(), Not allowed to transmit more bytes!\n",
+ __func__);
/* Requeue the skb */
skb_queue_head(&self->txq, skb_get(skb));
@@ -1833,7 +1823,7 @@ static int irlap_state_xmit_s(struct irlap_cb *self, IRLAP_EVENT event,
ret = -EPROTO;
}
} else {
- IRDA_DEBUG(2, "%s(), Unable to send!\n", __func__);
+ pr_debug("%s(), Unable to send!\n", __func__);
skb_queue_head(&self->txq, skb_get(skb));
ret = -EPROTO;
}
@@ -1849,8 +1839,8 @@ static int irlap_state_xmit_s(struct irlap_cb *self, IRLAP_EVENT event,
* when we return... - Jean II */
break;
default:
- IRDA_DEBUG(2, "%s(), Unknown event %s\n", __func__,
- irlap_event[event]);
+ pr_debug("%s(), Unknown event %s\n", __func__,
+ irlap_event[event]);
ret = -EINVAL;
break;
@@ -1872,7 +1862,7 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event,
int nr_status;
int ret = 0;
- IRDA_DEBUG(4, "%s(), event=%s\n", __func__, irlap_event[ event]);
+ pr_debug("%s(), event=%s\n", __func__, irlap_event[event]);
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;);
@@ -1880,10 +1870,9 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event,
switch (event) {
case RECV_I_CMD: /* Optimize for the common case */
/* FIXME: must check for remote_busy below */
- IRDA_DEBUG(4, "%s(), event=%s nr=%d, vs=%d, ns=%d, "
- "vr=%d, pf=%d\n", __func__,
- irlap_event[event], info->nr,
- self->vs, info->ns, self->vr, info->pf);
+ pr_debug("%s(), event=%s nr=%d, vs=%d, ns=%d, vr=%d, pf=%d\n",
+ __func__, irlap_event[event], info->nr,
+ self->vs, info->ns, self->vr, info->pf);
self->retry_count = 0;
@@ -1983,7 +1972,7 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event,
if ((ns_status == NS_EXPECTED) && (nr_status == NR_UNEXPECTED))
{
if (info->pf) {
- IRDA_DEBUG(4, "RECV_I_RSP: frame(s) lost\n");
+ pr_debug("RECV_I_RSP: frame(s) lost\n");
self->vr = (self->vr + 1) % 8;
@@ -2020,10 +2009,10 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event,
}
if (ret == NR_INVALID) {
- IRDA_DEBUG(0, "NRM_S, NR_INVALID not implemented!\n");
+ pr_debug("NRM_S, NR_INVALID not implemented!\n");
}
if (ret == NS_INVALID) {
- IRDA_DEBUG(0, "NRM_S, NS_INVALID not implemented!\n");
+ pr_debug("NRM_S, NS_INVALID not implemented!\n");
}
break;
case RECV_UI_FRAME:
@@ -2112,22 +2101,21 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event,
/* Keep state */
irlap_next_state(self, LAP_NRM_S);
} else {
- IRDA_DEBUG(1, "%s(), invalid nr not implemented!\n",
- __func__);
+ pr_debug("%s(), invalid nr not implemented!\n",
+ __func__);
}
break;
case RECV_SNRM_CMD:
/* SNRM frame is not allowed to contain an I-field */
if (!info) {
del_timer(&self->wd_timer);
- IRDA_DEBUG(1, "%s(), received SNRM cmd\n", __func__);
+ pr_debug("%s(), received SNRM cmd\n", __func__);
irlap_next_state(self, LAP_RESET_CHECK);
irlap_reset_indication(self);
} else {
- IRDA_DEBUG(0,
- "%s(), SNRM frame contained an I-field!\n",
- __func__);
+ pr_debug("%s(), SNRM frame contained an I-field!\n",
+ __func__);
}
break;
@@ -2159,8 +2147,8 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event,
* which explain why we use (self->N2 / 2) here !!!
* Jean II
*/
- IRDA_DEBUG(1, "%s(), retry_count = %d\n", __func__,
- self->retry_count);
+ pr_debug("%s(), retry_count = %d\n", __func__,
+ self->retry_count);
if (self->retry_count < (self->N2 / 2)) {
/* No retry, just wait for primary */
@@ -2212,8 +2200,8 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event,
irlap_send_test_frame(self, self->caddr, info->daddr, skb);
break;
default:
- IRDA_DEBUG(1, "%s(), Unknown event %d, (%s)\n", __func__,
- event, irlap_event[event]);
+ pr_debug("%s(), Unknown event %d, (%s)\n", __func__,
+ event, irlap_event[event]);
ret = -EINVAL;
break;
@@ -2227,8 +2215,6 @@ static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event,
static int irlap_state_sclose(struct irlap_cb *self, IRLAP_EVENT event,
struct sk_buff *skb, struct irlap_info *info)
{
- IRDA_DEBUG(1, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return -ENODEV;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return -EBADR;);
@@ -2284,8 +2270,8 @@ static int irlap_state_sclose(struct irlap_cb *self, IRLAP_EVENT event,
break; /* stay in SCLOSE */
}
- IRDA_DEBUG(1, "%s(), Unknown event %d, (%s)\n", __func__,
- event, irlap_event[event]);
+ pr_debug("%s(), Unknown event %d, (%s)\n", __func__,
+ event, irlap_event[event]);
break;
}
@@ -2299,7 +2285,7 @@ static int irlap_state_reset_check( struct irlap_cb *self, IRLAP_EVENT event,
{
int ret = 0;
- IRDA_DEBUG(1, "%s(), event=%s\n", __func__, irlap_event[event]);
+ pr_debug("%s(), event=%s\n", __func__, irlap_event[event]);
IRDA_ASSERT(self != NULL, return -ENODEV;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return -EBADR;);
@@ -2320,8 +2306,8 @@ static int irlap_state_reset_check( struct irlap_cb *self, IRLAP_EVENT event,
irlap_next_state(self, LAP_SCLOSE);
break;
default:
- IRDA_DEBUG(1, "%s(), Unknown event %d, (%s)\n", __func__,
- event, irlap_event[event]);
+ pr_debug("%s(), Unknown event %d, (%s)\n", __func__,
+ event, irlap_event[event]);
ret = -EINVAL;
break;
diff --git a/net/irda/irlap_frame.c b/net/irda/irlap_frame.c
index a37998c6273d..b936b1251a66 100644
--- a/net/irda/irlap_frame.c
+++ b/net/irda/irlap_frame.c
@@ -103,8 +103,8 @@ void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb)
irlap_insert_info(self, skb);
if (unlikely(self->mode & IRDA_MODE_MONITOR)) {
- IRDA_DEBUG(3, "%s(): %s is in monitor mode\n", __func__,
- self->netdev->name);
+ pr_debug("%s(): %s is in monitor mode\n", __func__,
+ self->netdev->name);
dev_kfree_skb(skb);
return;
}
@@ -182,8 +182,8 @@ static void irlap_recv_snrm_cmd(struct irlap_cb *self, struct sk_buff *skb,
/* Check if the new connection address is valid */
if ((info->caddr == 0x00) || (info->caddr == 0xfe)) {
- IRDA_DEBUG(3, "%s(), invalid connection address!\n",
- __func__);
+ pr_debug("%s(), invalid connection address!\n",
+ __func__);
return;
}
@@ -193,8 +193,8 @@ static void irlap_recv_snrm_cmd(struct irlap_cb *self, struct sk_buff *skb,
/* Only accept if addressed directly to us */
if (info->saddr != self->saddr) {
- IRDA_DEBUG(2, "%s(), not addressed to us!\n",
- __func__);
+ pr_debug("%s(), not addressed to us!\n",
+ __func__);
return;
}
irlap_do_event(self, RECV_SNRM_CMD, skb, info);
@@ -216,7 +216,7 @@ void irlap_send_ua_response_frame(struct irlap_cb *self, struct qos_info *qos)
struct ua_frame *frame;
int ret;
- IRDA_DEBUG(2, "%s() <%ld>\n", __func__, jiffies);
+ pr_debug("%s() <%ld>\n", __func__, jiffies);
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
@@ -291,8 +291,6 @@ void irlap_send_disc_frame(struct irlap_cb *self)
struct sk_buff *tx_skb = NULL;
struct disc_frame *frame;
- IRDA_DEBUG(3, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
@@ -322,8 +320,8 @@ void irlap_send_discovery_xid_frame(struct irlap_cb *self, int S, __u8 s,
__u32 bcast = BROADCAST;
__u8 *info;
- IRDA_DEBUG(4, "%s(), s=%d, S=%d, command=%d\n", __func__,
- s, S, command);
+ pr_debug("%s(), s=%d, S=%d, command=%d\n", __func__,
+ s, S, command);
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
@@ -415,13 +413,11 @@ static void irlap_recv_discovery_xid_rsp(struct irlap_cb *self,
__u8 *discovery_info;
char *text;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
if (!pskb_may_pull(skb, sizeof(struct xid_frame))) {
- IRDA_ERROR("%s: frame too short!\n", __func__);
+ net_err_ratelimited("%s: frame too short!\n", __func__);
return;
}
@@ -432,13 +428,13 @@ static void irlap_recv_discovery_xid_rsp(struct irlap_cb *self,
/* Make sure frame is addressed to us */
if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) {
- IRDA_DEBUG(0, "%s(), frame is not addressed to us!\n",
- __func__);
+ pr_debug("%s(), frame is not addressed to us!\n",
+ __func__);
return;
}
if ((discovery = kzalloc(sizeof(discovery_t), GFP_ATOMIC)) == NULL) {
- IRDA_WARNING("%s: kmalloc failed!\n", __func__);
+ net_warn_ratelimited("%s: kmalloc failed!\n", __func__);
return;
}
@@ -446,15 +442,15 @@ static void irlap_recv_discovery_xid_rsp(struct irlap_cb *self,
discovery->data.saddr = self->saddr;
discovery->timestamp = jiffies;
- IRDA_DEBUG(4, "%s(), daddr=%08x\n", __func__,
- discovery->data.daddr);
+ pr_debug("%s(), daddr=%08x\n", __func__,
+ discovery->data.daddr);
discovery_info = skb_pull(skb, sizeof(struct xid_frame));
/* Get info returned from peer */
discovery->data.hints[0] = discovery_info[0];
if (discovery_info[0] & HINT_EXTENSION) {
- IRDA_DEBUG(4, "EXTENSION\n");
+ pr_debug("EXTENSION\n");
discovery->data.hints[1] = discovery_info[1];
discovery->data.charset = discovery_info[2];
text = (char *) &discovery_info[3];
@@ -492,7 +488,7 @@ static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self,
char *text;
if (!pskb_may_pull(skb, sizeof(struct xid_frame))) {
- IRDA_ERROR("%s: frame too short!\n", __func__);
+ net_err_ratelimited("%s: frame too short!\n", __func__);
return;
}
@@ -503,8 +499,8 @@ static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self,
/* Make sure frame is addressed to us */
if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) {
- IRDA_DEBUG(0, "%s(), frame is not addressed to us!\n",
- __func__);
+ pr_debug("%s(), frame is not addressed to us!\n",
+ __func__);
return;
}
@@ -536,8 +532,8 @@ static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self,
/* Check if things are sane at this point... */
if((discovery_info == NULL) ||
!pskb_may_pull(skb, 3)) {
- IRDA_ERROR("%s: discovery frame too short!\n",
- __func__);
+ net_err_ratelimited("%s: discovery frame too short!\n",
+ __func__);
return;
}
@@ -545,10 +541,8 @@ static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self,
* We now have some discovery info to deliver!
*/
discovery = kzalloc(sizeof(discovery_t), GFP_ATOMIC);
- if (!discovery) {
- IRDA_WARNING("%s: unable to malloc!\n", __func__);
+ if (!discovery)
return;
- }
discovery->data.daddr = info->daddr;
discovery->data.saddr = self->saddr;
@@ -658,7 +652,7 @@ static void irlap_recv_rnr_frame(struct irlap_cb *self, struct sk_buff *skb,
{
info->nr = skb->data[1] >> 5;
- IRDA_DEBUG(4, "%s(), nr=%d, %ld\n", __func__, info->nr, jiffies);
+ pr_debug("%s(), nr=%d, %ld\n", __func__, info->nr, jiffies);
if (command)
irlap_do_event(self, RECV_RNR_CMD, skb, info);
@@ -669,8 +663,6 @@ static void irlap_recv_rnr_frame(struct irlap_cb *self, struct sk_buff *skb,
static void irlap_recv_rej_frame(struct irlap_cb *self, struct sk_buff *skb,
struct irlap_info *info, int command)
{
- IRDA_DEBUG(0, "%s()\n", __func__);
-
info->nr = skb->data[1] >> 5;
/* Check if this is a command or a response frame */
@@ -683,8 +675,6 @@ static void irlap_recv_rej_frame(struct irlap_cb *self, struct sk_buff *skb,
static void irlap_recv_srej_frame(struct irlap_cb *self, struct sk_buff *skb,
struct irlap_info *info, int command)
{
- IRDA_DEBUG(0, "%s()\n", __func__);
-
info->nr = skb->data[1] >> 5;
/* Check if this is a command or a response frame */
@@ -697,8 +687,6 @@ static void irlap_recv_srej_frame(struct irlap_cb *self, struct sk_buff *skb,
static void irlap_recv_disc_frame(struct irlap_cb *self, struct sk_buff *skb,
struct irlap_info *info, int command)
{
- IRDA_DEBUG(2, "%s()\n", __func__);
-
/* Check if this is a command or a response frame */
if (command)
irlap_do_event(self, RECV_DISC_CMD, skb, info);
@@ -756,7 +744,7 @@ void irlap_send_data_primary(struct irlap_cb *self, struct sk_buff *skb)
irlap_send_i_frame( self, tx_skb, CMD_FRAME);
} else {
- IRDA_DEBUG(4, "%s(), sending unreliable frame\n", __func__);
+ pr_debug("%s(), sending unreliable frame\n", __func__);
irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME);
self->window -= 1;
}
@@ -809,7 +797,7 @@ void irlap_send_data_primary_poll(struct irlap_cb *self, struct sk_buff *skb)
irlap_next_state(self, LAP_NRM_P);
irlap_send_i_frame(self, tx_skb, CMD_FRAME);
} else {
- IRDA_DEBUG(4, "%s(), sending unreliable frame\n", __func__);
+ pr_debug("%s(), sending unreliable frame\n", __func__);
if (self->ack_required) {
irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME);
@@ -836,7 +824,9 @@ void irlap_send_data_primary_poll(struct irlap_cb *self, struct sk_buff *skb)
* See max_line_capacities[][] in qos.c for details. Jean II */
transmission_time -= (self->final_timeout * self->bytes_left
/ self->line_capacity);
- IRDA_DEBUG(4, "%s() adjusting transmission_time : ft=%d, bl=%d, lc=%d -> tt=%d\n", __func__, self->final_timeout, self->bytes_left, self->line_capacity, transmission_time);
+ pr_debug("%s() adjusting transmission_time : ft=%d, bl=%d, lc=%d -> tt=%d\n",
+ __func__, self->final_timeout, self->bytes_left,
+ self->line_capacity, transmission_time);
/* We are allowed to transmit a maximum number of bytes again. */
self->bytes_left = self->line_capacity;
@@ -997,7 +987,7 @@ void irlap_resend_rejected_frames(struct irlap_cb *self, int command)
/* tx_skb = skb_clone( skb, GFP_ATOMIC); */
tx_skb = skb_copy(skb, GFP_ATOMIC);
if (!tx_skb) {
- IRDA_DEBUG(0, "%s(), unable to copy\n", __func__);
+ pr_debug("%s(), unable to copy\n", __func__);
return;
}
@@ -1020,7 +1010,7 @@ void irlap_resend_rejected_frames(struct irlap_cb *self, int command)
*/
while (!skb_queue_empty(&self->txq)) {
- IRDA_DEBUG(0, "%s(), sending additional frames!\n", __func__);
+ pr_debug("%s(), sending additional frames!\n", __func__);
if (self->window > 0) {
skb = skb_dequeue( &self->txq);
IRDA_ASSERT(skb != NULL, return;);
@@ -1060,7 +1050,7 @@ void irlap_resend_rejected_frame(struct irlap_cb *self, int command)
/* tx_skb = skb_clone( skb, GFP_ATOMIC); */
tx_skb = skb_copy(skb, GFP_ATOMIC);
if (!tx_skb) {
- IRDA_DEBUG(0, "%s(), unable to copy\n", __func__);
+ pr_debug("%s(), unable to copy\n", __func__);
return;
}
@@ -1083,8 +1073,6 @@ void irlap_resend_rejected_frame(struct irlap_cb *self, int command)
void irlap_send_ui_frame(struct irlap_cb *self, struct sk_buff *skb,
__u8 caddr, int command)
{
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
IRDA_ASSERT(skb != NULL, return;);
@@ -1143,8 +1131,6 @@ static inline void irlap_recv_i_frame(struct irlap_cb *self,
static void irlap_recv_ui_frame(struct irlap_cb *self, struct sk_buff *skb,
struct irlap_info *info)
{
- IRDA_DEBUG( 4, "%s()\n", __func__);
-
info->pf = skb->data[1] & PF_BIT; /* Final bit */
irlap_do_event(self, RECV_UI_FRAME, skb, info);
@@ -1162,15 +1148,13 @@ static void irlap_recv_frmr_frame(struct irlap_cb *self, struct sk_buff *skb,
__u8 *frame;
int w, x, y, z;
- IRDA_DEBUG(0, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LAP_MAGIC, return;);
IRDA_ASSERT(skb != NULL, return;);
IRDA_ASSERT(info != NULL, return;);
if (!pskb_may_pull(skb, 4)) {
- IRDA_ERROR("%s: frame too short!\n", __func__);
+ net_err_ratelimited("%s: frame too short!\n", __func__);
return;
}
@@ -1186,21 +1170,16 @@ static void irlap_recv_frmr_frame(struct irlap_cb *self, struct sk_buff *skb,
z = frame[3] & 0x08;
if (w) {
- IRDA_DEBUG(0, "Rejected control field is undefined or not "
- "implemented.\n");
+ pr_debug("Rejected control field is undefined or not implemented\n");
}
if (x) {
- IRDA_DEBUG(0, "Rejected control field was invalid because it "
- "contained a non permitted I field.\n");
+ pr_debug("Rejected control field was invalid because it contained a non permitted I field\n");
}
if (y) {
- IRDA_DEBUG(0, "Received I field exceeded the maximum negotiated "
- "for the existing connection or exceeded the maximum "
- "this station supports if no connection exists.\n");
+ pr_debug("Received I field exceeded the maximum negotiated for the existing connection or exceeded the maximum this station supports if no connection exists\n");
}
if (z) {
- IRDA_DEBUG(0, "Rejected control field control field contained an "
- "invalid Nr count.\n");
+ pr_debug("Rejected control field control field contained an invalid Nr count\n");
}
irlap_do_event(self, RECV_FRMR_RSP, skb, info);
}
@@ -1256,10 +1235,8 @@ static void irlap_recv_test_frame(struct irlap_cb *self, struct sk_buff *skb,
{
struct test_frame *frame;
- IRDA_DEBUG(2, "%s()\n", __func__);
-
if (!pskb_may_pull(skb, sizeof(*frame))) {
- IRDA_ERROR("%s: frame too short!\n", __func__);
+ net_err_ratelimited("%s: frame too short!\n", __func__);
return;
}
frame = (struct test_frame *) skb->data;
@@ -1267,8 +1244,8 @@ static void irlap_recv_test_frame(struct irlap_cb *self, struct sk_buff *skb,
/* Broadcast frames must carry saddr and daddr fields */
if (info->caddr == CBROADCAST) {
if (skb->len < sizeof(struct test_frame)) {
- IRDA_DEBUG(0, "%s() test frame too short!\n",
- __func__);
+ pr_debug("%s() test frame too short!\n",
+ __func__);
return;
}
@@ -1328,13 +1305,13 @@ int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev,
* share and non linear skbs. This should never happen, so
* we don't need to be clever about it. Jean II */
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
- IRDA_ERROR("%s: can't clone shared skb!\n", __func__);
+ net_err_ratelimited("%s: can't clone shared skb!\n", __func__);
goto err;
}
/* Check if frame is large enough for parsing */
if (!pskb_may_pull(skb, 2)) {
- IRDA_ERROR("%s: frame too short!\n", __func__);
+ net_err_ratelimited("%s: frame too short!\n", __func__);
goto err;
}
@@ -1348,8 +1325,8 @@ int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev,
/* First we check if this frame has a valid connection address */
if ((info.caddr != self->caddr) && (info.caddr != CBROADCAST)) {
- IRDA_DEBUG(0, "%s(), wrong connection address!\n",
- __func__);
+ pr_debug("%s(), wrong connection address!\n",
+ __func__);
goto out;
}
/*
@@ -1383,8 +1360,8 @@ int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev,
irlap_recv_srej_frame(self, skb, &info, command);
break;
default:
- IRDA_WARNING("%s: Unknown S-frame %02x received!\n",
- __func__, info.control);
+ net_warn_ratelimited("%s: Unknown S-frame %02x received!\n",
+ __func__, info.control);
break;
}
goto out;
@@ -1421,8 +1398,8 @@ int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev,
irlap_recv_ui_frame(self, skb, &info);
break;
default:
- IRDA_WARNING("%s: Unknown frame %02x received!\n",
- __func__, info.control);
+ net_warn_ratelimited("%s: Unknown frame %02x received!\n",
+ __func__, info.control);
break;
}
out:
diff --git a/net/irda/irlmp.c b/net/irda/irlmp.c
index a5f28d421ea8..a26c401ef4a4 100644
--- a/net/irda/irlmp.c
+++ b/net/irda/irlmp.c
@@ -83,7 +83,6 @@ const char *irlmp_reason_str(LM_REASON reason)
*/
int __init irlmp_init(void)
{
- IRDA_DEBUG(1, "%s()\n", __func__);
/* Initialize the irlmp structure. */
irlmp = kzalloc( sizeof(struct irlmp_cb), GFP_KERNEL);
if (irlmp == NULL)
@@ -170,10 +169,8 @@ struct lsap_cb *irlmp_open_lsap(__u8 slsap_sel, notify_t *notify, __u8 pid)
/* Allocate new instance of a LSAP connection */
self = kzalloc(sizeof(struct lsap_cb), GFP_ATOMIC);
- if (self == NULL) {
- IRDA_ERROR("%s: can't allocate memory\n", __func__);
+ if (self == NULL)
return NULL;
- }
self->magic = LMP_LSAP_MAGIC;
self->slsap_sel = slsap_sel;
@@ -209,8 +206,6 @@ EXPORT_SYMBOL(irlmp_open_lsap);
*/
static void __irlmp_close_lsap(struct lsap_cb *self)
{
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
@@ -269,9 +264,8 @@ void irlmp_close_lsap(struct lsap_cb *self)
NULL);
}
if (!lsap) {
- IRDA_DEBUG(0,
- "%s(), Looks like somebody has removed me already!\n",
- __func__);
+ pr_debug("%s(), Looks like somebody has removed me already!\n",
+ __func__);
return;
}
__irlmp_close_lsap(self);
@@ -297,10 +291,8 @@ void irlmp_register_link(struct irlap_cb *irlap, __u32 saddr, notify_t *notify)
* Allocate new instance of a LSAP connection
*/
lap = kzalloc(sizeof(struct lap_cb), GFP_KERNEL);
- if (lap == NULL) {
- IRDA_ERROR("%s: unable to kmalloc\n", __func__);
+ if (lap == NULL)
return;
- }
lap->irlap = irlap;
lap->magic = LMP_LAP_MAGIC;
@@ -311,7 +303,8 @@ void irlmp_register_link(struct irlap_cb *irlap, __u32 saddr, notify_t *notify)
#endif
lap->lsaps = hashbin_new(HB_LOCK);
if (lap->lsaps == NULL) {
- IRDA_WARNING("%s(), unable to kmalloc lsaps\n", __func__);
+ net_warn_ratelimited("%s(), unable to kmalloc lsaps\n",
+ __func__);
kfree(lap);
return;
}
@@ -343,8 +336,6 @@ void irlmp_unregister_link(__u32 saddr)
{
struct lap_cb *link;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
/* We must remove ourselves from the hashbin *first*. This ensure
* that no more LSAPs will be open on this link and no discovery
* will be triggered anymore. Jean II */
@@ -386,9 +377,8 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel,
IRDA_ASSERT(self != NULL, return -EBADR;);
IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -EBADR;);
- IRDA_DEBUG(2,
- "%s(), slsap_sel=%02x, dlsap_sel=%02x, saddr=%08x, daddr=%08x\n",
- __func__, self->slsap_sel, dlsap_sel, saddr, daddr);
+ pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x, saddr=%08x, daddr=%08x\n",
+ __func__, self->slsap_sel, dlsap_sel, saddr, daddr);
if (test_bit(0, &self->connected)) {
ret = -EISCONN;
@@ -432,7 +422,7 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel,
if (daddr != DEV_ADDR_ANY)
discovery = hashbin_find(irlmp->cachelog, daddr, NULL);
else {
- IRDA_DEBUG(2, "%s(), no daddr\n", __func__);
+ pr_debug("%s(), no daddr\n", __func__);
discovery = (discovery_t *)
hashbin_get_first(irlmp->cachelog);
}
@@ -445,7 +435,7 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel,
}
lap = hashbin_lock_find(irlmp->links, saddr, NULL);
if (lap == NULL) {
- IRDA_DEBUG(1, "%s(), Unable to find a usable link!\n", __func__);
+ pr_debug("%s(), Unable to find a usable link!\n", __func__);
ret = -EHOSTUNREACH;
goto err;
}
@@ -460,14 +450,15 @@ int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel,
* disconnected yet (waiting for timeout in LAP).
* Maybe we could give LAP a bit of help in this case.
*/
- IRDA_DEBUG(0, "%s(), sorry, but I'm waiting for LAP to timeout!\n", __func__);
+ pr_debug("%s(), sorry, but I'm waiting for LAP to timeout!\n",
+ __func__);
ret = -EAGAIN;
goto err;
}
/* LAP is already connected to a different node, and LAP
* can only talk to one node at a time */
- IRDA_DEBUG(0, "%s(), sorry, but link is busy!\n", __func__);
+ pr_debug("%s(), sorry, but link is busy!\n", __func__);
ret = -EBUSY;
goto err;
}
@@ -528,8 +519,8 @@ void irlmp_connect_indication(struct lsap_cb *self, struct sk_buff *skb)
IRDA_ASSERT(skb != NULL, return;);
IRDA_ASSERT(self->lap != NULL, return;);
- IRDA_DEBUG(2, "%s(), slsap_sel=%02x, dlsap_sel=%02x\n",
- __func__, self->slsap_sel, self->dlsap_sel);
+ pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x\n",
+ __func__, self->slsap_sel, self->dlsap_sel);
/* Note : self->lap is set in irlmp_link_data_indication(),
* (case CONNECT_CMD:) because we have no way to set it here.
@@ -569,8 +560,8 @@ int irlmp_connect_response(struct lsap_cb *self, struct sk_buff *userdata)
/* We set the connected bit and move the lsap to the connected list
* in the state machine itself. Jean II */
- IRDA_DEBUG(2, "%s(), slsap_sel=%02x, dlsap_sel=%02x\n",
- __func__, self->slsap_sel, self->dlsap_sel);
+ pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x\n",
+ __func__, self->slsap_sel, self->dlsap_sel);
/* Make room for MUX control header (3 bytes) */
IRDA_ASSERT(skb_headroom(userdata) >= LMP_CONTROL_HEADER, return -1;);
@@ -596,8 +587,6 @@ void irlmp_connect_confirm(struct lsap_cb *self, struct sk_buff *skb)
int lap_header_size;
int max_seg_size;
- IRDA_DEBUG(3, "%s()\n", __func__);
-
IRDA_ASSERT(skb != NULL, return;);
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
@@ -609,8 +598,8 @@ void irlmp_connect_confirm(struct lsap_cb *self, struct sk_buff *skb)
lap_header_size = IRLAP_GET_HEADER_SIZE(self->lap->irlap);
max_header_size = LMP_HEADER + lap_header_size;
- IRDA_DEBUG(2, "%s(), max_header_size=%d\n",
- __func__, max_header_size);
+ pr_debug("%s(), max_header_size=%d\n",
+ __func__, max_header_size);
/* Hide LMP_CONTROL_HEADER header from layer above */
skb_pull(skb, LMP_CONTROL_HEADER);
@@ -636,16 +625,14 @@ struct lsap_cb *irlmp_dup(struct lsap_cb *orig, void *instance)
struct lsap_cb *new;
unsigned long flags;
- IRDA_DEBUG(1, "%s()\n", __func__);
-
spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags);
/* Only allowed to duplicate unconnected LSAP's, and only LSAPs
* that have received a connect indication. Jean II */
if ((!hashbin_find(irlmp->unconnected_lsaps, (long) orig, NULL)) ||
(orig->lap == NULL)) {
- IRDA_DEBUG(0, "%s(), invalid LSAP (wrong state)\n",
- __func__);
+ pr_debug("%s(), invalid LSAP (wrong state)\n",
+ __func__);
spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock,
flags);
return NULL;
@@ -654,7 +641,7 @@ struct lsap_cb *irlmp_dup(struct lsap_cb *orig, void *instance)
/* Allocate a new instance */
new = kmemdup(orig, sizeof(*new), GFP_ATOMIC);
if (!new) {
- IRDA_DEBUG(0, "%s(), unable to kmalloc\n", __func__);
+ pr_debug("%s(), unable to kmalloc\n", __func__);
spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock,
flags);
return NULL;
@@ -700,7 +687,7 @@ int irlmp_disconnect_request(struct lsap_cb *self, struct sk_buff *userdata)
* and us that might mess up the hashbins below. This fixes it.
* Jean II */
if (! test_and_clear_bit(0, &self->connected)) {
- IRDA_DEBUG(0, "%s(), already disconnected!\n", __func__);
+ pr_debug("%s(), already disconnected!\n", __func__);
dev_kfree_skb(userdata);
return -1;
}
@@ -754,20 +741,20 @@ void irlmp_disconnect_indication(struct lsap_cb *self, LM_REASON reason,
{
struct lsap_cb *lsap;
- IRDA_DEBUG(1, "%s(), reason=%s [%d]\n", __func__,
- irlmp_reason_str(reason), reason);
+ pr_debug("%s(), reason=%s [%d]\n", __func__,
+ irlmp_reason_str(reason), reason);
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
- IRDA_DEBUG(3, "%s(), slsap_sel=%02x, dlsap_sel=%02x\n",
- __func__, self->slsap_sel, self->dlsap_sel);
+ pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x\n",
+ __func__, self->slsap_sel, self->dlsap_sel);
/* Already disconnected ?
* There is a race condition between irlmp_disconnect_request()
* and us that might mess up the hashbins below. This fixes it.
* Jean II */
if (! test_and_clear_bit(0, &self->connected)) {
- IRDA_DEBUG(0, "%s(), already disconnected!\n", __func__);
+ pr_debug("%s(), already disconnected!\n", __func__);
return;
}
@@ -800,7 +787,7 @@ void irlmp_disconnect_indication(struct lsap_cb *self, LM_REASON reason,
self->notify.disconnect_indication(self->notify.instance,
self, reason, skb);
} else {
- IRDA_DEBUG(0, "%s(), no handler\n", __func__);
+ pr_debug("%s(), no handler\n", __func__);
}
}
@@ -852,8 +839,8 @@ void irlmp_do_discovery(int nslots)
/* Make sure the value is sane */
if ((nslots != 1) && (nslots != 6) && (nslots != 8) && (nslots != 16)){
- IRDA_WARNING("%s: invalid value for number of slots!\n",
- __func__);
+ net_warn_ratelimited("%s: invalid value for number of slots!\n",
+ __func__);
nslots = sysctl_discovery_slots = 8;
}
@@ -971,8 +958,6 @@ irlmp_notify_client(irlmp_client_t *client,
int number; /* Number of nodes in the log */
int i;
- IRDA_DEBUG(3, "%s()\n", __func__);
-
/* Check if client wants or not partial/selective log (optimisation) */
if (!client->disco_callback)
return;
@@ -1022,8 +1007,6 @@ void irlmp_discovery_confirm(hashbin_t *log, DISCOVERY_MODE mode)
irlmp_client_t *client;
irlmp_client_t *client_next;
- IRDA_DEBUG(3, "%s()\n", __func__);
-
IRDA_ASSERT(log != NULL, return;);
if (!(HASHBIN_GET_SIZE(log)))
@@ -1057,8 +1040,6 @@ void irlmp_discovery_expiry(discinfo_t *expiries, int number)
irlmp_client_t *client_next;
int i;
- IRDA_DEBUG(3, "%s()\n", __func__);
-
IRDA_ASSERT(expiries != NULL, return;);
/* For each client - notify callback may touch client list */
@@ -1091,8 +1072,6 @@ void irlmp_discovery_expiry(discinfo_t *expiries, int number)
*/
discovery_t *irlmp_get_discovery_response(void)
{
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(irlmp != NULL, return NULL;);
put_unaligned(irlmp->hints.word, (__u16 *)irlmp->discovery_rsp.data.hints);
@@ -1169,8 +1148,6 @@ int irlmp_udata_request(struct lsap_cb *self, struct sk_buff *userdata)
{
int ret;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(userdata != NULL, return -1;);
/* Make room for MUX header */
@@ -1193,8 +1170,6 @@ int irlmp_udata_request(struct lsap_cb *self, struct sk_buff *userdata)
*/
void irlmp_udata_indication(struct lsap_cb *self, struct sk_buff *skb)
{
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
IRDA_ASSERT(skb != NULL, return;);
@@ -1220,8 +1195,6 @@ int irlmp_connless_data_request(struct lsap_cb *self, struct sk_buff *userdata,
struct sk_buff *clone_skb;
struct lap_cb *lap;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(userdata != NULL, return -1;);
/* Make room for MUX and PID header */
@@ -1271,8 +1244,6 @@ int irlmp_connless_data_request(struct lsap_cb *self, struct sk_buff *userdata,
#ifdef CONFIG_IRDA_ULTRA
void irlmp_connless_data_indication(struct lsap_cb *self, struct sk_buff *skb)
{
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
IRDA_ASSERT(skb != NULL, return;);
@@ -1314,7 +1285,7 @@ void irlmp_status_indication(struct lap_cb *self,
curr->notify.status_indication(curr->notify.instance,
link, lock);
else
- IRDA_DEBUG(2, "%s(), no handler\n", __func__);
+ pr_debug("%s(), no handler\n", __func__);
curr = next;
}
@@ -1342,7 +1313,7 @@ void irlmp_flow_indication(struct lap_cb *self, LOCAL_FLOW flow)
/* Get the number of lsap. That's the only safe way to know
* that we have looped around... - Jean II */
lsap_todo = HASHBIN_GET_SIZE(self->lsaps);
- IRDA_DEBUG(4, "%s() : %d lsaps to scan\n", __func__, lsap_todo);
+ pr_debug("%s() : %d lsaps to scan\n", __func__, lsap_todo);
/* Poll lsap in order until the queue is full or until we
* tried them all.
@@ -1361,14 +1332,16 @@ void irlmp_flow_indication(struct lap_cb *self, LOCAL_FLOW flow)
/* Uh-oh... Paranoia */
if(curr == NULL)
break;
- IRDA_DEBUG(4, "%s() : curr is %p, next was %p and is now %p, still %d to go - queue len = %d\n", __func__, curr, next, self->flow_next, lsap_todo, IRLAP_GET_TX_QUEUE_LEN(self->irlap));
+ pr_debug("%s() : curr is %p, next was %p and is now %p, still %d to go - queue len = %d\n",
+ __func__, curr, next, self->flow_next, lsap_todo,
+ IRLAP_GET_TX_QUEUE_LEN(self->irlap));
/* Inform lsap user that it can send one more packet. */
if (curr->notify.flow_indication != NULL)
curr->notify.flow_indication(curr->notify.instance,
curr, flow);
else
- IRDA_DEBUG(1, "%s(), no handler\n", __func__);
+ pr_debug("%s(), no handler\n", __func__);
}
}
@@ -1389,32 +1362,30 @@ __u8 *irlmp_hint_to_service(__u8 *hint)
* since we currently only support 2 hint bytes
*/
service = kmalloc(16, GFP_ATOMIC);
- if (!service) {
- IRDA_DEBUG(1, "%s(), Unable to kmalloc!\n", __func__);
+ if (!service)
return NULL;
- }
if (!hint[0]) {
- IRDA_DEBUG(1, "<None>\n");
+ pr_debug("<None>\n");
kfree(service);
return NULL;
}
if (hint[0] & HINT_PNP)
- IRDA_DEBUG(1, "PnP Compatible ");
+ pr_debug("PnP Compatible ");
if (hint[0] & HINT_PDA)
- IRDA_DEBUG(1, "PDA/Palmtop ");
+ pr_debug("PDA/Palmtop ");
if (hint[0] & HINT_COMPUTER)
- IRDA_DEBUG(1, "Computer ");
+ pr_debug("Computer ");
if (hint[0] & HINT_PRINTER) {
- IRDA_DEBUG(1, "Printer ");
+ pr_debug("Printer ");
service[i++] = S_PRINTER;
}
if (hint[0] & HINT_MODEM)
- IRDA_DEBUG(1, "Modem ");
+ pr_debug("Modem ");
if (hint[0] & HINT_FAX)
- IRDA_DEBUG(1, "Fax ");
+ pr_debug("Fax ");
if (hint[0] & HINT_LAN) {
- IRDA_DEBUG(1, "LAN Access ");
+ pr_debug("LAN Access ");
service[i++] = S_LAN;
}
/*
@@ -1424,22 +1395,22 @@ __u8 *irlmp_hint_to_service(__u8 *hint)
*/
if (hint[0] & HINT_EXTENSION) {
if (hint[1] & HINT_TELEPHONY) {
- IRDA_DEBUG(1, "Telephony ");
+ pr_debug("Telephony ");
service[i++] = S_TELEPHONY;
}
if (hint[1] & HINT_FILE_SERVER)
- IRDA_DEBUG(1, "File Server ");
+ pr_debug("File Server ");
if (hint[1] & HINT_COMM) {
- IRDA_DEBUG(1, "IrCOMM ");
+ pr_debug("IrCOMM ");
service[i++] = S_COMM;
}
if (hint[1] & HINT_OBEX) {
- IRDA_DEBUG(1, "IrOBEX ");
+ pr_debug("IrOBEX ");
service[i++] = S_OBEX;
}
}
- IRDA_DEBUG(1, "\n");
+ pr_debug("\n");
/* So that client can be notified about any discovery */
service[i++] = S_ANY;
@@ -1492,14 +1463,13 @@ void *irlmp_register_service(__u16 hints)
{
irlmp_service_t *service;
- IRDA_DEBUG(4, "%s(), hints = %04x\n", __func__, hints);
+ pr_debug("%s(), hints = %04x\n", __func__, hints);
/* Make a new registration */
service = kmalloc(sizeof(irlmp_service_t), GFP_ATOMIC);
- if (!service) {
- IRDA_DEBUG(1, "%s(), Unable to kmalloc!\n", __func__);
+ if (!service)
return NULL;
- }
+
service->hints.word = hints;
hashbin_insert(irlmp->services, (irda_queue_t *) service,
(long) service, NULL);
@@ -1522,15 +1492,13 @@ int irlmp_unregister_service(void *handle)
irlmp_service_t *service;
unsigned long flags;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
if (!handle)
return -1;
/* Caller may call with invalid handle (it's legal) - Jean II */
service = hashbin_lock_find(irlmp->services, (long) handle, NULL);
if (!service) {
- IRDA_DEBUG(1, "%s(), Unknown service!\n", __func__);
+ pr_debug("%s(), Unknown service!\n", __func__);
return -1;
}
@@ -1567,15 +1535,12 @@ void *irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 disco_clb,
{
irlmp_client_t *client;
- IRDA_DEBUG(1, "%s()\n", __func__);
IRDA_ASSERT(irlmp != NULL, return NULL;);
/* Make a new registration */
client = kmalloc(sizeof(irlmp_client_t), GFP_ATOMIC);
- if (!client) {
- IRDA_DEBUG( 1, "%s(), Unable to kmalloc!\n", __func__);
+ if (!client)
return NULL;
- }
/* Register the details */
client->hint_mask.word = hint_mask;
@@ -1609,7 +1574,7 @@ int irlmp_update_client(void *handle, __u16 hint_mask,
client = hashbin_lock_find(irlmp->clients, (long) handle, NULL);
if (!client) {
- IRDA_DEBUG(1, "%s(), Unknown client!\n", __func__);
+ pr_debug("%s(), Unknown client!\n", __func__);
return -1;
}
@@ -1632,19 +1597,17 @@ int irlmp_unregister_client(void *handle)
{
struct irlmp_client *client;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
if (!handle)
return -1;
/* Caller may call with invalid handle (it's legal) - Jean II */
client = hashbin_lock_find(irlmp->clients, (long) handle, NULL);
if (!client) {
- IRDA_DEBUG(1, "%s(), Unknown client!\n", __func__);
+ pr_debug("%s(), Unknown client!\n", __func__);
return -1;
}
- IRDA_DEBUG(4, "%s(), removing client!\n", __func__);
+ pr_debug("%s(), removing client!\n", __func__);
hashbin_remove_this(irlmp->clients, (irda_queue_t *) client);
kfree(client);
@@ -1673,8 +1636,6 @@ static int irlmp_slsap_inuse(__u8 slsap_sel)
IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return TRUE;);
IRDA_ASSERT(slsap_sel != LSAP_ANY, return TRUE;);
- IRDA_DEBUG(4, "%s()\n", __func__);
-
#ifdef CONFIG_IRDA_ULTRA
/* Accept all bindings to the connectionless LSAP */
if (slsap_sel == LSAP_CONNLESS)
@@ -1708,8 +1669,8 @@ static int irlmp_slsap_inuse(__u8 slsap_sel)
goto errlsap;);
if ((self->slsap_sel == slsap_sel)) {
- IRDA_DEBUG(4, "Source LSAP selector=%02x in use\n",
- self->slsap_sel);
+ pr_debug("Source LSAP selector=%02x in use\n",
+ self->slsap_sel);
goto errlsap;
}
self = (struct lsap_cb*) hashbin_get_next(lap->lsaps);
@@ -1733,8 +1694,8 @@ static int irlmp_slsap_inuse(__u8 slsap_sel)
while (self != NULL) {
IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, goto erruncon;);
if ((self->slsap_sel == slsap_sel)) {
- IRDA_DEBUG(4, "Source LSAP selector=%02x in use (unconnected)\n",
- self->slsap_sel);
+ pr_debug("Source LSAP selector=%02x in use (unconnected)\n",
+ self->slsap_sel);
goto erruncon;
}
self = (struct lsap_cb*) hashbin_get_next(irlmp->unconnected_lsaps);
@@ -1799,8 +1760,8 @@ static __u8 irlmp_find_free_slsap(void)
/* Make sure we terminate the loop */
if (wrapped++) {
- IRDA_ERROR("%s: no more free LSAPs !\n",
- __func__);
+ net_err_ratelimited("%s: no more free LSAPs !\n",
+ __func__);
return 0;
}
}
@@ -1814,8 +1775,8 @@ static __u8 irlmp_find_free_slsap(void)
/* Got it ! */
lsap_sel = irlmp->last_lsap_sel;
- IRDA_DEBUG(4, "%s(), found free lsap_sel=%02x\n",
- __func__, lsap_sel);
+ pr_debug("%s(), found free lsap_sel=%02x\n",
+ __func__, lsap_sel);
return lsap_sel;
}
@@ -1833,26 +1794,27 @@ LM_REASON irlmp_convert_lap_reason( LAP_REASON lap_reason)
switch (lap_reason) {
case LAP_DISC_INDICATION: /* Received a disconnect request from peer */
- IRDA_DEBUG( 1, "%s(), LAP_DISC_INDICATION\n", __func__);
+ pr_debug("%s(), LAP_DISC_INDICATION\n", __func__);
reason = LM_USER_REQUEST;
break;
case LAP_NO_RESPONSE: /* To many retransmits without response */
- IRDA_DEBUG( 1, "%s(), LAP_NO_RESPONSE\n", __func__);
+ pr_debug("%s(), LAP_NO_RESPONSE\n", __func__);
reason = LM_LAP_DISCONNECT;
break;
case LAP_RESET_INDICATION:
- IRDA_DEBUG( 1, "%s(), LAP_RESET_INDICATION\n", __func__);
+ pr_debug("%s(), LAP_RESET_INDICATION\n", __func__);
reason = LM_LAP_RESET;
break;
case LAP_FOUND_NONE:
case LAP_MEDIA_BUSY:
case LAP_PRIMARY_CONFLICT:
- IRDA_DEBUG(1, "%s(), LAP_FOUND_NONE, LAP_MEDIA_BUSY or LAP_PRIMARY_CONFLICT\n", __func__);
+ pr_debug("%s(), LAP_FOUND_NONE, LAP_MEDIA_BUSY or LAP_PRIMARY_CONFLICT\n",
+ __func__);
reason = LM_CONNECT_FAILURE;
break;
default:
- IRDA_DEBUG(1, "%s(), Unknown IrLAP disconnect reason %d!\n",
- __func__, lap_reason);
+ pr_debug("%s(), Unknown IrLAP disconnect reason %d!\n",
+ __func__, lap_reason);
reason = LM_LAP_DISCONNECT;
break;
}
diff --git a/net/irda/irlmp_event.c b/net/irda/irlmp_event.c
index 9505a7d06f1a..e306cf2c1e04 100644
--- a/net/irda/irlmp_event.c
+++ b/net/irda/irlmp_event.c
@@ -48,8 +48,7 @@ const char *const irlsap_state[] = {
"LSAP_SETUP_PEND",
};
-#ifdef CONFIG_IRDA_DEBUG
-static const char *const irlmp_event[] = {
+static const char *const irlmp_event[] __maybe_unused = {
"LM_CONNECT_REQUEST",
"LM_CONNECT_CONFIRM",
"LM_CONNECT_RESPONSE",
@@ -75,7 +74,6 @@ static const char *const irlmp_event[] = {
"LM_LAP_DISCOVERY_CONFIRM",
"LM_LAP_IDLE_TIMEOUT",
};
-#endif /* CONFIG_IRDA_DEBUG */
/* LAP Connection control proto declarations */
static void irlmp_state_standby (struct lap_cb *, IRLMP_EVENT,
@@ -120,7 +118,7 @@ static inline void irlmp_next_lap_state(struct lap_cb *self,
IRLMP_STATE state)
{
/*
- IRDA_DEBUG(4, "%s(), LMP LAP = %s\n", __func__, irlmp_state[state]);
+ pr_debug("%s(), LMP LAP = %s\n", __func__, irlmp_state[state]);
*/
self->lap_state = state;
}
@@ -130,7 +128,7 @@ static inline void irlmp_next_lsap_state(struct lsap_cb *self,
{
/*
IRDA_ASSERT(self != NULL, return;);
- IRDA_DEBUG(4, "%s(), LMP LSAP = %s\n", __func__, irlsap_state[state]);
+ pr_debug("%s(), LMP LSAP = %s\n", __func__, irlsap_state[state]);
*/
self->lsap_state = state;
}
@@ -142,8 +140,8 @@ int irlmp_do_lsap_event(struct lsap_cb *self, IRLMP_EVENT event,
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
- IRDA_DEBUG(4, "%s(), EVENT = %s, STATE = %s\n",
- __func__, irlmp_event[event], irlsap_state[ self->lsap_state]);
+ pr_debug("%s(), EVENT = %s, STATE = %s\n",
+ __func__, irlmp_event[event], irlsap_state[self->lsap_state]);
return (*lsap_state[self->lsap_state]) (self, event, skb);
}
@@ -160,17 +158,15 @@ void irlmp_do_lap_event(struct lap_cb *self, IRLMP_EVENT event,
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
- IRDA_DEBUG(4, "%s(), EVENT = %s, STATE = %s\n", __func__,
- irlmp_event[event],
- irlmp_state[self->lap_state]);
+ pr_debug("%s(), EVENT = %s, STATE = %s\n", __func__,
+ irlmp_event[event],
+ irlmp_state[self->lap_state]);
(*lap_state[self->lap_state]) (self, event, skb);
}
void irlmp_discovery_timer_expired(void *data)
{
- IRDA_DEBUG(4, "%s()\n", __func__);
-
/* We always cleanup the log (active & passive discovery) */
irlmp_do_expiry();
@@ -184,8 +180,6 @@ void irlmp_watchdog_timer_expired(void *data)
{
struct lsap_cb *self = (struct lsap_cb *) data;
- IRDA_DEBUG(2, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;);
@@ -196,8 +190,6 @@ void irlmp_idle_timer_expired(void *data)
{
struct lap_cb *self = (struct lap_cb *) data;
- IRDA_DEBUG(2, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
@@ -256,7 +248,6 @@ irlmp_do_all_lsap_event(hashbin_t * lsap_hashbin,
static void irlmp_state_standby(struct lap_cb *self, IRLMP_EVENT event,
struct sk_buff *skb)
{
- IRDA_DEBUG(4, "%s()\n", __func__);
IRDA_ASSERT(self->irlap != NULL, return;);
switch (event) {
@@ -276,7 +267,7 @@ static void irlmp_state_standby(struct lap_cb *self, IRLMP_EVENT event,
irlap_connect_response(self->irlap, skb);
break;
case LM_LAP_CONNECT_REQUEST:
- IRDA_DEBUG(4, "%s() LS_CONNECT_REQUEST\n", __func__);
+ pr_debug("%s() LS_CONNECT_REQUEST\n", __func__);
irlmp_next_lap_state(self, LAP_U_CONNECT);
@@ -284,14 +275,14 @@ static void irlmp_state_standby(struct lap_cb *self, IRLMP_EVENT event,
irlap_connect_request(self->irlap, self->daddr, NULL, 0);
break;
case LM_LAP_DISCONNECT_INDICATION:
- IRDA_DEBUG(4, "%s(), Error LM_LAP_DISCONNECT_INDICATION\n",
- __func__);
+ pr_debug("%s(), Error LM_LAP_DISCONNECT_INDICATION\n",
+ __func__);
irlmp_next_lap_state(self, LAP_STANDBY);
break;
default:
- IRDA_DEBUG(0, "%s(), Unknown event %s\n",
- __func__, irlmp_event[event]);
+ pr_debug("%s(), Unknown event %s\n",
+ __func__, irlmp_event[event]);
break;
}
}
@@ -306,7 +297,7 @@ static void irlmp_state_standby(struct lap_cb *self, IRLMP_EVENT event,
static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event,
struct sk_buff *skb)
{
- IRDA_DEBUG(2, "%s(), event=%s\n", __func__, irlmp_event[event]);
+ pr_debug("%s(), event=%s\n", __func__, irlmp_event[event]);
switch (event) {
case LM_LAP_CONNECT_INDICATION:
@@ -326,7 +317,7 @@ static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event,
* the lsaps may already have gone. This avoid getting stuck
* forever in LAP_ACTIVE state - Jean II */
if (HASHBIN_GET_SIZE(self->lsaps) == 0) {
- IRDA_DEBUG(0, "%s() NO LSAPs !\n", __func__);
+ pr_debug("%s() NO LSAPs !\n", __func__);
irlmp_start_idle_timer(self, LM_IDLE_TIMEOUT);
}
break;
@@ -344,12 +335,12 @@ static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event,
* the lsaps may already have gone. This avoid getting stuck
* forever in LAP_ACTIVE state - Jean II */
if (HASHBIN_GET_SIZE(self->lsaps) == 0) {
- IRDA_DEBUG(0, "%s() NO LSAPs !\n", __func__);
+ pr_debug("%s() NO LSAPs !\n", __func__);
irlmp_start_idle_timer(self, LM_IDLE_TIMEOUT);
}
break;
case LM_LAP_DISCONNECT_INDICATION:
- IRDA_DEBUG(4, "%s(), LM_LAP_DISCONNECT_INDICATION\n", __func__);
+ pr_debug("%s(), LM_LAP_DISCONNECT_INDICATION\n", __func__);
irlmp_next_lap_state(self, LAP_STANDBY);
/* Send disconnect event to all LSAPs using this link */
@@ -357,7 +348,7 @@ static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event,
LM_LAP_DISCONNECT_INDICATION);
break;
case LM_LAP_DISCONNECT_REQUEST:
- IRDA_DEBUG(4, "%s(), LM_LAP_DISCONNECT_REQUEST\n", __func__);
+ pr_debug("%s(), LM_LAP_DISCONNECT_REQUEST\n", __func__);
/* One of the LSAP did timeout or was closed, if it was
* the last one, try to get out of here - Jean II */
@@ -366,7 +357,7 @@ static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event,
}
break;
default:
- IRDA_DEBUG(0, "%s(), Unknown event %s\n",
+ pr_debug("%s(), Unknown event %s\n",
__func__, irlmp_event[event]);
break;
}
@@ -381,11 +372,9 @@ static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event,
static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event,
struct sk_buff *skb)
{
- IRDA_DEBUG(4, "%s()\n", __func__);
-
switch (event) {
case LM_LAP_CONNECT_REQUEST:
- IRDA_DEBUG(4, "%s(), LS_CONNECT_REQUEST\n", __func__);
+ pr_debug("%s(), LS_CONNECT_REQUEST\n", __func__);
/*
* IrLAP may have a pending disconnect. We tried to close
@@ -467,7 +456,7 @@ static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event,
irlmp_do_expiry();
break;
default:
- IRDA_DEBUG(0, "%s(), Unknown event %s\n",
+ pr_debug("%s(), Unknown event %s\n",
__func__, irlmp_event[event]);
break;
}
@@ -490,8 +479,6 @@ static int irlmp_state_disconnected(struct lsap_cb *self, IRLMP_EVENT event,
{
int ret = 0;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
@@ -505,11 +492,11 @@ static int irlmp_state_disconnected(struct lsap_cb *self, IRLMP_EVENT event,
break;
#endif /* CONFIG_IRDA_ULTRA */
case LM_CONNECT_REQUEST:
- IRDA_DEBUG(4, "%s(), LM_CONNECT_REQUEST\n", __func__);
+ pr_debug("%s(), LM_CONNECT_REQUEST\n", __func__);
if (self->conn_skb) {
- IRDA_WARNING("%s: busy with another request!\n",
- __func__);
+ net_warn_ratelimited("%s: busy with another request!\n",
+ __func__);
return -EBUSY;
}
/* Don't forget to refcount it (see irlmp_connect_request()) */
@@ -525,8 +512,8 @@ static int irlmp_state_disconnected(struct lsap_cb *self, IRLMP_EVENT event,
break;
case LM_CONNECT_INDICATION:
if (self->conn_skb) {
- IRDA_WARNING("%s: busy with another request!\n",
- __func__);
+ net_warn_ratelimited("%s: busy with another request!\n",
+ __func__);
return -EBUSY;
}
/* Don't forget to refcount it (see irlap_driver_rcv()) */
@@ -551,8 +538,8 @@ static int irlmp_state_disconnected(struct lsap_cb *self, IRLMP_EVENT event,
irlmp_do_lap_event(self->lap, LM_LAP_CONNECT_REQUEST, NULL);
break;
default:
- IRDA_DEBUG(1, "%s(), Unknown event %s on LSAP %#02x\n",
- __func__, irlmp_event[event], self->slsap_sel);
+ pr_debug("%s(), Unknown event %s on LSAP %#02x\n",
+ __func__, irlmp_event[event], self->slsap_sel);
break;
}
return ret;
@@ -570,8 +557,6 @@ static int irlmp_state_connect(struct lsap_cb *self, IRLMP_EVENT event,
struct lsap_cb *lsap;
int ret = 0;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
@@ -603,7 +588,7 @@ static int irlmp_state_connect(struct lsap_cb *self, IRLMP_EVENT event,
case LM_WATCHDOG_TIMEOUT:
/* May happen, who knows...
* Jean II */
- IRDA_DEBUG(0, "%s() WATCHDOG_TIMEOUT!\n", __func__);
+ pr_debug("%s() WATCHDOG_TIMEOUT!\n", __func__);
/* Disconnect, get out... - Jean II */
self->lap = NULL;
@@ -613,8 +598,8 @@ static int irlmp_state_connect(struct lsap_cb *self, IRLMP_EVENT event,
default:
/* LM_LAP_DISCONNECT_INDICATION : Should never happen, we
* are *not* yet bound to the IrLAP link. Jean II */
- IRDA_DEBUG(0, "%s(), Unknown event %s on LSAP %#02x\n",
- __func__, irlmp_event[event], self->slsap_sel);
+ pr_debug("%s(), Unknown event %s on LSAP %#02x\n",
+ __func__, irlmp_event[event], self->slsap_sel);
break;
}
return ret;
@@ -632,8 +617,6 @@ static int irlmp_state_connect_pend(struct lsap_cb *self, IRLMP_EVENT event,
struct sk_buff *tx_skb;
int ret = 0;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
@@ -642,17 +625,17 @@ static int irlmp_state_connect_pend(struct lsap_cb *self, IRLMP_EVENT event,
/* Keep state */
break;
case LM_CONNECT_RESPONSE:
- IRDA_DEBUG(0, "%s(), LM_CONNECT_RESPONSE, "
- "no indication issued yet\n", __func__);
+ pr_debug("%s(), LM_CONNECT_RESPONSE, no indication issued yet\n",
+ __func__);
/* Keep state */
break;
case LM_DISCONNECT_REQUEST:
- IRDA_DEBUG(0, "%s(), LM_DISCONNECT_REQUEST, "
- "not yet bound to IrLAP connection\n", __func__);
+ pr_debug("%s(), LM_DISCONNECT_REQUEST, not yet bound to IrLAP connection\n",
+ __func__);
/* Keep state */
break;
case LM_LAP_CONNECT_CONFIRM:
- IRDA_DEBUG(4, "%s(), LS_CONNECT_CONFIRM\n", __func__);
+ pr_debug("%s(), LS_CONNECT_CONFIRM\n", __func__);
irlmp_next_lsap_state(self, LSAP_CONNECT);
tx_skb = self->conn_skb;
@@ -666,7 +649,7 @@ static int irlmp_state_connect_pend(struct lsap_cb *self, IRLMP_EVENT event,
/* Will happen in some rare cases because of a race condition.
* Just make sure we don't stay there forever...
* Jean II */
- IRDA_DEBUG(0, "%s() WATCHDOG_TIMEOUT!\n", __func__);
+ pr_debug("%s() WATCHDOG_TIMEOUT!\n", __func__);
/* Go back to disconnected mode, keep the socket waiting */
self->lap = NULL;
@@ -679,8 +662,8 @@ static int irlmp_state_connect_pend(struct lsap_cb *self, IRLMP_EVENT event,
default:
/* LM_LAP_DISCONNECT_INDICATION : Should never happen, we
* are *not* yet bound to the IrLAP link. Jean II */
- IRDA_DEBUG(0, "%s(), Unknown event %s on LSAP %#02x\n",
- __func__, irlmp_event[event], self->slsap_sel);
+ pr_debug("%s(), Unknown event %s on LSAP %#02x\n",
+ __func__, irlmp_event[event], self->slsap_sel);
break;
}
return ret;
@@ -698,8 +681,6 @@ static int irlmp_state_dtr(struct lsap_cb *self, IRLMP_EVENT event,
LM_REASON reason;
int ret = 0;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
IRDA_ASSERT(self->lap != NULL, return -1;);
@@ -721,13 +702,13 @@ static int irlmp_state_dtr(struct lsap_cb *self, IRLMP_EVENT event,
irlmp_udata_indication(self, skb);
break;
case LM_CONNECT_REQUEST:
- IRDA_DEBUG(0, "%s(), LM_CONNECT_REQUEST, "
- "error, LSAP already connected\n", __func__);
+ pr_debug("%s(), LM_CONNECT_REQUEST, error, LSAP already connected\n",
+ __func__);
/* Keep state */
break;
case LM_CONNECT_RESPONSE:
- IRDA_DEBUG(0, "%s(), LM_CONNECT_RESPONSE, "
- "error, LSAP already connected\n", __func__);
+ pr_debug("%s(), LM_CONNECT_RESPONSE, error, LSAP already connected\n",
+ __func__);
/* Keep state */
break;
case LM_DISCONNECT_REQUEST:
@@ -739,8 +720,8 @@ static int irlmp_state_dtr(struct lsap_cb *self, IRLMP_EVENT event,
/* Try to close the LAP connection if its still there */
if (self->lap) {
- IRDA_DEBUG(4, "%s(), trying to close IrLAP\n",
- __func__);
+ pr_debug("%s(), trying to close IrLAP\n",
+ __func__);
irlmp_do_lap_event(self->lap,
LM_LAP_DISCONNECT_REQUEST,
NULL);
@@ -764,14 +745,14 @@ static int irlmp_state_dtr(struct lsap_cb *self, IRLMP_EVENT event,
reason = skb->data[3];
/* Try to close the LAP connection */
- IRDA_DEBUG(4, "%s(), trying to close IrLAP\n", __func__);
+ pr_debug("%s(), trying to close IrLAP\n", __func__);
irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL);
irlmp_disconnect_indication(self, reason, skb);
break;
default:
- IRDA_DEBUG(0, "%s(), Unknown event %s on LSAP %#02x\n",
- __func__, irlmp_event[event], self->slsap_sel);
+ pr_debug("%s(), Unknown event %s on LSAP %#02x\n",
+ __func__, irlmp_event[event], self->slsap_sel);
break;
}
return ret;
@@ -793,8 +774,6 @@ static int irlmp_state_setup(struct lsap_cb *self, IRLMP_EVENT event,
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;);
- IRDA_DEBUG(4, "%s()\n", __func__);
-
switch (event) {
case LM_CONNECT_CONFIRM:
irlmp_next_lsap_state(self, LSAP_DATA_TRANSFER_READY);
@@ -814,7 +793,7 @@ static int irlmp_state_setup(struct lsap_cb *self, IRLMP_EVENT event,
reason = skb->data[3];
/* Try to close the LAP connection */
- IRDA_DEBUG(4, "%s(), trying to close IrLAP\n", __func__);
+ pr_debug("%s(), trying to close IrLAP\n", __func__);
irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL);
irlmp_disconnect_indication(self, reason, skb);
@@ -832,7 +811,7 @@ static int irlmp_state_setup(struct lsap_cb *self, IRLMP_EVENT event,
irlmp_disconnect_indication(self, reason, skb);
break;
case LM_WATCHDOG_TIMEOUT:
- IRDA_DEBUG(0, "%s() WATCHDOG_TIMEOUT!\n", __func__);
+ pr_debug("%s() WATCHDOG_TIMEOUT!\n", __func__);
IRDA_ASSERT(self->lap != NULL, return -1;);
irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL);
@@ -841,8 +820,8 @@ static int irlmp_state_setup(struct lsap_cb *self, IRLMP_EVENT event,
irlmp_disconnect_indication(self, LM_CONNECT_FAILURE, NULL);
break;
default:
- IRDA_DEBUG(0, "%s(), Unknown event %s on LSAP %#02x\n",
- __func__, irlmp_event[event], self->slsap_sel);
+ pr_debug("%s(), Unknown event %s on LSAP %#02x\n",
+ __func__, irlmp_event[event], self->slsap_sel);
break;
}
return ret;
@@ -863,8 +842,6 @@ static int irlmp_state_setup_pend(struct lsap_cb *self, IRLMP_EVENT event,
LM_REASON reason;
int ret = 0;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(irlmp != NULL, return -1;);
@@ -883,7 +860,7 @@ static int irlmp_state_setup_pend(struct lsap_cb *self, IRLMP_EVENT event,
irlmp_next_lsap_state(self, LSAP_SETUP);
break;
case LM_WATCHDOG_TIMEOUT:
- IRDA_DEBUG(0, "%s() : WATCHDOG_TIMEOUT !\n", __func__);
+ pr_debug("%s() : WATCHDOG_TIMEOUT !\n", __func__);
IRDA_ASSERT(self->lap != NULL, return -1;);
irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL);
@@ -901,8 +878,8 @@ static int irlmp_state_setup_pend(struct lsap_cb *self, IRLMP_EVENT event,
irlmp_disconnect_indication(self, reason, NULL);
break;
default:
- IRDA_DEBUG(0, "%s(), Unknown event %s on LSAP %#02x\n",
- __func__, irlmp_event[event], self->slsap_sel);
+ pr_debug("%s(), Unknown event %s on LSAP %#02x\n",
+ __func__, irlmp_event[event], self->slsap_sel);
break;
}
return ret;
diff --git a/net/irda/irlmp_frame.c b/net/irda/irlmp_frame.c
index 062e63b1c5c4..38b0f994bc7b 100644
--- a/net/irda/irlmp_frame.c
+++ b/net/irda/irlmp_frame.c
@@ -44,7 +44,7 @@ inline void irlmp_send_data_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap,
skb->data[1] = slsap;
if (expedited) {
- IRDA_DEBUG(4, "%s(), sending expedited data\n", __func__);
+ pr_debug("%s(), sending expedited data\n", __func__);
irlap_data_request(self->irlap, skb, TRUE);
} else
irlap_data_request(self->irlap, skb, FALSE);
@@ -60,8 +60,6 @@ void irlmp_send_lcf_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap,
{
__u8 *frame;
- IRDA_DEBUG(2, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
IRDA_ASSERT(skb != NULL, return;);
@@ -95,8 +93,6 @@ void irlmp_link_data_indication(struct lap_cb *self, struct sk_buff *skb,
__u8 dlsap_sel; /* Destination LSAP address */
__u8 *fp;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
IRDA_ASSERT(skb->len > 2, return;);
@@ -115,9 +111,8 @@ void irlmp_link_data_indication(struct lap_cb *self, struct sk_buff *skb,
* it in a different way than other established connections.
*/
if ((fp[0] & CONTROL_BIT) && (fp[2] == CONNECT_CMD)) {
- IRDA_DEBUG(3, "%s(), incoming connection, "
- "source LSAP=%d, dest LSAP=%d\n",
- __func__, slsap_sel, dlsap_sel);
+ pr_debug("%s(), incoming connection, source LSAP=%d, dest LSAP=%d\n",
+ __func__, slsap_sel, dlsap_sel);
/* Try to find LSAP among the unconnected LSAPs */
lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, CONNECT_CMD,
@@ -125,7 +120,8 @@ void irlmp_link_data_indication(struct lap_cb *self, struct sk_buff *skb,
/* Maybe LSAP was already connected, so try one more time */
if (!lsap) {
- IRDA_DEBUG(1, "%s(), incoming connection for LSAP already connected\n", __func__);
+ pr_debug("%s(), incoming connection for LSAP already connected\n",
+ __func__);
lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, 0,
self->lsaps);
}
@@ -134,14 +130,14 @@ void irlmp_link_data_indication(struct lap_cb *self, struct sk_buff *skb,
self->lsaps);
if (lsap == NULL) {
- IRDA_DEBUG(2, "IrLMP, Sorry, no LSAP for received frame!\n");
- IRDA_DEBUG(2, "%s(), slsap_sel = %02x, dlsap_sel = %02x\n",
- __func__, slsap_sel, dlsap_sel);
+ pr_debug("IrLMP, Sorry, no LSAP for received frame!\n");
+ pr_debug("%s(), slsap_sel = %02x, dlsap_sel = %02x\n",
+ __func__, slsap_sel, dlsap_sel);
if (fp[0] & CONTROL_BIT) {
- IRDA_DEBUG(2, "%s(), received control frame %02x\n",
- __func__, fp[2]);
+ pr_debug("%s(), received control frame %02x\n",
+ __func__, fp[2]);
} else {
- IRDA_DEBUG(2, "%s(), received data frame\n", __func__);
+ pr_debug("%s(), received data frame\n", __func__);
}
return;
}
@@ -159,20 +155,20 @@ void irlmp_link_data_indication(struct lap_cb *self, struct sk_buff *skb,
irlmp_do_lsap_event(lsap, LM_CONNECT_CONFIRM, skb);
break;
case DISCONNECT:
- IRDA_DEBUG(4, "%s(), Disconnect indication!\n",
- __func__);
+ pr_debug("%s(), Disconnect indication!\n",
+ __func__);
irlmp_do_lsap_event(lsap, LM_DISCONNECT_INDICATION,
skb);
break;
case ACCESSMODE_CMD:
- IRDA_DEBUG(0, "Access mode cmd not implemented!\n");
+ pr_debug("Access mode cmd not implemented!\n");
break;
case ACCESSMODE_CNF:
- IRDA_DEBUG(0, "Access mode cnf not implemented!\n");
+ pr_debug("Access mode cnf not implemented!\n");
break;
default:
- IRDA_DEBUG(0, "%s(), Unknown control frame %02x\n",
- __func__, fp[2]);
+ pr_debug("%s(), Unknown control frame %02x\n",
+ __func__, fp[2]);
break;
}
} else if (unreliable) {
@@ -206,8 +202,6 @@ void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb)
__u8 *fp;
unsigned long flags;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
IRDA_ASSERT(skb->len > 2, return;);
@@ -223,14 +217,14 @@ void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb)
pid = fp[2];
if (pid & 0x80) {
- IRDA_DEBUG(0, "%s(), extension in PID not supp!\n",
- __func__);
+ pr_debug("%s(), extension in PID not supp!\n",
+ __func__);
return;
}
/* Check if frame is addressed to the connectionless LSAP */
if ((slsap_sel != LSAP_CONNLESS) || (dlsap_sel != LSAP_CONNLESS)) {
- IRDA_DEBUG(0, "%s(), dropping frame!\n", __func__);
+ pr_debug("%s(), dropping frame!\n", __func__);
return;
}
@@ -254,7 +248,7 @@ void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb)
if (lsap)
irlmp_connless_data_indication(lsap, skb);
else {
- IRDA_DEBUG(0, "%s(), found no matching LSAP!\n", __func__);
+ pr_debug("%s(), found no matching LSAP!\n", __func__);
}
}
#endif /* CONFIG_IRDA_ULTRA */
@@ -270,8 +264,6 @@ void irlmp_link_disconnect_indication(struct lap_cb *lap,
LAP_REASON reason,
struct sk_buff *skb)
{
- IRDA_DEBUG(2, "%s()\n", __func__);
-
IRDA_ASSERT(lap != NULL, return;);
IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;);
@@ -296,8 +288,6 @@ void irlmp_link_connect_indication(struct lap_cb *self, __u32 saddr,
__u32 daddr, struct qos_info *qos,
struct sk_buff *skb)
{
- IRDA_DEBUG(4, "%s()\n", __func__);
-
/* Copy QoS settings for this session */
self->qos = qos;
@@ -317,8 +307,6 @@ void irlmp_link_connect_indication(struct lap_cb *self, __u32 saddr,
void irlmp_link_connect_confirm(struct lap_cb *self, struct qos_info *qos,
struct sk_buff *skb)
{
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
IRDA_ASSERT(qos != NULL, return;);
@@ -383,8 +371,6 @@ void irlmp_link_discovery_indication(struct lap_cb *self,
*/
void irlmp_link_discovery_confirm(struct lap_cb *self, hashbin_t *log)
{
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;);
diff --git a/net/irda/irmod.c b/net/irda/irmod.c
index 303a68d92731..c5e35b85c477 100644
--- a/net/irda/irmod.c
+++ b/net/irda/irmod.c
@@ -42,16 +42,6 @@
#include <net/irda/irttp.h> /* irttp_init */
#include <net/irda/irda_device.h> /* irda_device_init */
-/*
- * Module parameters
- */
-#ifdef CONFIG_IRDA_DEBUG
-unsigned int irda_debug = IRDA_DEBUG_LEVEL;
-module_param_named(debug, irda_debug, uint, 0);
-MODULE_PARM_DESC(debug, "IRDA debugging level");
-EXPORT_SYMBOL(irda_debug);
-#endif
-
/* Packet type handler.
* Tell the kernel how IrDA packets should be handled.
*/
@@ -90,8 +80,6 @@ static int __init irda_init(void)
{
int ret = 0;
- IRDA_DEBUG(0, "%s()\n", __func__);
-
/* Lower layer of the stack */
irlmp_init();
irlap_init();
diff --git a/net/irda/irnetlink.c b/net/irda/irnetlink.c
index a37b81fe0479..e15c40e86660 100644
--- a/net/irda/irnetlink.c
+++ b/net/irda/irnetlink.c
@@ -41,7 +41,7 @@ static struct net_device * ifname_to_netdev(struct net *net, struct genl_info *i
ifname = nla_data(info->attrs[IRDA_NL_ATTR_IFNAME]);
- IRDA_DEBUG(5, "%s(): Looking for %s\n", __func__, ifname);
+ pr_debug("%s(): Looking for %s\n", __func__, ifname);
return dev_get_by_name(net, ifname);
}
@@ -57,7 +57,7 @@ static int irda_nl_set_mode(struct sk_buff *skb, struct genl_info *info)
mode = nla_get_u32(info->attrs[IRDA_NL_ATTR_MODE]);
- IRDA_DEBUG(5, "%s(): Switching to mode: %d\n", __func__, mode);
+ pr_debug("%s(): Switching to mode: %d\n", __func__, mode);
dev = ifname_to_netdev(&init_net, info);
if (!dev)
diff --git a/net/irda/irqueue.c b/net/irda/irqueue.c
index 7152624ed5f1..acbe61c7e683 100644
--- a/net/irda/irqueue.c
+++ b/net/irda/irqueue.c
@@ -233,8 +233,6 @@ static __u32 hash( const char* name)
static void enqueue_first(irda_queue_t **queue, irda_queue_t* element)
{
- IRDA_DEBUG( 4, "%s()\n", __func__);
-
/*
* Check if queue is empty.
*/
@@ -267,7 +265,7 @@ static irda_queue_t *dequeue_first(irda_queue_t **queue)
{
irda_queue_t *ret;
- IRDA_DEBUG( 4, "dequeue_first()\n");
+ pr_debug("dequeue_first()\n");
/*
* Set return value
@@ -308,7 +306,7 @@ static irda_queue_t *dequeue_general(irda_queue_t **queue, irda_queue_t* element
{
irda_queue_t *ret;
- IRDA_DEBUG( 4, "dequeue_general()\n");
+ pr_debug("dequeue_general()\n");
/*
* Set return value
@@ -452,8 +450,6 @@ void hashbin_insert(hashbin_t* hashbin, irda_queue_t* entry, long hashv,
unsigned long flags = 0;
int bin;
- IRDA_DEBUG( 4, "%s()\n", __func__);
-
IRDA_ASSERT( hashbin != NULL, return;);
IRDA_ASSERT( hashbin->magic == HB_MAGIC, return;);
@@ -565,8 +561,6 @@ void* hashbin_remove( hashbin_t* hashbin, long hashv, const char* name)
unsigned long flags = 0;
irda_queue_t* entry;
- IRDA_DEBUG( 4, "%s()\n", __func__);
-
IRDA_ASSERT( hashbin != NULL, return NULL;);
IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
@@ -658,8 +652,6 @@ void* hashbin_remove_this( hashbin_t* hashbin, irda_queue_t* entry)
int bin;
long hashv;
- IRDA_DEBUG( 4, "%s()\n", __func__);
-
IRDA_ASSERT( hashbin != NULL, return NULL;);
IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
IRDA_ASSERT( entry != NULL, return NULL;);
@@ -719,7 +711,7 @@ void* hashbin_find( hashbin_t* hashbin, long hashv, const char* name )
int bin;
irda_queue_t* entry;
- IRDA_DEBUG( 4, "hashbin_find()\n");
+ pr_debug("hashbin_find()\n");
IRDA_ASSERT( hashbin != NULL, return NULL;);
IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;);
diff --git a/net/irda/irsysctl.c b/net/irda/irsysctl.c
index d6a59651767a..873da5e7d428 100644
--- a/net/irda/irsysctl.c
+++ b/net/irda/irsysctl.c
@@ -126,15 +126,6 @@ static struct ctl_table irda_table[] = {
.mode = 0644,
.proc_handler = do_devname,
},
-#ifdef CONFIG_IRDA_DEBUG
- {
- .procname = "debug",
- .data = &irda_debug,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec
- },
-#endif
#ifdef CONFIG_IRDA_FAST_RR
{
.procname = "fast_poll_increase",
diff --git a/net/irda/irttp.c b/net/irda/irttp.c
index 85372cfa7b9f..b6ab41d5b3a3 100644
--- a/net/irda/irttp.c
+++ b/net/irda/irttp.c
@@ -71,11 +71,13 @@ static void irttp_status_indication(void *instance,
LINK_STATUS link, LOCK_STATUS lock);
/* Information for parsing parameters in IrTTP */
-static pi_minor_info_t pi_minor_call_table[] = {
+static const pi_minor_info_t pi_minor_call_table[] = {
{ NULL, 0 }, /* 0x00 */
{ irttp_param_max_sdu_size, PV_INTEGER | PV_BIG_ENDIAN } /* 0x01 */
};
-static pi_major_info_t pi_major_call_table[] = { { pi_minor_call_table, 2 } };
+static const pi_major_info_t pi_major_call_table[] = {
+ { pi_minor_call_table, 2 }
+};
static pi_param_info_t param_info = { pi_major_call_table, 1, 0x0f, 4 };
/************************ GLOBAL PROCEDURES ************************/
@@ -96,8 +98,8 @@ int __init irttp_init(void)
irttp->tsaps = hashbin_new(HB_LOCK);
if (!irttp->tsaps) {
- IRDA_ERROR("%s: can't allocate IrTTP hashbin!\n",
- __func__);
+ net_err_ratelimited("%s: can't allocate IrTTP hashbin!\n",
+ __func__);
kfree(irttp);
return -ENOMEM;
}
@@ -166,7 +168,7 @@ static void irttp_todo_expired(unsigned long data)
if (!self || self->magic != TTP_TSAP_MAGIC)
return;
- IRDA_DEBUG(4, "%s(instance=%p)\n", __func__, self);
+ pr_debug("%s(instance=%p)\n", __func__, self);
/* Try to make some progress, especially on Tx side - Jean II */
irttp_run_rx_queue(self);
@@ -207,8 +209,6 @@ static void irttp_flush_queues(struct tsap_cb *self)
{
struct sk_buff *skb;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
@@ -240,8 +240,8 @@ static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self)
IRDA_ASSERT(self != NULL, return NULL;);
IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return NULL;);
- IRDA_DEBUG(2, "%s(), self->rx_sdu_size=%d\n", __func__,
- self->rx_sdu_size);
+ pr_debug("%s(), self->rx_sdu_size=%d\n", __func__,
+ self->rx_sdu_size);
skb = dev_alloc_skb(TTP_HEADER + self->rx_sdu_size);
if (!skb)
@@ -264,9 +264,8 @@ static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self)
dev_kfree_skb(frag);
}
- IRDA_DEBUG(2,
- "%s(), frame len=%d, rx_sdu_size=%d, rx_max_sdu_size=%d\n",
- __func__, n, self->rx_sdu_size, self->rx_max_sdu_size);
+ pr_debug("%s(), frame len=%d, rx_sdu_size=%d, rx_max_sdu_size=%d\n",
+ __func__, n, self->rx_sdu_size, self->rx_max_sdu_size);
/* Note : irttp_run_rx_queue() calculate self->rx_sdu_size
* by summing the size of all fragments, so we should always
* have n == self->rx_sdu_size, except in cases where we
@@ -295,8 +294,6 @@ static inline void irttp_fragment_skb(struct tsap_cb *self,
struct sk_buff *frag;
__u8 *frame;
- IRDA_DEBUG(2, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
IRDA_ASSERT(skb != NULL, return;);
@@ -305,7 +302,7 @@ static inline void irttp_fragment_skb(struct tsap_cb *self,
* Split frame into a number of segments
*/
while (skb->len > self->max_seg_size) {
- IRDA_DEBUG(2, "%s(), fragmenting ...\n", __func__);
+ pr_debug("%s(), fragmenting ...\n", __func__);
/* Make new segment */
frag = alloc_skb(self->max_seg_size+self->max_header_size,
@@ -330,7 +327,7 @@ static inline void irttp_fragment_skb(struct tsap_cb *self,
skb_queue_tail(&self->tx_queue, frag);
}
/* Queue what is left of the original skb */
- IRDA_DEBUG(2, "%s(), queuing last segment\n", __func__);
+ pr_debug("%s(), queuing last segment\n", __func__);
frame = skb_push(skb, TTP_HEADER);
frame[0] = 0x00; /* Clear more bit */
@@ -361,7 +358,7 @@ static int irttp_param_max_sdu_size(void *instance, irda_param_t *param,
else
self->tx_max_sdu_size = param->pv.i;
- IRDA_DEBUG(1, "%s(), MaxSduSize=%d\n", __func__, param->pv.i);
+ pr_debug("%s(), MaxSduSize=%d\n", __func__, param->pv.i);
return 0;
}
@@ -402,15 +399,13 @@ struct tsap_cb *irttp_open_tsap(__u8 stsap_sel, int credit, notify_t *notify)
* JeanII */
if ((stsap_sel != LSAP_ANY) &&
((stsap_sel < 0x01) || (stsap_sel >= 0x70))) {
- IRDA_DEBUG(0, "%s(), invalid tsap!\n", __func__);
+ pr_debug("%s(), invalid tsap!\n", __func__);
return NULL;
}
self = kzalloc(sizeof(struct tsap_cb), GFP_ATOMIC);
- if (self == NULL) {
- IRDA_DEBUG(0, "%s(), unable to kmalloc!\n", __func__);
+ if (self == NULL)
return NULL;
- }
/* Initialize internal objects */
irttp_init_tsap(self);
@@ -440,7 +435,7 @@ struct tsap_cb *irttp_open_tsap(__u8 stsap_sel, int credit, notify_t *notify)
*/
lsap = irlmp_open_lsap(stsap_sel, &ttp_notify, 0);
if (lsap == NULL) {
- IRDA_DEBUG(0, "%s: unable to allocate LSAP!!\n", __func__);
+ pr_debug("%s: unable to allocate LSAP!!\n", __func__);
__irttp_close_tsap(self);
return NULL;
}
@@ -451,7 +446,7 @@ struct tsap_cb *irttp_open_tsap(__u8 stsap_sel, int credit, notify_t *notify)
* the stsap_sel we have might not be valid anymore
*/
self->stsap_sel = lsap->slsap_sel;
- IRDA_DEBUG(4, "%s(), stsap_sel=%02x\n", __func__, self->stsap_sel);
+ pr_debug("%s(), stsap_sel=%02x\n", __func__, self->stsap_sel);
self->notify = *notify;
self->lsap = lsap;
@@ -509,8 +504,6 @@ int irttp_close_tsap(struct tsap_cb *self)
{
struct tsap_cb *tsap;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
@@ -518,8 +511,8 @@ int irttp_close_tsap(struct tsap_cb *self)
if (self->connected) {
/* Check if disconnect is not pending */
if (!test_bit(0, &self->disconnect_pend)) {
- IRDA_WARNING("%s: TSAP still connected!\n",
- __func__);
+ net_warn_ratelimited("%s: TSAP still connected!\n",
+ __func__);
irttp_disconnect_request(self, NULL, P_NORMAL);
}
self->close_pend = TRUE;
@@ -558,8 +551,6 @@ int irttp_udata_request(struct tsap_cb *self, struct sk_buff *skb)
IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
IRDA_ASSERT(skb != NULL, return -1;);
- IRDA_DEBUG(4, "%s()\n", __func__);
-
/* Take shortcut on zero byte packets */
if (skb->len == 0) {
ret = 0;
@@ -568,13 +559,14 @@ int irttp_udata_request(struct tsap_cb *self, struct sk_buff *skb)
/* Check that nothing bad happens */
if (!self->connected) {
- IRDA_WARNING("%s(), Not connected\n", __func__);
+ net_warn_ratelimited("%s(), Not connected\n", __func__);
ret = -ENOTCONN;
goto err;
}
if (skb->len > self->max_seg_size) {
- IRDA_ERROR("%s(), UData is too large for IrLAP!\n", __func__);
+ net_err_ratelimited("%s(), UData is too large for IrLAP!\n",
+ __func__);
ret = -EMSGSIZE;
goto err;
}
@@ -606,8 +598,8 @@ int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb)
IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
IRDA_ASSERT(skb != NULL, return -1;);
- IRDA_DEBUG(2, "%s() : queue len = %d\n", __func__,
- skb_queue_len(&self->tx_queue));
+ pr_debug("%s() : queue len = %d\n", __func__,
+ skb_queue_len(&self->tx_queue));
/* Take shortcut on zero byte packets */
if (skb->len == 0) {
@@ -617,7 +609,7 @@ int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb)
/* Check that nothing bad happens */
if (!self->connected) {
- IRDA_WARNING("%s: Not connected\n", __func__);
+ net_warn_ratelimited("%s: Not connected\n", __func__);
ret = -ENOTCONN;
goto err;
}
@@ -627,8 +619,8 @@ int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb)
* inside an IrLAP frame
*/
if ((self->tx_max_sdu_size == 0) && (skb->len > self->max_seg_size)) {
- IRDA_ERROR("%s: SAR disabled, and data is too large for IrLAP!\n",
- __func__);
+ net_err_ratelimited("%s: SAR disabled, and data is too large for IrLAP!\n",
+ __func__);
ret = -EMSGSIZE;
goto err;
}
@@ -640,8 +632,8 @@ int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb)
if ((self->tx_max_sdu_size != 0) &&
(self->tx_max_sdu_size != TTP_SAR_UNBOUND) &&
(skb->len > self->tx_max_sdu_size)) {
- IRDA_ERROR("%s: SAR enabled, but data is larger than TxMaxSduSize!\n",
- __func__);
+ net_err_ratelimited("%s: SAR enabled, but data is larger than TxMaxSduSize!\n",
+ __func__);
ret = -EMSGSIZE;
goto err;
}
@@ -719,9 +711,9 @@ static void irttp_run_tx_queue(struct tsap_cb *self)
unsigned long flags;
int n;
- IRDA_DEBUG(2, "%s() : send_credit = %d, queue_len = %d\n",
- __func__,
- self->send_credit, skb_queue_len(&self->tx_queue));
+ pr_debug("%s() : send_credit = %d, queue_len = %d\n",
+ __func__,
+ self->send_credit, skb_queue_len(&self->tx_queue));
/* Get exclusive access to the tx queue, otherwise don't touch it */
if (irda_lock(&self->tx_queue_lock) == FALSE)
@@ -826,9 +818,9 @@ static inline void irttp_give_credit(struct tsap_cb *self)
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
- IRDA_DEBUG(4, "%s() send=%d,avail=%d,remote=%d\n",
- __func__,
- self->send_credit, self->avail_credit, self->remote_credit);
+ pr_debug("%s() send=%d,avail=%d,remote=%d\n",
+ __func__,
+ self->send_credit, self->avail_credit, self->remote_credit);
/* Give credit to peer */
tx_skb = alloc_skb(TTP_MAX_HEADER, GFP_ATOMIC);
@@ -876,8 +868,6 @@ static int irttp_udata_indication(void *instance, void *sap,
struct tsap_cb *self;
int err;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
self = instance;
IRDA_ASSERT(self != NULL, return -1;);
@@ -993,8 +983,6 @@ static void irttp_status_indication(void *instance,
{
struct tsap_cb *self;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
self = instance;
IRDA_ASSERT(self != NULL, return;);
@@ -1011,7 +999,7 @@ static void irttp_status_indication(void *instance,
self->notify.status_indication(self->notify.instance,
link, lock);
else
- IRDA_DEBUG(2, "%s(), no handler\n", __func__);
+ pr_debug("%s(), no handler\n", __func__);
}
/*
@@ -1029,7 +1017,7 @@ static void irttp_flow_indication(void *instance, void *sap, LOCAL_FLOW flow)
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
- IRDA_DEBUG(4, "%s(instance=%p)\n", __func__, self);
+ pr_debug("%s(instance=%p)\n", __func__, self);
/* We are "polled" directly from LAP, and the LAP want to fill
* its Tx window. We want to do our best to send it data, so that
@@ -1067,18 +1055,16 @@ static void irttp_flow_indication(void *instance, void *sap, LOCAL_FLOW flow)
*/
void irttp_flow_request(struct tsap_cb *self, LOCAL_FLOW flow)
{
- IRDA_DEBUG(1, "%s()\n", __func__);
-
IRDA_ASSERT(self != NULL, return;);
IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;);
switch (flow) {
case FLOW_STOP:
- IRDA_DEBUG(1, "%s(), flow stop\n", __func__);
+ pr_debug("%s(), flow stop\n", __func__);
self->rx_sdu_busy = TRUE;
break;
case FLOW_START:
- IRDA_DEBUG(1, "%s(), flow start\n", __func__);
+ pr_debug("%s(), flow start\n", __func__);
self->rx_sdu_busy = FALSE;
/* Client say he can accept more data, try to free our
@@ -1087,7 +1073,7 @@ void irttp_flow_request(struct tsap_cb *self, LOCAL_FLOW flow)
break;
default:
- IRDA_DEBUG(1, "%s(), Unknown flow command!\n", __func__);
+ pr_debug("%s(), Unknown flow command!\n", __func__);
}
}
EXPORT_SYMBOL(irttp_flow_request);
@@ -1107,7 +1093,7 @@ int irttp_connect_request(struct tsap_cb *self, __u8 dtsap_sel,
__u8 *frame;
__u8 n;
- IRDA_DEBUG(4, "%s(), max_sdu_size=%d\n", __func__, max_sdu_size);
+ pr_debug("%s(), max_sdu_size=%d\n", __func__, max_sdu_size);
IRDA_ASSERT(self != NULL, return -EBADR;);
IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -EBADR;);
@@ -1205,8 +1191,6 @@ static void irttp_connect_confirm(void *instance, void *sap,
__u8 plen;
__u8 n;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
self = instance;
IRDA_ASSERT(self != NULL, return;);
@@ -1221,15 +1205,15 @@ static void irttp_connect_confirm(void *instance, void *sap,
* negotiated QoS for the link.
*/
if (qos) {
- IRDA_DEBUG(4, "IrTTP, Negotiated BAUD_RATE: %02x\n",
- qos->baud_rate.bits);
- IRDA_DEBUG(4, "IrTTP, Negotiated BAUD_RATE: %d bps.\n",
- qos->baud_rate.value);
+ pr_debug("IrTTP, Negotiated BAUD_RATE: %02x\n",
+ qos->baud_rate.bits);
+ pr_debug("IrTTP, Negotiated BAUD_RATE: %d bps.\n",
+ qos->baud_rate.value);
}
n = skb->data[0] & 0x7f;
- IRDA_DEBUG(4, "%s(), Initial send_credit=%d\n", __func__, n);
+ pr_debug("%s(), Initial send_credit=%d\n", __func__, n);
self->send_credit = n;
self->tx_max_sdu_size = 0;
@@ -1249,8 +1233,8 @@ static void irttp_connect_confirm(void *instance, void *sap,
/* Any errors in the parameter list? */
if (ret < 0) {
- IRDA_WARNING("%s: error extracting parameters\n",
- __func__);
+ net_warn_ratelimited("%s: error extracting parameters\n",
+ __func__);
dev_kfree_skb(skb);
/* Do not accept this connection attempt */
@@ -1260,11 +1244,11 @@ static void irttp_connect_confirm(void *instance, void *sap,
skb_pull(skb, IRDA_MIN(skb->len, plen+1));
}
- IRDA_DEBUG(4, "%s() send=%d,avail=%d,remote=%d\n", __func__,
- self->send_credit, self->avail_credit, self->remote_credit);
+ pr_debug("%s() send=%d,avail=%d,remote=%d\n", __func__,
+ self->send_credit, self->avail_credit, self->remote_credit);
- IRDA_DEBUG(2, "%s(), MaxSduSize=%d\n", __func__,
- self->tx_max_sdu_size);
+ pr_debug("%s(), MaxSduSize=%d\n", __func__,
+ self->tx_max_sdu_size);
if (self->notify.connect_confirm) {
self->notify.connect_confirm(self->notify.instance, self, qos,
@@ -1302,7 +1286,7 @@ static void irttp_connect_indication(void *instance, void *sap,
self->max_seg_size = max_seg_size - TTP_HEADER;
self->max_header_size = max_header_size+TTP_HEADER;
- IRDA_DEBUG(4, "%s(), TSAP sel=%02x\n", __func__, self->stsap_sel);
+ pr_debug("%s(), TSAP sel=%02x\n", __func__, self->stsap_sel);
/* Need to update dtsap_sel if its equal to LSAP_ANY */
self->dtsap_sel = lsap->dlsap_sel;
@@ -1326,8 +1310,8 @@ static void irttp_connect_indication(void *instance, void *sap,
/* Any errors in the parameter list? */
if (ret < 0) {
- IRDA_WARNING("%s: error extracting parameters\n",
- __func__);
+ net_warn_ratelimited("%s: error extracting parameters\n",
+ __func__);
dev_kfree_skb(skb);
/* Do not accept this connection attempt */
@@ -1364,8 +1348,8 @@ int irttp_connect_response(struct tsap_cb *self, __u32 max_sdu_size,
IRDA_ASSERT(self != NULL, return -1;);
IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;);
- IRDA_DEBUG(4, "%s(), Source TSAP selector=%02x\n", __func__,
- self->stsap_sel);
+ pr_debug("%s(), Source TSAP selector=%02x\n", __func__,
+ self->stsap_sel);
/* Any userdata supplied? */
if (userdata == NULL) {
@@ -1446,14 +1430,12 @@ struct tsap_cb *irttp_dup(struct tsap_cb *orig, void *instance)
struct tsap_cb *new;
unsigned long flags;
- IRDA_DEBUG(1, "%s()\n", __func__);
-
/* Protect our access to the old tsap instance */
spin_lock_irqsave(&irttp->tsaps->hb_spinlock, flags);
/* Find the old instance */
if (!hashbin_find(irttp->tsaps, (long) orig, NULL)) {
- IRDA_DEBUG(0, "%s(), unable to find TSAP\n", __func__);
+ pr_debug("%s(), unable to find TSAP\n", __func__);
spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags);
return NULL;
}
@@ -1461,7 +1443,7 @@ struct tsap_cb *irttp_dup(struct tsap_cb *orig, void *instance)
/* Allocate a new instance */
new = kmemdup(orig, sizeof(struct tsap_cb), GFP_ATOMIC);
if (!new) {
- IRDA_DEBUG(0, "%s(), unable to kmalloc\n", __func__);
+ pr_debug("%s(), unable to kmalloc\n", __func__);
spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags);
return NULL;
}
@@ -1473,7 +1455,7 @@ struct tsap_cb *irttp_dup(struct tsap_cb *orig, void *instance)
/* Try to dup the LSAP (may fail if we were too slow) */
new->lsap = irlmp_dup(orig->lsap, new);
if (!new->lsap) {
- IRDA_DEBUG(0, "%s(), dup failed!\n", __func__);
+ pr_debug("%s(), dup failed!\n", __func__);
kfree(new);
return NULL;
}
@@ -1508,7 +1490,7 @@ int irttp_disconnect_request(struct tsap_cb *self, struct sk_buff *userdata,
/* Already disconnected? */
if (!self->connected) {
- IRDA_DEBUG(4, "%s(), already disconnected!\n", __func__);
+ pr_debug("%s(), already disconnected!\n", __func__);
if (userdata)
dev_kfree_skb(userdata);
return -1;
@@ -1520,8 +1502,8 @@ int irttp_disconnect_request(struct tsap_cb *self, struct sk_buff *userdata,
* for following a disconnect_indication() (i.e. net_bh).
* Jean II */
if (test_and_set_bit(0, &self->disconnect_pend)) {
- IRDA_DEBUG(0, "%s(), disconnect already pending\n",
- __func__);
+ pr_debug("%s(), disconnect already pending\n",
+ __func__);
if (userdata)
dev_kfree_skb(userdata);
@@ -1540,7 +1522,7 @@ int irttp_disconnect_request(struct tsap_cb *self, struct sk_buff *userdata,
* disconnecting right now since the data will
* not have any usable connection to be sent on
*/
- IRDA_DEBUG(1, "%s(): High priority!!()\n", __func__);
+ pr_debug("%s(): High priority!!()\n", __func__);
irttp_flush_queues(self);
} else if (priority == P_NORMAL) {
/*
@@ -1561,7 +1543,7 @@ int irttp_disconnect_request(struct tsap_cb *self, struct sk_buff *userdata,
* be sent at the LMP level (so even if the peer has its Tx queue
* full of data). - Jean II */
- IRDA_DEBUG(1, "%s(), Disconnecting ...\n", __func__);
+ pr_debug("%s(), Disconnecting ...\n", __func__);
self->connected = FALSE;
if (!userdata) {
@@ -1597,8 +1579,6 @@ static void irttp_disconnect_indication(void *instance, void *sap,
{
struct tsap_cb *self;
- IRDA_DEBUG(4, "%s()\n", __func__);
-
self = instance;
IRDA_ASSERT(self != NULL, return;);
@@ -1657,7 +1637,7 @@ static void irttp_do_data_indication(struct tsap_cb *self, struct sk_buff *skb)
* give an error back
*/
if (err) {
- IRDA_DEBUG(0, "%s() requeueing skb!\n", __func__);
+ pr_debug("%s() requeueing skb!\n", __func__);
/* Make sure we take a break */
self->rx_sdu_busy = TRUE;
@@ -1682,8 +1662,8 @@ static void irttp_run_rx_queue(struct tsap_cb *self)
struct sk_buff *skb;
int more = 0;
- IRDA_DEBUG(2, "%s() send=%d,avail=%d,remote=%d\n", __func__,
- self->send_credit, self->avail_credit, self->remote_credit);
+ pr_debug("%s() send=%d,avail=%d,remote=%d\n", __func__,
+ self->send_credit, self->avail_credit, self->remote_credit);
/* Get exclusive access to the rx queue, otherwise don't touch it */
if (irda_lock(&self->rx_queue_lock) == FALSE)
@@ -1722,8 +1702,8 @@ static void irttp_run_rx_queue(struct tsap_cb *self)
* limits of the maximum size of the rx_sdu
*/
if (self->rx_sdu_size <= self->rx_max_sdu_size) {
- IRDA_DEBUG(4, "%s(), queueing frag\n",
- __func__);
+ pr_debug("%s(), queueing frag\n",
+ __func__);
skb_queue_tail(&self->rx_fragments, skb);
} else {
/* Free the part of the SDU that is too big */
@@ -1752,7 +1732,7 @@ static void irttp_run_rx_queue(struct tsap_cb *self)
/* Now we can deliver the reassembled skb */
irttp_do_data_indication(self, skb);
} else {
- IRDA_DEBUG(1, "%s(), Truncated frame\n", __func__);
+ pr_debug("%s(), Truncated frame\n", __func__);
/* Free the part of the SDU that is too big */
dev_kfree_skb(skb);
diff --git a/net/irda/parameters.c b/net/irda/parameters.c
index 6d0869716bf6..16ce32ffe004 100644
--- a/net/irda/parameters.c
+++ b/net/irda/parameters.c
@@ -52,7 +52,7 @@ static int irda_insert_no_value(void *self, __u8 *buf, int len, __u8 pi,
static int irda_param_unpack(__u8 *buf, char *fmt, ...);
/* Parameter value call table. Must match PV_TYPE */
-static PV_HANDLER pv_extract_table[] = {
+static const PV_HANDLER pv_extract_table[] = {
irda_extract_integer, /* Handler for any length integers */
irda_extract_integer, /* Handler for 8 bits integers */
irda_extract_integer, /* Handler for 16 bits integers */
@@ -62,7 +62,7 @@ static PV_HANDLER pv_extract_table[] = {
irda_extract_no_value /* Handler for no value parameters */
};
-static PV_HANDLER pv_insert_table[] = {
+static const PV_HANDLER pv_insert_table[] = {
irda_insert_integer, /* Handler for any length integers */
irda_insert_integer, /* Handler for 8 bits integers */
irda_insert_integer, /* Handler for 16 bits integers */
@@ -146,24 +146,24 @@ static int irda_insert_integer(void *self, __u8 *buf, int len, __u8 pi,
*/
if (p.pl == 0) {
if (p.pv.i < 0xff) {
- IRDA_DEBUG(2, "%s(), using 1 byte\n", __func__);
+ pr_debug("%s(), using 1 byte\n", __func__);
p.pl = 1;
} else if (p.pv.i < 0xffff) {
- IRDA_DEBUG(2, "%s(), using 2 bytes\n", __func__);
+ pr_debug("%s(), using 2 bytes\n", __func__);
p.pl = 2;
} else {
- IRDA_DEBUG(2, "%s(), using 4 bytes\n", __func__);
+ pr_debug("%s(), using 4 bytes\n", __func__);
p.pl = 4; /* Default length */
}
}
/* Check if buffer is long enough for insertion */
if (len < (2+p.pl)) {
- IRDA_WARNING("%s: buffer too short for insertion!\n",
- __func__);
+ net_warn_ratelimited("%s: buffer too short for insertion!\n",
+ __func__);
return -1;
}
- IRDA_DEBUG(2, "%s(), pi=%#x, pl=%d, pi=%d\n", __func__,
- p.pi, p.pl, p.pv.i);
+ pr_debug("%s(), pi=%#x, pl=%d, pi=%d\n", __func__,
+ p.pi, p.pl, p.pv.i);
switch (p.pl) {
case 1:
n += irda_param_pack(buf, "bbb", p.pi, p.pl, (__u8) p.pv.i);
@@ -184,8 +184,8 @@ static int irda_insert_integer(void *self, __u8 *buf, int len, __u8 pi,
break;
default:
- IRDA_WARNING("%s: length %d not supported\n",
- __func__, p.pl);
+ net_warn_ratelimited("%s: length %d not supported\n",
+ __func__, p.pl);
/* Skip parameter */
return -1;
}
@@ -214,9 +214,8 @@ static int irda_extract_integer(void *self, __u8 *buf, int len, __u8 pi,
/* Check if buffer is long enough for parsing */
if (len < (2+p.pl)) {
- IRDA_WARNING("%s: buffer too short for parsing! "
- "Need %d bytes, but len is only %d\n",
- __func__, p.pl, len);
+ net_warn_ratelimited("%s: buffer too short for parsing! Need %d bytes, but len is only %d\n",
+ __func__, p.pl, len);
return -1;
}
@@ -226,9 +225,8 @@ static int irda_extract_integer(void *self, __u8 *buf, int len, __u8 pi,
* PV_INTEGER means that the handler is flexible.
*/
if (((type & PV_MASK) != PV_INTEGER) && ((type & PV_MASK) != p.pl)) {
- IRDA_ERROR("%s: invalid parameter length! "
- "Expected %d bytes, but value had %d bytes!\n",
- __func__, type & PV_MASK, p.pl);
+ net_err_ratelimited("%s: invalid parameter length! Expected %d bytes, but value had %d bytes!\n",
+ __func__, type & PV_MASK, p.pl);
/* Most parameters are bit/byte fields or little endian,
* so it's ok to only extract a subset of it (the subset
@@ -265,15 +263,15 @@ static int irda_extract_integer(void *self, __u8 *buf, int len, __u8 pi,
le32_to_cpus(&p.pv.i);
break;
default:
- IRDA_WARNING("%s: length %d not supported\n",
- __func__, p.pl);
+ net_warn_ratelimited("%s: length %d not supported\n",
+ __func__, p.pl);
/* Skip parameter */
return p.pl+2;
}
- IRDA_DEBUG(2, "%s(), pi=%#x, pl=%d, pi=%d\n", __func__,
- p.pi, p.pl, p.pv.i);
+ pr_debug("%s(), pi=%#x, pl=%d, pi=%d\n", __func__,
+ p.pi, p.pl, p.pv.i);
/* Call handler for this parameter */
err = (*func)(self, &p, PV_PUT);
if (err < 0)
@@ -292,21 +290,18 @@ static int irda_extract_string(void *self, __u8 *buf, int len, __u8 pi,
irda_param_t p;
int err;
- IRDA_DEBUG(2, "%s()\n", __func__);
-
p.pi = pi; /* In case handler needs to know */
p.pl = buf[1]; /* Extract length of value */
if (p.pl > 32)
p.pl = 32;
- IRDA_DEBUG(2, "%s(), pi=%#x, pl=%d\n", __func__,
- p.pi, p.pl);
+ pr_debug("%s(), pi=%#x, pl=%d\n", __func__,
+ p.pi, p.pl);
/* Check if buffer is long enough for parsing */
if (len < (2+p.pl)) {
- IRDA_WARNING("%s: buffer too short for parsing! "
- "Need %d bytes, but len is only %d\n",
- __func__, p.pl, len);
+ net_warn_ratelimited("%s: buffer too short for parsing! Need %d bytes, but len is only %d\n",
+ __func__, p.pl, len);
return -1;
}
@@ -314,8 +309,8 @@ static int irda_extract_string(void *self, __u8 *buf, int len, __u8 pi,
* checked that the buffer is long enough */
strncpy(str, buf+2, p.pl);
- IRDA_DEBUG(2, "%s(), str=0x%02x 0x%02x\n", __func__,
- (__u8) str[0], (__u8) str[1]);
+ pr_debug("%s(), str=0x%02x 0x%02x\n",
+ __func__, (__u8)str[0], (__u8)str[1]);
/* Null terminate string */
str[p.pl] = '\0';
@@ -343,13 +338,12 @@ static int irda_extract_octseq(void *self, __u8 *buf, int len, __u8 pi,
/* Check if buffer is long enough for parsing */
if (len < (2+p.pl)) {
- IRDA_WARNING("%s: buffer too short for parsing! "
- "Need %d bytes, but len is only %d\n",
- __func__, p.pl, len);
+ net_warn_ratelimited("%s: buffer too short for parsing! Need %d bytes, but len is only %d\n",
+ __func__, p.pl, len);
return -1;
}
- IRDA_DEBUG(0, "%s(), not impl\n", __func__);
+ pr_debug("%s(), not impl\n", __func__);
return p.pl+2; /* Extracted pl+2 bytes */
}
@@ -455,7 +449,7 @@ static int irda_param_unpack(__u8 *buf, char *fmt, ...)
int irda_param_insert(void *self, __u8 pi, __u8 *buf, int len,
pi_param_info_t *info)
{
- pi_minor_info_t *pi_minor_info;
+ const pi_minor_info_t *pi_minor_info;
__u8 pi_minor;
__u8 pi_major;
int type;
@@ -472,8 +466,8 @@ int irda_param_insert(void *self, __u8 pi, __u8 *buf, int len,
if ((pi_major > info->len-1) ||
(pi_minor > info->tables[pi_major].len-1))
{
- IRDA_DEBUG(0, "%s(), no handler for parameter=0x%02x\n",
- __func__, pi);
+ pr_debug("%s(), no handler for parameter=0x%02x\n",
+ __func__, pi);
/* Skip this parameter */
return -1;
@@ -487,7 +481,8 @@ int irda_param_insert(void *self, __u8 pi, __u8 *buf, int len,
/* Check if handler has been implemented */
if (!pi_minor_info->func) {
- IRDA_MESSAGE("%s: no handler for pi=%#x\n", __func__, pi);
+ net_info_ratelimited("%s: no handler for pi=%#x\n",
+ __func__, pi);
/* Skip this parameter */
return -1;
}
@@ -509,7 +504,7 @@ EXPORT_SYMBOL(irda_param_insert);
static int irda_param_extract(void *self, __u8 *buf, int len,
pi_param_info_t *info)
{
- pi_minor_info_t *pi_minor_info;
+ const pi_minor_info_t *pi_minor_info;
__u8 pi_minor;
__u8 pi_major;
int type;
@@ -526,8 +521,8 @@ static int irda_param_extract(void *self, __u8 *buf, int len,
if ((pi_major > info->len-1) ||
(pi_minor > info->tables[pi_major].len-1))
{
- IRDA_DEBUG(0, "%s(), no handler for parameter=0x%02x\n",
- __func__, buf[0]);
+ pr_debug("%s(), no handler for parameter=0x%02x\n",
+ __func__, buf[0]);
/* Skip this parameter */
return 2 + buf[n + 1]; /* Continue */
@@ -539,13 +534,13 @@ static int irda_param_extract(void *self, __u8 *buf, int len,
/* Find expected data type for this parameter identifier (pi)*/
type = pi_minor_info->type;
- IRDA_DEBUG(3, "%s(), pi=[%d,%d], type=%d\n", __func__,
- pi_major, pi_minor, type);
+ pr_debug("%s(), pi=[%d,%d], type=%d\n", __func__,
+ pi_major, pi_minor, type);
/* Check if handler has been implemented */
if (!pi_minor_info->func) {
- IRDA_MESSAGE("%s: no handler for pi=%#x\n",
- __func__, buf[n]);
+ net_info_ratelimited("%s: no handler for pi=%#x\n",
+ __func__, buf[n]);
/* Skip this parameter */
return 2 + buf[n + 1]; /* Continue */
}
diff --git a/net/irda/qos.c b/net/irda/qos.c
index 11a7cc0cbc28..25ba8509ad3e 100644
--- a/net/irda/qos.c
+++ b/net/irda/qos.c
@@ -122,7 +122,7 @@ static __u32 max_line_capacities[10][4] = {
{ 800000, 400000, 160000, 80000 }, /* 16000000 bps */
};
-static pi_minor_info_t pi_minor_call_table_type_0[] = {
+static const pi_minor_info_t pi_minor_call_table_type_0[] = {
{ NULL, 0 },
/* 01 */{ irlap_param_baud_rate, PV_INTEGER | PV_LITTLE_ENDIAN },
{ NULL, 0 },
@@ -134,7 +134,7 @@ static pi_minor_info_t pi_minor_call_table_type_0[] = {
/* 08 */{ irlap_param_link_disconnect, PV_INT_8_BITS }
};
-static pi_minor_info_t pi_minor_call_table_type_1[] = {
+static const pi_minor_info_t pi_minor_call_table_type_1[] = {
{ NULL, 0 },
{ NULL, 0 },
/* 82 */{ irlap_param_max_turn_time, PV_INT_8_BITS },
@@ -144,7 +144,7 @@ static pi_minor_info_t pi_minor_call_table_type_1[] = {
/* 86 */{ irlap_param_min_turn_time, PV_INT_8_BITS },
};
-static pi_major_info_t pi_major_call_table[] = {
+static const pi_major_info_t pi_major_call_table[] = {
{ pi_minor_call_table_type_0, 9 },
{ pi_minor_call_table_type_1, 7 },
};
@@ -200,8 +200,8 @@ static int msb_index (__u16 word)
* able to check precisely what's going on. If a end user sees this,
* it's very likely the peer. - Jean II */
if (word == 0) {
- IRDA_WARNING("%s(), Detected buggy peer, adjust null PV to 0x1!\n",
- __func__);
+ net_warn_ratelimited("%s(), Detected buggy peer, adjust null PV to 0x1!\n",
+ __func__);
/* The only safe choice (we don't know the array size) */
word = 0x1;
}
@@ -342,8 +342,6 @@ static void irlap_adjust_qos_settings(struct qos_info *qos)
__u32 line_capacity;
int index;
- IRDA_DEBUG(2, "%s()\n", __func__);
-
/*
* Make sure the mintt is sensible.
* Main culprit : Ericsson T39. - Jean II
@@ -351,8 +349,8 @@ static void irlap_adjust_qos_settings(struct qos_info *qos)
if (sysctl_min_tx_turn_time > qos->min_turn_time.value) {
int i;
- IRDA_WARNING("%s(), Detected buggy peer, adjust mtt to %dus!\n",
- __func__, sysctl_min_tx_turn_time);
+ net_warn_ratelimited("%s(), Detected buggy peer, adjust mtt to %dus!\n",
+ __func__, sysctl_min_tx_turn_time);
/* We don't really need bits, but easier this way */
i = value_highest_bit(sysctl_min_tx_turn_time, min_turn_times,
@@ -368,9 +366,8 @@ static void irlap_adjust_qos_settings(struct qos_info *qos)
if ((qos->baud_rate.value < 115200) &&
(qos->max_turn_time.value < 500))
{
- IRDA_DEBUG(0,
- "%s(), adjusting max turn time from %d to 500 ms\n",
- __func__, qos->max_turn_time.value);
+ pr_debug("%s(), adjusting max turn time from %d to 500 ms\n",
+ __func__, qos->max_turn_time.value);
qos->max_turn_time.value = 500;
}
@@ -385,8 +382,8 @@ static void irlap_adjust_qos_settings(struct qos_info *qos)
#ifdef CONFIG_IRDA_DYNAMIC_WINDOW
while ((qos->data_size.value > line_capacity) && (index > 0)) {
qos->data_size.value = data_sizes[index--];
- IRDA_DEBUG(2, "%s(), reducing data size to %d\n",
- __func__, qos->data_size.value);
+ pr_debug("%s(), reducing data size to %d\n",
+ __func__, qos->data_size.value);
}
#else /* Use method described in section 6.6.11 of IrLAP */
while (irlap_requested_line_capacity(qos) > line_capacity) {
@@ -395,15 +392,15 @@ static void irlap_adjust_qos_settings(struct qos_info *qos)
/* Must be able to send at least one frame */
if (qos->window_size.value > 1) {
qos->window_size.value--;
- IRDA_DEBUG(2, "%s(), reducing window size to %d\n",
- __func__, qos->window_size.value);
+ pr_debug("%s(), reducing window size to %d\n",
+ __func__, qos->window_size.value);
} else if (index > 1) {
qos->data_size.value = data_sizes[index--];
- IRDA_DEBUG(2, "%s(), reducing data size to %d\n",
- __func__, qos->data_size.value);
+ pr_debug("%s(), reducing data size to %d\n",
+ __func__, qos->data_size.value);
} else {
- IRDA_WARNING("%s(), nothing more we can do!\n",
- __func__);
+ net_warn_ratelimited("%s(), nothing more we can do!\n",
+ __func__);
}
}
#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */
@@ -440,20 +437,20 @@ int irlap_qos_negotiate(struct irlap_cb *self, struct sk_buff *skb)
irlap_adjust_qos_settings(&self->qos_tx);
- IRDA_DEBUG(2, "Setting BAUD_RATE to %d bps.\n",
- self->qos_tx.baud_rate.value);
- IRDA_DEBUG(2, "Setting DATA_SIZE to %d bytes\n",
- self->qos_tx.data_size.value);
- IRDA_DEBUG(2, "Setting WINDOW_SIZE to %d\n",
- self->qos_tx.window_size.value);
- IRDA_DEBUG(2, "Setting XBOFS to %d\n",
- self->qos_tx.additional_bofs.value);
- IRDA_DEBUG(2, "Setting MAX_TURN_TIME to %d ms.\n",
- self->qos_tx.max_turn_time.value);
- IRDA_DEBUG(2, "Setting MIN_TURN_TIME to %d usecs.\n",
- self->qos_tx.min_turn_time.value);
- IRDA_DEBUG(2, "Setting LINK_DISC to %d secs.\n",
- self->qos_tx.link_disc_time.value);
+ pr_debug("Setting BAUD_RATE to %d bps.\n",
+ self->qos_tx.baud_rate.value);
+ pr_debug("Setting DATA_SIZE to %d bytes\n",
+ self->qos_tx.data_size.value);
+ pr_debug("Setting WINDOW_SIZE to %d\n",
+ self->qos_tx.window_size.value);
+ pr_debug("Setting XBOFS to %d\n",
+ self->qos_tx.additional_bofs.value);
+ pr_debug("Setting MAX_TURN_TIME to %d ms.\n",
+ self->qos_tx.max_turn_time.value);
+ pr_debug("Setting MIN_TURN_TIME to %d usecs.\n",
+ self->qos_tx.min_turn_time.value);
+ pr_debug("Setting LINK_DISC to %d secs.\n",
+ self->qos_tx.link_disc_time.value);
return ret;
}
@@ -537,17 +534,17 @@ static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get)
if (get) {
param->pv.i = self->qos_rx.baud_rate.bits;
- IRDA_DEBUG(2, "%s(), baud rate = 0x%02x\n",
- __func__, param->pv.i);
+ pr_debug("%s(), baud rate = 0x%02x\n",
+ __func__, param->pv.i);
} else {
/*
* Stations must agree on baud rate, so calculate
* intersection
*/
- IRDA_DEBUG(2, "Requested BAUD_RATE: 0x%04x\n", (__u16) param->pv.i);
+ pr_debug("Requested BAUD_RATE: 0x%04x\n", (__u16)param->pv.i);
final = (__u16) param->pv.i & self->qos_rx.baud_rate.bits;
- IRDA_DEBUG(2, "Final BAUD_RATE: 0x%04x\n", final);
+ pr_debug("Final BAUD_RATE: 0x%04x\n", final);
self->qos_tx.baud_rate.bits = final;
self->qos_rx.baud_rate.bits = final;
}
@@ -578,10 +575,10 @@ static int irlap_param_link_disconnect(void *instance, irda_param_t *param,
* Stations must agree on link disconnect/threshold
* time.
*/
- IRDA_DEBUG(2, "LINK_DISC: %02x\n", (__u8) param->pv.i);
+ pr_debug("LINK_DISC: %02x\n", (__u8)param->pv.i);
final = (__u8) param->pv.i & self->qos_rx.link_disc_time.bits;
- IRDA_DEBUG(2, "Final LINK_DISC: %02x\n", final);
+ pr_debug("Final LINK_DISC: %02x\n", final);
self->qos_tx.link_disc_time.bits = final;
self->qos_rx.link_disc_time.bits = final;
}
@@ -710,8 +707,8 @@ __u32 irlap_max_line_capacity(__u32 speed, __u32 max_turn_time)
__u32 line_capacity;
int i,j;
- IRDA_DEBUG(2, "%s(), speed=%d, max_turn_time=%d\n",
- __func__, speed, max_turn_time);
+ pr_debug("%s(), speed=%d, max_turn_time=%d\n",
+ __func__, speed, max_turn_time);
i = value_index(speed, baud_rates, 10);
j = value_index(max_turn_time, max_turn_times, 4);
@@ -721,8 +718,8 @@ __u32 irlap_max_line_capacity(__u32 speed, __u32 max_turn_time)
line_capacity = max_line_capacities[i][j];
- IRDA_DEBUG(2, "%s(), line capacity=%d bytes\n",
- __func__, line_capacity);
+ pr_debug("%s(), line capacity=%d bytes\n",
+ __func__, line_capacity);
return line_capacity;
}
@@ -737,8 +734,8 @@ static __u32 irlap_requested_line_capacity(struct qos_info *qos)
irlap_min_turn_time_in_bytes(qos->baud_rate.value,
qos->min_turn_time.value);
- IRDA_DEBUG(2, "%s(), requested line capacity=%d\n",
- __func__, line_capacity);
+ pr_debug("%s(), requested line capacity=%d\n",
+ __func__, line_capacity);
return line_capacity;
}
diff --git a/net/irda/wrapper.c b/net/irda/wrapper.c
index fd0995b1323a..40a0f993bf13 100644
--- a/net/irda/wrapper.c
+++ b/net/irda/wrapper.c
@@ -106,17 +106,17 @@ int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize)
* Nothing to worry about, but we set the default number of
* BOF's
*/
- IRDA_DEBUG(1, "%s(), wrong magic in skb!\n", __func__);
+ pr_debug("%s(), wrong magic in skb!\n", __func__);
xbofs = 10;
} else
xbofs = cb->xbofs + cb->xbofs_delay;
- IRDA_DEBUG(4, "%s(), xbofs=%d\n", __func__, xbofs);
+ pr_debug("%s(), xbofs=%d\n", __func__, xbofs);
/* Check that we never use more than 115 + 48 xbofs */
if (xbofs > 163) {
- IRDA_DEBUG(0, "%s(), too many xbofs (%d)\n", __func__,
- xbofs);
+ pr_debug("%s(), too many xbofs (%d)\n", __func__,
+ xbofs);
xbofs = 163;
}
@@ -134,8 +134,8 @@ int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize)
* transmitted after this point is 5.
*/
if(n >= (buffsize-5)) {
- IRDA_ERROR("%s(), tx buffer overflow (n=%d)\n",
- __func__, n);
+ net_err_ratelimited("%s(), tx buffer overflow (n=%d)\n",
+ __func__, n);
return n;
}
@@ -286,8 +286,8 @@ async_unwrap_bof(struct net_device *dev,
case INSIDE_FRAME:
/* Not supposed to happen, the previous frame is not
* finished - Jean II */
- IRDA_DEBUG(1, "%s(), Discarding incomplete frame\n",
- __func__);
+ pr_debug("%s(), Discarding incomplete frame\n",
+ __func__);
stats->rx_errors++;
stats->rx_missed_errors++;
irda_device_set_media_busy(dev, TRUE);
@@ -360,7 +360,7 @@ async_unwrap_eof(struct net_device *dev,
/* Wrong CRC, discard frame! */
irda_device_set_media_busy(dev, TRUE);
- IRDA_DEBUG(1, "%s(), crc error\n", __func__);
+ pr_debug("%s(), crc error\n", __func__);
stats->rx_errors++;
stats->rx_crc_errors++;
}
@@ -386,7 +386,7 @@ async_unwrap_ce(struct net_device *dev,
break;
case LINK_ESCAPE:
- IRDA_WARNING("%s: state not defined\n", __func__);
+ net_warn_ratelimited("%s: state not defined\n", __func__);
break;
case BEGIN_FRAME:
@@ -420,8 +420,8 @@ async_unwrap_other(struct net_device *dev,
rx_buff->fcs = irda_fcs(rx_buff->fcs, byte);
#endif
} else {
- IRDA_DEBUG(1, "%s(), Rx buffer overflow, aborting\n",
- __func__);
+ pr_debug("%s(), Rx buffer overflow, aborting\n",
+ __func__);
rx_buff->state = OUTSIDE_FRAME;
}
break;
@@ -439,8 +439,8 @@ async_unwrap_other(struct net_device *dev,
#endif
rx_buff->state = INSIDE_FRAME;
} else {
- IRDA_DEBUG(1, "%s(), Rx buffer overflow, aborting\n",
- __func__);
+ pr_debug("%s(), Rx buffer overflow, aborting\n",
+ __func__);
rx_buff->state = OUTSIDE_FRAME;
}
break;
diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c
index a089b6b91650..2e9953b2db84 100644
--- a/net/iucv/af_iucv.c
+++ b/net/iucv/af_iucv.c
@@ -1070,9 +1070,7 @@ static int iucv_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
txmsg.class = 0;
/* iterate over control messages */
- for (cmsg = CMSG_FIRSTHDR(msg); cmsg;
- cmsg = CMSG_NXTHDR(msg, cmsg)) {
-
+ for_each_cmsghdr(cmsg, msg) {
if (!CMSG_OK(msg, cmsg)) {
err = -EINVAL;
goto out;
@@ -1122,7 +1120,7 @@ static int iucv_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
}
if (iucv->transport == AF_IUCV_TRANS_HIPER)
skb_reserve(skb, sizeof(struct af_iucv_trans_hdr) + ETH_HLEN);
- if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
+ if (memcpy_from_msg(skb_put(skb, len), msg, len)) {
err = -EFAULT;
goto fail;
}
@@ -1355,7 +1353,7 @@ static int iucv_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
sk->sk_shutdown = sk->sk_shutdown | RCV_SHUTDOWN;
cskb = skb;
- if (skb_copy_datagram_iovec(cskb, offset, msg->msg_iov, copied)) {
+ if (skb_copy_datagram_msg(cskb, offset, msg, copied)) {
if (!(flags & MSG_PEEK))
skb_queue_head(&sk->sk_receive_queue, skb);
return -EFAULT;
diff --git a/net/key/af_key.c b/net/key/af_key.c
index 1847ec4e3930..f8ac939d52b4 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -3611,7 +3611,7 @@ static int pfkey_sendmsg(struct kiocb *kiocb,
goto out;
err = -EFAULT;
- if (memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len))
+ if (memcpy_from_msg(skb_put(skb,len), msg, len))
goto out;
hdr = pfkey_get_base_msg(skb, &err);
@@ -3654,7 +3654,7 @@ static int pfkey_recvmsg(struct kiocb *kiocb,
}
skb_reset_transport_header(skb);
- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
if (err)
goto out_free;
diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c
index edb78e69efe4..781b3a226ba7 100644
--- a/net/l2tp/l2tp_eth.c
+++ b/net/l2tp/l2tp_eth.c
@@ -126,6 +126,7 @@ static struct net_device_ops l2tp_eth_netdev_ops = {
.ndo_uninit = l2tp_eth_dev_uninit,
.ndo_start_xmit = l2tp_eth_dev_xmit,
.ndo_get_stats64 = l2tp_eth_get_stats64,
+ .ndo_set_mac_address = eth_mac_addr,
};
static void l2tp_eth_dev_setup(struct net_device *dev)
diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c
index 369a9822488c..05dfc8aa36af 100644
--- a/net/l2tp/l2tp_ip.c
+++ b/net/l2tp/l2tp_ip.c
@@ -441,7 +441,7 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m
*((__be32 *) skb_put(skb, 4)) = 0;
/* Copy user data into skb */
- rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
+ rc = memcpy_from_msg(skb_put(skb, len), msg, len);
if (rc < 0) {
kfree_skb(skb);
goto error;
@@ -528,7 +528,7 @@ static int l2tp_ip_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m
copied = len;
}
- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
if (err)
goto done;
diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c
index 0edb263cc002..8611f1b63141 100644
--- a/net/l2tp/l2tp_ip6.c
+++ b/net/l2tp/l2tp_ip6.c
@@ -619,7 +619,7 @@ static int l2tp_ip6_sendmsg(struct kiocb *iocb, struct sock *sk,
back_from_confirm:
lock_sock(sk);
- err = ip6_append_data(sk, ip_generic_getfrag, msg->msg_iov,
+ err = ip6_append_data(sk, ip_generic_getfrag, msg,
ulen, transhdrlen, hlimit, tclass, opt,
&fl6, (struct rt6_info *)dst,
msg->msg_flags, dontfrag);
@@ -672,7 +672,7 @@ static int l2tp_ip6_recvmsg(struct kiocb *iocb, struct sock *sk,
copied = len;
}
- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
if (err)
goto done;
diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c
index b704a9356208..cc7a828fc914 100644
--- a/net/l2tp/l2tp_ppp.c
+++ b/net/l2tp/l2tp_ppp.c
@@ -208,7 +208,7 @@ static int pppol2tp_recvmsg(struct kiocb *iocb, struct socket *sock,
else if (len < skb->len)
msg->msg_flags |= MSG_TRUNC;
- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len);
+ err = skb_copy_datagram_msg(skb, 0, msg, len);
if (likely(err == 0))
err = len;
@@ -346,8 +346,7 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
skb_put(skb, 2);
/* Copy user data into skb */
- error = memcpy_fromiovec(skb_put(skb, total_len), m->msg_iov,
- total_len);
+ error = memcpy_from_msg(skb_put(skb, total_len), m, total_len);
if (error < 0) {
kfree_skb(skb);
goto error_put_sess_tun;
diff --git a/net/lapb/lapb_iface.c b/net/lapb/lapb_iface.c
index 3cdaa046c1bc..fc60d9d738b5 100644
--- a/net/lapb/lapb_iface.c
+++ b/net/lapb/lapb_iface.c
@@ -73,6 +73,7 @@ static void __lapb_remove_cb(struct lapb_cb *lapb)
lapb_put(lapb);
}
}
+EXPORT_SYMBOL(lapb_register);
/*
* Add a socket to the bound sockets list.
@@ -195,6 +196,7 @@ out:
write_unlock_bh(&lapb_list_lock);
return rc;
}
+EXPORT_SYMBOL(lapb_unregister);
int lapb_getparms(struct net_device *dev, struct lapb_parms_struct *parms)
{
@@ -227,6 +229,7 @@ int lapb_getparms(struct net_device *dev, struct lapb_parms_struct *parms)
out:
return rc;
}
+EXPORT_SYMBOL(lapb_getparms);
int lapb_setparms(struct net_device *dev, struct lapb_parms_struct *parms)
{
@@ -262,6 +265,7 @@ out_put:
out:
return rc;
}
+EXPORT_SYMBOL(lapb_setparms);
int lapb_connect_request(struct net_device *dev)
{
@@ -290,6 +294,7 @@ out_put:
out:
return rc;
}
+EXPORT_SYMBOL(lapb_connect_request);
int lapb_disconnect_request(struct net_device *dev)
{
@@ -334,6 +339,7 @@ out_put:
out:
return rc;
}
+EXPORT_SYMBOL(lapb_disconnect_request);
int lapb_data_request(struct net_device *dev, struct sk_buff *skb)
{
@@ -355,6 +361,7 @@ out_put:
out:
return rc;
}
+EXPORT_SYMBOL(lapb_data_request);
int lapb_data_received(struct net_device *dev, struct sk_buff *skb)
{
@@ -369,6 +376,7 @@ int lapb_data_received(struct net_device *dev, struct sk_buff *skb)
return rc;
}
+EXPORT_SYMBOL(lapb_data_received);
void lapb_connect_confirmation(struct lapb_cb *lapb, int reason)
{
@@ -415,15 +423,6 @@ int lapb_data_transmit(struct lapb_cb *lapb, struct sk_buff *skb)
return used;
}
-EXPORT_SYMBOL(lapb_register);
-EXPORT_SYMBOL(lapb_unregister);
-EXPORT_SYMBOL(lapb_getparms);
-EXPORT_SYMBOL(lapb_setparms);
-EXPORT_SYMBOL(lapb_connect_request);
-EXPORT_SYMBOL(lapb_disconnect_request);
-EXPORT_SYMBOL(lapb_data_request);
-EXPORT_SYMBOL(lapb_data_received);
-
static int __init lapb_init(void)
{
return 0;
diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c
index bb9cbc17d926..2c0b83ce43bd 100644
--- a/net/llc/af_llc.c
+++ b/net/llc/af_llc.c
@@ -819,8 +819,7 @@ static int llc_ui_recvmsg(struct kiocb *iocb, struct socket *sock,
used = len;
if (!(flags & MSG_TRUNC)) {
- int rc = skb_copy_datagram_iovec(skb, offset,
- msg->msg_iov, used);
+ int rc = skb_copy_datagram_msg(skb, offset, msg, used);
if (rc) {
/* Exception. Bailout! */
if (!copied)
@@ -922,7 +921,7 @@ static int llc_ui_sendmsg(struct kiocb *iocb, struct socket *sock,
skb->dev = llc->dev;
skb->protocol = llc_proto_type(addr->sllc_arphrd);
skb_reserve(skb, hdrlen);
- rc = memcpy_fromiovec(skb_put(skb, copied), msg->msg_iov, copied);
+ rc = memcpy_from_msg(skb_put(skb, copied), msg, copied);
if (rc)
goto out;
if (sk->sk_type == SOCK_DGRAM || addr->sllc_ua) {
diff --git a/net/llc/llc_c_st.c b/net/llc/llc_c_st.c
index 818a9428823b..2467573b5f84 100644
--- a/net/llc/llc_c_st.c
+++ b/net/llc/llc_c_st.c
@@ -33,7 +33,7 @@
* LLC_CONN_STATE_AWAIT_REJ states
*/
/* State transitions for LLC_CONN_EV_DISC_REQ event */
-static llc_conn_action_t llc_common_actions_1[] = {
+static const llc_conn_action_t llc_common_actions_1[] = {
[0] = llc_conn_ac_send_disc_cmd_p_set_x,
[1] = llc_conn_ac_start_ack_timer,
[2] = llc_conn_ac_stop_other_timers,
@@ -50,7 +50,7 @@ static struct llc_conn_state_trans llc_common_state_trans_1 = {
};
/* State transitions for LLC_CONN_EV_RESET_REQ event */
-static llc_conn_action_t llc_common_actions_2[] = {
+static const llc_conn_action_t llc_common_actions_2[] = {
[0] = llc_conn_ac_send_sabme_cmd_p_set_x,
[1] = llc_conn_ac_start_ack_timer,
[2] = llc_conn_ac_stop_other_timers,
@@ -67,7 +67,7 @@ static struct llc_conn_state_trans llc_common_state_trans_2 = {
};
/* State transitions for LLC_CONN_EV_RX_SABME_CMD_Pbit_SET_X event */
-static llc_conn_action_t llc_common_actions_3[] = {
+static const llc_conn_action_t llc_common_actions_3[] = {
[0] = llc_conn_ac_stop_all_timers,
[1] = llc_conn_ac_set_vs_0,
[2] = llc_conn_ac_set_vr_0,
@@ -87,7 +87,7 @@ static struct llc_conn_state_trans llc_common_state_trans_3 = {
};
/* State transitions for LLC_CONN_EV_RX_DISC_CMD_Pbit_SET_X event */
-static llc_conn_action_t llc_common_actions_4[] = {
+static const llc_conn_action_t llc_common_actions_4[] = {
[0] = llc_conn_ac_stop_all_timers,
[1] = llc_conn_ac_send_ua_rsp_f_set_p,
[2] = llc_conn_ac_disc_ind,
@@ -103,7 +103,7 @@ static struct llc_conn_state_trans llc_common_state_trans_4 = {
};
/* State transitions for LLC_CONN_EV_RX_FRMR_RSP_Fbit_SET_X event */
-static llc_conn_action_t llc_common_actions_5[] = {
+static const llc_conn_action_t llc_common_actions_5[] = {
[0] = llc_conn_ac_send_sabme_cmd_p_set_x,
[1] = llc_conn_ac_start_ack_timer,
[2] = llc_conn_ac_stop_other_timers,
@@ -122,7 +122,7 @@ static struct llc_conn_state_trans llc_common_state_trans_5 = {
};
/* State transitions for LLC_CONN_EV_RX_DM_RSP_Fbit_SET_X event */
-static llc_conn_action_t llc_common_actions_6[] = {
+static const llc_conn_action_t llc_common_actions_6[] = {
[0] = llc_conn_ac_disc_ind,
[1] = llc_conn_ac_stop_all_timers,
[2] = llc_conn_disc,
@@ -137,7 +137,7 @@ static struct llc_conn_state_trans llc_common_state_trans_6 = {
};
/* State transitions for LLC_CONN_EV_RX_ZZZ_CMD_Pbit_SET_X_INVAL_Nr event */
-static llc_conn_action_t llc_common_actions_7a[] = {
+static const llc_conn_action_t llc_common_actions_7a[] = {
[0] = llc_conn_ac_send_frmr_rsp_f_set_x,
[1] = llc_conn_ac_start_ack_timer,
[2] = llc_conn_ac_stop_other_timers,
@@ -153,7 +153,7 @@ static struct llc_conn_state_trans llc_common_state_trans_7a = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_X_INVAL_Ns event */
-static llc_conn_action_t llc_common_actions_7b[] = {
+static const llc_conn_action_t llc_common_actions_7b[] = {
[0] = llc_conn_ac_send_frmr_rsp_f_set_x,
[1] = llc_conn_ac_start_ack_timer,
[2] = llc_conn_ac_stop_other_timers,
@@ -169,7 +169,7 @@ static struct llc_conn_state_trans llc_common_state_trans_7b = {
};
/* State transitions for LLC_CONN_EV_RX_ZZZ_RSP_Fbit_SET_X_INVAL_Nr event */
-static llc_conn_action_t llc_common_actions_8a[] = {
+static const llc_conn_action_t llc_common_actions_8a[] = {
[0] = llc_conn_ac_send_frmr_rsp_f_set_x,
[1] = llc_conn_ac_start_ack_timer,
[2] = llc_conn_ac_stop_other_timers,
@@ -185,7 +185,7 @@ static struct llc_conn_state_trans llc_common_state_trans_8a = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_X_INVAL_Ns event */
-static llc_conn_action_t llc_common_actions_8b[] = {
+static const llc_conn_action_t llc_common_actions_8b[] = {
[0] = llc_conn_ac_send_frmr_rsp_f_set_x,
[1] = llc_conn_ac_start_ack_timer,
[2] = llc_conn_ac_stop_other_timers,
@@ -201,7 +201,7 @@ static struct llc_conn_state_trans llc_common_state_trans_8b = {
};
/* State transitions for LLC_CONN_EV_RX_BAD_PDU event */
-static llc_conn_action_t llc_common_actions_8c[] = {
+static const llc_conn_action_t llc_common_actions_8c[] = {
[0] = llc_conn_ac_send_frmr_rsp_f_set_x,
[1] = llc_conn_ac_start_ack_timer,
[2] = llc_conn_ac_stop_other_timers,
@@ -217,7 +217,7 @@ static struct llc_conn_state_trans llc_common_state_trans_8c = {
};
/* State transitions for LLC_CONN_EV_RX_UA_RSP_Fbit_SET_X event */
-static llc_conn_action_t llc_common_actions_9[] = {
+static const llc_conn_action_t llc_common_actions_9[] = {
[0] = llc_conn_ac_send_frmr_rsp_f_set_x,
[1] = llc_conn_ac_start_ack_timer,
[2] = llc_conn_ac_stop_other_timers,
@@ -234,12 +234,12 @@ static struct llc_conn_state_trans llc_common_state_trans_9 = {
/* State transitions for LLC_CONN_EV_RX_XXX_RSP_Fbit_SET_1 event */
#if 0
-static llc_conn_ev_qfyr_t llc_common_ev_qfyrs_10[] = {
+static const llc_conn_ev_qfyr_t llc_common_ev_qfyrs_10[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_0,
[1] = NULL,
};
-static llc_conn_action_t llc_common_actions_10[] = {
+static const llc_conn_action_t llc_common_actions_10[] = {
[0] = llc_conn_ac_send_frmr_rsp_f_set_x,
[1] = llc_conn_ac_start_ack_timer,
[2] = llc_conn_ac_stop_other_timers,
@@ -256,12 +256,12 @@ static struct llc_conn_state_trans llc_common_state_trans_10 = {
#endif
/* State transitions for LLC_CONN_EV_P_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_common_ev_qfyrs_11a[] = {
+static const llc_conn_ev_qfyr_t llc_common_ev_qfyrs_11a[] = {
[0] = llc_conn_ev_qlfy_retry_cnt_gte_n2,
[1] = NULL,
};
-static llc_conn_action_t llc_common_actions_11a[] = {
+static const llc_conn_action_t llc_common_actions_11a[] = {
[0] = llc_conn_ac_send_sabme_cmd_p_set_x,
[1] = llc_conn_ac_start_ack_timer,
[2] = llc_conn_ac_stop_other_timers,
@@ -278,12 +278,12 @@ static struct llc_conn_state_trans llc_common_state_trans_11a = {
};
/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_common_ev_qfyrs_11b[] = {
+static const llc_conn_ev_qfyr_t llc_common_ev_qfyrs_11b[] = {
[0] = llc_conn_ev_qlfy_retry_cnt_gte_n2,
[1] = NULL,
};
-static llc_conn_action_t llc_common_actions_11b[] = {
+static const llc_conn_action_t llc_common_actions_11b[] = {
[0] = llc_conn_ac_send_sabme_cmd_p_set_x,
[1] = llc_conn_ac_start_ack_timer,
[2] = llc_conn_ac_stop_other_timers,
@@ -300,12 +300,12 @@ static struct llc_conn_state_trans llc_common_state_trans_11b = {
};
/* State transitions for LLC_CONN_EV_REJ_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_common_ev_qfyrs_11c[] = {
+static const llc_conn_ev_qfyr_t llc_common_ev_qfyrs_11c[] = {
[0] = llc_conn_ev_qlfy_retry_cnt_gte_n2,
[1] = NULL,
};
-static llc_conn_action_t llc_common_actions_11c[] = {
+static const llc_conn_action_t llc_common_actions_11c[] = {
[0] = llc_conn_ac_send_sabme_cmd_p_set_x,
[1] = llc_conn_ac_start_ack_timer,
[2] = llc_conn_ac_stop_other_timers,
@@ -322,12 +322,12 @@ static struct llc_conn_state_trans llc_common_state_trans_11c = {
};
/* State transitions for LLC_CONN_EV_BUSY_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_common_ev_qfyrs_11d[] = {
+static const llc_conn_ev_qfyr_t llc_common_ev_qfyrs_11d[] = {
[0] = llc_conn_ev_qlfy_retry_cnt_gte_n2,
[1] = NULL,
};
-static llc_conn_action_t llc_common_actions_11d[] = {
+static const llc_conn_action_t llc_common_actions_11d[] = {
[0] = llc_conn_ac_send_sabme_cmd_p_set_x,
[1] = llc_conn_ac_start_ack_timer,
[2] = llc_conn_ac_stop_other_timers,
@@ -351,7 +351,7 @@ static struct llc_conn_state_trans llc_common_state_trans_end;
/* LLC_CONN_STATE_ADM transitions */
/* State transitions for LLC_CONN_EV_CONN_REQ event */
-static llc_conn_action_t llc_adm_actions_1[] = {
+static const llc_conn_action_t llc_adm_actions_1[] = {
[0] = llc_conn_ac_send_sabme_cmd_p_set_x,
[1] = llc_conn_ac_start_ack_timer,
[2] = llc_conn_ac_set_retry_cnt_0,
@@ -367,7 +367,7 @@ static struct llc_conn_state_trans llc_adm_state_trans_1 = {
};
/* State transitions for LLC_CONN_EV_RX_SABME_CMD_Pbit_SET_X event */
-static llc_conn_action_t llc_adm_actions_2[] = {
+static const llc_conn_action_t llc_adm_actions_2[] = {
[0] = llc_conn_ac_send_ua_rsp_f_set_p,
[1] = llc_conn_ac_set_vs_0,
[2] = llc_conn_ac_set_vr_0,
@@ -386,7 +386,7 @@ static struct llc_conn_state_trans llc_adm_state_trans_2 = {
};
/* State transitions for LLC_CONN_EV_RX_DISC_CMD_Pbit_SET_X event */
-static llc_conn_action_t llc_adm_actions_3[] = {
+static const llc_conn_action_t llc_adm_actions_3[] = {
[0] = llc_conn_ac_send_dm_rsp_f_set_p,
[1] = llc_conn_disc,
[2] = NULL,
@@ -400,7 +400,7 @@ static struct llc_conn_state_trans llc_adm_state_trans_3 = {
};
/* State transitions for LLC_CONN_EV_RX_XXX_CMD_Pbit_SET_1 event */
-static llc_conn_action_t llc_adm_actions_4[] = {
+static const llc_conn_action_t llc_adm_actions_4[] = {
[0] = llc_conn_ac_send_dm_rsp_f_set_1,
[1] = llc_conn_disc,
[2] = NULL,
@@ -414,7 +414,7 @@ static struct llc_conn_state_trans llc_adm_state_trans_4 = {
};
/* State transitions for LLC_CONN_EV_RX_XXX_YYY event */
-static llc_conn_action_t llc_adm_actions_5[] = {
+static const llc_conn_action_t llc_adm_actions_5[] = {
[0] = llc_conn_disc,
[1] = NULL,
};
@@ -445,7 +445,7 @@ static struct llc_conn_state_trans *llc_adm_state_transitions[] = {
/* LLC_CONN_STATE_SETUP transitions */
/* State transitions for LLC_CONN_EV_RX_SABME_CMD_Pbit_SET_X event */
-static llc_conn_action_t llc_setup_actions_1[] = {
+static const llc_conn_action_t llc_setup_actions_1[] = {
[0] = llc_conn_ac_send_ua_rsp_f_set_p,
[1] = llc_conn_ac_set_vs_0,
[2] = llc_conn_ac_set_vr_0,
@@ -461,13 +461,13 @@ static struct llc_conn_state_trans llc_setup_state_trans_1 = {
};
/* State transitions for LLC_CONN_EV_RX_UA_RSP_Fbit_SET_X event */
-static llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_2[] = {
+static const llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_2[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_f,
[1] = llc_conn_ev_qlfy_set_status_conn,
[2] = NULL,
};
-static llc_conn_action_t llc_setup_actions_2[] = {
+static const llc_conn_action_t llc_setup_actions_2[] = {
[0] = llc_conn_ac_stop_ack_timer,
[1] = llc_conn_ac_set_vs_0,
[2] = llc_conn_ac_set_vr_0,
@@ -485,13 +485,13 @@ static struct llc_conn_state_trans llc_setup_state_trans_2 = {
};
/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_3[] = {
+static const llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_3[] = {
[0] = llc_conn_ev_qlfy_s_flag_eq_1,
[1] = llc_conn_ev_qlfy_set_status_conn,
[2] = NULL,
};
-static llc_conn_action_t llc_setup_actions_3[] = {
+static const llc_conn_action_t llc_setup_actions_3[] = {
[0] = llc_conn_ac_set_p_flag_0,
[1] = llc_conn_ac_set_remote_busy_0,
[2] = llc_conn_ac_conn_confirm,
@@ -506,12 +506,12 @@ static struct llc_conn_state_trans llc_setup_state_trans_3 = {
};
/* State transitions for LLC_CONN_EV_RX_DISC_CMD_Pbit_SET_X event */
-static llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_4[] = {
+static const llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_4[] = {
[0] = llc_conn_ev_qlfy_set_status_disc,
[1] = NULL,
};
-static llc_conn_action_t llc_setup_actions_4[] = {
+static const llc_conn_action_t llc_setup_actions_4[] = {
[0] = llc_conn_ac_send_dm_rsp_f_set_p,
[1] = llc_conn_ac_stop_ack_timer,
[2] = llc_conn_ac_conn_confirm,
@@ -527,12 +527,12 @@ static struct llc_conn_state_trans llc_setup_state_trans_4 = {
};
/* State transitions for LLC_CONN_EV_RX_DM_RSP_Fbit_SET_X event */
-static llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_5[] = {
+static const llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_5[] = {
[0] = llc_conn_ev_qlfy_set_status_disc,
[1] = NULL,
};
-static llc_conn_action_t llc_setup_actions_5[] = {
+static const llc_conn_action_t llc_setup_actions_5[] = {
[0] = llc_conn_ac_stop_ack_timer,
[1] = llc_conn_ac_conn_confirm,
[2] = llc_conn_disc,
@@ -547,13 +547,13 @@ static struct llc_conn_state_trans llc_setup_state_trans_5 = {
};
/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_7[] = {
+static const llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_7[] = {
[0] = llc_conn_ev_qlfy_retry_cnt_lt_n2,
[1] = llc_conn_ev_qlfy_s_flag_eq_0,
[2] = NULL,
};
-static llc_conn_action_t llc_setup_actions_7[] = {
+static const llc_conn_action_t llc_setup_actions_7[] = {
[0] = llc_conn_ac_send_sabme_cmd_p_set_x,
[1] = llc_conn_ac_start_ack_timer,
[2] = llc_conn_ac_inc_retry_cnt_by_1,
@@ -568,14 +568,14 @@ static struct llc_conn_state_trans llc_setup_state_trans_7 = {
};
/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_8[] = {
+static const llc_conn_ev_qfyr_t llc_setup_ev_qfyrs_8[] = {
[0] = llc_conn_ev_qlfy_retry_cnt_gte_n2,
[1] = llc_conn_ev_qlfy_s_flag_eq_0,
[2] = llc_conn_ev_qlfy_set_status_failed,
[3] = NULL,
};
-static llc_conn_action_t llc_setup_actions_8[] = {
+static const llc_conn_action_t llc_setup_actions_8[] = {
[0] = llc_conn_ac_conn_confirm,
[1] = llc_conn_disc,
[2] = NULL,
@@ -609,14 +609,14 @@ static struct llc_conn_state_trans *llc_setup_state_transitions[] = {
/* LLC_CONN_STATE_NORMAL transitions */
/* State transitions for LLC_CONN_EV_DATA_REQ event */
-static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_1[] = {
+static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_1[] = {
[0] = llc_conn_ev_qlfy_remote_busy_eq_0,
[1] = llc_conn_ev_qlfy_p_flag_eq_0,
[2] = llc_conn_ev_qlfy_last_frame_eq_0,
[3] = NULL,
};
-static llc_conn_action_t llc_normal_actions_1[] = {
+static const llc_conn_action_t llc_normal_actions_1[] = {
[0] = llc_conn_ac_send_i_as_ack,
[1] = llc_conn_ac_start_ack_tmr_if_not_running,
[2] = NULL,
@@ -630,14 +630,14 @@ static struct llc_conn_state_trans llc_normal_state_trans_1 = {
};
/* State transitions for LLC_CONN_EV_DATA_REQ event */
-static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_2[] = {
+static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_2[] = {
[0] = llc_conn_ev_qlfy_remote_busy_eq_0,
[1] = llc_conn_ev_qlfy_p_flag_eq_0,
[2] = llc_conn_ev_qlfy_last_frame_eq_1,
[3] = NULL,
};
-static llc_conn_action_t llc_normal_actions_2[] = {
+static const llc_conn_action_t llc_normal_actions_2[] = {
[0] = llc_conn_ac_send_i_cmd_p_set_1,
[1] = llc_conn_ac_start_p_timer,
[2] = NULL,
@@ -651,14 +651,14 @@ static struct llc_conn_state_trans llc_normal_state_trans_2 = {
};
/* State transitions for LLC_CONN_EV_DATA_REQ event */
-static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_2_1[] = {
+static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_2_1[] = {
[0] = llc_conn_ev_qlfy_remote_busy_eq_1,
[1] = llc_conn_ev_qlfy_set_status_remote_busy,
[2] = NULL,
};
/* just one member, NULL, .bss zeroes it */
-static llc_conn_action_t llc_normal_actions_2_1[1];
+static const llc_conn_action_t llc_normal_actions_2_1[1];
static struct llc_conn_state_trans llc_normal_state_trans_2_1 = {
.ev = llc_conn_ev_data_req,
@@ -668,12 +668,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_2_1 = {
};
/* State transitions for LLC_CONN_EV_LOCAL_BUSY_DETECTED event */
-static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_3[] = {
+static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_3[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_0,
[1] = NULL,
};
-static llc_conn_action_t llc_normal_actions_3[] = {
+static const llc_conn_action_t llc_normal_actions_3[] = {
[0] = llc_conn_ac_rst_sendack_flag,
[1] = llc_conn_ac_send_rnr_xxx_x_set_0,
[2] = llc_conn_ac_set_data_flag_0,
@@ -688,12 +688,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_3 = {
};
/* State transitions for LLC_CONN_EV_LOCAL_BUSY_DETECTED event */
-static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_4[] = {
+static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_4[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_normal_actions_4[] = {
+static const llc_conn_action_t llc_normal_actions_4[] = {
[0] = llc_conn_ac_rst_sendack_flag,
[1] = llc_conn_ac_send_rnr_xxx_x_set_0,
[2] = llc_conn_ac_set_data_flag_0,
@@ -708,12 +708,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_4 = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns event */
-static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_5a[] = {
+static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_5a[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_0,
[1] = NULL,
};
-static llc_conn_action_t llc_normal_actions_5a[] = {
+static const llc_conn_action_t llc_normal_actions_5a[] = {
[0] = llc_conn_ac_rst_sendack_flag,
[1] = llc_conn_ac_send_rej_xxx_x_set_0,
[2] = llc_conn_ac_upd_nr_received,
@@ -731,12 +731,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_5a = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0_UNEXPD_Ns event */
-static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_5b[] = {
+static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_5b[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_0,
[1] = NULL,
};
-static llc_conn_action_t llc_normal_actions_5b[] = {
+static const llc_conn_action_t llc_normal_actions_5b[] = {
[0] = llc_conn_ac_rst_sendack_flag,
[1] = llc_conn_ac_send_rej_xxx_x_set_0,
[2] = llc_conn_ac_upd_nr_received,
@@ -754,12 +754,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_5b = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_1_UNEXPD_Ns event */
-static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_5c[] = {
+static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_5c[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_normal_actions_5c[] = {
+static const llc_conn_action_t llc_normal_actions_5c[] = {
[0] = llc_conn_ac_rst_sendack_flag,
[1] = llc_conn_ac_send_rej_xxx_x_set_0,
[2] = llc_conn_ac_upd_nr_received,
@@ -777,12 +777,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_5c = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns event */
-static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_6a[] = {
+static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_6a[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_normal_actions_6a[] = {
+static const llc_conn_action_t llc_normal_actions_6a[] = {
[0] = llc_conn_ac_rst_sendack_flag,
[1] = llc_conn_ac_send_rej_xxx_x_set_0,
[2] = llc_conn_ac_upd_nr_received,
@@ -798,12 +798,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_6a = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0_UNEXPD_Ns event */
-static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_6b[] = {
+static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_6b[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_normal_actions_6b[] = {
+static const llc_conn_action_t llc_normal_actions_6b[] = {
[0] = llc_conn_ac_rst_sendack_flag,
[1] = llc_conn_ac_send_rej_xxx_x_set_0,
[2] = llc_conn_ac_upd_nr_received,
@@ -819,7 +819,7 @@ static struct llc_conn_state_trans llc_normal_state_trans_6b = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1_UNEXPD_Ns event */
-static llc_conn_action_t llc_normal_actions_7[] = {
+static const llc_conn_action_t llc_normal_actions_7[] = {
[0] = llc_conn_ac_rst_sendack_flag,
[1] = llc_conn_ac_send_rej_rsp_f_set_1,
[2] = llc_conn_ac_upd_nr_received,
@@ -835,12 +835,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_7 = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_X event */
-static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_8a[] = {
+static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_8a[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_f,
[1] = NULL,
};
-static llc_conn_action_t llc_normal_actions_8[] = {
+static const llc_conn_action_t llc_normal_actions_8[] = {
[0] = llc_conn_ac_inc_vr_by_1,
[1] = llc_conn_ac_data_ind,
[2] = llc_conn_ac_upd_p_flag,
@@ -858,7 +858,7 @@ static struct llc_conn_state_trans llc_normal_state_trans_8a = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */
-static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_8b[] = {
+static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_8b[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_0,
[1] = NULL,
};
@@ -871,12 +871,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_8b = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0 event */
-static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_9a[] = {
+static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_9a[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_normal_actions_9a[] = {
+static const llc_conn_action_t llc_normal_actions_9a[] = {
[0] = llc_conn_ac_inc_vr_by_1,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_data_ind,
@@ -892,12 +892,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_9a = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */
-static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_9b[] = {
+static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_9b[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_normal_actions_9b[] = {
+static const llc_conn_action_t llc_normal_actions_9b[] = {
[0] = llc_conn_ac_inc_vr_by_1,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_data_ind,
@@ -913,7 +913,7 @@ static struct llc_conn_state_trans llc_normal_state_trans_9b = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1 event */
-static llc_conn_action_t llc_normal_actions_10[] = {
+static const llc_conn_action_t llc_normal_actions_10[] = {
[0] = llc_conn_ac_inc_vr_by_1,
[1] = llc_conn_ac_send_ack_rsp_f_set_1,
[2] = llc_conn_ac_rst_sendack_flag,
@@ -930,7 +930,7 @@ static struct llc_conn_state_trans llc_normal_state_trans_10 = {
};
/* State transitions for * LLC_CONN_EV_RX_RR_CMD_Pbit_SET_0 event */
-static llc_conn_action_t llc_normal_actions_11a[] = {
+static const llc_conn_action_t llc_normal_actions_11a[] = {
[0] = llc_conn_ac_upd_p_flag,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_clear_remote_busy,
@@ -945,7 +945,7 @@ static struct llc_conn_state_trans llc_normal_state_trans_11a = {
};
/* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_0 event */
-static llc_conn_action_t llc_normal_actions_11b[] = {
+static const llc_conn_action_t llc_normal_actions_11b[] = {
[0] = llc_conn_ac_upd_p_flag,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_clear_remote_busy,
@@ -960,12 +960,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_11b = {
};
/* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_1 event */
-static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_11c[] = {
+static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_11c[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_normal_actions_11c[] = {
+static const llc_conn_action_t llc_normal_actions_11c[] = {
[0] = llc_conn_ac_upd_p_flag,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_inc_tx_win_size,
@@ -981,7 +981,7 @@ static struct llc_conn_state_trans llc_normal_state_trans_11c = {
};
/* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_1 event */
-static llc_conn_action_t llc_normal_actions_12[] = {
+static const llc_conn_action_t llc_normal_actions_12[] = {
[0] = llc_conn_ac_send_ack_rsp_f_set_1,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_adjust_npta_by_rr,
@@ -998,7 +998,7 @@ static struct llc_conn_state_trans llc_normal_state_trans_12 = {
};
/* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_0 event */
-static llc_conn_action_t llc_normal_actions_13a[] = {
+static const llc_conn_action_t llc_normal_actions_13a[] = {
[0] = llc_conn_ac_upd_p_flag,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_set_remote_busy,
@@ -1013,7 +1013,7 @@ static struct llc_conn_state_trans llc_normal_state_trans_13a = {
};
/* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_0 event */
-static llc_conn_action_t llc_normal_actions_13b[] = {
+static const llc_conn_action_t llc_normal_actions_13b[] = {
[0] = llc_conn_ac_upd_p_flag,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_set_remote_busy,
@@ -1028,12 +1028,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_13b = {
};
/* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_1 event */
-static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_13c[] = {
+static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_13c[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_normal_actions_13c[] = {
+static const llc_conn_action_t llc_normal_actions_13c[] = {
[0] = llc_conn_ac_upd_p_flag,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_set_remote_busy,
@@ -1048,7 +1048,7 @@ static struct llc_conn_state_trans llc_normal_state_trans_13c = {
};
/* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_1 event */
-static llc_conn_action_t llc_normal_actions_14[] = {
+static const llc_conn_action_t llc_normal_actions_14[] = {
[0] = llc_conn_ac_send_rr_rsp_f_set_1,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_adjust_npta_by_rnr,
@@ -1065,12 +1065,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_14 = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */
-static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_15a[] = {
+static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_15a[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_0,
[1] = NULL,
};
-static llc_conn_action_t llc_normal_actions_15a[] = {
+static const llc_conn_action_t llc_normal_actions_15a[] = {
[0] = llc_conn_ac_set_vs_nr,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_upd_p_flag,
@@ -1088,12 +1088,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_15a = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_X event */
-static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_15b[] = {
+static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_15b[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_f,
[1] = NULL,
};
-static llc_conn_action_t llc_normal_actions_15b[] = {
+static const llc_conn_action_t llc_normal_actions_15b[] = {
[0] = llc_conn_ac_set_vs_nr,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_upd_p_flag,
@@ -1111,12 +1111,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_15b = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */
-static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_16a[] = {
+static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_16a[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_normal_actions_16a[] = {
+static const llc_conn_action_t llc_normal_actions_16a[] = {
[0] = llc_conn_ac_set_vs_nr,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_dec_tx_win_size,
@@ -1133,12 +1133,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_16a = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_0 event */
-static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_16b[] = {
+static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_16b[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_normal_actions_16b[] = {
+static const llc_conn_action_t llc_normal_actions_16b[] = {
[0] = llc_conn_ac_set_vs_nr,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_dec_tx_win_size,
@@ -1155,7 +1155,7 @@ static struct llc_conn_state_trans llc_normal_state_trans_16b = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_1 event */
-static llc_conn_action_t llc_normal_actions_17[] = {
+static const llc_conn_action_t llc_normal_actions_17[] = {
[0] = llc_conn_ac_set_vs_nr,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_dec_tx_win_size,
@@ -1172,12 +1172,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_17 = {
};
/* State transitions for LLC_CONN_EV_INIT_P_F_CYCLE event */
-static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_18[] = {
+static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_18[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_0,
[1] = NULL,
};
-static llc_conn_action_t llc_normal_actions_18[] = {
+static const llc_conn_action_t llc_normal_actions_18[] = {
[0] = llc_conn_ac_send_rr_cmd_p_set_1,
[1] = llc_conn_ac_start_p_timer,
[2] = NULL,
@@ -1191,12 +1191,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_18 = {
};
/* State transitions for LLC_CONN_EV_P_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_19[] = {
+static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_19[] = {
[0] = llc_conn_ev_qlfy_retry_cnt_lt_n2,
[1] = NULL,
};
-static llc_conn_action_t llc_normal_actions_19[] = {
+static const llc_conn_action_t llc_normal_actions_19[] = {
[0] = llc_conn_ac_rst_sendack_flag,
[1] = llc_conn_ac_send_rr_cmd_p_set_1,
[2] = llc_conn_ac_rst_vs,
@@ -1213,13 +1213,13 @@ static struct llc_conn_state_trans llc_normal_state_trans_19 = {
};
/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_20a[] = {
+static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_20a[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_0,
[1] = llc_conn_ev_qlfy_retry_cnt_lt_n2,
[2] = NULL,
};
-static llc_conn_action_t llc_normal_actions_20a[] = {
+static const llc_conn_action_t llc_normal_actions_20a[] = {
[0] = llc_conn_ac_rst_sendack_flag,
[1] = llc_conn_ac_send_rr_cmd_p_set_1,
[2] = llc_conn_ac_rst_vs,
@@ -1236,13 +1236,13 @@ static struct llc_conn_state_trans llc_normal_state_trans_20a = {
};
/* State transitions for LLC_CONN_EV_BUSY_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_20b[] = {
+static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_20b[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_0,
[1] = llc_conn_ev_qlfy_retry_cnt_lt_n2,
[2] = NULL,
};
-static llc_conn_action_t llc_normal_actions_20b[] = {
+static const llc_conn_action_t llc_normal_actions_20b[] = {
[0] = llc_conn_ac_rst_sendack_flag,
[1] = llc_conn_ac_send_rr_cmd_p_set_1,
[2] = llc_conn_ac_rst_vs,
@@ -1259,12 +1259,12 @@ static struct llc_conn_state_trans llc_normal_state_trans_20b = {
};
/* State transitions for LLC_CONN_EV_TX_BUFF_FULL event */
-static llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_21[] = {
+static const llc_conn_ev_qfyr_t llc_normal_ev_qfyrs_21[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_0,
[1] = NULL,
};
-static llc_conn_action_t llc_normal_actions_21[] = {
+static const llc_conn_action_t llc_normal_actions_21[] = {
[0] = llc_conn_ac_send_rr_cmd_p_set_1,
[1] = llc_conn_ac_start_p_timer,
[2] = NULL,
@@ -1342,13 +1342,13 @@ static struct llc_conn_state_trans *llc_normal_state_transitions[] = {
/* LLC_CONN_STATE_BUSY transitions */
/* State transitions for LLC_CONN_EV_DATA_REQ event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_1[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_1[] = {
[0] = llc_conn_ev_qlfy_remote_busy_eq_0,
[1] = llc_conn_ev_qlfy_p_flag_eq_0,
[2] = NULL,
};
-static llc_conn_action_t llc_busy_actions_1[] = {
+static const llc_conn_action_t llc_busy_actions_1[] = {
[0] = llc_conn_ac_send_i_xxx_x_set_0,
[1] = llc_conn_ac_start_ack_tmr_if_not_running,
[2] = NULL,
@@ -1362,13 +1362,13 @@ static struct llc_conn_state_trans llc_busy_state_trans_1 = {
};
/* State transitions for LLC_CONN_EV_DATA_REQ event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_2[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_2[] = {
[0] = llc_conn_ev_qlfy_remote_busy_eq_0,
[1] = llc_conn_ev_qlfy_p_flag_eq_1,
[2] = NULL,
};
-static llc_conn_action_t llc_busy_actions_2[] = {
+static const llc_conn_action_t llc_busy_actions_2[] = {
[0] = llc_conn_ac_send_i_xxx_x_set_0,
[1] = llc_conn_ac_start_ack_tmr_if_not_running,
[2] = NULL,
@@ -1382,14 +1382,14 @@ static struct llc_conn_state_trans llc_busy_state_trans_2 = {
};
/* State transitions for LLC_CONN_EV_DATA_REQ event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_2_1[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_2_1[] = {
[0] = llc_conn_ev_qlfy_remote_busy_eq_1,
[1] = llc_conn_ev_qlfy_set_status_remote_busy,
[2] = NULL,
};
/* just one member, NULL, .bss zeroes it */
-static llc_conn_action_t llc_busy_actions_2_1[1];
+static const llc_conn_action_t llc_busy_actions_2_1[1];
static struct llc_conn_state_trans llc_busy_state_trans_2_1 = {
.ev = llc_conn_ev_data_req,
@@ -1399,13 +1399,13 @@ static struct llc_conn_state_trans llc_busy_state_trans_2_1 = {
};
/* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_3[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_3[] = {
[0] = llc_conn_ev_qlfy_data_flag_eq_1,
[1] = llc_conn_ev_qlfy_p_flag_eq_0,
[2] = NULL,
};
-static llc_conn_action_t llc_busy_actions_3[] = {
+static const llc_conn_action_t llc_busy_actions_3[] = {
[0] = llc_conn_ac_send_rej_xxx_x_set_0,
[1] = llc_conn_ac_start_rej_timer,
[2] = NULL,
@@ -1419,13 +1419,13 @@ static struct llc_conn_state_trans llc_busy_state_trans_3 = {
};
/* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_4[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_4[] = {
[0] = llc_conn_ev_qlfy_data_flag_eq_1,
[1] = llc_conn_ev_qlfy_p_flag_eq_1,
[2] = NULL,
};
-static llc_conn_action_t llc_busy_actions_4[] = {
+static const llc_conn_action_t llc_busy_actions_4[] = {
[0] = llc_conn_ac_send_rej_xxx_x_set_0,
[1] = llc_conn_ac_start_rej_timer,
[2] = NULL,
@@ -1439,13 +1439,13 @@ static struct llc_conn_state_trans llc_busy_state_trans_4 = {
};
/* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_5[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_5[] = {
[0] = llc_conn_ev_qlfy_data_flag_eq_0,
[1] = llc_conn_ev_qlfy_p_flag_eq_0,
[2] = NULL,
};
-static llc_conn_action_t llc_busy_actions_5[] = {
+static const llc_conn_action_t llc_busy_actions_5[] = {
[0] = llc_conn_ac_send_rr_xxx_x_set_0,
[1] = NULL,
};
@@ -1458,13 +1458,13 @@ static struct llc_conn_state_trans llc_busy_state_trans_5 = {
};
/* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_6[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_6[] = {
[0] = llc_conn_ev_qlfy_data_flag_eq_0,
[1] = llc_conn_ev_qlfy_p_flag_eq_1,
[2] = NULL,
};
-static llc_conn_action_t llc_busy_actions_6[] = {
+static const llc_conn_action_t llc_busy_actions_6[] = {
[0] = llc_conn_ac_send_rr_xxx_x_set_0,
[1] = NULL,
};
@@ -1477,13 +1477,13 @@ static struct llc_conn_state_trans llc_busy_state_trans_6 = {
};
/* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_7[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_7[] = {
[0] = llc_conn_ev_qlfy_data_flag_eq_2,
[1] = llc_conn_ev_qlfy_p_flag_eq_0,
[2] = NULL,
};
-static llc_conn_action_t llc_busy_actions_7[] = {
+static const llc_conn_action_t llc_busy_actions_7[] = {
[0] = llc_conn_ac_send_rr_xxx_x_set_0,
[1] = NULL,
};
@@ -1496,13 +1496,13 @@ static struct llc_conn_state_trans llc_busy_state_trans_7 = {
};
/* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_8[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_8[] = {
[0] = llc_conn_ev_qlfy_data_flag_eq_2,
[1] = llc_conn_ev_qlfy_p_flag_eq_1,
[2] = NULL,
};
-static llc_conn_action_t llc_busy_actions_8[] = {
+static const llc_conn_action_t llc_busy_actions_8[] = {
[0] = llc_conn_ac_send_rr_xxx_x_set_0,
[1] = NULL,
};
@@ -1515,12 +1515,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_8 = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_X_UNEXPD_Ns event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_9a[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_9a[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_f,
[1] = NULL,
};
-static llc_conn_action_t llc_busy_actions_9a[] = {
+static const llc_conn_action_t llc_busy_actions_9a[] = {
[0] = llc_conn_ac_opt_send_rnr_xxx_x_set_0,
[1] = llc_conn_ac_upd_p_flag,
[2] = llc_conn_ac_upd_nr_received,
@@ -1537,12 +1537,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_9a = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_9b[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_9b[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_0,
[1] = NULL,
};
-static llc_conn_action_t llc_busy_actions_9b[] = {
+static const llc_conn_action_t llc_busy_actions_9b[] = {
[0] = llc_conn_ac_opt_send_rnr_xxx_x_set_0,
[1] = llc_conn_ac_upd_p_flag,
[2] = llc_conn_ac_upd_nr_received,
@@ -1559,12 +1559,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_9b = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0_UNEXPD_Ns event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_10a[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_10a[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_busy_actions_10a[] = {
+static const llc_conn_action_t llc_busy_actions_10a[] = {
[0] = llc_conn_ac_opt_send_rnr_xxx_x_set_0,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_set_data_flag_1_if_data_flag_eq_0,
@@ -1579,12 +1579,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_10a = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_10b[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_10b[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_busy_actions_10b[] = {
+static const llc_conn_action_t llc_busy_actions_10b[] = {
[0] = llc_conn_ac_opt_send_rnr_xxx_x_set_0,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_set_data_flag_1_if_data_flag_eq_0,
@@ -1599,7 +1599,7 @@ static struct llc_conn_state_trans llc_busy_state_trans_10b = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1_UNEXPD_Ns event */
-static llc_conn_action_t llc_busy_actions_11[] = {
+static const llc_conn_action_t llc_busy_actions_11[] = {
[0] = llc_conn_ac_send_rnr_rsp_f_set_1,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_set_data_flag_1_if_data_flag_eq_0,
@@ -1614,7 +1614,7 @@ static struct llc_conn_state_trans llc_busy_state_trans_11 = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1 event */
-static llc_conn_action_t llc_busy_actions_12[] = {
+static const llc_conn_action_t llc_busy_actions_12[] = {
[0] = llc_conn_ac_inc_vr_by_1,
[1] = llc_conn_ac_data_ind,
[2] = llc_conn_ac_send_rnr_rsp_f_set_1,
@@ -1632,12 +1632,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_12 = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_X event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_13a[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_13a[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_f,
[1] = NULL,
};
-static llc_conn_action_t llc_busy_actions_13a[] = {
+static const llc_conn_action_t llc_busy_actions_13a[] = {
[0] = llc_conn_ac_inc_vr_by_1,
[1] = llc_conn_ac_data_ind,
[2] = llc_conn_ac_upd_p_flag,
@@ -1657,12 +1657,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_13a = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_13b[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_13b[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_0,
[1] = NULL,
};
-static llc_conn_action_t llc_busy_actions_13b[] = {
+static const llc_conn_action_t llc_busy_actions_13b[] = {
[0] = llc_conn_ac_inc_vr_by_1,
[1] = llc_conn_ac_data_ind,
[2] = llc_conn_ac_upd_p_flag,
@@ -1682,12 +1682,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_13b = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0 event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_14a[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_14a[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_busy_actions_14a[] = {
+static const llc_conn_action_t llc_busy_actions_14a[] = {
[0] = llc_conn_ac_inc_vr_by_1,
[1] = llc_conn_ac_data_ind,
[2] = llc_conn_ac_opt_send_rnr_xxx_x_set_0,
@@ -1705,12 +1705,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_14a = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_14b[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_14b[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_busy_actions_14b[] = {
+static const llc_conn_action_t llc_busy_actions_14b[] = {
[0] = llc_conn_ac_inc_vr_by_1,
[1] = llc_conn_ac_data_ind,
[2] = llc_conn_ac_opt_send_rnr_xxx_x_set_0,
@@ -1728,7 +1728,7 @@ static struct llc_conn_state_trans llc_busy_state_trans_14b = {
};
/* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_0 event */
-static llc_conn_action_t llc_busy_actions_15a[] = {
+static const llc_conn_action_t llc_busy_actions_15a[] = {
[0] = llc_conn_ac_upd_p_flag,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_clear_remote_busy,
@@ -1743,7 +1743,7 @@ static struct llc_conn_state_trans llc_busy_state_trans_15a = {
};
/* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_0 event */
-static llc_conn_action_t llc_busy_actions_15b[] = {
+static const llc_conn_action_t llc_busy_actions_15b[] = {
[0] = llc_conn_ac_upd_p_flag,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_clear_remote_busy,
@@ -1758,12 +1758,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_15b = {
};
/* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_1 event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_15c[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_15c[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_busy_actions_15c[] = {
+static const llc_conn_action_t llc_busy_actions_15c[] = {
[0] = llc_conn_ac_upd_p_flag,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_clear_remote_busy,
@@ -1778,7 +1778,7 @@ static struct llc_conn_state_trans llc_busy_state_trans_15c = {
};
/* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_1 event */
-static llc_conn_action_t llc_busy_actions_16[] = {
+static const llc_conn_action_t llc_busy_actions_16[] = {
[0] = llc_conn_ac_send_rnr_rsp_f_set_1,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_clear_remote_busy,
@@ -1793,7 +1793,7 @@ static struct llc_conn_state_trans llc_busy_state_trans_16 = {
};
/* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_0 event */
-static llc_conn_action_t llc_busy_actions_17a[] = {
+static const llc_conn_action_t llc_busy_actions_17a[] = {
[0] = llc_conn_ac_upd_p_flag,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_set_remote_busy,
@@ -1808,7 +1808,7 @@ static struct llc_conn_state_trans llc_busy_state_trans_17a = {
};
/* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_0 event */
-static llc_conn_action_t llc_busy_actions_17b[] = {
+static const llc_conn_action_t llc_busy_actions_17b[] = {
[0] = llc_conn_ac_upd_p_flag,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_set_remote_busy,
@@ -1823,12 +1823,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_17b = {
};
/* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_1 event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_17c[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_17c[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_busy_actions_17c[] = {
+static const llc_conn_action_t llc_busy_actions_17c[] = {
[0] = llc_conn_ac_upd_p_flag,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_set_remote_busy,
@@ -1843,7 +1843,7 @@ static struct llc_conn_state_trans llc_busy_state_trans_17c = {
};
/* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_1 event */
-static llc_conn_action_t llc_busy_actions_18[] = {
+static const llc_conn_action_t llc_busy_actions_18[] = {
[0] = llc_conn_ac_send_rnr_rsp_f_set_1,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_set_remote_busy,
@@ -1858,12 +1858,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_18 = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_19a[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_19a[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_0,
[1] = NULL,
};
-static llc_conn_action_t llc_busy_actions_19a[] = {
+static const llc_conn_action_t llc_busy_actions_19a[] = {
[0] = llc_conn_ac_set_vs_nr,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_upd_p_flag,
@@ -1880,12 +1880,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_19a = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_X event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_19b[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_19b[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_f,
[1] = NULL,
};
-static llc_conn_action_t llc_busy_actions_19b[] = {
+static const llc_conn_action_t llc_busy_actions_19b[] = {
[0] = llc_conn_ac_set_vs_nr,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_upd_p_flag,
@@ -1902,12 +1902,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_19b = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_20a[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_20a[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_busy_actions_20a[] = {
+static const llc_conn_action_t llc_busy_actions_20a[] = {
[0] = llc_conn_ac_set_vs_nr,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_resend_i_xxx_x_set_0,
@@ -1923,12 +1923,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_20a = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_0 event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_20b[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_20b[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_busy_actions_20b[] = {
+static const llc_conn_action_t llc_busy_actions_20b[] = {
[0] = llc_conn_ac_set_vs_nr,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_resend_i_xxx_x_set_0,
@@ -1944,7 +1944,7 @@ static struct llc_conn_state_trans llc_busy_state_trans_20b = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_1 event */
-static llc_conn_action_t llc_busy_actions_21[] = {
+static const llc_conn_action_t llc_busy_actions_21[] = {
[0] = llc_conn_ac_set_vs_nr,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_send_rnr_rsp_f_set_1,
@@ -1961,12 +1961,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_21 = {
};
/* State transitions for LLC_CONN_EV_INIT_P_F_CYCLE event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_22[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_22[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_0,
[1] = NULL,
};
-static llc_conn_action_t llc_busy_actions_22[] = {
+static const llc_conn_action_t llc_busy_actions_22[] = {
[0] = llc_conn_ac_send_rnr_cmd_p_set_1,
[1] = llc_conn_ac_start_p_timer,
[2] = NULL,
@@ -1980,12 +1980,12 @@ static struct llc_conn_state_trans llc_busy_state_trans_22 = {
};
/* State transitions for LLC_CONN_EV_P_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_23[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_23[] = {
[0] = llc_conn_ev_qlfy_retry_cnt_lt_n2,
[1] = NULL,
};
-static llc_conn_action_t llc_busy_actions_23[] = {
+static const llc_conn_action_t llc_busy_actions_23[] = {
[0] = llc_conn_ac_send_rnr_cmd_p_set_1,
[1] = llc_conn_ac_rst_vs,
[2] = llc_conn_ac_start_p_timer,
@@ -2001,13 +2001,13 @@ static struct llc_conn_state_trans llc_busy_state_trans_23 = {
};
/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_24a[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_24a[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_0,
[1] = llc_conn_ev_qlfy_retry_cnt_lt_n2,
[2] = NULL,
};
-static llc_conn_action_t llc_busy_actions_24a[] = {
+static const llc_conn_action_t llc_busy_actions_24a[] = {
[0] = llc_conn_ac_send_rnr_cmd_p_set_1,
[1] = llc_conn_ac_start_p_timer,
[2] = llc_conn_ac_inc_retry_cnt_by_1,
@@ -2023,13 +2023,13 @@ static struct llc_conn_state_trans llc_busy_state_trans_24a = {
};
/* State transitions for LLC_CONN_EV_BUSY_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_24b[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_24b[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_0,
[1] = llc_conn_ev_qlfy_retry_cnt_lt_n2,
[2] = NULL,
};
-static llc_conn_action_t llc_busy_actions_24b[] = {
+static const llc_conn_action_t llc_busy_actions_24b[] = {
[0] = llc_conn_ac_send_rnr_cmd_p_set_1,
[1] = llc_conn_ac_start_p_timer,
[2] = llc_conn_ac_inc_retry_cnt_by_1,
@@ -2045,13 +2045,13 @@ static struct llc_conn_state_trans llc_busy_state_trans_24b = {
};
/* State transitions for LLC_CONN_EV_REJ_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_25[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_25[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_0,
[1] = llc_conn_ev_qlfy_retry_cnt_lt_n2,
[2] = NULL,
};
-static llc_conn_action_t llc_busy_actions_25[] = {
+static const llc_conn_action_t llc_busy_actions_25[] = {
[0] = llc_conn_ac_send_rnr_cmd_p_set_1,
[1] = llc_conn_ac_start_p_timer,
[2] = llc_conn_ac_inc_retry_cnt_by_1,
@@ -2068,13 +2068,13 @@ static struct llc_conn_state_trans llc_busy_state_trans_25 = {
};
/* State transitions for LLC_CONN_EV_REJ_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_26[] = {
+static const llc_conn_ev_qfyr_t llc_busy_ev_qfyrs_26[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = llc_conn_ev_qlfy_retry_cnt_lt_n2,
[2] = NULL,
};
-static llc_conn_action_t llc_busy_actions_26[] = {
+static const llc_conn_action_t llc_busy_actions_26[] = {
[0] = llc_conn_ac_set_data_flag_1,
[1] = NULL,
};
@@ -2155,13 +2155,13 @@ static struct llc_conn_state_trans *llc_busy_state_transitions[] = {
/* LLC_CONN_STATE_REJ transitions */
/* State transitions for LLC_CONN_EV_DATA_REQ event */
-static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_1[] = {
+static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_1[] = {
[0] = llc_conn_ev_qlfy_remote_busy_eq_0,
[1] = llc_conn_ev_qlfy_p_flag_eq_0,
[2] = NULL,
};
-static llc_conn_action_t llc_reject_actions_1[] = {
+static const llc_conn_action_t llc_reject_actions_1[] = {
[0] = llc_conn_ac_send_i_xxx_x_set_0,
[1] = NULL,
};
@@ -2174,13 +2174,13 @@ static struct llc_conn_state_trans llc_reject_state_trans_1 = {
};
/* State transitions for LLC_CONN_EV_DATA_REQ event */
-static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_2[] = {
+static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_2[] = {
[0] = llc_conn_ev_qlfy_remote_busy_eq_0,
[1] = llc_conn_ev_qlfy_p_flag_eq_1,
[2] = NULL,
};
-static llc_conn_action_t llc_reject_actions_2[] = {
+static const llc_conn_action_t llc_reject_actions_2[] = {
[0] = llc_conn_ac_send_i_xxx_x_set_0,
[1] = NULL,
};
@@ -2193,14 +2193,14 @@ static struct llc_conn_state_trans llc_reject_state_trans_2 = {
};
/* State transitions for LLC_CONN_EV_DATA_REQ event */
-static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_2_1[] = {
+static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_2_1[] = {
[0] = llc_conn_ev_qlfy_remote_busy_eq_1,
[1] = llc_conn_ev_qlfy_set_status_remote_busy,
[2] = NULL,
};
/* just one member, NULL, .bss zeroes it */
-static llc_conn_action_t llc_reject_actions_2_1[1];
+static const llc_conn_action_t llc_reject_actions_2_1[1];
static struct llc_conn_state_trans llc_reject_state_trans_2_1 = {
.ev = llc_conn_ev_data_req,
@@ -2211,12 +2211,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_2_1 = {
/* State transitions for LLC_CONN_EV_LOCAL_BUSY_DETECTED event */
-static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_3[] = {
+static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_3[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_0,
[1] = NULL,
};
-static llc_conn_action_t llc_reject_actions_3[] = {
+static const llc_conn_action_t llc_reject_actions_3[] = {
[0] = llc_conn_ac_send_rnr_xxx_x_set_0,
[1] = llc_conn_ac_set_data_flag_2,
[2] = NULL,
@@ -2230,12 +2230,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_3 = {
};
/* State transitions for LLC_CONN_EV_LOCAL_BUSY_DETECTED event */
-static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_4[] = {
+static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_4[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_reject_actions_4[] = {
+static const llc_conn_action_t llc_reject_actions_4[] = {
[0] = llc_conn_ac_send_rnr_xxx_x_set_0,
[1] = llc_conn_ac_set_data_flag_2,
[2] = NULL,
@@ -2249,7 +2249,7 @@ static struct llc_conn_state_trans llc_reject_state_trans_4 = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns event */
-static llc_conn_action_t llc_reject_actions_5a[] = {
+static const llc_conn_action_t llc_reject_actions_5a[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_p_flag,
[2] = llc_conn_ac_clear_remote_busy_if_f_eq_1,
@@ -2264,7 +2264,7 @@ static struct llc_conn_state_trans llc_reject_state_trans_5a = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0_UNEXPD_Ns event */
-static llc_conn_action_t llc_reject_actions_5b[] = {
+static const llc_conn_action_t llc_reject_actions_5b[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_p_flag,
[2] = llc_conn_ac_clear_remote_busy_if_f_eq_1,
@@ -2279,12 +2279,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_5b = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_1_UNEXPD_Ns event */
-static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_5c[] = {
+static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_5c[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_reject_actions_5c[] = {
+static const llc_conn_action_t llc_reject_actions_5c[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_p_flag,
[2] = llc_conn_ac_clear_remote_busy_if_f_eq_1,
@@ -2299,7 +2299,7 @@ static struct llc_conn_state_trans llc_reject_state_trans_5c = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1_UNEXPD_Ns event */
-static llc_conn_action_t llc_reject_actions_6[] = {
+static const llc_conn_action_t llc_reject_actions_6[] = {
[0] = llc_conn_ac_send_rr_rsp_f_set_1,
[1] = llc_conn_ac_upd_nr_received,
[2] = NULL,
@@ -2313,12 +2313,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_6 = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_X event */
-static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_7a[] = {
+static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_7a[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_f,
[1] = NULL,
};
-static llc_conn_action_t llc_reject_actions_7a[] = {
+static const llc_conn_action_t llc_reject_actions_7a[] = {
[0] = llc_conn_ac_inc_vr_by_1,
[1] = llc_conn_ac_data_ind,
[2] = llc_conn_ac_upd_p_flag,
@@ -2338,12 +2338,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_7a = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */
-static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_7b[] = {
+static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_7b[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_0,
[1] = NULL,
};
-static llc_conn_action_t llc_reject_actions_7b[] = {
+static const llc_conn_action_t llc_reject_actions_7b[] = {
[0] = llc_conn_ac_inc_vr_by_1,
[1] = llc_conn_ac_data_ind,
[2] = llc_conn_ac_upd_p_flag,
@@ -2362,12 +2362,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_7b = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0 event */
-static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_8a[] = {
+static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_8a[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_reject_actions_8a[] = {
+static const llc_conn_action_t llc_reject_actions_8a[] = {
[0] = llc_conn_ac_inc_vr_by_1,
[1] = llc_conn_ac_data_ind,
[2] = llc_conn_ac_send_ack_xxx_x_set_0,
@@ -2384,12 +2384,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_8a = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */
-static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_8b[] = {
+static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_8b[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_reject_actions_8b[] = {
+static const llc_conn_action_t llc_reject_actions_8b[] = {
[0] = llc_conn_ac_inc_vr_by_1,
[1] = llc_conn_ac_data_ind,
[2] = llc_conn_ac_send_ack_xxx_x_set_0,
@@ -2406,7 +2406,7 @@ static struct llc_conn_state_trans llc_reject_state_trans_8b = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1 event */
-static llc_conn_action_t llc_reject_actions_9[] = {
+static const llc_conn_action_t llc_reject_actions_9[] = {
[0] = llc_conn_ac_inc_vr_by_1,
[1] = llc_conn_ac_data_ind,
[2] = llc_conn_ac_send_ack_rsp_f_set_1,
@@ -2423,7 +2423,7 @@ static struct llc_conn_state_trans llc_reject_state_trans_9 = {
};
/* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_0 event */
-static llc_conn_action_t llc_reject_actions_10a[] = {
+static const llc_conn_action_t llc_reject_actions_10a[] = {
[0] = llc_conn_ac_upd_p_flag,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_clear_remote_busy,
@@ -2438,7 +2438,7 @@ static struct llc_conn_state_trans llc_reject_state_trans_10a = {
};
/* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_0 event */
-static llc_conn_action_t llc_reject_actions_10b[] = {
+static const llc_conn_action_t llc_reject_actions_10b[] = {
[0] = llc_conn_ac_upd_p_flag,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_clear_remote_busy,
@@ -2453,12 +2453,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_10b = {
};
/* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_1 event */
-static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_10c[] = {
+static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_10c[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_reject_actions_10c[] = {
+static const llc_conn_action_t llc_reject_actions_10c[] = {
[0] = llc_conn_ac_upd_p_flag,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_clear_remote_busy,
@@ -2473,7 +2473,7 @@ static struct llc_conn_state_trans llc_reject_state_trans_10c = {
};
/* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_1 event */
-static llc_conn_action_t llc_reject_actions_11[] = {
+static const llc_conn_action_t llc_reject_actions_11[] = {
[0] = llc_conn_ac_send_ack_rsp_f_set_1,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_clear_remote_busy,
@@ -2488,7 +2488,7 @@ static struct llc_conn_state_trans llc_reject_state_trans_11 = {
};
/* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_0 event */
-static llc_conn_action_t llc_reject_actions_12a[] = {
+static const llc_conn_action_t llc_reject_actions_12a[] = {
[0] = llc_conn_ac_upd_p_flag,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_set_remote_busy,
@@ -2503,7 +2503,7 @@ static struct llc_conn_state_trans llc_reject_state_trans_12a = {
};
/* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_0 event */
-static llc_conn_action_t llc_reject_actions_12b[] = {
+static const llc_conn_action_t llc_reject_actions_12b[] = {
[0] = llc_conn_ac_upd_p_flag,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_set_remote_busy,
@@ -2518,12 +2518,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_12b = {
};
/* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_1 event */
-static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_12c[] = {
+static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_12c[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_reject_actions_12c[] = {
+static const llc_conn_action_t llc_reject_actions_12c[] = {
[0] = llc_conn_ac_upd_p_flag,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_set_remote_busy,
@@ -2538,7 +2538,7 @@ static struct llc_conn_state_trans llc_reject_state_trans_12c = {
};
/* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_1 event */
-static llc_conn_action_t llc_reject_actions_13[] = {
+static const llc_conn_action_t llc_reject_actions_13[] = {
[0] = llc_conn_ac_send_rr_rsp_f_set_1,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_set_remote_busy,
@@ -2553,12 +2553,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_13 = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */
-static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_14a[] = {
+static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_14a[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_0,
[1] = NULL,
};
-static llc_conn_action_t llc_reject_actions_14a[] = {
+static const llc_conn_action_t llc_reject_actions_14a[] = {
[0] = llc_conn_ac_set_vs_nr,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_upd_p_flag,
@@ -2575,12 +2575,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_14a = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_X event */
-static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_14b[] = {
+static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_14b[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_f,
[1] = NULL,
};
-static llc_conn_action_t llc_reject_actions_14b[] = {
+static const llc_conn_action_t llc_reject_actions_14b[] = {
[0] = llc_conn_ac_set_vs_nr,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_upd_p_flag,
@@ -2597,12 +2597,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_14b = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */
-static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_15a[] = {
+static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_15a[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_reject_actions_15a[] = {
+static const llc_conn_action_t llc_reject_actions_15a[] = {
[0] = llc_conn_ac_set_vs_nr,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_resend_i_xxx_x_set_0,
@@ -2618,12 +2618,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_15a = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_0 event */
-static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_15b[] = {
+static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_15b[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_reject_actions_15b[] = {
+static const llc_conn_action_t llc_reject_actions_15b[] = {
[0] = llc_conn_ac_set_vs_nr,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_resend_i_xxx_x_set_0,
@@ -2639,7 +2639,7 @@ static struct llc_conn_state_trans llc_reject_state_trans_15b = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_1 event */
-static llc_conn_action_t llc_reject_actions_16[] = {
+static const llc_conn_action_t llc_reject_actions_16[] = {
[0] = llc_conn_ac_set_vs_nr,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_resend_i_rsp_f_set_1,
@@ -2655,12 +2655,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_16 = {
};
/* State transitions for LLC_CONN_EV_INIT_P_F_CYCLE event */
-static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_17[] = {
+static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_17[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_0,
[1] = NULL,
};
-static llc_conn_action_t llc_reject_actions_17[] = {
+static const llc_conn_action_t llc_reject_actions_17[] = {
[0] = llc_conn_ac_send_rr_cmd_p_set_1,
[1] = llc_conn_ac_start_p_timer,
[2] = NULL,
@@ -2674,13 +2674,13 @@ static struct llc_conn_state_trans llc_reject_state_trans_17 = {
};
/* State transitions for LLC_CONN_EV_REJ_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_18[] = {
+static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_18[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_0,
[1] = llc_conn_ev_qlfy_retry_cnt_lt_n2,
[2] = NULL,
};
-static llc_conn_action_t llc_reject_actions_18[] = {
+static const llc_conn_action_t llc_reject_actions_18[] = {
[0] = llc_conn_ac_send_rej_cmd_p_set_1,
[1] = llc_conn_ac_start_p_timer,
[2] = llc_conn_ac_start_rej_timer,
@@ -2696,12 +2696,12 @@ static struct llc_conn_state_trans llc_reject_state_trans_18 = {
};
/* State transitions for LLC_CONN_EV_P_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_19[] = {
+static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_19[] = {
[0] = llc_conn_ev_qlfy_retry_cnt_lt_n2,
[1] = NULL,
};
-static llc_conn_action_t llc_reject_actions_19[] = {
+static const llc_conn_action_t llc_reject_actions_19[] = {
[0] = llc_conn_ac_send_rr_cmd_p_set_1,
[1] = llc_conn_ac_start_p_timer,
[2] = llc_conn_ac_start_rej_timer,
@@ -2718,13 +2718,13 @@ static struct llc_conn_state_trans llc_reject_state_trans_19 = {
};
/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_20a[] = {
+static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_20a[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_0,
[1] = llc_conn_ev_qlfy_retry_cnt_lt_n2,
[2] = NULL,
};
-static llc_conn_action_t llc_reject_actions_20a[] = {
+static const llc_conn_action_t llc_reject_actions_20a[] = {
[0] = llc_conn_ac_send_rr_cmd_p_set_1,
[1] = llc_conn_ac_start_p_timer,
[2] = llc_conn_ac_start_rej_timer,
@@ -2741,13 +2741,13 @@ static struct llc_conn_state_trans llc_reject_state_trans_20a = {
};
/* State transitions for LLC_CONN_EV_BUSY_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_20b[] = {
+static const llc_conn_ev_qfyr_t llc_reject_ev_qfyrs_20b[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_0,
[1] = llc_conn_ev_qlfy_retry_cnt_lt_n2,
[2] = NULL,
};
-static llc_conn_action_t llc_reject_actions_20b[] = {
+static const llc_conn_action_t llc_reject_actions_20b[] = {
[0] = llc_conn_ac_send_rr_cmd_p_set_1,
[1] = llc_conn_ac_start_p_timer,
[2] = llc_conn_ac_start_rej_timer,
@@ -2826,13 +2826,13 @@ static struct llc_conn_state_trans *llc_reject_state_transitions[] = {
/* LLC_CONN_STATE_AWAIT transitions */
/* State transitions for LLC_CONN_EV_DATA_REQ event */
-static llc_conn_ev_qfyr_t llc_await_ev_qfyrs_1_0[] = {
+static const llc_conn_ev_qfyr_t llc_await_ev_qfyrs_1_0[] = {
[0] = llc_conn_ev_qlfy_set_status_refuse,
[1] = NULL,
};
/* just one member, NULL, .bss zeroes it */
-static llc_conn_action_t llc_await_actions_1_0[1];
+static const llc_conn_action_t llc_await_actions_1_0[1];
static struct llc_conn_state_trans llc_await_state_trans_1_0 = {
.ev = llc_conn_ev_data_req,
@@ -2842,7 +2842,7 @@ static struct llc_conn_state_trans llc_await_state_trans_1_0 = {
};
/* State transitions for LLC_CONN_EV_LOCAL_BUSY_DETECTED event */
-static llc_conn_action_t llc_await_actions_1[] = {
+static const llc_conn_action_t llc_await_actions_1[] = {
[0] = llc_conn_ac_send_rnr_xxx_x_set_0,
[1] = llc_conn_ac_set_data_flag_0,
[2] = NULL,
@@ -2856,7 +2856,7 @@ static struct llc_conn_state_trans llc_await_state_trans_1 = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_1_UNEXPD_Ns event */
-static llc_conn_action_t llc_await_actions_2[] = {
+static const llc_conn_action_t llc_await_actions_2[] = {
[0] = llc_conn_ac_send_rej_xxx_x_set_0,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_upd_vs,
@@ -2875,7 +2875,7 @@ static struct llc_conn_state_trans llc_await_state_trans_2 = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns event */
-static llc_conn_action_t llc_await_actions_3a[] = {
+static const llc_conn_action_t llc_await_actions_3a[] = {
[0] = llc_conn_ac_send_rej_xxx_x_set_0,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_upd_vs,
@@ -2891,7 +2891,7 @@ static struct llc_conn_state_trans llc_await_state_trans_3a = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0_UNEXPD_Ns event */
-static llc_conn_action_t llc_await_actions_3b[] = {
+static const llc_conn_action_t llc_await_actions_3b[] = {
[0] = llc_conn_ac_send_rej_xxx_x_set_0,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_upd_vs,
@@ -2907,7 +2907,7 @@ static struct llc_conn_state_trans llc_await_state_trans_3b = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1_UNEXPD_Ns event */
-static llc_conn_action_t llc_await_actions_4[] = {
+static const llc_conn_action_t llc_await_actions_4[] = {
[0] = llc_conn_ac_send_rej_rsp_f_set_1,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_upd_vs,
@@ -2924,7 +2924,7 @@ static struct llc_conn_state_trans llc_await_state_trans_4 = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_1 event */
-static llc_conn_action_t llc_await_actions_5[] = {
+static const llc_conn_action_t llc_await_actions_5[] = {
[0] = llc_conn_ac_inc_vr_by_1,
[1] = llc_conn_ac_data_ind,
[2] = llc_conn_ac_stop_p_timer,
@@ -2943,7 +2943,7 @@ static struct llc_conn_state_trans llc_await_state_trans_5 = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0 event */
-static llc_conn_action_t llc_await_actions_6a[] = {
+static const llc_conn_action_t llc_await_actions_6a[] = {
[0] = llc_conn_ac_inc_vr_by_1,
[1] = llc_conn_ac_data_ind,
[2] = llc_conn_ac_send_rr_xxx_x_set_0,
@@ -2960,7 +2960,7 @@ static struct llc_conn_state_trans llc_await_state_trans_6a = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */
-static llc_conn_action_t llc_await_actions_6b[] = {
+static const llc_conn_action_t llc_await_actions_6b[] = {
[0] = llc_conn_ac_inc_vr_by_1,
[1] = llc_conn_ac_data_ind,
[2] = llc_conn_ac_send_rr_xxx_x_set_0,
@@ -2977,7 +2977,7 @@ static struct llc_conn_state_trans llc_await_state_trans_6b = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1 event */
-static llc_conn_action_t llc_await_actions_7[] = {
+static const llc_conn_action_t llc_await_actions_7[] = {
[0] = llc_conn_ac_inc_vr_by_1,
[1] = llc_conn_ac_data_ind,
[2] = llc_conn_ac_send_rr_rsp_f_set_1,
@@ -2994,7 +2994,7 @@ static struct llc_conn_state_trans llc_await_state_trans_7 = {
};
/* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_1 event */
-static llc_conn_action_t llc_await_actions_8a[] = {
+static const llc_conn_action_t llc_await_actions_8a[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_stop_p_timer,
@@ -3011,7 +3011,7 @@ static struct llc_conn_state_trans llc_await_state_trans_8a = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_1 event */
-static llc_conn_action_t llc_await_actions_8b[] = {
+static const llc_conn_action_t llc_await_actions_8b[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_stop_p_timer,
@@ -3028,7 +3028,7 @@ static struct llc_conn_state_trans llc_await_state_trans_8b = {
};
/* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_0 event */
-static llc_conn_action_t llc_await_actions_9a[] = {
+static const llc_conn_action_t llc_await_actions_9a[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_clear_remote_busy,
@@ -3043,7 +3043,7 @@ static struct llc_conn_state_trans llc_await_state_trans_9a = {
};
/* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_0 event */
-static llc_conn_action_t llc_await_actions_9b[] = {
+static const llc_conn_action_t llc_await_actions_9b[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_clear_remote_busy,
@@ -3058,7 +3058,7 @@ static struct llc_conn_state_trans llc_await_state_trans_9b = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */
-static llc_conn_action_t llc_await_actions_9c[] = {
+static const llc_conn_action_t llc_await_actions_9c[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_clear_remote_busy,
@@ -3073,7 +3073,7 @@ static struct llc_conn_state_trans llc_await_state_trans_9c = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_0 event */
-static llc_conn_action_t llc_await_actions_9d[] = {
+static const llc_conn_action_t llc_await_actions_9d[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_clear_remote_busy,
@@ -3088,7 +3088,7 @@ static struct llc_conn_state_trans llc_await_state_trans_9d = {
};
/* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_1 event */
-static llc_conn_action_t llc_await_actions_10a[] = {
+static const llc_conn_action_t llc_await_actions_10a[] = {
[0] = llc_conn_ac_send_rr_rsp_f_set_1,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_upd_vs,
@@ -3104,7 +3104,7 @@ static struct llc_conn_state_trans llc_await_state_trans_10a = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_1 event */
-static llc_conn_action_t llc_await_actions_10b[] = {
+static const llc_conn_action_t llc_await_actions_10b[] = {
[0] = llc_conn_ac_send_rr_rsp_f_set_1,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_upd_vs,
@@ -3120,7 +3120,7 @@ static struct llc_conn_state_trans llc_await_state_trans_10b = {
};
/* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_1 event */
-static llc_conn_action_t llc_await_actions_11[] = {
+static const llc_conn_action_t llc_await_actions_11[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_stop_p_timer,
@@ -3136,7 +3136,7 @@ static struct llc_conn_state_trans llc_await_state_trans_11 = {
};
/* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_0 event */
-static llc_conn_action_t llc_await_actions_12a[] = {
+static const llc_conn_action_t llc_await_actions_12a[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_set_remote_busy,
@@ -3151,7 +3151,7 @@ static struct llc_conn_state_trans llc_await_state_trans_12a = {
};
/* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_0 event */
-static llc_conn_action_t llc_await_actions_12b[] = {
+static const llc_conn_action_t llc_await_actions_12b[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_set_remote_busy,
@@ -3166,7 +3166,7 @@ static struct llc_conn_state_trans llc_await_state_trans_12b = {
};
/* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_1 event */
-static llc_conn_action_t llc_await_actions_13[] = {
+static const llc_conn_action_t llc_await_actions_13[] = {
[0] = llc_conn_ac_send_rr_rsp_f_set_1,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_upd_vs,
@@ -3182,12 +3182,12 @@ static struct llc_conn_state_trans llc_await_state_trans_13 = {
};
/* State transitions for LLC_CONN_EV_P_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_await_ev_qfyrs_14[] = {
+static const llc_conn_ev_qfyr_t llc_await_ev_qfyrs_14[] = {
[0] = llc_conn_ev_qlfy_retry_cnt_lt_n2,
[1] = NULL,
};
-static llc_conn_action_t llc_await_actions_14[] = {
+static const llc_conn_action_t llc_await_actions_14[] = {
[0] = llc_conn_ac_send_rr_cmd_p_set_1,
[1] = llc_conn_ac_start_p_timer,
[2] = llc_conn_ac_inc_retry_cnt_by_1,
@@ -3255,13 +3255,13 @@ static struct llc_conn_state_trans *llc_await_state_transitions[] = {
/* LLC_CONN_STATE_AWAIT_BUSY transitions */
/* State transitions for LLC_CONN_EV_DATA_CONN_REQ event */
-static llc_conn_ev_qfyr_t llc_await_busy_ev_qfyrs_1_0[] = {
+static const llc_conn_ev_qfyr_t llc_await_busy_ev_qfyrs_1_0[] = {
[0] = llc_conn_ev_qlfy_set_status_refuse,
[1] = NULL,
};
/* just one member, NULL, .bss zeroes it */
-static llc_conn_action_t llc_await_busy_actions_1_0[1];
+static const llc_conn_action_t llc_await_busy_actions_1_0[1];
static struct llc_conn_state_trans llc_await_busy_state_trans_1_0 = {
.ev = llc_conn_ev_data_req,
@@ -3271,12 +3271,12 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_1_0 = {
};
/* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */
-static llc_conn_ev_qfyr_t llc_await_busy_ev_qfyrs_1[] = {
+static const llc_conn_ev_qfyr_t llc_await_busy_ev_qfyrs_1[] = {
[0] = llc_conn_ev_qlfy_data_flag_eq_1,
[1] = NULL,
};
-static llc_conn_action_t llc_await_busy_actions_1[] = {
+static const llc_conn_action_t llc_await_busy_actions_1[] = {
[0] = llc_conn_ac_send_rej_xxx_x_set_0,
[1] = llc_conn_ac_start_rej_timer,
[2] = NULL,
@@ -3290,12 +3290,12 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_1 = {
};
/* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */
-static llc_conn_ev_qfyr_t llc_await_busy_ev_qfyrs_2[] = {
+static const llc_conn_ev_qfyr_t llc_await_busy_ev_qfyrs_2[] = {
[0] = llc_conn_ev_qlfy_data_flag_eq_0,
[1] = NULL,
};
-static llc_conn_action_t llc_await_busy_actions_2[] = {
+static const llc_conn_action_t llc_await_busy_actions_2[] = {
[0] = llc_conn_ac_send_rr_xxx_x_set_0,
[1] = NULL,
};
@@ -3308,12 +3308,12 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_2 = {
};
/* State transitions for LLC_CONN_EV_LOCAL_BUSY_CLEARED event */
-static llc_conn_ev_qfyr_t llc_await_busy_ev_qfyrs_3[] = {
+static const llc_conn_ev_qfyr_t llc_await_busy_ev_qfyrs_3[] = {
[0] = llc_conn_ev_qlfy_data_flag_eq_2,
[1] = NULL,
};
-static llc_conn_action_t llc_await_busy_actions_3[] = {
+static const llc_conn_action_t llc_await_busy_actions_3[] = {
[0] = llc_conn_ac_send_rr_xxx_x_set_0,
[1] = NULL,
};
@@ -3326,7 +3326,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_3 = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_1_UNEXPD_Ns event */
-static llc_conn_action_t llc_await_busy_actions_4[] = {
+static const llc_conn_action_t llc_await_busy_actions_4[] = {
[0] = llc_conn_ac_opt_send_rnr_xxx_x_set_0,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_upd_vs,
@@ -3345,7 +3345,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_4 = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns event */
-static llc_conn_action_t llc_await_busy_actions_5a[] = {
+static const llc_conn_action_t llc_await_busy_actions_5a[] = {
[0] = llc_conn_ac_opt_send_rnr_xxx_x_set_0,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_upd_vs,
@@ -3361,7 +3361,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_5a = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0_UNEXPD_Ns event */
-static llc_conn_action_t llc_await_busy_actions_5b[] = {
+static const llc_conn_action_t llc_await_busy_actions_5b[] = {
[0] = llc_conn_ac_opt_send_rnr_xxx_x_set_0,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_upd_vs,
@@ -3377,7 +3377,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_5b = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1_UNEXPD_Ns event */
-static llc_conn_action_t llc_await_busy_actions_6[] = {
+static const llc_conn_action_t llc_await_busy_actions_6[] = {
[0] = llc_conn_ac_send_rnr_rsp_f_set_1,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_upd_vs,
@@ -3393,7 +3393,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_6 = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_1 event */
-static llc_conn_action_t llc_await_busy_actions_7[] = {
+static const llc_conn_action_t llc_await_busy_actions_7[] = {
[0] = llc_conn_ac_opt_send_rnr_xxx_x_set_0,
[1] = llc_conn_ac_inc_vr_by_1,
[2] = llc_conn_ac_data_ind,
@@ -3414,7 +3414,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_7 = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0 event */
-static llc_conn_action_t llc_await_busy_actions_8a[] = {
+static const llc_conn_action_t llc_await_busy_actions_8a[] = {
[0] = llc_conn_ac_opt_send_rnr_xxx_x_set_0,
[1] = llc_conn_ac_inc_vr_by_1,
[2] = llc_conn_ac_data_ind,
@@ -3432,7 +3432,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_8a = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */
-static llc_conn_action_t llc_await_busy_actions_8b[] = {
+static const llc_conn_action_t llc_await_busy_actions_8b[] = {
[0] = llc_conn_ac_opt_send_rnr_xxx_x_set_0,
[1] = llc_conn_ac_inc_vr_by_1,
[2] = llc_conn_ac_data_ind,
@@ -3450,7 +3450,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_8b = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1 event */
-static llc_conn_action_t llc_await_busy_actions_9[] = {
+static const llc_conn_action_t llc_await_busy_actions_9[] = {
[0] = llc_conn_ac_send_rnr_rsp_f_set_1,
[1] = llc_conn_ac_inc_vr_by_1,
[2] = llc_conn_ac_data_ind,
@@ -3468,7 +3468,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_9 = {
};
/* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_1 event */
-static llc_conn_action_t llc_await_busy_actions_10a[] = {
+static const llc_conn_action_t llc_await_busy_actions_10a[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_stop_p_timer,
@@ -3485,7 +3485,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_10a = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_1 event */
-static llc_conn_action_t llc_await_busy_actions_10b[] = {
+static const llc_conn_action_t llc_await_busy_actions_10b[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_stop_p_timer,
@@ -3502,7 +3502,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_10b = {
};
/* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_0 event */
-static llc_conn_action_t llc_await_busy_actions_11a[] = {
+static const llc_conn_action_t llc_await_busy_actions_11a[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_clear_remote_busy,
@@ -3517,7 +3517,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_11a = {
};
/* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_0 event */
-static llc_conn_action_t llc_await_busy_actions_11b[] = {
+static const llc_conn_action_t llc_await_busy_actions_11b[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_clear_remote_busy,
@@ -3532,7 +3532,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_11b = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */
-static llc_conn_action_t llc_await_busy_actions_11c[] = {
+static const llc_conn_action_t llc_await_busy_actions_11c[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_clear_remote_busy,
@@ -3547,7 +3547,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_11c = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_0 event */
-static llc_conn_action_t llc_await_busy_actions_11d[] = {
+static const llc_conn_action_t llc_await_busy_actions_11d[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_clear_remote_busy,
@@ -3562,7 +3562,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_11d = {
};
/* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_1 event */
-static llc_conn_action_t llc_await_busy_actions_12a[] = {
+static const llc_conn_action_t llc_await_busy_actions_12a[] = {
[0] = llc_conn_ac_send_rnr_rsp_f_set_1,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_upd_vs,
@@ -3578,7 +3578,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_12a = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_1 event */
-static llc_conn_action_t llc_await_busy_actions_12b[] = {
+static const llc_conn_action_t llc_await_busy_actions_12b[] = {
[0] = llc_conn_ac_send_rnr_rsp_f_set_1,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_upd_vs,
@@ -3594,7 +3594,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_12b = {
};
/* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_1 event */
-static llc_conn_action_t llc_await_busy_actions_13[] = {
+static const llc_conn_action_t llc_await_busy_actions_13[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_stop_p_timer,
@@ -3610,7 +3610,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_13 = {
};
/* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_0 event */
-static llc_conn_action_t llc_await_busy_actions_14a[] = {
+static const llc_conn_action_t llc_await_busy_actions_14a[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_set_remote_busy,
@@ -3625,7 +3625,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_14a = {
};
/* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_0 event */
-static llc_conn_action_t llc_await_busy_actions_14b[] = {
+static const llc_conn_action_t llc_await_busy_actions_14b[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_set_remote_busy,
@@ -3640,7 +3640,7 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_14b = {
};
/* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_1 event */
-static llc_conn_action_t llc_await_busy_actions_15[] = {
+static const llc_conn_action_t llc_await_busy_actions_15[] = {
[0] = llc_conn_ac_send_rnr_rsp_f_set_1,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_upd_vs,
@@ -3656,12 +3656,12 @@ static struct llc_conn_state_trans llc_await_busy_state_trans_15 = {
};
/* State transitions for LLC_CONN_EV_P_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_await_busy_ev_qfyrs_16[] = {
+static const llc_conn_ev_qfyr_t llc_await_busy_ev_qfyrs_16[] = {
[0] = llc_conn_ev_qlfy_retry_cnt_lt_n2,
[1] = NULL,
};
-static llc_conn_action_t llc_await_busy_actions_16[] = {
+static const llc_conn_action_t llc_await_busy_actions_16[] = {
[0] = llc_conn_ac_send_rnr_cmd_p_set_1,
[1] = llc_conn_ac_start_p_timer,
[2] = llc_conn_ac_inc_retry_cnt_by_1,
@@ -3731,13 +3731,13 @@ static struct llc_conn_state_trans *llc_await_busy_state_transitions[] = {
/* ----------------- LLC_CONN_STATE_AWAIT_REJ transitions --------------- */
/* State transitions for LLC_CONN_EV_DATA_CONN_REQ event */
-static llc_conn_ev_qfyr_t llc_await_reject_ev_qfyrs_1_0[] = {
+static const llc_conn_ev_qfyr_t llc_await_reject_ev_qfyrs_1_0[] = {
[0] = llc_conn_ev_qlfy_set_status_refuse,
[1] = NULL,
};
/* just one member, NULL, .bss zeroes it */
-static llc_conn_action_t llc_await_reject_actions_1_0[1];
+static const llc_conn_action_t llc_await_reject_actions_1_0[1];
static struct llc_conn_state_trans llc_await_reject_state_trans_1_0 = {
.ev = llc_conn_ev_data_req,
@@ -3747,7 +3747,7 @@ static struct llc_conn_state_trans llc_await_reject_state_trans_1_0 = {
};
/* State transitions for LLC_CONN_EV_LOCAL_BUSY_DETECTED event */
-static llc_conn_action_t llc_await_rejct_actions_1[] = {
+static const llc_conn_action_t llc_await_rejct_actions_1[] = {
[0] = llc_conn_ac_send_rnr_xxx_x_set_0,
[1] = llc_conn_ac_set_data_flag_2,
[2] = NULL
@@ -3761,7 +3761,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_1 = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0_UNEXPD_Ns event */
-static llc_conn_action_t llc_await_rejct_actions_2a[] = {
+static const llc_conn_action_t llc_await_rejct_actions_2a[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = NULL
@@ -3775,7 +3775,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_2a = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0_UNEXPD_Ns event */
-static llc_conn_action_t llc_await_rejct_actions_2b[] = {
+static const llc_conn_action_t llc_await_rejct_actions_2b[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = NULL
@@ -3789,7 +3789,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_2b = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1_UNEXPD_Ns event */
-static llc_conn_action_t llc_await_rejct_actions_3[] = {
+static const llc_conn_action_t llc_await_rejct_actions_3[] = {
[0] = llc_conn_ac_send_rr_rsp_f_set_1,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_upd_vs,
@@ -3804,7 +3804,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_3 = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_1 event */
-static llc_conn_action_t llc_await_rejct_actions_4[] = {
+static const llc_conn_action_t llc_await_rejct_actions_4[] = {
[0] = llc_conn_ac_inc_vr_by_1,
[1] = llc_conn_ac_data_ind,
[2] = llc_conn_ac_stop_p_timer,
@@ -3824,7 +3824,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_4 = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_0 event */
-static llc_conn_action_t llc_await_rejct_actions_5a[] = {
+static const llc_conn_action_t llc_await_rejct_actions_5a[] = {
[0] = llc_conn_ac_inc_vr_by_1,
[1] = llc_conn_ac_data_ind,
[2] = llc_conn_ac_send_rr_xxx_x_set_0,
@@ -3842,7 +3842,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_5a = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_0 event */
-static llc_conn_action_t llc_await_rejct_actions_5b[] = {
+static const llc_conn_action_t llc_await_rejct_actions_5b[] = {
[0] = llc_conn_ac_inc_vr_by_1,
[1] = llc_conn_ac_data_ind,
[2] = llc_conn_ac_send_rr_xxx_x_set_0,
@@ -3860,7 +3860,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_5b = {
};
/* State transitions for LLC_CONN_EV_RX_I_CMD_Pbit_SET_1 event */
-static llc_conn_action_t llc_await_rejct_actions_6[] = {
+static const llc_conn_action_t llc_await_rejct_actions_6[] = {
[0] = llc_conn_ac_inc_vr_by_1,
[1] = llc_conn_ac_data_ind,
[2] = llc_conn_ac_send_rr_rsp_f_set_1,
@@ -3878,7 +3878,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_6 = {
};
/* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_1 event */
-static llc_conn_action_t llc_await_rejct_actions_7a[] = {
+static const llc_conn_action_t llc_await_rejct_actions_7a[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_stop_p_timer,
@@ -3895,7 +3895,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_7a = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_1 event */
-static llc_conn_action_t llc_await_rejct_actions_7b[] = {
+static const llc_conn_action_t llc_await_rejct_actions_7b[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_stop_p_timer,
@@ -3912,7 +3912,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_7b = {
};
/* State transitions for LLC_CONN_EV_RX_I_RSP_Fbit_SET_1_UNEXPD_Ns event */
-static llc_conn_action_t llc_await_rejct_actions_7c[] = {
+static const llc_conn_action_t llc_await_rejct_actions_7c[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_stop_p_timer,
@@ -3929,7 +3929,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_7c = {
};
/* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_0 event */
-static llc_conn_action_t llc_await_rejct_actions_8a[] = {
+static const llc_conn_action_t llc_await_rejct_actions_8a[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_clear_remote_busy,
@@ -3944,7 +3944,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_8a = {
};
/* State transitions for LLC_CONN_EV_RX_RR_RSP_Fbit_SET_0 event */
-static llc_conn_action_t llc_await_rejct_actions_8b[] = {
+static const llc_conn_action_t llc_await_rejct_actions_8b[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_clear_remote_busy,
@@ -3959,7 +3959,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_8b = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_0 event */
-static llc_conn_action_t llc_await_rejct_actions_8c[] = {
+static const llc_conn_action_t llc_await_rejct_actions_8c[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_clear_remote_busy,
@@ -3974,7 +3974,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_8c = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_RSP_Fbit_SET_0 event */
-static llc_conn_action_t llc_await_rejct_actions_8d[] = {
+static const llc_conn_action_t llc_await_rejct_actions_8d[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_clear_remote_busy,
@@ -3989,7 +3989,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_8d = {
};
/* State transitions for LLC_CONN_EV_RX_RR_CMD_Pbit_SET_1 event */
-static llc_conn_action_t llc_await_rejct_actions_9a[] = {
+static const llc_conn_action_t llc_await_rejct_actions_9a[] = {
[0] = llc_conn_ac_send_rr_rsp_f_set_1,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_upd_vs,
@@ -4005,7 +4005,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_9a = {
};
/* State transitions for LLC_CONN_EV_RX_REJ_CMD_Pbit_SET_1 event */
-static llc_conn_action_t llc_await_rejct_actions_9b[] = {
+static const llc_conn_action_t llc_await_rejct_actions_9b[] = {
[0] = llc_conn_ac_send_rr_rsp_f_set_1,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_upd_vs,
@@ -4021,7 +4021,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_9b = {
};
/* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_1 event */
-static llc_conn_action_t llc_await_rejct_actions_10[] = {
+static const llc_conn_action_t llc_await_rejct_actions_10[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_stop_p_timer,
@@ -4037,7 +4037,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_10 = {
};
/* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_0 event */
-static llc_conn_action_t llc_await_rejct_actions_11a[] = {
+static const llc_conn_action_t llc_await_rejct_actions_11a[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_set_remote_busy,
@@ -4052,7 +4052,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_11a = {
};
/* State transitions for LLC_CONN_EV_RX_RNR_RSP_Fbit_SET_0 event */
-static llc_conn_action_t llc_await_rejct_actions_11b[] = {
+static const llc_conn_action_t llc_await_rejct_actions_11b[] = {
[0] = llc_conn_ac_upd_nr_received,
[1] = llc_conn_ac_upd_vs,
[2] = llc_conn_ac_set_remote_busy,
@@ -4067,7 +4067,7 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_11b = {
};
/* State transitions for LLC_CONN_EV_RX_RNR_CMD_Pbit_SET_1 event */
-static llc_conn_action_t llc_await_rejct_actions_12[] = {
+static const llc_conn_action_t llc_await_rejct_actions_12[] = {
[0] = llc_conn_ac_send_rr_rsp_f_set_1,
[1] = llc_conn_ac_upd_nr_received,
[2] = llc_conn_ac_upd_vs,
@@ -4083,12 +4083,12 @@ static struct llc_conn_state_trans llc_await_rejct_state_trans_12 = {
};
/* State transitions for LLC_CONN_EV_P_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_await_rejct_ev_qfyrs_13[] = {
+static const llc_conn_ev_qfyr_t llc_await_rejct_ev_qfyrs_13[] = {
[0] = llc_conn_ev_qlfy_retry_cnt_lt_n2,
[1] = NULL,
};
-static llc_conn_action_t llc_await_rejct_actions_13[] = {
+static const llc_conn_action_t llc_await_rejct_actions_13[] = {
[0] = llc_conn_ac_send_rej_cmd_p_set_1,
[1] = llc_conn_ac_stop_p_timer,
[2] = llc_conn_ac_inc_retry_cnt_by_1,
@@ -4157,13 +4157,13 @@ static struct llc_conn_state_trans *llc_await_rejct_state_transitions[] = {
/* LLC_CONN_STATE_D_CONN transitions */
/* State transitions for LLC_CONN_EV_RX_SABME_CMD_Pbit_SET_X event,
* cause_flag = 1 */
-static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_1[] = {
+static const llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_1[] = {
[0] = llc_conn_ev_qlfy_cause_flag_eq_1,
[1] = llc_conn_ev_qlfy_set_status_conflict,
[2] = NULL,
};
-static llc_conn_action_t llc_d_conn_actions_1[] = {
+static const llc_conn_action_t llc_d_conn_actions_1[] = {
[0] = llc_conn_ac_send_dm_rsp_f_set_p,
[1] = llc_conn_ac_stop_ack_timer,
[2] = llc_conn_ac_disc_confirm,
@@ -4181,13 +4181,13 @@ static struct llc_conn_state_trans llc_d_conn_state_trans_1 = {
/* State transitions for LLC_CONN_EV_RX_SABME_CMD_Pbit_SET_X event,
* cause_flag = 0
*/
-static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_1_1[] = {
+static const llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_1_1[] = {
[0] = llc_conn_ev_qlfy_cause_flag_eq_0,
[1] = llc_conn_ev_qlfy_set_status_conflict,
[2] = NULL,
};
-static llc_conn_action_t llc_d_conn_actions_1_1[] = {
+static const llc_conn_action_t llc_d_conn_actions_1_1[] = {
[0] = llc_conn_ac_send_dm_rsp_f_set_p,
[1] = llc_conn_ac_stop_ack_timer,
[2] = llc_conn_disc,
@@ -4204,14 +4204,14 @@ static struct llc_conn_state_trans llc_d_conn_state_trans_1_1 = {
/* State transitions for LLC_CONN_EV_RX_UA_RSP_Fbit_SET_X event,
* cause_flag = 1
*/
-static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_2[] = {
+static const llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_2[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_f,
[1] = llc_conn_ev_qlfy_cause_flag_eq_1,
[2] = llc_conn_ev_qlfy_set_status_disc,
[3] = NULL,
};
-static llc_conn_action_t llc_d_conn_actions_2[] = {
+static const llc_conn_action_t llc_d_conn_actions_2[] = {
[0] = llc_conn_ac_stop_ack_timer,
[1] = llc_conn_ac_disc_confirm,
[2] = llc_conn_disc,
@@ -4228,14 +4228,14 @@ static struct llc_conn_state_trans llc_d_conn_state_trans_2 = {
/* State transitions for LLC_CONN_EV_RX_UA_RSP_Fbit_SET_X event,
* cause_flag = 0
*/
-static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_2_1[] = {
+static const llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_2_1[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_f,
[1] = llc_conn_ev_qlfy_cause_flag_eq_0,
[2] = llc_conn_ev_qlfy_set_status_disc,
[3] = NULL,
};
-static llc_conn_action_t llc_d_conn_actions_2_1[] = {
+static const llc_conn_action_t llc_d_conn_actions_2_1[] = {
[0] = llc_conn_ac_stop_ack_timer,
[1] = llc_conn_disc,
[2] = NULL,
@@ -4249,7 +4249,7 @@ static struct llc_conn_state_trans llc_d_conn_state_trans_2_1 = {
};
/* State transitions for LLC_CONN_EV_RX_DISC_CMD_Pbit_SET_X event */
-static llc_conn_action_t llc_d_conn_actions_3[] = {
+static const llc_conn_action_t llc_d_conn_actions_3[] = {
[0] = llc_conn_ac_send_ua_rsp_f_set_p,
[1] = NULL,
};
@@ -4264,13 +4264,13 @@ static struct llc_conn_state_trans llc_d_conn_state_trans_3 = {
/* State transitions for LLC_CONN_EV_RX_DM_RSP_Fbit_SET_X event,
* cause_flag = 1
*/
-static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_4[] = {
+static const llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_4[] = {
[0] = llc_conn_ev_qlfy_cause_flag_eq_1,
[1] = llc_conn_ev_qlfy_set_status_disc,
[2] = NULL,
};
-static llc_conn_action_t llc_d_conn_actions_4[] = {
+static const llc_conn_action_t llc_d_conn_actions_4[] = {
[0] = llc_conn_ac_stop_ack_timer,
[1] = llc_conn_ac_disc_confirm,
[2] = llc_conn_disc,
@@ -4287,13 +4287,13 @@ static struct llc_conn_state_trans llc_d_conn_state_trans_4 = {
/* State transitions for LLC_CONN_EV_RX_DM_RSP_Fbit_SET_X event,
* cause_flag = 0
*/
-static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_4_1[] = {
+static const llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_4_1[] = {
[0] = llc_conn_ev_qlfy_cause_flag_eq_0,
[1] = llc_conn_ev_qlfy_set_status_disc,
[2] = NULL,
};
-static llc_conn_action_t llc_d_conn_actions_4_1[] = {
+static const llc_conn_action_t llc_d_conn_actions_4_1[] = {
[0] = llc_conn_ac_stop_ack_timer,
[1] = llc_conn_disc,
[2] = NULL,
@@ -4310,13 +4310,13 @@ static struct llc_conn_state_trans llc_d_conn_state_trans_4_1 = {
* State transition for
* LLC_CONN_EV_DATA_CONN_REQ event
*/
-static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_5[] = {
+static const llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_5[] = {
[0] = llc_conn_ev_qlfy_set_status_refuse,
[1] = NULL,
};
/* just one member, NULL, .bss zeroes it */
-static llc_conn_action_t llc_d_conn_actions_5[1];
+static const llc_conn_action_t llc_d_conn_actions_5[1];
static struct llc_conn_state_trans llc_d_conn_state_trans_5 = {
.ev = llc_conn_ev_data_req,
@@ -4326,12 +4326,12 @@ static struct llc_conn_state_trans llc_d_conn_state_trans_5 = {
};
/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_6[] = {
+static const llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_6[] = {
[0] = llc_conn_ev_qlfy_retry_cnt_lt_n2,
[1] = NULL,
};
-static llc_conn_action_t llc_d_conn_actions_6[] = {
+static const llc_conn_action_t llc_d_conn_actions_6[] = {
[0] = llc_conn_ac_send_disc_cmd_p_set_x,
[1] = llc_conn_ac_start_ack_timer,
[2] = llc_conn_ac_inc_retry_cnt_by_1,
@@ -4346,14 +4346,14 @@ static struct llc_conn_state_trans llc_d_conn_state_trans_6 = {
};
/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event, cause_flag = 1 */
-static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_7[] = {
+static const llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_7[] = {
[0] = llc_conn_ev_qlfy_retry_cnt_gte_n2,
[1] = llc_conn_ev_qlfy_cause_flag_eq_1,
[2] = llc_conn_ev_qlfy_set_status_failed,
[3] = NULL,
};
-static llc_conn_action_t llc_d_conn_actions_7[] = {
+static const llc_conn_action_t llc_d_conn_actions_7[] = {
[0] = llc_conn_ac_disc_confirm,
[1] = llc_conn_disc,
[2] = NULL,
@@ -4367,14 +4367,14 @@ static struct llc_conn_state_trans llc_d_conn_state_trans_7 = {
};
/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event, cause_flag = 0 */
-static llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_8[] = {
+static const llc_conn_ev_qfyr_t llc_d_conn_ev_qfyrs_8[] = {
[0] = llc_conn_ev_qlfy_retry_cnt_gte_n2,
[1] = llc_conn_ev_qlfy_cause_flag_eq_0,
[2] = llc_conn_ev_qlfy_set_status_failed,
[3] = NULL,
};
-static llc_conn_action_t llc_d_conn_actions_8[] = {
+static const llc_conn_action_t llc_d_conn_actions_8[] = {
[0] = llc_conn_disc,
[1] = NULL,
};
@@ -4411,7 +4411,7 @@ static struct llc_conn_state_trans *llc_d_conn_state_transitions[] = {
/* LLC_CONN_STATE_RESET transitions */
/* State transitions for LLC_CONN_EV_RX_SABME_CMD_Pbit_SET_X event */
-static llc_conn_action_t llc_rst_actions_1[] = {
+static const llc_conn_action_t llc_rst_actions_1[] = {
[0] = llc_conn_ac_set_vs_0,
[1] = llc_conn_ac_set_vr_0,
[2] = llc_conn_ac_set_s_flag_1,
@@ -4429,14 +4429,14 @@ static struct llc_conn_state_trans llc_rst_state_trans_1 = {
/* State transitions for LLC_CONN_EV_RX_UA_RSP_Fbit_SET_X event,
* cause_flag = 1
*/
-static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_2[] = {
+static const llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_2[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_f,
[1] = llc_conn_ev_qlfy_cause_flag_eq_1,
[2] = llc_conn_ev_qlfy_set_status_conn,
[3] = NULL,
};
-static llc_conn_action_t llc_rst_actions_2[] = {
+static const llc_conn_action_t llc_rst_actions_2[] = {
[0] = llc_conn_ac_stop_ack_timer,
[1] = llc_conn_ac_set_vs_0,
[2] = llc_conn_ac_set_vr_0,
@@ -4457,14 +4457,14 @@ static struct llc_conn_state_trans llc_rst_state_trans_2 = {
/* State transitions for LLC_CONN_EV_RX_UA_RSP_Fbit_SET_X event,
* cause_flag = 0
*/
-static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_2_1[] = {
+static const llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_2_1[] = {
[0] = llc_conn_ev_qlfy_p_flag_eq_f,
[1] = llc_conn_ev_qlfy_cause_flag_eq_0,
[2] = llc_conn_ev_qlfy_set_status_rst_done,
[3] = NULL,
};
-static llc_conn_action_t llc_rst_actions_2_1[] = {
+static const llc_conn_action_t llc_rst_actions_2_1[] = {
[0] = llc_conn_ac_stop_ack_timer,
[1] = llc_conn_ac_set_vs_0,
[2] = llc_conn_ac_set_vr_0,
@@ -4483,13 +4483,13 @@ static struct llc_conn_state_trans llc_rst_state_trans_2_1 = {
};
/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_3[] = {
+static const llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_3[] = {
[0] = llc_conn_ev_qlfy_s_flag_eq_1,
[1] = llc_conn_ev_qlfy_set_status_rst_done,
[2] = NULL,
};
-static llc_conn_action_t llc_rst_actions_3[] = {
+static const llc_conn_action_t llc_rst_actions_3[] = {
[0] = llc_conn_ac_set_p_flag_0,
[1] = llc_conn_ac_set_remote_busy_0,
[2] = NULL,
@@ -4505,12 +4505,12 @@ static struct llc_conn_state_trans llc_rst_state_trans_3 = {
/* State transitions for LLC_CONN_EV_RX_DISC_CMD_Pbit_SET_X event,
* cause_flag = 1
*/
-static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_4[] = {
+static const llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_4[] = {
[0] = llc_conn_ev_qlfy_cause_flag_eq_1,
[1] = llc_conn_ev_qlfy_set_status_disc,
[2] = NULL,
};
-static llc_conn_action_t llc_rst_actions_4[] = {
+static const llc_conn_action_t llc_rst_actions_4[] = {
[0] = llc_conn_ac_send_dm_rsp_f_set_p,
[1] = llc_conn_ac_disc_ind,
[2] = llc_conn_ac_stop_ack_timer,
@@ -4528,13 +4528,13 @@ static struct llc_conn_state_trans llc_rst_state_trans_4 = {
/* State transitions for LLC_CONN_EV_RX_DISC_CMD_Pbit_SET_X event,
* cause_flag = 0
*/
-static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_4_1[] = {
+static const llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_4_1[] = {
[0] = llc_conn_ev_qlfy_cause_flag_eq_0,
[1] = llc_conn_ev_qlfy_set_status_refuse,
[2] = NULL,
};
-static llc_conn_action_t llc_rst_actions_4_1[] = {
+static const llc_conn_action_t llc_rst_actions_4_1[] = {
[0] = llc_conn_ac_send_dm_rsp_f_set_p,
[1] = llc_conn_ac_stop_ack_timer,
[2] = llc_conn_disc,
@@ -4551,13 +4551,13 @@ static struct llc_conn_state_trans llc_rst_state_trans_4_1 = {
/* State transitions for LLC_CONN_EV_RX_DM_RSP_Fbit_SET_X event,
* cause_flag = 1
*/
-static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_5[] = {
+static const llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_5[] = {
[0] = llc_conn_ev_qlfy_cause_flag_eq_1,
[1] = llc_conn_ev_qlfy_set_status_disc,
[2] = NULL,
};
-static llc_conn_action_t llc_rst_actions_5[] = {
+static const llc_conn_action_t llc_rst_actions_5[] = {
[0] = llc_conn_ac_disc_ind,
[1] = llc_conn_ac_stop_ack_timer,
[2] = llc_conn_disc,
@@ -4574,13 +4574,13 @@ static struct llc_conn_state_trans llc_rst_state_trans_5 = {
/* State transitions for LLC_CONN_EV_RX_DM_RSP_Fbit_SET_X event,
* cause_flag = 0
*/
-static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_5_1[] = {
+static const llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_5_1[] = {
[0] = llc_conn_ev_qlfy_cause_flag_eq_0,
[1] = llc_conn_ev_qlfy_set_status_refuse,
[2] = NULL,
};
-static llc_conn_action_t llc_rst_actions_5_1[] = {
+static const llc_conn_action_t llc_rst_actions_5_1[] = {
[0] = llc_conn_ac_stop_ack_timer,
[1] = llc_conn_disc,
[2] = NULL,
@@ -4594,13 +4594,13 @@ static struct llc_conn_state_trans llc_rst_state_trans_5_1 = {
};
/* State transitions for DATA_CONN_REQ event */
-static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_6[] = {
+static const llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_6[] = {
[0] = llc_conn_ev_qlfy_set_status_refuse,
[1] = NULL,
};
/* just one member, NULL, .bss zeroes it */
-static llc_conn_action_t llc_rst_actions_6[1];
+static const llc_conn_action_t llc_rst_actions_6[1];
static struct llc_conn_state_trans llc_rst_state_trans_6 = {
.ev = llc_conn_ev_data_req,
@@ -4610,13 +4610,13 @@ static struct llc_conn_state_trans llc_rst_state_trans_6 = {
};
/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_7[] = {
+static const llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_7[] = {
[0] = llc_conn_ev_qlfy_retry_cnt_lt_n2,
[1] = llc_conn_ev_qlfy_s_flag_eq_0,
[2] = NULL,
};
-static llc_conn_action_t llc_rst_actions_7[] = {
+static const llc_conn_action_t llc_rst_actions_7[] = {
[0] = llc_conn_ac_send_sabme_cmd_p_set_x,
[1] = llc_conn_ac_start_ack_timer,
[2] = llc_conn_ac_inc_retry_cnt_by_1,
@@ -4631,14 +4631,14 @@ static struct llc_conn_state_trans llc_rst_state_trans_7 = {
};
/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_8[] = {
+static const llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_8[] = {
[0] = llc_conn_ev_qlfy_retry_cnt_gte_n2,
[1] = llc_conn_ev_qlfy_s_flag_eq_0,
[2] = llc_conn_ev_qlfy_cause_flag_eq_1,
[3] = llc_conn_ev_qlfy_set_status_failed,
[4] = NULL,
};
-static llc_conn_action_t llc_rst_actions_8[] = {
+static const llc_conn_action_t llc_rst_actions_8[] = {
[0] = llc_conn_ac_disc_ind,
[1] = llc_conn_disc,
[2] = NULL,
@@ -4652,14 +4652,14 @@ static struct llc_conn_state_trans llc_rst_state_trans_8 = {
};
/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_8_1[] = {
+static const llc_conn_ev_qfyr_t llc_rst_ev_qfyrs_8_1[] = {
[0] = llc_conn_ev_qlfy_retry_cnt_gte_n2,
[1] = llc_conn_ev_qlfy_s_flag_eq_0,
[2] = llc_conn_ev_qlfy_cause_flag_eq_0,
[3] = llc_conn_ev_qlfy_set_status_failed,
[4] = NULL,
};
-static llc_conn_action_t llc_rst_actions_8_1[] = {
+static const llc_conn_action_t llc_rst_actions_8_1[] = {
[0] = llc_conn_ac_disc_ind,
[1] = llc_conn_disc,
[2] = NULL,
@@ -4698,7 +4698,7 @@ static struct llc_conn_state_trans *llc_rst_state_transitions[] = {
/* LLC_CONN_STATE_ERROR transitions */
/* State transitions for LLC_CONN_EV_RX_SABME_CMD_Pbit_SET_X event */
-static llc_conn_action_t llc_error_actions_1[] = {
+static const llc_conn_action_t llc_error_actions_1[] = {
[0] = llc_conn_ac_set_vs_0,
[1] = llc_conn_ac_set_vr_0,
[2] = llc_conn_ac_send_ua_rsp_f_set_p,
@@ -4718,7 +4718,7 @@ static struct llc_conn_state_trans llc_error_state_trans_1 = {
};
/* State transitions for LLC_CONN_EV_RX_DISC_CMD_Pbit_SET_X event */
-static llc_conn_action_t llc_error_actions_2[] = {
+static const llc_conn_action_t llc_error_actions_2[] = {
[0] = llc_conn_ac_send_ua_rsp_f_set_p,
[1] = llc_conn_ac_disc_ind,
[2] = llc_conn_ac_stop_ack_timer,
@@ -4734,7 +4734,7 @@ static struct llc_conn_state_trans llc_error_state_trans_2 = {
};
/* State transitions for LLC_CONN_EV_RX_DM_RSP_Fbit_SET_X event */
-static llc_conn_action_t llc_error_actions_3[] = {
+static const llc_conn_action_t llc_error_actions_3[] = {
[0] = llc_conn_ac_disc_ind,
[1] = llc_conn_ac_stop_ack_timer,
[2] = llc_conn_disc,
@@ -4749,7 +4749,7 @@ static struct llc_conn_state_trans llc_error_state_trans_3 = {
};
/* State transitions for LLC_CONN_EV_RX_FRMR_RSP_Fbit_SET_X event */
-static llc_conn_action_t llc_error_actions_4[] = {
+static const llc_conn_action_t llc_error_actions_4[] = {
[0] = llc_conn_ac_send_sabme_cmd_p_set_x,
[1] = llc_conn_ac_start_ack_timer,
[2] = llc_conn_ac_set_retry_cnt_0,
@@ -4765,7 +4765,7 @@ static struct llc_conn_state_trans llc_error_state_trans_4 = {
};
/* State transitions for LLC_CONN_EV_RX_XXX_CMD_Pbit_SET_X event */
-static llc_conn_action_t llc_error_actions_5[] = {
+static const llc_conn_action_t llc_error_actions_5[] = {
[0] = llc_conn_ac_resend_frmr_rsp_f_set_p,
[1] = NULL,
};
@@ -4786,12 +4786,12 @@ static struct llc_conn_state_trans llc_error_state_trans_6 = {
};
/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_error_ev_qfyrs_7[] = {
+static const llc_conn_ev_qfyr_t llc_error_ev_qfyrs_7[] = {
[0] = llc_conn_ev_qlfy_retry_cnt_lt_n2,
[1] = NULL,
};
-static llc_conn_action_t llc_error_actions_7[] = {
+static const llc_conn_action_t llc_error_actions_7[] = {
[0] = llc_conn_ac_resend_frmr_rsp_f_set_0,
[1] = llc_conn_ac_start_ack_timer,
[2] = llc_conn_ac_inc_retry_cnt_by_1,
@@ -4806,12 +4806,12 @@ static struct llc_conn_state_trans llc_error_state_trans_7 = {
};
/* State transitions for LLC_CONN_EV_ACK_TMR_EXP event */
-static llc_conn_ev_qfyr_t llc_error_ev_qfyrs_8[] = {
+static const llc_conn_ev_qfyr_t llc_error_ev_qfyrs_8[] = {
[0] = llc_conn_ev_qlfy_retry_cnt_gte_n2,
[1] = NULL,
};
-static llc_conn_action_t llc_error_actions_8[] = {
+static const llc_conn_action_t llc_error_actions_8[] = {
[0] = llc_conn_ac_send_sabme_cmd_p_set_x,
[1] = llc_conn_ac_set_s_flag_0,
[2] = llc_conn_ac_start_ack_timer,
@@ -4828,13 +4828,13 @@ static struct llc_conn_state_trans llc_error_state_trans_8 = {
};
/* State transitions for LLC_CONN_EV_DATA_CONN_REQ event */
-static llc_conn_ev_qfyr_t llc_error_ev_qfyrs_9[] = {
+static const llc_conn_ev_qfyr_t llc_error_ev_qfyrs_9[] = {
[0] = llc_conn_ev_qlfy_set_status_refuse,
[1] = NULL,
};
/* just one member, NULL, .bss zeroes it */
-static llc_conn_action_t llc_error_actions_9[1];
+static const llc_conn_action_t llc_error_actions_9[1];
static struct llc_conn_state_trans llc_error_state_trans_9 = {
.ev = llc_conn_ev_data_req,
@@ -4866,7 +4866,7 @@ static struct llc_conn_state_trans *llc_error_state_transitions[] = {
/* LLC_CONN_STATE_TEMP transitions */
/* State transitions for LLC_CONN_EV_DISC_REQ event */
-static llc_conn_action_t llc_temp_actions_1[] = {
+static const llc_conn_action_t llc_temp_actions_1[] = {
[0] = llc_conn_ac_stop_all_timers,
[1] = llc_conn_ac_send_disc_cmd_p_set_x,
[2] = llc_conn_disc,
diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c
index 42dc2e45c921..81a61fce3afb 100644
--- a/net/llc/llc_conn.c
+++ b/net/llc/llc_conn.c
@@ -406,7 +406,7 @@ static struct llc_conn_state_trans *llc_qualify_conn_ev(struct sock *sk,
struct sk_buff *skb)
{
struct llc_conn_state_trans **next_trans;
- llc_conn_ev_qfyr_t *next_qualifier;
+ const llc_conn_ev_qfyr_t *next_qualifier;
struct llc_conn_state_ev *ev = llc_conn_ev(skb);
struct llc_sock *llc = llc_sk(sk);
struct llc_conn_state *curr_state =
@@ -454,7 +454,7 @@ static int llc_exec_conn_trans_actions(struct sock *sk,
struct sk_buff *skb)
{
int rc = 0;
- llc_conn_action_t *next_action;
+ const llc_conn_action_t *next_action;
for (next_action = trans->ev_actions;
next_action && *next_action; next_action++) {
diff --git a/net/llc/llc_if.c b/net/llc/llc_if.c
index 25c31c0a3fdb..6daf391b3e84 100644
--- a/net/llc/llc_if.c
+++ b/net/llc/llc_if.c
@@ -15,7 +15,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
-#include <asm/errno.h>
+#include <linux/errno.h>
#include <net/llc_if.h>
#include <net/llc_sap.h>
#include <net/llc_s_ev.h>
diff --git a/net/llc/llc_s_st.c b/net/llc/llc_s_st.c
index 135f7d80069e..308c616883a4 100644
--- a/net/llc/llc_s_st.c
+++ b/net/llc/llc_s_st.c
@@ -29,7 +29,7 @@ static struct llc_sap_state_trans llc_sap_state_trans_end;
/* state LLC_SAP_STATE_INACTIVE transition for
* LLC_SAP_EV_ACTIVATION_REQ event
*/
-static llc_sap_action_t llc_sap_inactive_state_actions_1[] = {
+static const llc_sap_action_t llc_sap_inactive_state_actions_1[] = {
[0] = llc_sap_action_report_status,
[1] = NULL,
};
@@ -47,7 +47,7 @@ static struct llc_sap_state_trans *llc_sap_inactive_state_transitions[] = {
};
/* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_RX_UI event */
-static llc_sap_action_t llc_sap_active_state_actions_1[] = {
+static const llc_sap_action_t llc_sap_active_state_actions_1[] = {
[0] = llc_sap_action_unitdata_ind,
[1] = NULL,
};
@@ -59,7 +59,7 @@ static struct llc_sap_state_trans llc_sap_active_state_trans_1 = {
};
/* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_UNITDATA_REQ event */
-static llc_sap_action_t llc_sap_active_state_actions_2[] = {
+static const llc_sap_action_t llc_sap_active_state_actions_2[] = {
[0] = llc_sap_action_send_ui,
[1] = NULL,
};
@@ -71,7 +71,7 @@ static struct llc_sap_state_trans llc_sap_active_state_trans_2 = {
};
/* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_XID_REQ event */
-static llc_sap_action_t llc_sap_active_state_actions_3[] = {
+static const llc_sap_action_t llc_sap_active_state_actions_3[] = {
[0] = llc_sap_action_send_xid_c,
[1] = NULL,
};
@@ -83,7 +83,7 @@ static struct llc_sap_state_trans llc_sap_active_state_trans_3 = {
};
/* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_RX_XID_C event */
-static llc_sap_action_t llc_sap_active_state_actions_4[] = {
+static const llc_sap_action_t llc_sap_active_state_actions_4[] = {
[0] = llc_sap_action_send_xid_r,
[1] = NULL,
};
@@ -95,7 +95,7 @@ static struct llc_sap_state_trans llc_sap_active_state_trans_4 = {
};
/* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_RX_XID_R event */
-static llc_sap_action_t llc_sap_active_state_actions_5[] = {
+static const llc_sap_action_t llc_sap_active_state_actions_5[] = {
[0] = llc_sap_action_xid_ind,
[1] = NULL,
};
@@ -107,7 +107,7 @@ static struct llc_sap_state_trans llc_sap_active_state_trans_5 = {
};
/* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_TEST_REQ event */
-static llc_sap_action_t llc_sap_active_state_actions_6[] = {
+static const llc_sap_action_t llc_sap_active_state_actions_6[] = {
[0] = llc_sap_action_send_test_c,
[1] = NULL,
};
@@ -119,7 +119,7 @@ static struct llc_sap_state_trans llc_sap_active_state_trans_6 = {
};
/* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_RX_TEST_C event */
-static llc_sap_action_t llc_sap_active_state_actions_7[] = {
+static const llc_sap_action_t llc_sap_active_state_actions_7[] = {
[0] = llc_sap_action_send_test_r,
[1] = NULL,
};
@@ -131,7 +131,7 @@ static struct llc_sap_state_trans llc_sap_active_state_trans_7 = {
};
/* state LLC_SAP_STATE_ACTIVE transition for LLC_SAP_EV_RX_TEST_R event */
-static llc_sap_action_t llc_sap_active_state_actions_8[] = {
+static const llc_sap_action_t llc_sap_active_state_actions_8[] = {
[0] = llc_sap_action_test_ind,
[1] = NULL,
};
@@ -145,7 +145,7 @@ static struct llc_sap_state_trans llc_sap_active_state_trans_8 = {
/* state LLC_SAP_STATE_ACTIVE transition for
* LLC_SAP_EV_DEACTIVATION_REQ event
*/
-static llc_sap_action_t llc_sap_active_state_actions_9[] = {
+static const llc_sap_action_t llc_sap_active_state_actions_9[] = {
[0] = llc_sap_action_report_status,
[1] = NULL,
};
diff --git a/net/llc/llc_sap.c b/net/llc/llc_sap.c
index 06033f6c845f..d0e1e804ebd7 100644
--- a/net/llc/llc_sap.c
+++ b/net/llc/llc_sap.c
@@ -146,7 +146,7 @@ static int llc_exec_sap_trans_actions(struct llc_sap *sap,
struct sk_buff *skb)
{
int rc = 0;
- llc_sap_action_t *next_action = trans->ev_actions;
+ const llc_sap_action_t *next_action = trans->ev_actions;
for (; next_action && *next_action; next_action++)
if ((*next_action)(sap, skb))
diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index aeb6a483b3bc..75cc6801a431 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -33,6 +33,13 @@ config MAC80211_RC_MINSTREL_HT
---help---
This option enables the 'minstrel_ht' TX rate control algorithm
+config MAC80211_RC_MINSTREL_VHT
+ bool "Minstrel 802.11ac support" if EXPERT
+ depends on MAC80211_RC_MINSTREL_HT
+ default n
+ ---help---
+ This option enables VHT in the 'minstrel_ht' TX rate control algorithm
+
choice
prompt "Default rate control algorithm"
depends on MAC80211_HAS_RC
@@ -169,6 +176,17 @@ config MAC80211_HT_DEBUG
Do not select this option.
+config MAC80211_OCB_DEBUG
+ bool "Verbose OCB debugging"
+ depends on MAC80211_DEBUG_MENU
+ ---help---
+ Selecting this option causes mac80211 to print out
+ very verbose OCB debugging messages. It should not
+ be selected on production systems as those messages
+ are remotely triggerable.
+
+ Do not select this option.
+
config MAC80211_IBSS_DEBUG
bool "Verbose IBSS debugging"
depends on MAC80211_DEBUG_MENU
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index 7273d2796dd1..e53671b1105e 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -27,7 +27,8 @@ mac80211-y := \
event.o \
chan.o \
trace.o mlme.o \
- tdls.o
+ tdls.o \
+ ocb.o
mac80211-$(CONFIG_MAC80211_LEDS) += led.o
mac80211-$(CONFIG_MAC80211_DEBUGFS) += \
diff --git a/net/mac80211/aes_ccm.c b/net/mac80211/aes_ccm.c
index ec24378caaaf..09d9caaec591 100644
--- a/net/mac80211/aes_ccm.c
+++ b/net/mac80211/aes_ccm.c
@@ -53,6 +53,9 @@ int ieee80211_aes_ccm_decrypt(struct crypto_aead *tfm, u8 *b_0, u8 *aad,
__aligned(__alignof__(struct aead_request));
struct aead_request *aead_req = (void *) aead_req_data;
+ if (data_len == 0)
+ return -EINVAL;
+
memset(aead_req, 0, sizeof(aead_req_data));
sg_init_one(&pt, data, data_len);
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index d6986f3aa5c4..a360c15cc978 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -149,11 +149,6 @@ void ieee80211_assign_tid_tx(struct sta_info *sta, int tid,
rcu_assign_pointer(sta->ampdu_mlme.tid_tx[tid], tid_tx);
}
-static inline int ieee80211_ac_from_tid(int tid)
-{
- return ieee802_1d_to_ac[tid & 7];
-}
-
/*
* When multiple aggregation sessions on multiple stations
* are being created/destroyed simultaneously, we need to
@@ -514,6 +509,10 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid,
struct tid_ampdu_tx *tid_tx;
int ret = 0;
+ if (WARN(sta->reserved_tid == tid,
+ "Requested to start BA session on reserved tid=%d", tid))
+ return -EINVAL;
+
trace_api_start_tx_ba_session(pubsta, tid);
if (WARN_ON_ONCE(!local->ops->ampdu_action))
@@ -770,6 +769,9 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid)
goto unlock;
}
+ WARN(sta->reserved_tid == tid,
+ "Requested to stop BA session on reserved tid=%d", tid);
+
if (test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) {
/* already in progress stopping it */
ret = 0;
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 343da1e35025..e75d5c53e97b 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -20,6 +20,7 @@
#include "cfg.h"
#include "rate.h"
#include "mesh.h"
+#include "wme.h"
static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
const char *name,
@@ -190,7 +191,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
* receive the key. When wpa_supplicant has roamed
* using FT, it attempts to set the key before
* association has completed, this rejects that attempt
- * so it will set the key again after assocation.
+ * so it will set the key again after association.
*
* TODO: accept the key if we have a station entry and
* add it to the device after the station.
@@ -229,6 +230,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
case NUM_NL80211_IFTYPES:
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_P2P_GO:
+ case NL80211_IFTYPE_OCB:
/* shouldn't happen */
WARN_ON_ONCE(1);
break;
@@ -1040,6 +1042,13 @@ static int sta_apply_parameters(struct ieee80211_local *local,
clear_sta_flag(sta, WLAN_STA_TDLS_PEER);
}
+ /* mark TDLS channel switch support, if the AP allows it */
+ if (test_sta_flag(sta, WLAN_STA_TDLS_PEER) &&
+ !sdata->u.mgd.tdls_chan_switch_prohibited &&
+ params->ext_capab_len >= 4 &&
+ params->ext_capab[3] & WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH)
+ set_sta_flag(sta, WLAN_STA_TDLS_CHAN_SWITCH);
+
if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) {
sta->sta.uapsd_queues = params->uapsd_queues;
sta->sta.max_sp = params->max_sp;
@@ -1225,14 +1234,14 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
}
static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
- const u8 *mac)
+ struct station_del_parameters *params)
{
struct ieee80211_sub_if_data *sdata;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
- if (mac)
- return sta_info_destroy_addr_bss(sdata, mac);
+ if (params->mac)
+ return sta_info_destroy_addr_bss(sdata, params->mac);
sta_info_flush(sdata);
return 0;
@@ -1516,6 +1525,57 @@ static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
return 0;
}
+static void mpp_set_pinfo(struct mesh_path *mpath, u8 *mpp,
+ struct mpath_info *pinfo)
+{
+ memset(pinfo, 0, sizeof(*pinfo));
+ memcpy(mpp, mpath->mpp, ETH_ALEN);
+
+ pinfo->generation = mpp_paths_generation;
+}
+
+static int ieee80211_get_mpp(struct wiphy *wiphy, struct net_device *dev,
+ u8 *dst, u8 *mpp, struct mpath_info *pinfo)
+
+{
+ struct ieee80211_sub_if_data *sdata;
+ struct mesh_path *mpath;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ rcu_read_lock();
+ mpath = mpp_path_lookup(sdata, dst);
+ if (!mpath) {
+ rcu_read_unlock();
+ return -ENOENT;
+ }
+ memcpy(dst, mpath->dst, ETH_ALEN);
+ mpp_set_pinfo(mpath, mpp, pinfo);
+ rcu_read_unlock();
+ return 0;
+}
+
+static int ieee80211_dump_mpp(struct wiphy *wiphy, struct net_device *dev,
+ int idx, u8 *dst, u8 *mpp,
+ struct mpath_info *pinfo)
+{
+ struct ieee80211_sub_if_data *sdata;
+ struct mesh_path *mpath;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ rcu_read_lock();
+ mpath = mpp_path_lookup_by_idx(sdata, idx);
+ if (!mpath) {
+ rcu_read_unlock();
+ return -ENOENT;
+ }
+ memcpy(dst, mpath->dst, ETH_ALEN);
+ mpp_set_pinfo(mpath, mpp, pinfo);
+ rcu_read_unlock();
+ return 0;
+}
+
static int ieee80211_get_mesh_config(struct wiphy *wiphy,
struct net_device *dev,
struct mesh_config *conf)
@@ -1966,6 +2026,17 @@ static int ieee80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
return ieee80211_ibss_leave(IEEE80211_DEV_TO_SUB_IF(dev));
}
+static int ieee80211_join_ocb(struct wiphy *wiphy, struct net_device *dev,
+ struct ocb_setup *setup)
+{
+ return ieee80211_ocb_join(IEEE80211_DEV_TO_SUB_IF(dev), setup);
+}
+
+static int ieee80211_leave_ocb(struct wiphy *wiphy, struct net_device *dev)
+{
+ return ieee80211_ocb_leave(IEEE80211_DEV_TO_SUB_IF(dev));
+}
+
static int ieee80211_set_mcast_rate(struct wiphy *wiphy, struct net_device *dev,
int rate[IEEE80211_NUM_BANDS])
{
@@ -2081,6 +2152,9 @@ static int ieee80211_get_tx_power(struct wiphy *wiphy,
struct ieee80211_local *local = wiphy_priv(wiphy);
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
+ if (local->ops->get_txpower)
+ return drv_get_txpower(local, sdata, dbm);
+
if (!local->use_chanctx)
*dbm = local->hw.conf.power_level;
else
@@ -2850,11 +2924,7 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
if (sdata->reserved_ready)
return 0;
- err = ieee80211_vif_use_reserved_context(sdata);
- if (err)
- return err;
-
- return 0;
+ return ieee80211_vif_use_reserved_context(sdata);
}
if (!cfg80211_chandef_identical(&sdata->vif.bss_conf.chandef,
@@ -2868,7 +2938,6 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
return err;
ieee80211_bss_info_change_notify(sdata, changed);
- cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
if (sdata->csa_block_tx) {
ieee80211_wake_vif_queues(local, sdata,
@@ -2876,6 +2945,12 @@ static int __ieee80211_csa_finalize(struct ieee80211_sub_if_data *sdata)
sdata->csa_block_tx = false;
}
+ err = drv_post_channel_switch(sdata);
+ if (err)
+ return err;
+
+ cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
+
return 0;
}
@@ -3053,9 +3128,11 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
+ struct ieee80211_channel_switch ch_switch;
struct ieee80211_chanctx_conf *conf;
struct ieee80211_chanctx *chanctx;
- int err, changed = 0;
+ u32 changed = 0;
+ int err;
sdata_assert_lock(sdata);
lockdep_assert_held(&local->mtx);
@@ -3088,6 +3165,16 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
goto out;
}
+ ch_switch.timestamp = 0;
+ ch_switch.device_timestamp = 0;
+ ch_switch.block_tx = params->block_tx;
+ ch_switch.chandef = params->chandef;
+ ch_switch.count = params->count;
+
+ err = drv_pre_channel_switch(sdata, &ch_switch);
+ if (err)
+ goto out;
+
err = ieee80211_vif_reserve_chanctx(sdata, &params->chandef,
chanctx->mode,
params->radar_required);
@@ -3115,6 +3202,9 @@ __ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
ieee80211_stop_vif_queues(local, sdata,
IEEE80211_QUEUE_STOP_REASON_CSA);
+ cfg80211_ch_switch_started_notify(sdata->dev, &sdata->csa_chandef,
+ params->count);
+
if (changed) {
ieee80211_bss_info_change_notify(sdata, changed);
drv_channel_switch_beacon(sdata, &params->chandef);
@@ -3431,6 +3521,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS |
IEEE80211_TX_INTFL_NL80211_FRAME_TX;
+ info->band = band;
skb_set_queue_mapping(skb, IEEE80211_AC_VO);
skb->priority = 7;
@@ -3438,7 +3529,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev,
nullfunc->qos_ctrl = cpu_to_le16(7);
local_bh_disable();
- ieee80211_xmit(sdata, skb, band);
+ ieee80211_xmit(sdata, skb);
local_bh_enable();
rcu_read_unlock();
@@ -3521,6 +3612,76 @@ static int ieee80211_set_ap_chanwidth(struct wiphy *wiphy,
return ret;
}
+static int ieee80211_add_tx_ts(struct wiphy *wiphy, struct net_device *dev,
+ u8 tsid, const u8 *peer, u8 up,
+ u16 admitted_time)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ int ac = ieee802_1d_to_ac[up];
+
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ return -EOPNOTSUPP;
+
+ if (!(sdata->wmm_acm & BIT(up)))
+ return -EINVAL;
+
+ if (ifmgd->tx_tspec[ac].admitted_time)
+ return -EBUSY;
+
+ if (admitted_time) {
+ ifmgd->tx_tspec[ac].admitted_time = 32 * admitted_time;
+ ifmgd->tx_tspec[ac].tsid = tsid;
+ ifmgd->tx_tspec[ac].up = up;
+ }
+
+ return 0;
+}
+
+static int ieee80211_del_tx_ts(struct wiphy *wiphy, struct net_device *dev,
+ u8 tsid, const u8 *peer)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+ int ac;
+
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+ struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac];
+
+ /* skip unused entries */
+ if (!tx_tspec->admitted_time)
+ continue;
+
+ if (tx_tspec->tsid != tsid)
+ continue;
+
+ /* due to this new packets will be reassigned to non-ACM ACs */
+ tx_tspec->up = -1;
+
+ /* Make sure that all packets have been sent to avoid to
+ * restore the QoS params on packets that are still on the
+ * queues.
+ */
+ synchronize_net();
+ ieee80211_flush_queues(local, sdata);
+
+ /* restore the normal QoS parameters
+ * (unconditionally to avoid races)
+ */
+ tx_tspec->action = TX_TSPEC_ACTION_STOP_DOWNGRADE;
+ tx_tspec->downgraded = false;
+ ieee80211_sta_handle_tspec_ac_params(sdata);
+
+ /* finally clear all the data */
+ memset(tx_tspec, 0, sizeof(*tx_tspec));
+
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
const struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
@@ -3547,11 +3708,15 @@ const struct cfg80211_ops mac80211_config_ops = {
.change_mpath = ieee80211_change_mpath,
.get_mpath = ieee80211_get_mpath,
.dump_mpath = ieee80211_dump_mpath,
+ .get_mpp = ieee80211_get_mpp,
+ .dump_mpp = ieee80211_dump_mpp,
.update_mesh_config = ieee80211_update_mesh_config,
.get_mesh_config = ieee80211_get_mesh_config,
.join_mesh = ieee80211_join_mesh,
.leave_mesh = ieee80211_leave_mesh,
#endif
+ .join_ocb = ieee80211_join_ocb,
+ .leave_ocb = ieee80211_leave_ocb,
.change_bss = ieee80211_change_bss,
.set_txq_params = ieee80211_set_txq_params,
.set_monitor_channel = ieee80211_set_monitor_channel,
@@ -3587,6 +3752,8 @@ const struct cfg80211_ops mac80211_config_ops = {
.set_rekey_data = ieee80211_set_rekey_data,
.tdls_oper = ieee80211_tdls_oper,
.tdls_mgmt = ieee80211_tdls_mgmt,
+ .tdls_channel_switch = ieee80211_tdls_channel_switch,
+ .tdls_cancel_channel_switch = ieee80211_tdls_cancel_channel_switch,
.probe_client = ieee80211_probe_client,
.set_noack_map = ieee80211_set_noack_map,
#ifdef CONFIG_PM
@@ -3597,4 +3764,6 @@ const struct cfg80211_ops mac80211_config_ops = {
.channel_switch = ieee80211_channel_switch,
.set_qos_map = ieee80211_set_qos_map,
.set_ap_chanwidth = ieee80211_set_ap_chanwidth,
+ .add_tx_ts = ieee80211_add_tx_ts,
+ .del_tx_ts = ieee80211_del_tx_ts,
};
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 4c74e8da64b9..5d6dae9e4aac 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -270,6 +270,7 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_OCB:
width = vif->bss_conf.chandef.width;
break;
case NL80211_IFTYPE_UNSPECIFIED:
@@ -674,6 +675,7 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_OCB:
break;
default:
WARN_ON_ONCE(1);
@@ -909,6 +911,7 @@ ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data *sdata)
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_OCB:
ieee80211_queue_work(&sdata->local->hw,
&sdata->csa_finalize_work);
break;
@@ -929,6 +932,21 @@ ieee80211_vif_chanctx_reservation_complete(struct ieee80211_sub_if_data *sdata)
}
}
+static void
+ieee80211_vif_update_chandef(struct ieee80211_sub_if_data *sdata,
+ const struct cfg80211_chan_def *chandef)
+{
+ struct ieee80211_sub_if_data *vlan;
+
+ sdata->vif.bss_conf.chandef = *chandef;
+
+ if (sdata->vif.type != NL80211_IFTYPE_AP)
+ return;
+
+ list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+ vlan->vif.bss_conf.chandef = *chandef;
+}
+
static int
ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata)
{
@@ -991,7 +1009,7 @@ ieee80211_vif_use_reserved_reassign(struct ieee80211_sub_if_data *sdata)
if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width)
changed = BSS_CHANGED_BANDWIDTH;
- sdata->vif.bss_conf.chandef = sdata->reserved_chandef;
+ ieee80211_vif_update_chandef(sdata, &sdata->reserved_chandef);
if (changed)
ieee80211_bss_info_change_notify(sdata, changed);
@@ -1333,7 +1351,7 @@ static int ieee80211_vif_use_reserved_switch(struct ieee80211_local *local)
sdata->reserved_chandef.width)
changed = BSS_CHANGED_BANDWIDTH;
- sdata->vif.bss_conf.chandef = sdata->reserved_chandef;
+ ieee80211_vif_update_chandef(sdata, &sdata->reserved_chandef);
if (changed)
ieee80211_bss_info_change_notify(sdata,
changed);
@@ -1504,7 +1522,7 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
goto out;
}
- sdata->vif.bss_conf.chandef = *chandef;
+ ieee80211_vif_update_chandef(sdata, chandef);
ret = ieee80211_assign_vif_chanctx(sdata, ctx);
if (ret) {
@@ -1634,7 +1652,7 @@ int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
}
break;
case IEEE80211_CHANCTX_WILL_BE_REPLACED:
- /* TODO: Perhaps the bandwith change could be treated as a
+ /* TODO: Perhaps the bandwidth change could be treated as a
* reservation itself? */
ret = -EBUSY;
goto out;
@@ -1646,7 +1664,7 @@ int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
break;
}
- sdata->vif.bss_conf.chandef = *chandef;
+ ieee80211_vif_update_chandef(sdata, chandef);
ieee80211_recalc_chanctx_chantype(local, ctx);
diff --git a/net/mac80211/debug.h b/net/mac80211/debug.h
index 493d68061f0c..1956b3115dd5 100644
--- a/net/mac80211/debug.h
+++ b/net/mac80211/debug.h
@@ -2,6 +2,12 @@
#define __MAC80211_DEBUG_H
#include <net/cfg80211.h>
+#ifdef CONFIG_MAC80211_OCB_DEBUG
+#define MAC80211_OCB_DEBUG 1
+#else
+#define MAC80211_OCB_DEBUG 0
+#endif
+
#ifdef CONFIG_MAC80211_IBSS_DEBUG
#define MAC80211_IBSS_DEBUG 1
#else
@@ -131,6 +137,10 @@ do { \
_sdata_dbg(MAC80211_HT_DEBUG && net_ratelimit(), \
sdata, fmt, ##__VA_ARGS__)
+#define ocb_dbg(sdata, fmt, ...) \
+ _sdata_dbg(MAC80211_OCB_DEBUG, \
+ sdata, fmt, ##__VA_ARGS__)
+
#define ibss_dbg(sdata, fmt, ...) \
_sdata_dbg(MAC80211_IBSS_DEBUG, \
sdata, fmt, ##__VA_ARGS__)
diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c
index 1521cabad3d6..5523b94c7c90 100644
--- a/net/mac80211/debugfs_key.c
+++ b/net/mac80211/debugfs_key.c
@@ -300,10 +300,8 @@ void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata)
lockdep_assert_held(&sdata->local->key_mtx);
- if (sdata->debugfs.default_unicast_key) {
- debugfs_remove(sdata->debugfs.default_unicast_key);
- sdata->debugfs.default_unicast_key = NULL;
- }
+ debugfs_remove(sdata->debugfs.default_unicast_key);
+ sdata->debugfs.default_unicast_key = NULL;
if (sdata->default_unicast_key) {
key = key_mtx_dereference(sdata->local,
@@ -314,10 +312,8 @@ void ieee80211_debugfs_key_update_default(struct ieee80211_sub_if_data *sdata)
sdata->vif.debugfs_dir, buf);
}
- if (sdata->debugfs.default_multicast_key) {
- debugfs_remove(sdata->debugfs.default_multicast_key);
- sdata->debugfs.default_multicast_key = NULL;
- }
+ debugfs_remove(sdata->debugfs.default_multicast_key);
+ sdata->debugfs.default_multicast_key = NULL;
if (sdata->default_multicast_key) {
key = key_mtx_dereference(sdata->local,
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index bafe48916229..94c70091bbd7 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -74,7 +74,7 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
test_sta_flag(sta, WLAN_STA_##flg) ? #flg "\n" : ""
int res = scnprintf(buf, sizeof(buf),
- "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
TEST(AUTH), TEST(ASSOC), TEST(PS_STA),
TEST(PS_DRIVER), TEST(AUTHORIZED),
TEST(SHORT_PREAMBLE),
@@ -82,10 +82,11 @@ static ssize_t sta_flags_read(struct file *file, char __user *userbuf,
TEST(WDS), TEST(CLEAR_PS_FILT),
TEST(MFP), TEST(BLOCK_BA), TEST(PSPOLL),
TEST(UAPSD), TEST(SP), TEST(TDLS_PEER),
- TEST(TDLS_PEER_AUTH), TEST(4ADDR_EVENT),
- TEST(INSERTED), TEST(RATE_CONTROL),
- TEST(TOFFSET_KNOWN), TEST(MPSP_OWNER),
- TEST(MPSP_RECIPIENT));
+ TEST(TDLS_PEER_AUTH), TEST(TDLS_INITIATOR),
+ TEST(TDLS_CHAN_SWITCH), TEST(TDLS_OFF_CHANNEL),
+ TEST(4ADDR_EVENT), TEST(INSERTED),
+ TEST(RATE_CONTROL), TEST(TOFFSET_KNOWN),
+ TEST(MPSP_OWNER), TEST(MPSP_RECIPIENT));
#undef TEST
return simple_read_from_buffer(userbuf, count, ppos, buf, res);
}
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index 196d48c68134..2ebc9ead9695 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -214,7 +214,8 @@ static inline void drv_bss_info_changed(struct ieee80211_local *local,
BSS_CHANGED_BEACON_ENABLED) &&
sdata->vif.type != NL80211_IFTYPE_AP &&
sdata->vif.type != NL80211_IFTYPE_ADHOC &&
- sdata->vif.type != NL80211_IFTYPE_MESH_POINT))
+ sdata->vif.type != NL80211_IFTYPE_MESH_POINT &&
+ sdata->vif.type != NL80211_IFTYPE_OCB))
return;
if (WARN_ON_ONCE(sdata->vif.type == NL80211_IFTYPE_P2P_DEVICE ||
@@ -379,23 +380,26 @@ static inline int drv_sched_scan_stop(struct ieee80211_local *local,
return ret;
}
-static inline void drv_sw_scan_start(struct ieee80211_local *local)
+static inline void drv_sw_scan_start(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ const u8 *mac_addr)
{
might_sleep();
- trace_drv_sw_scan_start(local);
+ trace_drv_sw_scan_start(local, sdata, mac_addr);
if (local->ops->sw_scan_start)
- local->ops->sw_scan_start(&local->hw);
+ local->ops->sw_scan_start(&local->hw, &sdata->vif, mac_addr);
trace_drv_return_void(local);
}
-static inline void drv_sw_scan_complete(struct ieee80211_local *local)
+static inline void drv_sw_scan_complete(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
{
might_sleep();
- trace_drv_sw_scan_complete(local);
+ trace_drv_sw_scan_complete(local, sdata);
if (local->ops->sw_scan_complete)
- local->ops->sw_scan_complete(&local->hw);
+ local->ops->sw_scan_complete(&local->hw, &sdata->vif);
trace_drv_return_void(local);
}
@@ -620,6 +624,21 @@ static inline void drv_sta_rc_update(struct ieee80211_local *local,
trace_drv_return_void(local);
}
+static inline void drv_sta_rate_tbl_update(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta)
+{
+ sdata = get_bss_sdata(sdata);
+ if (!check_sdata_in_driver(sdata))
+ return;
+
+ trace_drv_sta_rate_tbl_update(local, sdata, sta);
+ if (local->ops->sta_rate_tbl_update)
+ local->ops->sta_rate_tbl_update(&local->hw, &sdata->vif, sta);
+
+ trace_drv_return_void(local);
+}
+
static inline int drv_conf_tx(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata, u16 ac,
const struct ieee80211_tx_queue_params *params)
@@ -631,6 +650,12 @@ static inline int drv_conf_tx(struct ieee80211_local *local,
if (!check_sdata_in_driver(sdata))
return -EIO;
+ if (WARN_ONCE(params->cw_min == 0 ||
+ params->cw_min > params->cw_max,
+ "%s: invalid CW_min/CW_max: %d/%d\n",
+ sdata->name, params->cw_min, params->cw_max))
+ return -EINVAL;
+
trace_drv_conf_tx(local, sdata, ac, params);
if (local->ops->conf_tx)
ret = local->ops->conf_tx(&local->hw, &sdata->vif,
@@ -764,12 +789,13 @@ static inline void drv_flush(struct ieee80211_local *local,
}
static inline void drv_channel_switch(struct ieee80211_local *local,
- struct ieee80211_channel_switch *ch_switch)
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_channel_switch *ch_switch)
{
might_sleep();
- trace_drv_channel_switch(local, ch_switch);
- local->ops->channel_switch(&local->hw, ch_switch);
+ trace_drv_channel_switch(local, sdata, ch_switch);
+ local->ops->channel_switch(&local->hw, &sdata->vif, ch_switch);
trace_drv_return_void(local);
}
@@ -1144,13 +1170,15 @@ static inline void drv_stop_ap(struct ieee80211_local *local,
trace_drv_return_void(local);
}
-static inline void drv_restart_complete(struct ieee80211_local *local)
+static inline void
+drv_reconfig_complete(struct ieee80211_local *local,
+ enum ieee80211_reconfig_type reconfig_type)
{
might_sleep();
- trace_drv_restart_complete(local);
- if (local->ops->restart_complete)
- local->ops->restart_complete(&local->hw);
+ trace_drv_reconfig_complete(local, reconfig_type);
+ if (local->ops->reconfig_complete)
+ local->ops->reconfig_complete(&local->hw, reconfig_type);
trace_drv_return_void(local);
}
@@ -1196,6 +1224,40 @@ drv_channel_switch_beacon(struct ieee80211_sub_if_data *sdata,
}
}
+static inline int
+drv_pre_channel_switch(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_channel_switch *ch_switch)
+{
+ struct ieee80211_local *local = sdata->local;
+ int ret = 0;
+
+ if (!check_sdata_in_driver(sdata))
+ return -EIO;
+
+ trace_drv_pre_channel_switch(local, sdata, ch_switch);
+ if (local->ops->pre_channel_switch)
+ ret = local->ops->pre_channel_switch(&local->hw, &sdata->vif,
+ ch_switch);
+ trace_drv_return_int(local, ret);
+ return ret;
+}
+
+static inline int
+drv_post_channel_switch(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ int ret = 0;
+
+ if (!check_sdata_in_driver(sdata))
+ return -EIO;
+
+ trace_drv_post_channel_switch(local, sdata);
+ if (local->ops->post_channel_switch)
+ ret = local->ops->post_channel_switch(&local->hw, &sdata->vif);
+ trace_drv_return_int(local, ret);
+ return ret;
+}
+
static inline int drv_join_ibss(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata)
{
@@ -1238,4 +1300,71 @@ static inline u32 drv_get_expected_throughput(struct ieee80211_local *local,
return ret;
}
+static inline int drv_get_txpower(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata, int *dbm)
+{
+ int ret;
+
+ if (!local->ops->get_txpower)
+ return -EOPNOTSUPP;
+
+ ret = local->ops->get_txpower(&local->hw, &sdata->vif, dbm);
+ trace_drv_get_txpower(local, sdata, *dbm, ret);
+
+ return ret;
+}
+
+static inline int
+drv_tdls_channel_switch(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta, u8 oper_class,
+ struct cfg80211_chan_def *chandef,
+ struct sk_buff *tmpl_skb, u32 ch_sw_tm_ie)
+{
+ int ret;
+
+ might_sleep();
+ if (!check_sdata_in_driver(sdata))
+ return -EIO;
+
+ if (!local->ops->tdls_channel_switch)
+ return -EOPNOTSUPP;
+
+ trace_drv_tdls_channel_switch(local, sdata, sta, oper_class, chandef);
+ ret = local->ops->tdls_channel_switch(&local->hw, &sdata->vif, sta,
+ oper_class, chandef, tmpl_skb,
+ ch_sw_tm_ie);
+ trace_drv_return_int(local, ret);
+ return ret;
+}
+
+static inline void
+drv_tdls_cancel_channel_switch(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta)
+{
+ might_sleep();
+ if (!check_sdata_in_driver(sdata))
+ return;
+
+ if (!local->ops->tdls_cancel_channel_switch)
+ return;
+
+ trace_drv_tdls_cancel_channel_switch(local, sdata, sta);
+ local->ops->tdls_cancel_channel_switch(&local->hw, &sdata->vif, sta);
+ trace_drv_return_void(local);
+}
+
+static inline void
+drv_tdls_recv_channel_switch(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_tdls_ch_sw_params *params)
+{
+ trace_drv_tdls_recv_channel_switch(local, sdata, params);
+ if (local->ops->tdls_recv_channel_switch)
+ local->ops->tdls_recv_channel_switch(&local->hw, &sdata->vif,
+ params);
+ trace_drv_return_void(local);
+}
+
#endif /* __MAC80211_DRIVER_OPS */
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 56b53571c807..509bc157ce55 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -805,7 +805,7 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
memset(&params, 0, sizeof(params));
memset(&csa_ie, 0, sizeof(csa_ie));
- err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon,
+ err = ieee80211_parse_ch_switch_ie(sdata, elems,
ifibss->chandef.chan->band,
sta_flags, ifibss->bssid, &csa_ie);
/* can't switch to destination channel, fail */
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index c2aaec4dfcf0..cc6e964d9837 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -131,7 +131,7 @@ enum ieee80211_bss_corrupt_data_flags {
*
* These are bss flags that are attached to a bss in the
* @valid_data field of &struct ieee80211_bss. They show which parts
- * of the data structure were recieved as a result of an un-corrupted
+ * of the data structure were received as a result of an un-corrupted
* beacon/probe response.
*/
enum ieee80211_bss_valid_data_flags {
@@ -399,6 +399,24 @@ struct ieee80211_mgd_assoc_data {
u8 ie[];
};
+struct ieee80211_sta_tx_tspec {
+ /* timestamp of the first packet in the time slice */
+ unsigned long time_slice_start;
+
+ u32 admitted_time; /* in usecs, unlike over the air */
+ u8 tsid;
+ s8 up; /* signed to be able to invalidate with -1 during teardown */
+
+ /* consumed TX time in microseconds in the time slice */
+ u32 consumed_tx_time;
+ enum {
+ TX_TSPEC_ACTION_NONE = 0,
+ TX_TSPEC_ACTION_DOWNGRADE,
+ TX_TSPEC_ACTION_STOP_DOWNGRADE,
+ } action;
+ bool downgraded;
+};
+
struct ieee80211_if_managed {
struct timer_list timer;
struct timer_list conn_mon_timer;
@@ -434,6 +452,8 @@ struct ieee80211_if_managed {
unsigned int flags;
+ bool csa_waiting_bcn;
+
bool beacon_crc_valid;
u32 beacon_crc;
@@ -505,8 +525,23 @@ struct ieee80211_if_managed {
struct ieee80211_vht_cap vht_capa; /* configured VHT overrides */
struct ieee80211_vht_cap vht_capa_mask; /* Valid parts of vht_capa */
+ /* TDLS support */
u8 tdls_peer[ETH_ALEN] __aligned(2);
struct delayed_work tdls_peer_del_work;
+ struct sk_buff *orig_teardown_skb; /* The original teardown skb */
+ struct sk_buff *teardown_skb; /* A copy to send through the AP */
+ spinlock_t teardown_lock; /* To lock changing teardown_skb */
+ bool tdls_chan_switch_prohibited;
+
+ /* WMM-AC TSPEC support */
+ struct ieee80211_sta_tx_tspec tx_tspec[IEEE80211_NUM_ACS];
+ /* Use a separate work struct so that we can do something here
+ * while the sdata->work is flushing the queues, for example.
+ * otherwise, in scenarios where we hardly get any traffic out
+ * on the BE queue, but there's a lot of VO traffic, we might
+ * get stuck in a downgraded situation and flush takes forever.
+ */
+ struct delayed_work tx_tspec_wk;
};
struct ieee80211_if_ibss {
@@ -547,6 +582,25 @@ struct ieee80211_if_ibss {
};
/**
+ * struct ieee80211_if_ocb - OCB mode state
+ *
+ * @housekeeping_timer: timer for periodic invocation of a housekeeping task
+ * @wrkq_flags: OCB deferred task action
+ * @incomplete_lock: delayed STA insertion lock
+ * @incomplete_stations: list of STAs waiting for delayed insertion
+ * @joined: indication if the interface is connected to an OCB network
+ */
+struct ieee80211_if_ocb {
+ struct timer_list housekeeping_timer;
+ unsigned long wrkq_flags;
+
+ spinlock_t incomplete_lock;
+ struct list_head incomplete_stations;
+
+ bool joined;
+};
+
+/**
* struct ieee80211_mesh_sync_ops - Extensible synchronization framework interface
*
* these declarations define the interface, which enables
@@ -839,6 +893,7 @@ struct ieee80211_sub_if_data {
struct ieee80211_if_managed mgd;
struct ieee80211_if_ibss ibss;
struct ieee80211_if_mesh mesh;
+ struct ieee80211_if_ocb ocb;
u32 mntr_flags;
} u;
@@ -938,6 +993,7 @@ enum sdata_queue_type {
IEEE80211_SDATA_QUEUE_AGG_STOP = 2,
IEEE80211_SDATA_QUEUE_RX_AGG_START = 3,
IEEE80211_SDATA_QUEUE_RX_AGG_STOP = 4,
+ IEEE80211_SDATA_QUEUE_TDLS_CHSW = 5,
};
enum {
@@ -955,6 +1011,7 @@ enum queue_stop_reason {
IEEE80211_QUEUE_STOP_REASON_OFFCHANNEL,
IEEE80211_QUEUE_STOP_REASON_FLUSH,
IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN,
+ IEEE80211_QUEUE_STOP_REASON_RESERVE_TID,
IEEE80211_QUEUE_STOP_REASONS,
};
@@ -1181,7 +1238,7 @@ struct ieee80211_local {
unsigned long scanning;
struct cfg80211_ssid scan_ssid;
struct cfg80211_scan_request *int_scan_req;
- struct cfg80211_scan_request *scan_req;
+ struct cfg80211_scan_request __rcu *scan_req;
struct ieee80211_scan_request *hw_scan_req;
struct cfg80211_chan_def scan_chandef;
enum ieee80211_band hw_scan_band;
@@ -1191,7 +1248,8 @@ struct ieee80211_local {
struct work_struct sched_scan_stopped_work;
struct ieee80211_sub_if_data __rcu *sched_scan_sdata;
- struct cfg80211_sched_scan_request *sched_scan_req;
+ struct cfg80211_sched_scan_request __rcu *sched_scan_req;
+ u8 scan_addr[ETH_ALEN];
unsigned long leave_oper_channel_time;
enum mac80211_scan_state next_scan_state;
@@ -1307,6 +1365,9 @@ struct ieee80211_local {
/* virtual monitor interface */
struct ieee80211_sub_if_data __rcu *monitor_sdata;
struct cfg80211_chan_def monitor_chandef;
+
+ /* extended capabilities provided by mac80211 */
+ u8 ext_capa[8];
};
static inline struct ieee80211_sub_if_data *
@@ -1342,6 +1403,9 @@ struct ieee802_11_elems {
size_t total_len;
/* pointers to IEs */
+ const struct ieee80211_tdls_lnkie *lnk_id;
+ const struct ieee80211_ch_switch_timing *ch_sw_timing;
+ const u8 *ext_capab;
const u8 *ssid;
const u8 *supp_rates;
const u8 *ds_params;
@@ -1376,6 +1440,7 @@ struct ieee802_11_elems {
const struct ieee80211_mesh_chansw_params_ie *mesh_chansw_params_ie;
/* length of them, respectively */
+ u8 ext_capab_len;
u8 ssid_len;
u8 supp_rates_len;
u8 tim_len;
@@ -1454,6 +1519,7 @@ void ieee80211_mgd_conn_tx_status(struct ieee80211_sub_if_data *sdata,
__le16 fc, bool acked);
void ieee80211_mgd_quiesce(struct ieee80211_sub_if_data *sdata);
void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata);
+void ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata);
/* IBSS code */
void ieee80211_ibss_notify_scan_completed(struct ieee80211_local *local);
@@ -1471,6 +1537,15 @@ int ieee80211_ibss_csa_beacon(struct ieee80211_sub_if_data *sdata,
int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata);
void ieee80211_ibss_stop(struct ieee80211_sub_if_data *sdata);
+/* OCB code */
+void ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata);
+void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata,
+ const u8 *bssid, const u8 *addr, u32 supp_rates);
+void ieee80211_ocb_setup_sdata(struct ieee80211_sub_if_data *sdata);
+int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata,
+ struct ocb_setup *setup);
+int ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata);
+
/* mesh code */
void ieee80211_mesh_work(struct ieee80211_sub_if_data *sdata);
void ieee80211_mesh_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
@@ -1562,8 +1637,14 @@ 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);
+void __ieee80211_subif_start_xmit(struct sk_buff *skb,
+ struct net_device *dev,
+ u32 info_flags);
void ieee80211_purge_tx_queue(struct ieee80211_hw *hw,
struct sk_buff_head *skbs);
+struct sk_buff *
+ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, u32 info_flags);
/* HT */
void ieee80211_apply_htcap_overrides(struct ieee80211_sub_if_data *sdata,
@@ -1642,7 +1723,6 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
* ieee80211_parse_ch_switch_ie - parses channel switch IEs
* @sdata: the sdata of the interface which has received the frame
* @elems: parsed 802.11 elements received with the frame
- * @beacon: indicates if the frame was a beacon or probe response
* @current_band: indicates the current band
* @sta_flags: contains information about own capabilities and restrictions
* to decide which channel switch announcements can be accepted. Only the
@@ -1656,7 +1736,7 @@ void ieee80211_process_measurement_req(struct ieee80211_sub_if_data *sdata,
* Return: 0 on success, <0 on error and >0 if there is nothing to parse.
*/
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
- struct ieee802_11_elems *elems, bool beacon,
+ struct ieee802_11_elems *elems,
enum ieee80211_band current_band,
u32 sta_flags, u8 *bssid,
struct ieee80211_csa_ie *csa_ie);
@@ -1691,8 +1771,7 @@ void mac80211_ev_michael_mic_failure(struct ieee80211_sub_if_data *sdata, int ke
gfp_t gfp);
void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
bool bss_notify);
-void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
- enum ieee80211_band band);
+void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb);
void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, int tid,
@@ -1758,6 +1837,13 @@ static inline bool ieee80211_rx_reorder_ready(struct sk_buff_head *frames)
return true;
}
+extern const int ieee802_1d_to_ac[8];
+
+static inline int ieee80211_ac_from_tid(int tid)
+{
+ return ieee802_1d_to_ac[tid & 7];
+}
+
void ieee80211_dynamic_ps_enable_work(struct work_struct *work);
void ieee80211_dynamic_ps_disable_work(struct work_struct *work);
void ieee80211_dynamic_ps_timer(unsigned long data);
@@ -1767,7 +1853,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
struct ieee80211_hdr *hdr);
void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_hdr *hdr, bool ack);
+ struct ieee80211_hdr *hdr, bool ack, u16 tx_time);
void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
unsigned long queues,
@@ -1796,6 +1882,9 @@ void ieee80211_add_pending_skbs(struct ieee80211_local *local,
struct sk_buff_head *skbs);
void ieee80211_flush_queues(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata);
+void __ieee80211_flush_queues(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ unsigned int queues);
void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
u16 transaction, u16 auth_alg, u16 status,
@@ -1812,12 +1901,14 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
u8 bands_used, u32 *rate_masks,
struct cfg80211_chan_def *chandef);
struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
- u8 *dst, u32 ratemask,
+ const u8 *src, const u8 *dst,
+ u32 ratemask,
struct ieee80211_channel *chan,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len,
bool directed);
-void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
+void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata,
+ const u8 *src, const u8 *dst,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len,
u32 ratemask, bool directed, u32 tx_flags,
@@ -1833,8 +1924,10 @@ int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata);
void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata);
-size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
- const u8 *ids, int n_ids, size_t offset);
+size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
+ const u8 *ids, int n_ids,
+ const u8 *after_ric, int n_after_ric,
+ size_t offset);
size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset);
u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap,
u16 cap);
@@ -1921,6 +2014,14 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
const u8 *peer, enum nl80211_tdls_operation oper);
void ieee80211_tdls_peer_del_work(struct work_struct *wk);
+int ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *addr, u8 oper_class,
+ struct cfg80211_chan_def *chandef);
+void ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
+ struct net_device *dev,
+ const u8 *addr);
+void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb);
extern const struct ethtool_ops ieee80211_ethtool_ops;
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index af237223a8cd..417355390873 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -259,6 +259,15 @@ static int ieee80211_check_concurrent_iface(struct ieee80211_sub_if_data *sdata,
list_for_each_entry(nsdata, &local->interfaces, list) {
if (nsdata != sdata && ieee80211_sdata_running(nsdata)) {
/*
+ * Only OCB and monitor mode may coexist
+ */
+ if ((sdata->vif.type == NL80211_IFTYPE_OCB &&
+ nsdata->vif.type != NL80211_IFTYPE_MONITOR) ||
+ (sdata->vif.type != NL80211_IFTYPE_MONITOR &&
+ nsdata->vif.type == NL80211_IFTYPE_OCB))
+ return -EBUSY;
+
+ /*
* Allow only a single IBSS interface to be up at any
* time. This is restricted because beacon distribution
* cannot work properly if both are in the same IBSS.
@@ -511,6 +520,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
sdata->vif.cab_queue = master->vif.cab_queue;
memcpy(sdata->vif.hw_queue, master->vif.hw_queue,
sizeof(sdata->vif.hw_queue));
+ sdata->vif.bss_conf.chandef = master->vif.bss_conf.chandef;
break;
}
case NL80211_IFTYPE_AP:
@@ -521,6 +531,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_P2P_DEVICE:
+ case NL80211_IFTYPE_OCB:
/* no special treatment */
break;
case NL80211_IFTYPE_UNSPECIFIED:
@@ -631,6 +642,7 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_MESH_POINT:
+ case NL80211_IFTYPE_OCB:
netif_carrier_off(dev);
break;
case NL80211_IFTYPE_WDS:
@@ -766,10 +778,12 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
int i, flushed;
struct ps_data *ps;
struct cfg80211_chan_def chandef;
+ bool cancel_scan;
clear_bit(SDATA_STATE_RUNNING, &sdata->state);
- if (rcu_access_pointer(local->scan_sdata) == sdata)
+ cancel_scan = rcu_access_pointer(local->scan_sdata) == sdata;
+ if (cancel_scan)
ieee80211_scan_cancel(local);
/*
@@ -842,6 +856,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
sdata_lock(sdata);
mutex_lock(&local->mtx);
sdata->vif.csa_active = false;
+ if (sdata->vif.type == NL80211_IFTYPE_STATION)
+ sdata->u.mgd.csa_waiting_bcn = false;
if (sdata->csa_block_tx) {
ieee80211_wake_vif_queues(local, sdata,
IEEE80211_QUEUE_STOP_REASON_CSA);
@@ -898,6 +914,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
list_del(&sdata->u.vlan.list);
mutex_unlock(&local->mtx);
RCU_INIT_POINTER(sdata->vif.chanctx_conf, NULL);
+ /* see comment in the default case below */
+ ieee80211_free_keys(sdata, true);
/* no need to tell driver */
break;
case NL80211_IFTYPE_MONITOR:
@@ -923,17 +941,16 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
/*
* When we get here, the interface is marked down.
* Free the remaining keys, if there are any
- * (shouldn't be, except maybe in WDS mode?)
+ * (which can happen in AP mode if userspace sets
+ * keys before the interface is operating, and maybe
+ * also in WDS mode)
*
* Force the key freeing to always synchronize_net()
* to wait for the RX path in case it is using this
- * interface enqueuing frames * at this very time on
+ * interface enqueuing frames at this very time on
* another CPU.
*/
ieee80211_free_keys(sdata, true);
-
- /* fall through */
- case NL80211_IFTYPE_AP:
skb_queue_purge(&sdata->skb_queue);
}
@@ -991,6 +1008,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
ieee80211_recalc_ps(local, -1);
+ if (cancel_scan)
+ flush_delayed_work(&local->scan_work);
+
if (local->open_count == 0) {
ieee80211_stop_device(local);
@@ -1189,6 +1209,8 @@ static void ieee80211_iface_work(struct work_struct *work)
WLAN_BACK_RECIPIENT, 0,
false);
mutex_unlock(&local->sta_mtx);
+ } else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_TDLS_CHSW) {
+ ieee80211_process_tdls_channel_switch(sdata, skb);
} else if (ieee80211_is_action(mgmt->frame_control) &&
mgmt->u.action.category == WLAN_CATEGORY_BACK) {
int len = skb->len;
@@ -1279,6 +1301,9 @@ static void ieee80211_iface_work(struct work_struct *work)
break;
ieee80211_mesh_work(sdata);
break;
+ case NL80211_IFTYPE_OCB:
+ ieee80211_ocb_work(sdata);
+ break;
default:
break;
}
@@ -1298,6 +1323,9 @@ static void ieee80211_recalc_smps_work(struct work_struct *work)
static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
enum nl80211_iftype type)
{
+ static const u8 bssid_wildcard[ETH_ALEN] = {0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff};
+
/* clear type-dependent union */
memset(&sdata->u, 0, sizeof(sdata->u));
@@ -1349,6 +1377,10 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
sdata->vif.bss_conf.bssid = sdata->u.mgd.bssid;
ieee80211_sta_setup_sdata(sdata);
break;
+ case NL80211_IFTYPE_OCB:
+ sdata->vif.bss_conf.bssid = bssid_wildcard;
+ ieee80211_ocb_setup_sdata(sdata);
+ break;
case NL80211_IFTYPE_ADHOC:
sdata->vif.bss_conf.bssid = sdata->u.ibss.bssid;
ieee80211_ibss_setup_sdata(sdata);
@@ -1396,6 +1428,7 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_OCB:
/*
* Could maybe also all others here?
* Just not sure how that interacts
@@ -1411,6 +1444,7 @@ static int ieee80211_runtime_change_iftype(struct ieee80211_sub_if_data *sdata,
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_OCB:
/*
* Could probably support everything
* but WDS here (WDS do_open can fail
@@ -1669,7 +1703,10 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
}
ieee80211_assign_perm_addr(local, ndev->perm_addr, type);
- memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN);
+ if (params && is_valid_ether_addr(params->macaddr))
+ memcpy(ndev->dev_addr, params->macaddr, ETH_ALEN);
+ else
+ memcpy(ndev->dev_addr, ndev->perm_addr, ETH_ALEN);
SET_NETDEV_DEV(ndev, wiphy_dev(local->hw.wiphy));
/* don't use IEEE80211_DEV_TO_SUB_IF -- it checks too much */
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 4712150dc210..434a91ad12c8 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -94,8 +94,17 @@ static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
might_sleep();
- if (key->flags & KEY_FLAG_TAINTED)
+ if (key->flags & KEY_FLAG_TAINTED) {
+ /* If we get here, it's during resume and the key is
+ * tainted so shouldn't be used/programmed any more.
+ * However, its flags may still indicate that it was
+ * programmed into the device (since we're in resume)
+ * so clear that flag now to avoid trying to remove
+ * it again later.
+ */
+ key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
return -EINVAL;
+ }
if (!key->local->ops->set_key)
goto out_unsupported;
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 0de7c93bf62b..6ab99da38db9 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -478,13 +478,9 @@ static const struct ieee80211_vht_cap mac80211_vht_capa_mod_mask = {
},
};
-static const u8 extended_capabilities[] = {
- 0, 0, 0, 0, 0, 0, 0,
- WLAN_EXT_CAPA8_OPMODE_NOTIF,
-};
-
-struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
- const struct ieee80211_ops *ops)
+struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
+ const struct ieee80211_ops *ops,
+ const char *requested_name)
{
struct ieee80211_local *local;
int priv_size, i;
@@ -524,7 +520,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
*/
priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len;
- wiphy = wiphy_new(&mac80211_config_ops, priv_size);
+ wiphy = wiphy_new_nm(&mac80211_config_ops, priv_size, requested_name);
if (!wiphy)
return NULL;
@@ -539,10 +535,6 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
WIPHY_FLAG_REPORTS_OBSS |
WIPHY_FLAG_OFFCHAN_TX;
- wiphy->extended_capabilities = extended_capabilities;
- wiphy->extended_capabilities_mask = extended_capabilities;
- wiphy->extended_capabilities_len = ARRAY_SIZE(extended_capabilities);
-
if (ops->remain_on_channel)
wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
@@ -550,6 +542,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
NL80211_FEATURE_SAE |
NL80211_FEATURE_HT_IBSS |
NL80211_FEATURE_VIF_TXPOWER |
+ NL80211_FEATURE_MAC_ON_CREATE |
NL80211_FEATURE_USERSPACE_MPM;
if (!ops->hw_scan)
@@ -591,6 +584,13 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask;
wiphy->vht_capa_mod_mask = &mac80211_vht_capa_mod_mask;
+ local->ext_capa[7] = WLAN_EXT_CAPA8_OPMODE_NOTIF;
+
+ wiphy->extended_capabilities = local->ext_capa;
+ wiphy->extended_capabilities_mask = local->ext_capa;
+ wiphy->extended_capabilities_len =
+ ARRAY_SIZE(local->ext_capa);
+
INIT_LIST_HEAD(&local->interfaces);
__hw_addr_init(&local->mc_list);
@@ -651,7 +651,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
return &local->hw;
}
-EXPORT_SYMBOL(ieee80211_alloc_hw);
+EXPORT_SYMBOL(ieee80211_alloc_hw_nm);
static int ieee80211_init_cipher_suites(struct ieee80211_local *local)
{
@@ -764,6 +764,12 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
local->hw.offchannel_tx_hw_queue >= local->hw.queues))
return -EINVAL;
+ if ((hw->wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) &&
+ (!local->ops->tdls_channel_switch ||
+ !local->ops->tdls_cancel_channel_switch ||
+ !local->ops->tdls_recv_channel_switch))
+ return -EOPNOTSUPP;
+
#ifdef CONFIG_PM
if (hw->wiphy->wowlan && (!local->ops->suspend || !local->ops->resume))
return -EINVAL;
@@ -787,13 +793,14 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_WDS))
return -EINVAL;
- /* DFS currently not supported with channel context drivers */
+ /* DFS is not supported with multi-channel combinations yet */
for (i = 0; i < local->hw.wiphy->n_iface_combinations; i++) {
const struct ieee80211_iface_combination *comb;
comb = &local->hw.wiphy->iface_combinations[i];
- if (comb->radar_detect_widths)
+ if (comb->radar_detect_widths &&
+ comb->num_different_channels > 1)
return -EINVAL;
}
}
@@ -958,6 +965,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
if (local->hw.wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)
local->hw.wiphy->flags |= WIPHY_FLAG_TDLS_EXTERNAL_SETUP;
+ /* mac80211 supports eCSA, if the driver supports STA CSA at all */
+ if (local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)
+ local->ext_capa[0] |= WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING;
+
local->hw.wiphy->max_num_csa_counters = IEEE80211_MAX_CSA_COUNTERS_NUM;
result = wiphy_register(local->hw.wiphy);
@@ -1019,7 +1030,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
}
/* add one default STA interface if supported */
- if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION)) {
+ if (local->hw.wiphy->interface_modes & BIT(NL80211_IFTYPE_STATION) &&
+ !(hw->flags & IEEE80211_HW_NO_AUTO_VIF)) {
result = ieee80211_if_add(local, "wlan%d", NULL,
NL80211_IFTYPE_STATION, NULL);
if (result)
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index e9f99c1e3fad..0c8b2a77d312 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -874,7 +874,7 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
memset(&params, 0, sizeof(params));
memset(&csa_ie, 0, sizeof(csa_ie));
- err = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, band,
+ err = ieee80211_parse_ch_switch_ie(sdata, elems, band,
sta_flags, sdata->vif.addr,
&csa_ie);
if (err < 0)
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index f39a19f9090f..50c8473cf9dc 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -270,6 +270,8 @@ int mpp_path_add(struct ieee80211_sub_if_data *sdata,
const u8 *dst, const u8 *mpp);
struct mesh_path *
mesh_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx);
+struct mesh_path *
+mpp_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx);
void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop);
void mesh_path_expire(struct ieee80211_sub_if_data *sdata);
void mesh_rx_path_sel_frame(struct ieee80211_sub_if_data *sdata,
@@ -317,6 +319,7 @@ void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata);
bool mesh_action_is_path_sel(struct ieee80211_mgmt *mgmt);
extern int mesh_paths_generation;
+extern int mpp_paths_generation;
#ifdef CONFIG_MAC80211_MESH
static inline
diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c
index a6699dceae7c..b890e225a8f1 100644
--- a/net/mac80211/mesh_pathtbl.c
+++ b/net/mac80211/mesh_pathtbl.c
@@ -44,6 +44,7 @@ static struct mesh_table __rcu *mesh_paths;
static struct mesh_table __rcu *mpp_paths; /* Store paths for MPP&MAP */
int mesh_paths_generation;
+int mpp_paths_generation;
/* This lock will have the grow table function as writer and add / delete nodes
* as readers. RCU provides sufficient protection only when reading the table
@@ -410,6 +411,33 @@ mesh_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx)
}
/**
+ * mpp_path_lookup_by_idx - look up a path in the proxy path table by its index
+ * @idx: index
+ * @sdata: local subif, or NULL for all entries
+ *
+ * Returns: pointer to the proxy path structure, or NULL if not found.
+ *
+ * Locking: must be called within a read rcu section.
+ */
+struct mesh_path *
+mpp_path_lookup_by_idx(struct ieee80211_sub_if_data *sdata, int idx)
+{
+ struct mesh_table *tbl = rcu_dereference(mpp_paths);
+ struct mpath_node *node;
+ int i;
+ int j = 0;
+
+ for_each_mesh_entry(tbl, node, i) {
+ if (sdata && node->mpath->sdata != sdata)
+ continue;
+ if (j++ == idx)
+ return node->mpath;
+ }
+
+ return NULL;
+}
+
+/**
* mesh_path_add_gate - add the given mpath to a mesh gate to our path table
* @mpath: gate path to add to table
*/
@@ -691,6 +719,9 @@ int mpp_path_add(struct ieee80211_sub_if_data *sdata,
spin_unlock(&tbl->hashwlock[hash_idx]);
read_unlock_bh(&pathtbl_resize_lock);
+
+ mpp_paths_generation++;
+
if (grow) {
set_bit(MESH_WORK_GROW_MPP_TABLE, &ifmsh->wrkq_flags);
ieee80211_queue_work(&local->hw, &sdata->work);
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 2de88704278b..75a9bf50207e 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -552,13 +552,17 @@ static void ieee80211_add_vht_ie(struct ieee80211_sub_if_data *sdata,
cap = vht_cap.cap;
if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_80P80MHZ) {
- cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
- cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+ u32 bw = cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+
+ cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+ if (bw == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ ||
+ bw == IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)
+ cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
}
if (sdata->u.mgd.flags & IEEE80211_STA_DISABLE_160MHZ) {
cap &= ~IEEE80211_VHT_CAP_SHORT_GI_160;
- cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+ cap &= ~IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK;
}
/*
@@ -775,11 +779,30 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
WLAN_EID_QOS_CAPA,
WLAN_EID_RRM_ENABLED_CAPABILITIES,
WLAN_EID_MOBILITY_DOMAIN,
+ WLAN_EID_FAST_BSS_TRANSITION, /* reassoc only */
+ WLAN_EID_RIC_DATA, /* reassoc only */
WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
};
- noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len,
- before_ht, ARRAY_SIZE(before_ht),
- offset);
+ static const u8 after_ric[] = {
+ WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
+ WLAN_EID_HT_CAPABILITY,
+ WLAN_EID_BSS_COEX_2040,
+ WLAN_EID_EXT_CAPABILITY,
+ WLAN_EID_QOS_TRAFFIC_CAPA,
+ WLAN_EID_TIM_BCAST_REQ,
+ WLAN_EID_INTERWORKING,
+ /* 60GHz doesn't happen right now */
+ WLAN_EID_VHT_CAPABILITY,
+ WLAN_EID_OPMODE_NOTIF,
+ };
+
+ noffset = ieee80211_ie_split_ric(assoc_data->ie,
+ assoc_data->ie_len,
+ before_ht,
+ ARRAY_SIZE(before_ht),
+ after_ric,
+ ARRAY_SIZE(after_ric),
+ offset);
pos = skb_put(skb, noffset - offset);
memcpy(pos, assoc_data->ie + offset, noffset - offset);
offset = noffset;
@@ -813,6 +836,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
WLAN_EID_TIM_BCAST_REQ,
WLAN_EID_INTERWORKING,
};
+
+ /* RIC already taken above, so no need to handle here anymore */
noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len,
before_vht, ARRAY_SIZE(before_vht),
offset);
@@ -1001,14 +1026,7 @@ static void ieee80211_chswitch_work(struct work_struct *work)
/* XXX: shouldn't really modify cfg80211-owned data! */
ifmgd->associated->channel = sdata->csa_chandef.chan;
- sdata->vif.csa_active = false;
-
- /* XXX: wait for a beacon first? */
- if (sdata->csa_block_tx) {
- ieee80211_wake_vif_queues(local, sdata,
- IEEE80211_QUEUE_STOP_REASON_CSA);
- sdata->csa_block_tx = false;
- }
+ ifmgd->csa_waiting_bcn = true;
ieee80211_sta_reset_beacon_monitor(sdata);
ieee80211_sta_reset_conn_monitor(sdata);
@@ -1019,6 +1037,37 @@ out:
sdata_unlock(sdata);
}
+static void ieee80211_chswitch_post_beacon(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ int ret;
+
+ sdata_assert_lock(sdata);
+
+ WARN_ON(!sdata->vif.csa_active);
+
+ if (sdata->csa_block_tx) {
+ ieee80211_wake_vif_queues(local, sdata,
+ IEEE80211_QUEUE_STOP_REASON_CSA);
+ sdata->csa_block_tx = false;
+ }
+
+ cfg80211_ch_switch_notify(sdata->dev, &sdata->reserved_chandef);
+
+ sdata->vif.csa_active = false;
+ ifmgd->csa_waiting_bcn = false;
+
+ ret = drv_post_channel_switch(sdata);
+ if (ret) {
+ sdata_info(sdata,
+ "driver post channel switch failed, disconnecting\n");
+ ieee80211_queue_work(&local->hw,
+ &ifmgd->csa_connection_drop_work);
+ return;
+ }
+}
+
void ieee80211_chswitch_done(struct ieee80211_vif *vif, bool success)
{
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
@@ -1046,7 +1095,8 @@ static void ieee80211_chswitch_timer(unsigned long data)
static void
ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
- u64 timestamp, struct ieee802_11_elems *elems,
+ u64 timestamp, u32 device_timestamp,
+ struct ieee802_11_elems *elems,
bool beacon)
{
struct ieee80211_local *local = sdata->local;
@@ -1056,6 +1106,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
struct ieee80211_chanctx *chanctx;
enum ieee80211_band current_band;
struct ieee80211_csa_ie csa_ie;
+ struct ieee80211_channel_switch ch_switch;
int res;
sdata_assert_lock(sdata);
@@ -1072,7 +1123,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
current_band = cbss->channel->band;
memset(&csa_ie, 0, sizeof(csa_ie));
- res = ieee80211_parse_ch_switch_ie(sdata, elems, beacon, current_band,
+ res = ieee80211_parse_ch_switch_ie(sdata, elems, current_band,
ifmgd->flags,
ifmgd->associated->bssid, &csa_ie);
if (res < 0)
@@ -1110,21 +1161,31 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
chanctx = container_of(conf, struct ieee80211_chanctx, conf);
- if (local->use_chanctx) {
- u32 num_chanctx = 0;
- list_for_each_entry(chanctx, &local->chanctx_list, list)
- num_chanctx++;
+ if (local->use_chanctx &&
+ !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
+ sdata_info(sdata,
+ "driver doesn't support chan-switch with channel contexts\n");
+ ieee80211_queue_work(&local->hw,
+ &ifmgd->csa_connection_drop_work);
+ mutex_unlock(&local->chanctx_mtx);
+ mutex_unlock(&local->mtx);
+ return;
+ }
+
+ ch_switch.timestamp = timestamp;
+ ch_switch.device_timestamp = device_timestamp;
+ ch_switch.block_tx = csa_ie.mode;
+ ch_switch.chandef = csa_ie.chandef;
+ ch_switch.count = csa_ie.count;
- if (num_chanctx > 1 ||
- !(local->hw.flags & IEEE80211_HW_CHANCTX_STA_CSA)) {
- sdata_info(sdata,
- "not handling chan-switch with channel contexts\n");
- ieee80211_queue_work(&local->hw,
- &ifmgd->csa_connection_drop_work);
- mutex_unlock(&local->chanctx_mtx);
- mutex_unlock(&local->mtx);
- return;
- }
+ if (drv_pre_channel_switch(sdata, &ch_switch)) {
+ sdata_info(sdata,
+ "preparing for channel switch failed, disconnecting\n");
+ ieee80211_queue_work(&local->hw,
+ &ifmgd->csa_connection_drop_work);
+ mutex_unlock(&local->chanctx_mtx);
+ mutex_unlock(&local->mtx);
+ return;
}
res = ieee80211_vif_reserve_chanctx(sdata, &csa_ie.chandef,
@@ -1150,16 +1211,12 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
IEEE80211_QUEUE_STOP_REASON_CSA);
mutex_unlock(&local->mtx);
+ cfg80211_ch_switch_started_notify(sdata->dev, &csa_ie.chandef,
+ csa_ie.count);
+
if (local->ops->channel_switch) {
/* use driver's channel switch callback */
- struct ieee80211_channel_switch ch_switch = {
- .timestamp = timestamp,
- .block_tx = csa_ie.mode,
- .chandef = csa_ie.chandef,
- .count = csa_ie.count,
- };
-
- drv_channel_switch(local, &ch_switch);
+ drv_channel_switch(local, sdata, &ch_switch);
return;
}
@@ -1168,7 +1225,8 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
ieee80211_queue_work(&local->hw, &ifmgd->chswitch_work);
else
mod_timer(&ifmgd->chswitch_timer,
- TU_TO_EXP_TIME(csa_ie.count * cbss->beacon_interval));
+ TU_TO_EXP_TIME((csa_ie.count - 1) *
+ cbss->beacon_interval));
}
static bool
@@ -1579,6 +1637,95 @@ void ieee80211_dfs_cac_timer_work(struct work_struct *work)
mutex_unlock(&sdata->local->mtx);
}
+static bool
+__ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ bool ret;
+ int ac;
+
+ if (local->hw.queues < IEEE80211_NUM_ACS)
+ return false;
+
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+ struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac];
+ int non_acm_ac;
+ unsigned long now = jiffies;
+
+ if (tx_tspec->action == TX_TSPEC_ACTION_NONE &&
+ tx_tspec->admitted_time &&
+ time_after(now, tx_tspec->time_slice_start + HZ)) {
+ tx_tspec->consumed_tx_time = 0;
+ tx_tspec->time_slice_start = now;
+
+ if (tx_tspec->downgraded)
+ tx_tspec->action =
+ TX_TSPEC_ACTION_STOP_DOWNGRADE;
+ }
+
+ switch (tx_tspec->action) {
+ case TX_TSPEC_ACTION_STOP_DOWNGRADE:
+ /* take the original parameters */
+ if (drv_conf_tx(local, sdata, ac, &sdata->tx_conf[ac]))
+ sdata_err(sdata,
+ "failed to set TX queue parameters for queue %d\n",
+ ac);
+ tx_tspec->action = TX_TSPEC_ACTION_NONE;
+ tx_tspec->downgraded = false;
+ ret = true;
+ break;
+ case TX_TSPEC_ACTION_DOWNGRADE:
+ if (time_after(now, tx_tspec->time_slice_start + HZ)) {
+ tx_tspec->action = TX_TSPEC_ACTION_NONE;
+ ret = true;
+ break;
+ }
+ /* downgrade next lower non-ACM AC */
+ for (non_acm_ac = ac + 1;
+ non_acm_ac < IEEE80211_NUM_ACS;
+ non_acm_ac++)
+ if (!(sdata->wmm_acm & BIT(7 - 2 * non_acm_ac)))
+ break;
+ /* The loop will result in using BK even if it requires
+ * admission control, such configuration makes no sense
+ * and we have to transmit somehow - the AC selection
+ * does the same thing.
+ */
+ if (drv_conf_tx(local, sdata, ac,
+ &sdata->tx_conf[non_acm_ac]))
+ sdata_err(sdata,
+ "failed to set TX queue parameters for queue %d\n",
+ ac);
+ tx_tspec->action = TX_TSPEC_ACTION_NONE;
+ ret = true;
+ schedule_delayed_work(&ifmgd->tx_tspec_wk,
+ tx_tspec->time_slice_start + HZ - now + 1);
+ break;
+ case TX_TSPEC_ACTION_NONE:
+ /* nothing now */
+ break;
+ }
+ }
+
+ return ret;
+}
+
+void ieee80211_sta_handle_tspec_ac_params(struct ieee80211_sub_if_data *sdata)
+{
+ if (__ieee80211_sta_handle_tspec_ac_params(sdata))
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_QOS);
+}
+
+static void ieee80211_sta_handle_tspec_ac_params_wk(struct work_struct *work)
+{
+ struct ieee80211_sub_if_data *sdata;
+
+ sdata = container_of(work, struct ieee80211_sub_if_data,
+ u.mgd.tx_tspec_wk.work);
+ ieee80211_sta_handle_tspec_ac_params(sdata);
+}
+
/* MLME */
static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
@@ -1663,12 +1810,14 @@ static bool ieee80211_sta_wmm_params(struct ieee80211_local *local,
params.uapsd = uapsd;
mlme_dbg(sdata,
- "WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d\n",
+ "WMM queue=%d aci=%d acm=%d aifs=%d cWmin=%d cWmax=%d txop=%d uapsd=%d, downgraded=%d\n",
queue, aci, acm,
params.aifs, params.cw_min, params.cw_max,
- params.txop, params.uapsd);
+ params.txop, params.uapsd,
+ ifmgd->tx_tspec[queue].downgraded);
sdata->tx_conf[queue] = params;
- if (drv_conf_tx(local, sdata, queue, &params))
+ if (!ifmgd->tx_tspec[queue].downgraded &&
+ drv_conf_tx(local, sdata, queue, &params))
sdata_err(sdata,
"failed to set TX queue parameters for queue %d\n",
queue);
@@ -1923,6 +2072,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
ieee80211_vif_release_channel(sdata);
sdata->vif.csa_active = false;
+ ifmgd->csa_waiting_bcn = false;
if (sdata->csa_block_tx) {
ieee80211_wake_vif_queues(local, sdata,
IEEE80211_QUEUE_STOP_REASON_CSA);
@@ -1930,6 +2080,10 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
}
mutex_unlock(&local->mtx);
+ /* existing TX TSPEC sessions no longer exist */
+ memset(ifmgd->tx_tspec, 0, sizeof(ifmgd->tx_tspec));
+ cancel_delayed_work_sync(&ifmgd->tx_tspec_wk);
+
sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
}
@@ -1982,9 +2136,46 @@ out:
mutex_unlock(&local->mtx);
}
+static void ieee80211_sta_tx_wmm_ac_notify(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_hdr *hdr,
+ u16 tx_time)
+{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ u16 tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
+ int ac = ieee80211_ac_from_tid(tid);
+ struct ieee80211_sta_tx_tspec *tx_tspec = &ifmgd->tx_tspec[ac];
+ unsigned long now = jiffies;
+
+ if (likely(!tx_tspec->admitted_time))
+ return;
+
+ if (time_after(now, tx_tspec->time_slice_start + HZ)) {
+ tx_tspec->consumed_tx_time = 0;
+ tx_tspec->time_slice_start = now;
+
+ if (tx_tspec->downgraded) {
+ tx_tspec->action = TX_TSPEC_ACTION_STOP_DOWNGRADE;
+ schedule_delayed_work(&ifmgd->tx_tspec_wk, 0);
+ }
+ }
+
+ if (tx_tspec->downgraded)
+ return;
+
+ tx_tspec->consumed_tx_time += tx_time;
+
+ if (tx_tspec->consumed_tx_time >= tx_tspec->admitted_time) {
+ tx_tspec->downgraded = true;
+ tx_tspec->action = TX_TSPEC_ACTION_DOWNGRADE;
+ schedule_delayed_work(&ifmgd->tx_tspec_wk, 0);
+ }
+}
+
void ieee80211_sta_tx_notify(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_hdr *hdr, bool ack)
+ struct ieee80211_hdr *hdr, bool ack, u16 tx_time)
{
+ ieee80211_sta_tx_wmm_ac_notify(sdata, hdr, tx_time);
+
if (!ieee80211_is_data(hdr->frame_control))
return;
@@ -2039,7 +2230,8 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
else
ssid_len = ssid[1];
- ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL,
+ ieee80211_send_probe_req(sdata, sdata->vif.addr, NULL,
+ ssid + 2, ssid_len, NULL,
0, (u32) -1, true, 0,
ifmgd->associated->channel, false);
rcu_read_unlock();
@@ -2047,8 +2239,6 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
run_again(sdata, ifmgd->probe_timeout);
- if (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)
- ieee80211_flush_queues(sdata->local, sdata);
}
static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
@@ -2077,9 +2267,7 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
"detected beacon loss from AP (missed %d beacons) - probing\n",
beacon_loss_count);
- ieee80211_cqm_rssi_notify(&sdata->vif,
- NL80211_CQM_RSSI_BEACON_LOSS_EVENT,
- GFP_KERNEL);
+ ieee80211_cqm_beacon_loss_notify(&sdata->vif, GFP_KERNEL);
}
/*
@@ -2144,7 +2332,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
else
ssid_len = ssid[1];
- skb = ieee80211_build_probe_req(sdata, cbss->bssid,
+ skb = ieee80211_build_probe_req(sdata, sdata->vif.addr, cbss->bssid,
(u32) -1, cbss->channel,
ssid + 2, ssid_len,
NULL, 0, true);
@@ -2171,6 +2359,7 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata)
true, frame_buf);
mutex_lock(&local->mtx);
sdata->vif.csa_active = false;
+ ifmgd->csa_waiting_bcn = false;
if (sdata->csa_block_tx) {
ieee80211_wake_vif_queues(local, sdata,
IEEE80211_QUEUE_STOP_REASON_CSA);
@@ -2617,6 +2806,9 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
}
ifmgd->aid = aid;
+ ifmgd->tdls_chan_switch_prohibited =
+ elems.ext_capab && elems.ext_capab_len >= 5 &&
+ (elems.ext_capab[4] & WLAN_EXT_CAPA5_TDLS_CH_SW_PROHIBITED);
/*
* Some APs are erroneously not including some information in their
@@ -3195,6 +3387,9 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
}
}
+ if (ifmgd->csa_waiting_bcn)
+ ieee80211_chswitch_post_beacon(sdata);
+
if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid)
return;
ifmgd->beacon_crc = ncrc;
@@ -3203,6 +3398,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems);
ieee80211_sta_process_chanswitch(sdata, rx_status->mactime,
+ rx_status->device_timestamp,
&elems, true);
if (!(ifmgd->flags & IEEE80211_STA_DISABLE_WMM) &&
@@ -3334,8 +3530,9 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
break;
ieee80211_sta_process_chanswitch(sdata,
- rx_status->mactime,
- &elems, false);
+ rx_status->mactime,
+ rx_status->device_timestamp,
+ &elems, false);
} else if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) {
ies_len = skb->len -
offsetof(struct ieee80211_mgmt,
@@ -3356,8 +3553,9 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
&mgmt->u.action.u.ext_chan_switch.data;
ieee80211_sta_process_chanswitch(sdata,
- rx_status->mactime,
- &elems, false);
+ rx_status->mactime,
+ rx_status->device_timestamp,
+ &elems, false);
}
break;
}
@@ -3455,7 +3653,8 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
* 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, ssidie + 2, ssidie[1],
+ ieee80211_send_probe_req(sdata, sdata->vif.addr, NULL,
+ ssidie + 2, ssidie[1],
NULL, 0, (u32) -1, true, 0,
auth_data->bss->channel, false);
rcu_read_unlock();
@@ -3664,11 +3863,12 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data)
struct ieee80211_sub_if_data *sdata =
(struct ieee80211_sub_if_data *) data;
struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
if (local->quiescing)
return;
- if (sdata->vif.csa_active)
+ if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
return;
sdata->u.mgd.connection_loss = false;
@@ -3686,7 +3886,7 @@ static void ieee80211_sta_conn_mon_timer(unsigned long data)
if (local->quiescing)
return;
- if (sdata->vif.csa_active)
+ if (sdata->vif.csa_active && !ifmgd->csa_waiting_bcn)
return;
ieee80211_queue_work(&local->hw, &ifmgd->monitor_work);
@@ -3798,6 +3998,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
(unsigned long) sdata);
setup_timer(&ifmgd->chswitch_timer, ieee80211_chswitch_timer,
(unsigned long) sdata);
+ INIT_DELAYED_WORK(&ifmgd->tx_tspec_wk,
+ ieee80211_sta_handle_tspec_ac_params_wk);
ifmgd->flags = 0;
ifmgd->powersave = sdata->wdev.ps;
@@ -3809,6 +4011,11 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
ifmgd->req_smps = IEEE80211_SMPS_AUTOMATIC;
else
ifmgd->req_smps = IEEE80211_SMPS_OFF;
+
+ /* Setup TDLS data */
+ spin_lock_init(&ifmgd->teardown_lock);
+ ifmgd->teardown_skb = NULL;
+ ifmgd->orig_teardown_skb = NULL;
}
/* scan finished notification */
@@ -4671,6 +4878,13 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata)
}
if (ifmgd->auth_data)
ieee80211_destroy_auth_data(sdata, false);
+ spin_lock_bh(&ifmgd->teardown_lock);
+ if (ifmgd->teardown_skb) {
+ kfree_skb(ifmgd->teardown_skb);
+ ifmgd->teardown_skb = NULL;
+ ifmgd->orig_teardown_skb = NULL;
+ }
+ spin_unlock_bh(&ifmgd->teardown_lock);
del_timer_sync(&ifmgd->timer);
sdata_unlock(sdata);
}
@@ -4686,3 +4900,13 @@ void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp);
}
EXPORT_SYMBOL(ieee80211_cqm_rssi_notify);
+
+void ieee80211_cqm_beacon_loss_notify(struct ieee80211_vif *vif, gfp_t gfp)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+ trace_api_cqm_beacon_loss_notify(sdata->local, sdata);
+
+ cfg80211_cqm_beacon_loss_notify(sdata->dev, gfp);
+}
+EXPORT_SYMBOL(ieee80211_cqm_beacon_loss_notify);
diff --git a/net/mac80211/ocb.c b/net/mac80211/ocb.c
new file mode 100644
index 000000000000..358d5f9d8207
--- /dev/null
+++ b/net/mac80211/ocb.c
@@ -0,0 +1,250 @@
+/*
+ * OCB mode implementation
+ *
+ * Copyright: (c) 2014 Czech Technical University in Prague
+ * (c) 2014 Volkswagen Group Research
+ * Author: Rostislav Lisovy <rostislav.lisovy@fel.cvut.cz>
+ * Funded by: Volkswagen Group Research
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include <net/mac80211.h>
+#include <asm/unaligned.h>
+
+#include "ieee80211_i.h"
+#include "driver-ops.h"
+#include "rate.h"
+
+#define IEEE80211_OCB_HOUSEKEEPING_INTERVAL (60 * HZ)
+#define IEEE80211_OCB_PEER_INACTIVITY_LIMIT (240 * HZ)
+#define IEEE80211_OCB_MAX_STA_ENTRIES 128
+
+/**
+ * enum ocb_deferred_task_flags - mac80211 OCB deferred tasks
+ * @OCB_WORK_HOUSEKEEPING: run the periodic OCB housekeeping tasks
+ *
+ * These flags are used in @wrkq_flags field of &struct ieee80211_if_ocb
+ */
+enum ocb_deferred_task_flags {
+ OCB_WORK_HOUSEKEEPING,
+};
+
+void ieee80211_ocb_rx_no_sta(struct ieee80211_sub_if_data *sdata,
+ const u8 *bssid, const u8 *addr,
+ u32 supp_rates)
+{
+ struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_supported_band *sband;
+ enum nl80211_bss_scan_width scan_width;
+ struct sta_info *sta;
+ int band;
+
+ /* XXX: Consider removing the least recently used entry and
+ * allow new one to be added.
+ */
+ if (local->num_sta >= IEEE80211_OCB_MAX_STA_ENTRIES) {
+ net_info_ratelimited("%s: No room for a new OCB STA entry %pM\n",
+ sdata->name, addr);
+ return;
+ }
+
+ ocb_dbg(sdata, "Adding new OCB station %pM\n", addr);
+
+ rcu_read_lock();
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (WARN_ON_ONCE(!chanctx_conf)) {
+ rcu_read_unlock();
+ return;
+ }
+ band = chanctx_conf->def.chan->band;
+ scan_width = cfg80211_chandef_to_scan_width(&chanctx_conf->def);
+ rcu_read_unlock();
+
+ sta = sta_info_alloc(sdata, addr, GFP_ATOMIC);
+ if (!sta)
+ return;
+
+ sta->last_rx = jiffies;
+
+ /* Add only mandatory rates for now */
+ sband = local->hw.wiphy->bands[band];
+ sta->sta.supp_rates[band] =
+ ieee80211_mandatory_rates(sband, scan_width);
+
+ spin_lock(&ifocb->incomplete_lock);
+ list_add(&sta->list, &ifocb->incomplete_stations);
+ spin_unlock(&ifocb->incomplete_lock);
+ ieee80211_queue_work(&local->hw, &sdata->work);
+}
+
+static struct sta_info *ieee80211_ocb_finish_sta(struct sta_info *sta)
+ __acquires(RCU)
+{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ u8 addr[ETH_ALEN];
+
+ memcpy(addr, sta->sta.addr, ETH_ALEN);
+
+ ocb_dbg(sdata, "Adding new IBSS station %pM (dev=%s)\n",
+ addr, sdata->name);
+
+ sta_info_move_state(sta, IEEE80211_STA_AUTH);
+ sta_info_move_state(sta, IEEE80211_STA_ASSOC);
+ sta_info_move_state(sta, IEEE80211_STA_AUTHORIZED);
+
+ rate_control_rate_init(sta);
+
+ /* If it fails, maybe we raced another insertion? */
+ if (sta_info_insert_rcu(sta))
+ return sta_info_get(sdata, addr);
+ return sta;
+}
+
+static void ieee80211_ocb_housekeeping(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
+
+ ocb_dbg(sdata, "Running ocb housekeeping\n");
+
+ ieee80211_sta_expire(sdata, IEEE80211_OCB_PEER_INACTIVITY_LIMIT);
+
+ mod_timer(&ifocb->housekeeping_timer,
+ round_jiffies(jiffies + IEEE80211_OCB_HOUSEKEEPING_INTERVAL));
+}
+
+void ieee80211_ocb_work(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
+ struct sta_info *sta;
+
+ if (ifocb->joined != true)
+ return;
+
+ sdata_lock(sdata);
+
+ spin_lock_bh(&ifocb->incomplete_lock);
+ while (!list_empty(&ifocb->incomplete_stations)) {
+ sta = list_first_entry(&ifocb->incomplete_stations,
+ struct sta_info, list);
+ list_del(&sta->list);
+ spin_unlock_bh(&ifocb->incomplete_lock);
+
+ ieee80211_ocb_finish_sta(sta);
+ rcu_read_unlock();
+ spin_lock_bh(&ifocb->incomplete_lock);
+ }
+ spin_unlock_bh(&ifocb->incomplete_lock);
+
+ if (test_and_clear_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags))
+ ieee80211_ocb_housekeeping(sdata);
+
+ sdata_unlock(sdata);
+}
+
+static void ieee80211_ocb_housekeeping_timer(unsigned long data)
+{
+ struct ieee80211_sub_if_data *sdata = (void *)data;
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
+
+ set_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags);
+
+ ieee80211_queue_work(&local->hw, &sdata->work);
+}
+
+void ieee80211_ocb_setup_sdata(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
+
+ setup_timer(&ifocb->housekeeping_timer,
+ ieee80211_ocb_housekeeping_timer,
+ (unsigned long)sdata);
+ INIT_LIST_HEAD(&ifocb->incomplete_stations);
+ spin_lock_init(&ifocb->incomplete_lock);
+}
+
+int ieee80211_ocb_join(struct ieee80211_sub_if_data *sdata,
+ struct ocb_setup *setup)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
+ u32 changed = BSS_CHANGED_OCB;
+ int err;
+
+ if (ifocb->joined == true)
+ return -EINVAL;
+
+ sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
+ sdata->smps_mode = IEEE80211_SMPS_OFF;
+ sdata->needed_rx_chains = sdata->local->rx_chains;
+
+ mutex_lock(&sdata->local->mtx);
+ err = ieee80211_vif_use_channel(sdata, &setup->chandef,
+ IEEE80211_CHANCTX_SHARED);
+ mutex_unlock(&sdata->local->mtx);
+ if (err)
+ return err;
+
+ ieee80211_bss_info_change_notify(sdata, changed);
+
+ ifocb->joined = true;
+
+ set_bit(OCB_WORK_HOUSEKEEPING, &ifocb->wrkq_flags);
+ ieee80211_queue_work(&local->hw, &sdata->work);
+
+ netif_carrier_on(sdata->dev);
+ return 0;
+}
+
+int ieee80211_ocb_leave(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_ocb *ifocb = &sdata->u.ocb;
+ struct ieee80211_local *local = sdata->local;
+ struct sta_info *sta;
+
+ ifocb->joined = false;
+ sta_info_flush(sdata);
+
+ spin_lock_bh(&ifocb->incomplete_lock);
+ while (!list_empty(&ifocb->incomplete_stations)) {
+ sta = list_first_entry(&ifocb->incomplete_stations,
+ struct sta_info, list);
+ list_del(&sta->list);
+ spin_unlock_bh(&ifocb->incomplete_lock);
+
+ sta_info_free(local, sta);
+ spin_lock_bh(&ifocb->incomplete_lock);
+ }
+ spin_unlock_bh(&ifocb->incomplete_lock);
+
+ netif_carrier_off(sdata->dev);
+ clear_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_OCB);
+
+ mutex_lock(&sdata->local->mtx);
+ ieee80211_vif_release_channel(sdata);
+ mutex_unlock(&sdata->local->mtx);
+
+ skb_queue_purge(&sdata->skb_queue);
+
+ del_timer_sync(&sdata->u.ocb.housekeeping_timer);
+ /* If the timer fired while we waited for it, it will have
+ * requeued the work. Now the work will be running again
+ * but will not rearm the timer again because it checks
+ * whether we are connected to the network or not -- at this
+ * point we shouldn't be anymore.
+ */
+
+ return 0;
+}
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index 6081329784dd..d53355b011f5 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -385,7 +385,7 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate,
*rate = alt_rate;
return;
}
- } else {
+ } else if (!(rate->flags & IEEE80211_TX_RC_VHT_MCS)) {
/* handle legacy rates */
if (rate_idx_match_legacy_mask(rate, sband->n_bitrates, mask))
return;
@@ -446,7 +446,8 @@ static void rate_fixup_ratelist(struct ieee80211_vif *vif,
*
* XXX: Should this check all retry rates?
*/
- if (!(rates[0].flags & IEEE80211_TX_RC_MCS)) {
+ if (!(rates[0].flags &
+ (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS))) {
u32 basic_rates = vif->bss_conf.basic_rates;
s8 baserate = basic_rates ? ffs(basic_rates) - 1 : 0;
@@ -696,6 +697,7 @@ int rate_control_set_rates(struct ieee80211_hw *hw,
struct ieee80211_sta *pubsta,
struct ieee80211_sta_rates *rates)
{
+ struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
struct ieee80211_sta_rates *old;
/*
@@ -709,6 +711,8 @@ int rate_control_set_rates(struct ieee80211_hw *hw,
if (old)
kfree_rcu(old, rcu_head);
+ drv_sta_rate_tbl_update(hw_to_local(hw), sta->sdata, pubsta);
+
return 0;
}
EXPORT_SYMBOL(rate_control_set_rates);
diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h
index 18babe302832..38652f09feaf 100644
--- a/net/mac80211/rate.h
+++ b/net/mac80211/rate.h
@@ -37,13 +37,35 @@ static inline void rate_control_tx_status(struct ieee80211_local *local,
struct rate_control_ref *ref = local->rate_ctrl;
struct ieee80211_sta *ista = &sta->sta;
void *priv_sta = sta->rate_ctrl_priv;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
if (!ref || !test_sta_flag(sta, WLAN_STA_RATE_CONTROL))
return;
- ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb);
+ if (ref->ops->tx_status)
+ ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb);
+ else
+ ref->ops->tx_status_noskb(ref->priv, sband, ista, priv_sta, info);
}
+static inline void
+rate_control_tx_status_noskb(struct ieee80211_local *local,
+ struct ieee80211_supported_band *sband,
+ struct sta_info *sta,
+ struct ieee80211_tx_info *info)
+{
+ struct rate_control_ref *ref = local->rate_ctrl;
+ struct ieee80211_sta *ista = &sta->sta;
+ void *priv_sta = sta->rate_ctrl_priv;
+
+ if (!ref || !test_sta_flag(sta, WLAN_STA_RATE_CONTROL))
+ return;
+
+ if (WARN_ON_ONCE(!ref->ops->tx_status_noskb))
+ return;
+
+ ref->ops->tx_status_noskb(ref->priv, sband, ista, priv_sta, info);
+}
static inline void rate_control_rate_init(struct sta_info *sta)
{
diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c
index 2baa7ed8789d..d51f6b1c549b 100644
--- a/net/mac80211/rc80211_minstrel.c
+++ b/net/mac80211/rc80211_minstrel.c
@@ -191,7 +191,7 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
* (1) if any success probabilitiy >= 95%, out of those rates
* choose the maximum throughput rate as max_prob_rate
* (2) if all success probabilities < 95%, the rate with
- * highest success probability is choosen as max_prob_rate */
+ * highest success probability is chosen as max_prob_rate */
if (mrs->probability >= MINSTREL_FRAC(95, 100)) {
if (mrs->cur_tp >= mi->r[tmp_prob_rate].stats.cur_tp)
tmp_prob_rate = i;
@@ -223,11 +223,10 @@ minstrel_update_stats(struct minstrel_priv *mp, struct minstrel_sta_info *mi)
static void
minstrel_tx_status(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta,
- struct sk_buff *skb)
+ struct ieee80211_tx_info *info)
{
struct minstrel_priv *mp = priv;
struct minstrel_sta_info *mi = priv_sta;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_tx_rate *ar = info->status.rates;
int i, ndx;
int success;
@@ -674,7 +673,7 @@ static u32 minstrel_get_expected_throughput(void *priv_sta)
const struct rate_control_ops mac80211_minstrel = {
.name = "minstrel",
- .tx_status = minstrel_tx_status,
+ .tx_status_noskb = minstrel_tx_status,
.get_rate = minstrel_get_rate,
.rate_init = minstrel_rate_init,
.alloc = minstrel_alloc,
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index df90ce2db00c..80452cfd2dc5 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -10,6 +10,7 @@
#include <linux/skbuff.h>
#include <linux/debugfs.h>
#include <linux/random.h>
+#include <linux/moduleparam.h>
#include <linux/ieee80211.h>
#include <net/mac80211.h>
#include "rate.h"
@@ -34,12 +35,17 @@
/* Transmit duration for the raw data part of an average sized packet */
#define MCS_DURATION(streams, sgi, bps) MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps)))
+#define BW_20 0
+#define BW_40 1
+#define BW_80 2
+
/*
* Define group sort order: HT40 -> SGI -> #streams
*/
#define GROUP_IDX(_streams, _sgi, _ht40) \
+ MINSTREL_HT_GROUP_0 + \
MINSTREL_MAX_STREAMS * 2 * _ht40 + \
- MINSTREL_MAX_STREAMS * _sgi + \
+ MINSTREL_MAX_STREAMS * _sgi + \
_streams - 1
/* MCS rate information for an MCS group */
@@ -47,6 +53,7 @@
[GROUP_IDX(_streams, _sgi, _ht40)] = { \
.streams = _streams, \
.flags = \
+ IEEE80211_TX_RC_MCS | \
(_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) | \
(_ht40 ? IEEE80211_TX_RC_40_MHZ_WIDTH : 0), \
.duration = { \
@@ -61,6 +68,47 @@
} \
}
+#define VHT_GROUP_IDX(_streams, _sgi, _bw) \
+ (MINSTREL_VHT_GROUP_0 + \
+ MINSTREL_MAX_STREAMS * 2 * (_bw) + \
+ MINSTREL_MAX_STREAMS * (_sgi) + \
+ (_streams) - 1)
+
+#define BW2VBPS(_bw, r3, r2, r1) \
+ (_bw == BW_80 ? r3 : _bw == BW_40 ? r2 : r1)
+
+#define VHT_GROUP(_streams, _sgi, _bw) \
+ [VHT_GROUP_IDX(_streams, _sgi, _bw)] = { \
+ .streams = _streams, \
+ .flags = \
+ IEEE80211_TX_RC_VHT_MCS | \
+ (_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) | \
+ (_bw == BW_80 ? IEEE80211_TX_RC_80_MHZ_WIDTH : \
+ _bw == BW_40 ? IEEE80211_TX_RC_40_MHZ_WIDTH : 0), \
+ .duration = { \
+ MCS_DURATION(_streams, _sgi, \
+ BW2VBPS(_bw, 117, 54, 26)), \
+ MCS_DURATION(_streams, _sgi, \
+ BW2VBPS(_bw, 234, 108, 52)), \
+ MCS_DURATION(_streams, _sgi, \
+ BW2VBPS(_bw, 351, 162, 78)), \
+ MCS_DURATION(_streams, _sgi, \
+ BW2VBPS(_bw, 468, 216, 104)), \
+ MCS_DURATION(_streams, _sgi, \
+ BW2VBPS(_bw, 702, 324, 156)), \
+ MCS_DURATION(_streams, _sgi, \
+ BW2VBPS(_bw, 936, 432, 208)), \
+ MCS_DURATION(_streams, _sgi, \
+ BW2VBPS(_bw, 1053, 486, 234)), \
+ MCS_DURATION(_streams, _sgi, \
+ BW2VBPS(_bw, 1170, 540, 260)), \
+ MCS_DURATION(_streams, _sgi, \
+ BW2VBPS(_bw, 1404, 648, 312)), \
+ MCS_DURATION(_streams, _sgi, \
+ BW2VBPS(_bw, 1560, 720, 346)) \
+ } \
+}
+
#define CCK_DURATION(_bitrate, _short, _len) \
(1000 * (10 /* SIFS */ + \
(_short ? 72 + 24 : 144 + 48) + \
@@ -76,53 +124,96 @@
CCK_ACK_DURATION(55, _short), \
CCK_ACK_DURATION(110, _short)
-#define CCK_GROUP \
- [MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS] = { \
- .streams = 0, \
- .duration = { \
- CCK_DURATION_LIST(false), \
- CCK_DURATION_LIST(true) \
- } \
+#define CCK_GROUP \
+ [MINSTREL_CCK_GROUP] = { \
+ .streams = 0, \
+ .flags = 0, \
+ .duration = { \
+ CCK_DURATION_LIST(false), \
+ CCK_DURATION_LIST(true) \
+ } \
}
+#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
+static bool minstrel_vht_only = true;
+module_param(minstrel_vht_only, bool, 0644);
+MODULE_PARM_DESC(minstrel_vht_only,
+ "Use only VHT rates when VHT is supported by sta.");
+#endif
+
/*
* To enable sufficiently targeted rate sampling, MCS rates are divided into
* groups, based on the number of streams and flags (HT40, SGI) that they
* use.
*
* Sortorder has to be fixed for GROUP_IDX macro to be applicable:
- * HT40 -> SGI -> #streams
+ * BW -> SGI -> #streams
*/
const struct mcs_group minstrel_mcs_groups[] = {
- MCS_GROUP(1, 0, 0),
- MCS_GROUP(2, 0, 0),
+ MCS_GROUP(1, 0, BW_20),
+ MCS_GROUP(2, 0, BW_20),
#if MINSTREL_MAX_STREAMS >= 3
- MCS_GROUP(3, 0, 0),
+ MCS_GROUP(3, 0, BW_20),
#endif
- MCS_GROUP(1, 1, 0),
- MCS_GROUP(2, 1, 0),
+ MCS_GROUP(1, 1, BW_20),
+ MCS_GROUP(2, 1, BW_20),
#if MINSTREL_MAX_STREAMS >= 3
- MCS_GROUP(3, 1, 0),
+ MCS_GROUP(3, 1, BW_20),
#endif
- MCS_GROUP(1, 0, 1),
- MCS_GROUP(2, 0, 1),
+ MCS_GROUP(1, 0, BW_40),
+ MCS_GROUP(2, 0, BW_40),
#if MINSTREL_MAX_STREAMS >= 3
- MCS_GROUP(3, 0, 1),
+ MCS_GROUP(3, 0, BW_40),
#endif
- MCS_GROUP(1, 1, 1),
- MCS_GROUP(2, 1, 1),
+ MCS_GROUP(1, 1, BW_40),
+ MCS_GROUP(2, 1, BW_40),
#if MINSTREL_MAX_STREAMS >= 3
- MCS_GROUP(3, 1, 1),
+ MCS_GROUP(3, 1, BW_40),
#endif
- /* must be last */
- CCK_GROUP
-};
+ CCK_GROUP,
+
+#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
+ VHT_GROUP(1, 0, BW_20),
+ VHT_GROUP(2, 0, BW_20),
+#if MINSTREL_MAX_STREAMS >= 3
+ VHT_GROUP(3, 0, BW_20),
+#endif
+
+ VHT_GROUP(1, 1, BW_20),
+ VHT_GROUP(2, 1, BW_20),
+#if MINSTREL_MAX_STREAMS >= 3
+ VHT_GROUP(3, 1, BW_20),
+#endif
+
+ VHT_GROUP(1, 0, BW_40),
+ VHT_GROUP(2, 0, BW_40),
+#if MINSTREL_MAX_STREAMS >= 3
+ VHT_GROUP(3, 0, BW_40),
+#endif
-#define MINSTREL_CCK_GROUP (ARRAY_SIZE(minstrel_mcs_groups) - 1)
+ VHT_GROUP(1, 1, BW_40),
+ VHT_GROUP(2, 1, BW_40),
+#if MINSTREL_MAX_STREAMS >= 3
+ VHT_GROUP(3, 1, BW_40),
+#endif
+
+ VHT_GROUP(1, 0, BW_80),
+ VHT_GROUP(2, 0, BW_80),
+#if MINSTREL_MAX_STREAMS >= 3
+ VHT_GROUP(3, 0, BW_80),
+#endif
+
+ VHT_GROUP(1, 1, BW_80),
+ VHT_GROUP(2, 1, BW_80),
+#if MINSTREL_MAX_STREAMS >= 3
+ VHT_GROUP(3, 1, BW_80),
+#endif
+#endif
+};
static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES] __read_mostly;
@@ -130,16 +221,64 @@ static void
minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi);
/*
+ * Some VHT MCSes are invalid (when Ndbps / Nes is not an integer)
+ * e.g for MCS9@20MHzx1Nss: Ndbps=8x52*(5/6) Nes=1
+ *
+ * Returns the valid mcs map for struct minstrel_mcs_group_data.supported
+ */
+static u16
+minstrel_get_valid_vht_rates(int bw, int nss, __le16 mcs_map)
+{
+ u16 mask = 0;
+
+ if (bw == BW_20) {
+ if (nss != 3 && nss != 6)
+ mask = BIT(9);
+ } else if (bw == BW_80) {
+ if (nss == 3 || nss == 7)
+ mask = BIT(6);
+ else if (nss == 6)
+ mask = BIT(9);
+ } else {
+ WARN_ON(bw != BW_40);
+ }
+
+ switch ((le16_to_cpu(mcs_map) >> (2 * (nss - 1))) & 3) {
+ case IEEE80211_VHT_MCS_SUPPORT_0_7:
+ mask |= 0x300;
+ break;
+ case IEEE80211_VHT_MCS_SUPPORT_0_8:
+ mask |= 0x200;
+ break;
+ case IEEE80211_VHT_MCS_SUPPORT_0_9:
+ break;
+ default:
+ mask = 0x3ff;
+ }
+
+ return 0x3ff & ~mask;
+}
+
+/*
* Look up an MCS group index based on mac80211 rate information
*/
static int
minstrel_ht_get_group_idx(struct ieee80211_tx_rate *rate)
{
- return GROUP_IDX((rate->idx / MCS_GROUP_RATES) + 1,
+ return GROUP_IDX((rate->idx / 8) + 1,
!!(rate->flags & IEEE80211_TX_RC_SHORT_GI),
!!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH));
}
+static int
+minstrel_vht_get_group_idx(struct ieee80211_tx_rate *rate)
+{
+ return VHT_GROUP_IDX(ieee80211_rate_get_vht_nss(rate),
+ !!(rate->flags & IEEE80211_TX_RC_SHORT_GI),
+ !!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) +
+ 2*!!(rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH));
+}
+
static struct minstrel_rate_stats *
minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
struct ieee80211_tx_rate *rate)
@@ -149,6 +288,9 @@ minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
if (rate->flags & IEEE80211_TX_RC_MCS) {
group = minstrel_ht_get_group_idx(rate);
idx = rate->idx % 8;
+ } else if (rate->flags & IEEE80211_TX_RC_VHT_MCS) {
+ group = minstrel_vht_get_group_idx(rate);
+ idx = ieee80211_rate_get_vht_mcs(rate);
} else {
group = MINSTREL_CCK_GROUP;
@@ -240,8 +382,8 @@ minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate)
* MCS groups, CCK rates do not provide aggregation and are therefore at last.
*/
static void
-minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u8 index,
- u8 *tp_list)
+minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u16 index,
+ u16 *tp_list)
{
int cur_group, cur_idx, cur_thr, cur_prob;
int tmp_group, tmp_idx, tmp_thr, tmp_prob;
@@ -252,19 +394,16 @@ minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u8 index,
cur_thr = mi->groups[cur_group].rates[cur_idx].cur_tp;
cur_prob = mi->groups[cur_group].rates[cur_idx].probability;
- tmp_group = tp_list[j - 1] / MCS_GROUP_RATES;
- tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES;
- tmp_thr = mi->groups[tmp_group].rates[tmp_idx].cur_tp;
- tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability;
-
- while (j > 0 && (cur_thr > tmp_thr ||
- (cur_thr == tmp_thr && cur_prob > tmp_prob))) {
- j--;
+ do {
tmp_group = tp_list[j - 1] / MCS_GROUP_RATES;
tmp_idx = tp_list[j - 1] % MCS_GROUP_RATES;
tmp_thr = mi->groups[tmp_group].rates[tmp_idx].cur_tp;
tmp_prob = mi->groups[tmp_group].rates[tmp_idx].probability;
- }
+ if (cur_thr < tmp_thr ||
+ (cur_thr == tmp_thr && cur_prob <= tmp_prob))
+ break;
+ j--;
+ } while (j > 0);
if (j < MAX_THR_RATES - 1) {
memmove(&tp_list[j + 1], &tp_list[j], (sizeof(*tp_list) *
@@ -278,7 +417,7 @@ minstrel_ht_sort_best_tp_rates(struct minstrel_ht_sta *mi, u8 index,
* Find and set the topmost probability rate per sta and per group
*/
static void
-minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u8 index)
+minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u16 index)
{
struct minstrel_mcs_group_data *mg;
struct minstrel_rate_stats *mr;
@@ -321,8 +460,8 @@ minstrel_ht_set_best_prob_rate(struct minstrel_ht_sta *mi, u8 index)
*/
static void
minstrel_ht_assign_best_tp_rates(struct minstrel_ht_sta *mi,
- u8 tmp_mcs_tp_rate[MAX_THR_RATES],
- u8 tmp_cck_tp_rate[MAX_THR_RATES])
+ u16 tmp_mcs_tp_rate[MAX_THR_RATES],
+ u16 tmp_cck_tp_rate[MAX_THR_RATES])
{
unsigned int tmp_group, tmp_idx, tmp_cck_tp, tmp_mcs_tp;
int i;
@@ -386,8 +525,8 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
struct minstrel_mcs_group_data *mg;
struct minstrel_rate_stats *mr;
int group, i, j;
- u8 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES];
- u8 tmp_cck_tp_rate[MAX_THR_RATES], index;
+ u16 tmp_mcs_tp_rate[MAX_THR_RATES], tmp_group_tp_rate[MAX_THR_RATES];
+ u16 tmp_cck_tp_rate[MAX_THR_RATES], index;
if (mi->ampdu_packets > 0) {
mi->avg_ampdu_len = minstrel_ewma(mi->avg_ampdu_len,
@@ -485,7 +624,8 @@ minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct ieee80211_tx_rate *rat
if (!rate->count)
return false;
- if (rate->flags & IEEE80211_TX_RC_MCS)
+ if (rate->flags & IEEE80211_TX_RC_MCS ||
+ rate->flags & IEEE80211_TX_RC_VHT_MCS)
return true;
return rate->idx == mp->cck_rates[0] ||
@@ -517,7 +657,7 @@ minstrel_next_sample_idx(struct minstrel_ht_sta *mi)
}
static void
-minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u8 *idx, bool primary)
+minstrel_downgrade_rate(struct minstrel_ht_sta *mi, u16 *idx, bool primary)
{
int group, orig_group;
@@ -547,6 +687,9 @@ minstrel_aggr_check(struct ieee80211_sta *pubsta, struct sk_buff *skb)
struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
u16 tid;
+ if (skb_get_queue_mapping(skb) == IEEE80211_AC_VO)
+ return;
+
if (unlikely(!ieee80211_is_data_qos(hdr->frame_control)))
return;
@@ -557,20 +700,16 @@ minstrel_aggr_check(struct ieee80211_sta *pubsta, struct sk_buff *skb)
if (likely(sta->ampdu_mlme.tid_tx[tid]))
return;
- if (skb_get_queue_mapping(skb) == IEEE80211_AC_VO)
- return;
-
ieee80211_start_tx_ba_session(pubsta, tid, 5000);
}
static void
minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
struct ieee80211_sta *sta, void *priv_sta,
- struct sk_buff *skb)
+ struct ieee80211_tx_info *info)
{
struct minstrel_ht_sta_priv *msp = priv_sta;
struct minstrel_ht_sta *mi = &msp->ht;
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_tx_rate *ar = info->status.rates;
struct minstrel_rate_stats *rate, *rate2;
struct minstrel_priv *mp = priv;
@@ -578,7 +717,8 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
int i;
if (!msp->is_ht)
- return mac80211_minstrel.tx_status(priv, sband, sta, &msp->legacy, skb);
+ return mac80211_minstrel.tx_status_noskb(priv, sband, sta,
+ &msp->legacy, info);
/* This packet was aggregated but doesn't carry status info */
if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
@@ -639,9 +779,6 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
if (time_after(jiffies, mi->stats_update + (mp->update_interval / 2 * HZ) / 1000)) {
update = true;
minstrel_ht_update_stats(mp, mi);
- if (!(info->flags & IEEE80211_TX_CTL_AMPDU) &&
- mi->max_prob_rate / MCS_GROUP_RATES != MINSTREL_CCK_GROUP)
- minstrel_aggr_check(sta, skb);
}
if (update)
@@ -714,7 +851,7 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES];
struct minstrel_rate_stats *mr;
u8 idx;
- u16 flags;
+ u16 flags = group->flags;
mr = minstrel_get_ratestats(mi, index);
if (!mr->retry_updated)
@@ -730,13 +867,13 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
ratetbl->rate[offset].count_rts = mr->retry_count_rtscts;
}
- if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) {
+ if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP)
idx = mp->cck_rates[index % ARRAY_SIZE(mp->cck_rates)];
- flags = 0;
- } else {
+ else if (flags & IEEE80211_TX_RC_VHT_MCS)
+ idx = ((group->streams - 1) << 4) |
+ ((index % MCS_GROUP_RATES) & 0xF);
+ else
idx = index % MCS_GROUP_RATES + (group->streams - 1) * 8;
- flags = IEEE80211_TX_RC_MCS | group->flags;
- }
if (offset > 0) {
ratetbl->rate[offset].count = ratetbl->rate[offset].count_rts;
@@ -883,6 +1020,10 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
if (!msp->is_ht)
return mac80211_minstrel.get_rate(priv, sta, &msp->legacy, txrc);
+ if (!(info->flags & IEEE80211_TX_CTL_AMPDU) &&
+ mi->max_prob_rate / MCS_GROUP_RATES != MINSTREL_CCK_GROUP)
+ minstrel_aggr_check(sta, txrc->skb);
+
info->flags |= mi->tx_flags;
minstrel_ht_check_cck_shortpreamble(mp, mi, txrc->short_preamble);
@@ -916,13 +1057,15 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
if (sample_idx / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) {
int idx = sample_idx % ARRAY_SIZE(mp->cck_rates);
rate->idx = mp->cck_rates[idx];
- rate->flags = 0;
- return;
+ } else if (sample_group->flags & IEEE80211_TX_RC_VHT_MCS) {
+ ieee80211_rate_set_vht(rate, sample_idx % MCS_GROUP_RATES,
+ sample_group->streams);
+ } else {
+ rate->idx = sample_idx % MCS_GROUP_RATES +
+ (sample_group->streams - 1) * 8;
}
- rate->idx = sample_idx % MCS_GROUP_RATES +
- (sample_group->streams - 1) * 8;
- rate->flags = IEEE80211_TX_RC_MCS | sample_group->flags;
+ rate->flags = sample_group->flags;
}
static void
@@ -962,6 +1105,8 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
struct minstrel_ht_sta *mi = &msp->ht;
struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs;
u16 sta_cap = sta->ht_cap.cap;
+ struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
+ int use_vht;
int n_supported = 0;
int ack_dur;
int stbc;
@@ -971,8 +1116,14 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
if (!sta->ht_cap.ht_supported)
goto use_legacy;
- BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) !=
- MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS + 1);
+ BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) != MINSTREL_GROUPS_NB);
+
+#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
+ if (vht_cap->vht_supported)
+ use_vht = vht_cap->vht_mcs.tx_mcs_map != cpu_to_le16(~0);
+ else
+#endif
+ use_vht = 0;
msp->is_ht = true;
memset(mi, 0, sizeof(*mi));
@@ -997,22 +1148,28 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
}
mi->sample_tries = 4;
- stbc = (sta_cap & IEEE80211_HT_CAP_RX_STBC) >>
- IEEE80211_HT_CAP_RX_STBC_SHIFT;
- mi->tx_flags |= stbc << IEEE80211_TX_CTL_STBC_SHIFT;
+ /* TODO tx_flags for vht - ATM the RC API is not fine-grained enough */
+ if (!use_vht) {
+ stbc = (sta_cap & IEEE80211_HT_CAP_RX_STBC) >>
+ IEEE80211_HT_CAP_RX_STBC_SHIFT;
+ mi->tx_flags |= stbc << IEEE80211_TX_CTL_STBC_SHIFT;
- if (sta_cap & IEEE80211_HT_CAP_LDPC_CODING)
- mi->tx_flags |= IEEE80211_TX_CTL_LDPC;
+ if (sta_cap & IEEE80211_HT_CAP_LDPC_CODING)
+ mi->tx_flags |= IEEE80211_TX_CTL_LDPC;
+ }
for (i = 0; i < ARRAY_SIZE(mi->groups); i++) {
+ u32 gflags = minstrel_mcs_groups[i].flags;
+ int bw, nss;
+
mi->groups[i].supported = 0;
if (i == MINSTREL_CCK_GROUP) {
minstrel_ht_update_cck(mp, mi, sband, sta);
continue;
}
- if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI) {
- if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
+ if (gflags & IEEE80211_TX_RC_SHORT_GI) {
+ if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH) {
if (!(sta_cap & IEEE80211_HT_CAP_SGI_40))
continue;
} else {
@@ -1021,17 +1178,51 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
}
}
- if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH &&
+ if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH &&
sta->bandwidth < IEEE80211_STA_RX_BW_40)
continue;
+ nss = minstrel_mcs_groups[i].streams;
+
/* Mark MCS > 7 as unsupported if STA is in static SMPS mode */
- if (sta->smps_mode == IEEE80211_SMPS_STATIC &&
- minstrel_mcs_groups[i].streams > 1)
+ if (sta->smps_mode == IEEE80211_SMPS_STATIC && nss > 1)
continue;
- mi->groups[i].supported =
- mcs->rx_mask[minstrel_mcs_groups[i].streams - 1];
+ /* HT rate */
+ if (gflags & IEEE80211_TX_RC_MCS) {
+#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
+ if (use_vht && minstrel_vht_only)
+ continue;
+#endif
+ mi->groups[i].supported = mcs->rx_mask[nss - 1];
+ if (mi->groups[i].supported)
+ n_supported++;
+ continue;
+ }
+
+ /* VHT rate */
+ if (!vht_cap->vht_supported ||
+ WARN_ON(!(gflags & IEEE80211_TX_RC_VHT_MCS)) ||
+ WARN_ON(gflags & IEEE80211_TX_RC_160_MHZ_WIDTH))
+ continue;
+
+ if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH) {
+ if (sta->bandwidth < IEEE80211_STA_RX_BW_80 ||
+ ((gflags & IEEE80211_TX_RC_SHORT_GI) &&
+ !(vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80))) {
+ continue;
+ }
+ }
+
+ if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+ bw = BW_40;
+ else if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH)
+ bw = BW_80;
+ else
+ bw = BW_20;
+
+ mi->groups[i].supported = minstrel_get_valid_vht_rates(bw, nss,
+ vht_cap->vht_mcs.tx_mcs_map);
if (mi->groups[i].supported)
n_supported++;
@@ -1149,7 +1340,7 @@ static u32 minstrel_ht_get_expected_throughput(void *priv_sta)
static const struct rate_control_ops mac80211_minstrel_ht = {
.name = "minstrel_ht",
- .tx_status = minstrel_ht_tx_status,
+ .tx_status_noskb = minstrel_ht_tx_status,
.get_rate = minstrel_ht_get_rate,
.rate_init = minstrel_ht_rate_init,
.rate_update = minstrel_ht_rate_update,
diff --git a/net/mac80211/rc80211_minstrel_ht.h b/net/mac80211/rc80211_minstrel_ht.h
index 01570e0e014b..f2217d6aa0c2 100644
--- a/net/mac80211/rc80211_minstrel_ht.h
+++ b/net/mac80211/rc80211_minstrel_ht.h
@@ -13,10 +13,32 @@
* The number of streams can be changed to 2 to reduce code
* size and memory footprint.
*/
-#define MINSTREL_MAX_STREAMS 3
-#define MINSTREL_STREAM_GROUPS 4
+#define MINSTREL_MAX_STREAMS 3
+#define MINSTREL_HT_STREAM_GROUPS 4 /* BW(=2) * SGI(=2) */
+#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
+#define MINSTREL_VHT_STREAM_GROUPS 6 /* BW(=3) * SGI(=2) */
+#else
+#define MINSTREL_VHT_STREAM_GROUPS 0
+#endif
-#define MCS_GROUP_RATES 8
+#define MINSTREL_HT_GROUPS_NB (MINSTREL_MAX_STREAMS * \
+ MINSTREL_HT_STREAM_GROUPS)
+#define MINSTREL_VHT_GROUPS_NB (MINSTREL_MAX_STREAMS * \
+ MINSTREL_VHT_STREAM_GROUPS)
+#define MINSTREL_CCK_GROUPS_NB 1
+#define MINSTREL_GROUPS_NB (MINSTREL_HT_GROUPS_NB + \
+ MINSTREL_VHT_GROUPS_NB + \
+ MINSTREL_CCK_GROUPS_NB)
+
+#define MINSTREL_HT_GROUP_0 0
+#define MINSTREL_CCK_GROUP (MINSTREL_HT_GROUP_0 + MINSTREL_HT_GROUPS_NB)
+#define MINSTREL_VHT_GROUP_0 (MINSTREL_CCK_GROUP + 1)
+
+#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
+#define MCS_GROUP_RATES 10
+#else
+#define MCS_GROUP_RATES 8
+#endif
struct mcs_group {
u32 flags;
@@ -31,11 +53,11 @@ struct minstrel_mcs_group_data {
u8 column;
/* bitfield of supported MCS rates of this group */
- u8 supported;
+ u16 supported;
/* sorted rate set within a MCS group*/
- u8 max_group_tp_rate[MAX_THR_RATES];
- u8 max_group_prob_rate;
+ u16 max_group_tp_rate[MAX_THR_RATES];
+ u16 max_group_prob_rate;
/* MCS rate statistics */
struct minstrel_rate_stats rates[MCS_GROUP_RATES];
@@ -52,8 +74,8 @@ struct minstrel_ht_sta {
unsigned int avg_ampdu_len;
/* overall sorted rate set */
- u8 max_tp_rate[MAX_THR_RATES];
- u8 max_prob_rate;
+ u16 max_tp_rate[MAX_THR_RATES];
+ u16 max_prob_rate;
/* time of last status update */
unsigned long stats_update;
@@ -80,7 +102,7 @@ struct minstrel_ht_sta {
u8 cck_supported_short;
/* MCS rate group info and statistics */
- struct minstrel_mcs_group_data groups[MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS + 1];
+ struct minstrel_mcs_group_data groups[MINSTREL_GROUPS_NB];
};
struct minstrel_ht_sta_priv {
diff --git a/net/mac80211/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c
index d537bec93754..20c676b8e5b6 100644
--- a/net/mac80211/rc80211_minstrel_ht_debugfs.c
+++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c
@@ -18,19 +18,23 @@
static char *
minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
{
- unsigned int max_mcs = MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS;
const struct mcs_group *mg;
unsigned int j, tp, prob, eprob;
char htmode = '2';
char gimode = 'L';
+ u32 gflags;
if (!mi->groups[i].supported)
return p;
mg = &minstrel_mcs_groups[i];
- if (mg->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+ gflags = mg->flags;
+
+ if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH)
htmode = '4';
- if (mg->flags & IEEE80211_TX_RC_SHORT_GI)
+ else if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH)
+ htmode = '8';
+ if (gflags & IEEE80211_TX_RC_SHORT_GI)
gimode = 'S';
for (j = 0; j < MCS_GROUP_RATES; j++) {
@@ -41,10 +45,12 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
if (!(mi->groups[i].supported & BIT(j)))
continue;
- if (i == max_mcs)
- p += sprintf(p, "CCK/%cP ", j < 4 ? 'L' : 'S');
+ if (gflags & IEEE80211_TX_RC_MCS)
+ p += sprintf(p, " HT%c0/%cGI ", htmode, gimode);
+ else if (gflags & IEEE80211_TX_RC_VHT_MCS)
+ p += sprintf(p, "VHT%c0/%cGI ", htmode, gimode);
else
- p += sprintf(p, "HT%c0/%cGI ", htmode, gimode);
+ p += sprintf(p, " CCK/%cP ", j < 4 ? 'L' : 'S');
*(p++) = (idx == mi->max_tp_rate[0]) ? 'A' : ' ';
*(p++) = (idx == mi->max_tp_rate[1]) ? 'B' : ' ';
@@ -52,11 +58,14 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
*(p++) = (idx == mi->max_tp_rate[3]) ? 'D' : ' ';
*(p++) = (idx == mi->max_prob_rate) ? 'P' : ' ';
- if (i == max_mcs) {
- int r = bitrates[j % 4];
- p += sprintf(p, " %2u.%1uM", r / 10, r % 10);
+ if (gflags & IEEE80211_TX_RC_MCS) {
+ p += sprintf(p, " MCS%-2u ", (mg->streams - 1) * 8 + j);
+ } else if (gflags & IEEE80211_TX_RC_VHT_MCS) {
+ p += sprintf(p, " MCS%-1u/%1u", j, mg->streams);
} else {
- p += sprintf(p, " MCS%-2u", (mg->streams - 1) * 8 + j);
+ int r = bitrates[j % 4];
+
+ p += sprintf(p, " %2u.%1uM ", r / 10, r % 10);
}
tp = mr->cur_tp / 10;
@@ -85,7 +94,6 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file)
struct minstrel_ht_sta *mi = &msp->ht;
struct minstrel_debugfs_info *ms;
unsigned int i;
- unsigned int max_mcs = MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS;
char *p;
int ret;
@@ -96,18 +104,19 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file)
return ret;
}
- ms = kmalloc(8192, GFP_KERNEL);
+ ms = kmalloc(32768, GFP_KERNEL);
if (!ms)
return -ENOMEM;
file->private_data = ms;
p = ms->buf;
- p += sprintf(p, "type rate tpt eprob *prob "
+ p += sprintf(p, " type rate tpt eprob *prob "
"ret *ok(*cum) ok( cum)\n");
-
- p = minstrel_ht_stats_dump(mi, max_mcs, p);
- for (i = 0; i < max_mcs; i++)
+ p = minstrel_ht_stats_dump(mi, MINSTREL_CCK_GROUP, p);
+ for (i = 0; i < MINSTREL_CCK_GROUP; i++)
+ p = minstrel_ht_stats_dump(mi, i, p);
+ for (i++; i < ARRAY_SIZE(mi->groups); i++)
p = minstrel_ht_stats_dump(mi, i, p);
p += sprintf(p, "\nTotal packet count:: ideal %d "
@@ -119,7 +128,7 @@ minstrel_ht_stats_open(struct inode *inode, struct file *file)
MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10);
ms->len = p - ms->buf;
- WARN_ON(ms->len + sizeof(*ms) > 8192);
+ WARN_ON(ms->len + sizeof(*ms) > 32768);
return nonseekable_open(inode, file);
}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index b04ca4049c95..49c23bdf08bb 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -39,7 +39,8 @@
* only useful for monitoring.
*/
static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
- struct sk_buff *skb)
+ struct sk_buff *skb,
+ unsigned int rtap_vendor_space)
{
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) {
if (likely(skb->len > FCS_LEN))
@@ -52,20 +53,25 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local,
}
}
+ __pskb_pull(skb, rtap_vendor_space);
+
return skb;
}
-static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len)
+static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len,
+ unsigned int rtap_vendor_space)
{
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
- struct ieee80211_hdr *hdr = (void *)skb->data;
+ struct ieee80211_hdr *hdr;
+
+ hdr = (void *)(skb->data + rtap_vendor_space);
if (status->flag & (RX_FLAG_FAILED_FCS_CRC |
RX_FLAG_FAILED_PLCP_CRC |
RX_FLAG_AMPDU_IS_ZEROLEN))
return true;
- if (unlikely(skb->len < 16 + present_fcs_len))
+ if (unlikely(skb->len < 16 + present_fcs_len + rtap_vendor_space))
return true;
if (ieee80211_is_ctl(hdr->frame_control) &&
@@ -77,8 +83,9 @@ static inline bool should_drop_frame(struct sk_buff *skb, int present_fcs_len)
}
static int
-ieee80211_rx_radiotap_space(struct ieee80211_local *local,
- struct ieee80211_rx_status *status)
+ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,
+ struct ieee80211_rx_status *status,
+ struct sk_buff *skb)
{
int len;
@@ -121,6 +128,21 @@ ieee80211_rx_radiotap_space(struct ieee80211_local *local,
len += 2 * hweight8(status->chains);
}
+ if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) {
+ struct ieee80211_vendor_radiotap *rtap = (void *)skb->data;
+
+ /* vendor presence bitmap */
+ len += 4;
+ /* alignment for fixed 6-byte vendor data header */
+ len = ALIGN(len, 2);
+ /* vendor data header */
+ len += 6;
+ if (WARN_ON(rtap->align == 0))
+ rtap->align = 1;
+ len = ALIGN(len, rtap->align);
+ len += rtap->len + rtap->pad;
+ }
+
return len;
}
@@ -144,13 +166,20 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
u16 channel_flags = 0;
int mpdulen, chain;
unsigned long chains = status->chains;
+ struct ieee80211_vendor_radiotap rtap = {};
+
+ if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) {
+ rtap = *(struct ieee80211_vendor_radiotap *)skb->data;
+ /* rtap.len and rtap.pad are undone immediately */
+ skb_pull(skb, sizeof(rtap) + rtap.len + rtap.pad);
+ }
mpdulen = skb->len;
if (!(has_fcs && (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)))
mpdulen += FCS_LEN;
rthdr = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len);
- memset(rthdr, 0, rtap_len);
+ memset(rthdr, 0, rtap_len - rtap.len - rtap.pad);
it_present = &rthdr->it_present;
/* radiotap header, set always present flags */
@@ -172,6 +201,14 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
BIT(IEEE80211_RADIOTAP_DBM_ANTSIGNAL);
}
+ if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) {
+ it_present_val |= BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE) |
+ BIT(IEEE80211_RADIOTAP_EXT);
+ put_unaligned_le32(it_present_val, it_present);
+ it_present++;
+ it_present_val = rtap.present;
+ }
+
put_unaligned_le32(it_present_val, it_present);
pos = (void *)(it_present + 1);
@@ -366,6 +403,22 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local,
*pos++ = status->chain_signal[chain];
*pos++ = chain;
}
+
+ if (status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) {
+ /* ensure 2 byte alignment for the vendor field as required */
+ if ((pos - (u8 *)rthdr) & 1)
+ *pos++ = 0;
+ *pos++ = rtap.oui[0];
+ *pos++ = rtap.oui[1];
+ *pos++ = rtap.oui[2];
+ *pos++ = rtap.subns;
+ put_unaligned_le16(rtap.len, pos);
+ pos += 2;
+ /* align the actual payload as requested */
+ while ((pos - (u8 *)rthdr) & (rtap.align - 1))
+ *pos++ = 0;
+ /* data (and possible padding) already follows */
+ }
}
/*
@@ -379,10 +432,17 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
{
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(origskb);
struct ieee80211_sub_if_data *sdata;
- int needed_headroom;
+ int rt_hdrlen, needed_headroom;
struct sk_buff *skb, *skb2;
struct net_device *prev_dev = NULL;
int present_fcs_len = 0;
+ unsigned int rtap_vendor_space = 0;
+
+ if (unlikely(status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA)) {
+ struct ieee80211_vendor_radiotap *rtap = (void *)origskb->data;
+
+ rtap_vendor_space = sizeof(*rtap) + rtap->len + rtap->pad;
+ }
/*
* First, we may need to make a copy of the skb because
@@ -396,25 +456,27 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS)
present_fcs_len = FCS_LEN;
- /* ensure hdr->frame_control is in skb head */
- if (!pskb_may_pull(origskb, 2)) {
+ /* ensure hdr->frame_control and vendor radiotap data are in skb head */
+ if (!pskb_may_pull(origskb, 2 + rtap_vendor_space)) {
dev_kfree_skb(origskb);
return NULL;
}
if (!local->monitors) {
- if (should_drop_frame(origskb, present_fcs_len)) {
+ if (should_drop_frame(origskb, present_fcs_len,
+ rtap_vendor_space)) {
dev_kfree_skb(origskb);
return NULL;
}
- return remove_monitor_info(local, origskb);
+ return remove_monitor_info(local, origskb, rtap_vendor_space);
}
/* room for the radiotap header based on driver features */
- needed_headroom = ieee80211_rx_radiotap_space(local, status);
+ rt_hdrlen = ieee80211_rx_radiotap_hdrlen(local, status, origskb);
+ needed_headroom = rt_hdrlen - rtap_vendor_space;
- if (should_drop_frame(origskb, present_fcs_len)) {
+ if (should_drop_frame(origskb, present_fcs_len, rtap_vendor_space)) {
/* only need to expand headroom if necessary */
skb = origskb;
origskb = NULL;
@@ -438,15 +500,15 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
*/
skb = skb_copy_expand(origskb, needed_headroom, 0, GFP_ATOMIC);
- origskb = remove_monitor_info(local, origskb);
+ origskb = remove_monitor_info(local, origskb,
+ rtap_vendor_space);
if (!skb)
return origskb;
}
/* prepend radiotap information */
- ieee80211_add_rx_radiotap_header(local, skb, rate, needed_headroom,
- true);
+ ieee80211_add_rx_radiotap_header(local, skb, rate, rt_hdrlen, true);
skb_reset_mac_header(skb);
skb->ip_summed = CHECKSUM_UNNECESSARY;
@@ -985,7 +1047,7 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
}
static ieee80211_rx_result debug_noinline
-ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
+ieee80211_rx_h_check_dup(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
@@ -994,10 +1056,16 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
* Drop duplicate 802.11 retransmissions
* (IEEE 802.11-2012: 9.3.2.10 "Duplicate detection and recovery")
*/
- if (rx->skb->len >= 24 && rx->sta &&
- !ieee80211_is_ctl(hdr->frame_control) &&
- !ieee80211_is_qos_nullfunc(hdr->frame_control) &&
- !is_multicast_ether_addr(hdr->addr1)) {
+
+ if (rx->skb->len < 24)
+ return RX_CONTINUE;
+
+ if (ieee80211_is_ctl(hdr->frame_control) ||
+ ieee80211_is_qos_nullfunc(hdr->frame_control) ||
+ is_multicast_ether_addr(hdr->addr1))
+ return RX_CONTINUE;
+
+ if (rx->sta) {
if (unlikely(ieee80211_has_retry(hdr->frame_control) &&
rx->sta->last_seq_ctrl[rx->seqno_idx] ==
hdr->seq_ctrl)) {
@@ -1011,6 +1079,14 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
}
}
+ return RX_CONTINUE;
+}
+
+static ieee80211_rx_result debug_noinline
+ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
+
if (unlikely(rx->skb->len < 16)) {
I802_DEBUG_INC(rx->local->rx_handlers_drop_short);
return RX_DROP_MONITOR;
@@ -1032,6 +1108,7 @@ ieee80211_rx_h_check(struct ieee80211_rx_data *rx)
ieee80211_is_pspoll(hdr->frame_control)) &&
rx->sdata->vif.type != NL80211_IFTYPE_ADHOC &&
rx->sdata->vif.type != NL80211_IFTYPE_WDS &&
+ rx->sdata->vif.type != NL80211_IFTYPE_OCB &&
(!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_ASSOC)))) {
/*
* accept port control frames from the AP even when it's not
@@ -1272,6 +1349,12 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
sta->last_rx_rate_vht_nss = status->vht_nss;
}
}
+ } else if (rx->sdata->vif.type == NL80211_IFTYPE_OCB) {
+ u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len,
+ NL80211_IFTYPE_OCB);
+ /* OCB uses wild-card BSSID */
+ if (is_broadcast_ether_addr(bssid))
+ sta->last_rx = jiffies;
} else if (!is_multicast_ether_addr(hdr->addr1)) {
/*
* Mesh beacons will update last_rx when if they are found to
@@ -1678,11 +1761,14 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
sc = le16_to_cpu(hdr->seq_ctrl);
frag = sc & IEEE80211_SCTL_FRAG;
- if (likely((!ieee80211_has_morefrags(fc) && frag == 0) ||
- is_multicast_ether_addr(hdr->addr1))) {
- /* not fragmented */
+ if (likely(!ieee80211_has_morefrags(fc) && frag == 0))
+ goto out;
+
+ if (is_multicast_ether_addr(hdr->addr1)) {
+ rx->local->dot11MulticastReceivedFrameCount++;
goto out;
}
+
I802_DEBUG_INC(rx->local->rx_handlers_fragments);
if (skb_linearize(rx->skb))
@@ -1775,10 +1861,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
out:
if (rx->sta)
rx->sta->rx_packets++;
- if (is_multicast_ether_addr(hdr->addr1))
- rx->local->dot11MulticastReceivedFrameCount++;
- else
- ieee80211_led_rx(rx->local);
+ ieee80211_led_rx(rx->local);
return RX_CONTINUE;
}
@@ -2250,6 +2333,27 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
if (!ieee80211_frame_allowed(rx, fc))
return RX_DROP_MONITOR;
+ /* directly handle TDLS channel switch requests/responses */
+ if (unlikely(((struct ethhdr *)rx->skb->data)->h_proto ==
+ cpu_to_be16(ETH_P_TDLS))) {
+ struct ieee80211_tdls_data *tf = (void *)rx->skb->data;
+
+ if (pskb_may_pull(rx->skb,
+ offsetof(struct ieee80211_tdls_data, u)) &&
+ tf->payload_type == WLAN_TDLS_SNAP_RFTYPE &&
+ tf->category == WLAN_CATEGORY_TDLS &&
+ (tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ||
+ tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE)) {
+ rx->skb->pkt_type = IEEE80211_SDATA_QUEUE_TDLS_CHSW;
+ skb_queue_tail(&sdata->skb_queue, rx->skb);
+ ieee80211_queue_work(&rx->local->hw, &sdata->work);
+ if (rx->sta)
+ rx->sta->rx_packets++;
+
+ return RX_QUEUED;
+ }
+ }
+
if (rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
unlikely(port_control) && sdata->bss) {
sdata = container_of(sdata->bss, struct ieee80211_sub_if_data,
@@ -2820,6 +2924,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
if (!ieee80211_vif_is_mesh(&sdata->vif) &&
sdata->vif.type != NL80211_IFTYPE_ADHOC &&
+ sdata->vif.type != NL80211_IFTYPE_OCB &&
sdata->vif.type != NL80211_IFTYPE_STATION)
return RX_DROP_MONITOR;
@@ -2884,8 +2989,10 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
if (!local->cooked_mntrs)
goto out_free_skb;
+ /* vendor data is long removed here */
+ status->flag &= ~RX_FLAG_RADIOTAP_VENDOR_DATA;
/* room for the radiotap header based on driver features */
- needed_headroom = ieee80211_rx_radiotap_space(local, status);
+ needed_headroom = ieee80211_rx_radiotap_hdrlen(local, status, skb);
if (skb_headroom(skb) < needed_headroom &&
pskb_expand_head(skb, needed_headroom, 0, GFP_ATOMIC))
@@ -3038,6 +3145,7 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)
goto rxh_next; \
} while (0);
+ CALL_RXH(ieee80211_rx_h_check_dup)
CALL_RXH(ieee80211_rx_h_check)
ieee80211_rx_reorder_ampdu(rx, &reorder_release);
@@ -3130,6 +3238,33 @@ static bool prepare_for_handlers(struct ieee80211_rx_data *rx,
BIT(rate_idx));
}
break;
+ case NL80211_IFTYPE_OCB:
+ if (!bssid)
+ return false;
+ if (ieee80211_is_beacon(hdr->frame_control)) {
+ return false;
+ } else if (!is_broadcast_ether_addr(bssid)) {
+ ocb_dbg(sdata, "BSSID mismatch in OCB mode!\n");
+ return false;
+ } else if (!multicast &&
+ !ether_addr_equal(sdata->dev->dev_addr,
+ hdr->addr1)) {
+ /* if we are in promisc mode we also accept
+ * packets not destined for us
+ */
+ if (!(sdata->dev->flags & IFF_PROMISC))
+ return false;
+ rx->flags &= ~IEEE80211_RX_RA_MATCH;
+ } else if (!rx->sta) {
+ int rate_idx;
+ if (status->flag & RX_FLAG_HT)
+ rate_idx = 0; /* TODO: HT rates */
+ else
+ rate_idx = status->rate_idx;
+ ieee80211_ocb_rx_no_sta(sdata, bssid, hdr->addr2,
+ BIT(rate_idx));
+ }
+ break;
case NL80211_IFTYPE_MESH_POINT:
if (!multicast &&
!ether_addr_equal(sdata->vif.addr, hdr->addr1)) {
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index af0d094b2f2f..ae842678b629 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -184,9 +184,21 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb)
return;
if (ieee80211_is_probe_resp(mgmt->frame_control)) {
- /* ignore ProbeResp to foreign address */
- if ((!sdata1 || !ether_addr_equal(mgmt->da, sdata1->vif.addr)) &&
- (!sdata2 || !ether_addr_equal(mgmt->da, sdata2->vif.addr)))
+ struct cfg80211_scan_request *scan_req;
+ struct cfg80211_sched_scan_request *sched_scan_req;
+
+ scan_req = rcu_dereference(local->scan_req);
+ sched_scan_req = rcu_dereference(local->sched_scan_req);
+
+ /* ignore ProbeResp to foreign address unless scanning
+ * with randomised address
+ */
+ if (!(sdata1 &&
+ (ether_addr_equal(mgmt->da, sdata1->vif.addr) ||
+ scan_req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)) &&
+ !(sdata2 &&
+ (ether_addr_equal(mgmt->da, sdata2->vif.addr) ||
+ sched_scan_req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)))
return;
elements = mgmt->u.probe_resp.variable;
@@ -234,11 +246,14 @@ ieee80211_prepare_scan_chandef(struct cfg80211_chan_def *chandef,
/* return false if no more work */
static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
{
- struct cfg80211_scan_request *req = local->scan_req;
+ struct cfg80211_scan_request *req;
struct cfg80211_chan_def chandef;
u8 bands_used = 0;
int i, ielen, n_chans;
+ req = rcu_dereference_protected(local->scan_req,
+ lockdep_is_held(&local->mtx));
+
if (test_bit(SCAN_HW_CANCELLED, &local->scanning))
return false;
@@ -281,6 +296,9 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
bands_used, req->rates, &chandef);
local->hw_scan_req->req.ie_len = ielen;
local->hw_scan_req->req.no_cck = req->no_cck;
+ ether_addr_copy(local->hw_scan_req->req.mac_addr, req->mac_addr);
+ ether_addr_copy(local->hw_scan_req->req.mac_addr_mask,
+ req->mac_addr_mask);
return true;
}
@@ -290,6 +308,8 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
struct ieee80211_local *local = hw_to_local(hw);
bool hw_scan = local->ops->hw_scan;
bool was_scanning = local->scanning;
+ struct cfg80211_scan_request *scan_req;
+ struct ieee80211_sub_if_data *scan_sdata;
lockdep_assert_held(&local->mtx);
@@ -322,9 +342,15 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
kfree(local->hw_scan_req);
local->hw_scan_req = NULL;
- if (local->scan_req != local->int_scan_req)
- cfg80211_scan_done(local->scan_req, aborted);
- local->scan_req = NULL;
+ scan_req = rcu_dereference_protected(local->scan_req,
+ lockdep_is_held(&local->mtx));
+
+ if (scan_req != local->int_scan_req)
+ cfg80211_scan_done(scan_req, aborted);
+ RCU_INIT_POINTER(local->scan_req, NULL);
+
+ scan_sdata = rcu_dereference_protected(local->scan_sdata,
+ lockdep_is_held(&local->mtx));
RCU_INIT_POINTER(local->scan_sdata, NULL);
local->scanning = 0;
@@ -335,7 +361,7 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
if (!hw_scan) {
ieee80211_configure_filter(local);
- drv_sw_scan_complete(local);
+ drv_sw_scan_complete(local, scan_sdata);
ieee80211_offchannel_return(local);
}
@@ -361,7 +387,8 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
}
EXPORT_SYMBOL(ieee80211_scan_completed);
-static int ieee80211_start_sw_scan(struct ieee80211_local *local)
+static int ieee80211_start_sw_scan(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
{
/* Software scan is not supported in multi-channel cases */
if (local->use_chanctx)
@@ -380,7 +407,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local)
* nullfunc frames and probe requests will be dropped in
* ieee80211_tx_h_check_assoc().
*/
- drv_sw_scan_start(local);
+ drv_sw_scan_start(local, sdata, local->scan_addr);
local->leave_oper_channel_time = jiffies;
local->next_scan_state = SCAN_DECISION;
@@ -440,23 +467,26 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local,
{
int i;
struct ieee80211_sub_if_data *sdata;
+ struct cfg80211_scan_request *scan_req;
enum ieee80211_band band = local->hw.conf.chandef.chan->band;
u32 tx_flags;
+ scan_req = rcu_dereference_protected(local->scan_req,
+ lockdep_is_held(&local->mtx));
+
tx_flags = IEEE80211_TX_INTFL_OFFCHAN_TX_OK;
- if (local->scan_req->no_cck)
+ if (scan_req->no_cck)
tx_flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
sdata = rcu_dereference_protected(local->scan_sdata,
lockdep_is_held(&local->mtx));
- for (i = 0; i < local->scan_req->n_ssids; i++)
+ for (i = 0; i < 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,
- local->scan_req->rates[band], false,
+ sdata, local->scan_addr, NULL,
+ scan_req->ssids[i].ssid, scan_req->ssids[i].ssid_len,
+ scan_req->ie, scan_req->ie_len,
+ scan_req->rates[band], false,
tx_flags, local->hw.conf.chandef.chan, true);
/*
@@ -480,7 +510,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
if (!ieee80211_can_scan(local, sdata)) {
/* wait for the work to finish/time out */
- local->scan_req = req;
+ rcu_assign_pointer(local->scan_req, req);
rcu_assign_pointer(local->scan_sdata, sdata);
return 0;
}
@@ -530,9 +560,16 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
*/
}
- local->scan_req = req;
+ rcu_assign_pointer(local->scan_req, req);
rcu_assign_pointer(local->scan_sdata, sdata);
+ if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)
+ get_random_mask_addr(local->scan_addr,
+ req->mac_addr,
+ req->mac_addr_mask);
+ else
+ memcpy(local->scan_addr, sdata->vif.addr, ETH_ALEN);
+
if (local->ops->hw_scan) {
__set_bit(SCAN_HW_SCANNING, &local->scanning);
} else if ((req->n_channels == 1) &&
@@ -549,7 +586,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
/* Notify driver scan is starting, keep order of operations
* same as normal software scan, in case that matters. */
- drv_sw_scan_start(local);
+ drv_sw_scan_start(local, sdata, local->scan_addr);
ieee80211_configure_filter(local); /* accept probe-responses */
@@ -558,7 +595,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
if ((req->channels[0]->flags &
IEEE80211_CHAN_NO_IR) ||
- !local->scan_req->n_ssids) {
+ !req->n_ssids) {
next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
} else {
ieee80211_scan_state_send_probe(local, &next_delay);
@@ -579,8 +616,9 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
if (local->ops->hw_scan) {
WARN_ON(!ieee80211_prep_hw_scan(local));
rc = drv_hw_scan(local, sdata, local->hw_scan_req);
- } else
- rc = ieee80211_start_sw_scan(local);
+ } else {
+ rc = ieee80211_start_sw_scan(local, sdata);
+ }
if (rc) {
kfree(local->hw_scan_req);
@@ -617,6 +655,7 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata;
struct ieee80211_channel *next_chan;
enum mac80211_scan_state next_scan_state;
+ struct cfg80211_scan_request *scan_req;
/*
* check if at least one STA interface is associated,
@@ -641,7 +680,10 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
}
mutex_unlock(&local->iflist_mtx);
- next_chan = local->scan_req->channels[local->scan_channel_idx];
+ scan_req = rcu_dereference_protected(local->scan_req,
+ lockdep_is_held(&local->mtx));
+
+ next_chan = scan_req->channels[local->scan_channel_idx];
/*
* we're currently scanning a different channel, let's
@@ -656,7 +698,7 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local,
local->leave_oper_channel_time + HZ / 8);
if (associated && !tx_empty) {
- if (local->scan_req->flags & NL80211_SCAN_FLAG_LOW_PRIORITY)
+ if (scan_req->flags & NL80211_SCAN_FLAG_LOW_PRIORITY)
next_scan_state = SCAN_ABORT;
else
next_scan_state = SCAN_SUSPEND;
@@ -677,14 +719,18 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
int skip;
struct ieee80211_channel *chan;
enum nl80211_bss_scan_width oper_scan_width;
+ struct cfg80211_scan_request *scan_req;
+
+ scan_req = rcu_dereference_protected(local->scan_req,
+ lockdep_is_held(&local->mtx));
skip = 0;
- chan = local->scan_req->channels[local->scan_channel_idx];
+ chan = scan_req->channels[local->scan_channel_idx];
local->scan_chandef.chan = chan;
local->scan_chandef.center_freq1 = chan->center_freq;
local->scan_chandef.center_freq2 = 0;
- switch (local->scan_req->scan_width) {
+ switch (scan_req->scan_width) {
case NL80211_BSS_CHAN_WIDTH_5:
local->scan_chandef.width = NL80211_CHAN_WIDTH_5;
break;
@@ -698,7 +744,7 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
oper_scan_width = cfg80211_chandef_to_scan_width(
&local->_oper_chandef);
if (chan == local->_oper_chandef.chan &&
- oper_scan_width == local->scan_req->scan_width)
+ oper_scan_width == scan_req->scan_width)
local->scan_chandef = local->_oper_chandef;
else
local->scan_chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
@@ -727,8 +773,7 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local,
*
* In any case, it is not necessary for a passive scan.
*/
- if (chan->flags & IEEE80211_CHAN_NO_IR ||
- !local->scan_req->n_ssids) {
+ if (chan->flags & IEEE80211_CHAN_NO_IR || !scan_req->n_ssids) {
*next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
local->next_scan_state = SCAN_DECISION;
return;
@@ -777,6 +822,7 @@ 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;
+ struct cfg80211_scan_request *scan_req;
unsigned long next_delay = 0;
bool aborted;
@@ -784,6 +830,8 @@ void ieee80211_scan_work(struct work_struct *work)
sdata = rcu_dereference_protected(local->scan_sdata,
lockdep_is_held(&local->mtx));
+ scan_req = rcu_dereference_protected(local->scan_req,
+ lockdep_is_held(&local->mtx));
/* When scanning on-channel, the first-callback means completed. */
if (test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning)) {
@@ -796,20 +844,19 @@ void ieee80211_scan_work(struct work_struct *work)
goto out_complete;
}
- if (!sdata || !local->scan_req)
+ if (!sdata || !scan_req)
goto out;
- if (local->scan_req && !local->scanning) {
- struct cfg80211_scan_request *req = local->scan_req;
+ if (!local->scanning) {
int rc;
- local->scan_req = NULL;
+ RCU_INIT_POINTER(local->scan_req, NULL);
RCU_INIT_POINTER(local->scan_sdata, NULL);
- rc = __ieee80211_start_scan(sdata, req);
+ rc = __ieee80211_start_scan(sdata, scan_req);
if (rc) {
/* need to complete scan in cfg80211 */
- local->scan_req = req;
+ rcu_assign_pointer(local->scan_req, scan_req);
aborted = true;
goto out_complete;
} else
@@ -829,7 +876,7 @@ void ieee80211_scan_work(struct work_struct *work)
switch (local->next_scan_state) {
case SCAN_DECISION:
/* if no more bands/channels left, complete scan */
- if (local->scan_channel_idx >= local->scan_req->n_channels) {
+ if (local->scan_channel_idx >= scan_req->n_channels) {
aborted = false;
goto out_complete;
}
@@ -1043,7 +1090,7 @@ int __ieee80211_request_sched_scan_start(struct ieee80211_sub_if_data *sdata,
ret = drv_sched_scan_start(local, sdata, req, &sched_scan_ies);
if (ret == 0) {
rcu_assign_pointer(local->sched_scan_sdata, sdata);
- local->sched_scan_req = req;
+ rcu_assign_pointer(local->sched_scan_req, req);
}
kfree(ie);
@@ -1052,7 +1099,7 @@ out:
if (ret) {
/* Clean in case of failure after HW restart or upon resume. */
RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
- local->sched_scan_req = NULL;
+ RCU_INIT_POINTER(local->sched_scan_req, NULL);
}
return ret;
@@ -1090,7 +1137,7 @@ int ieee80211_request_sched_scan_stop(struct ieee80211_sub_if_data *sdata)
}
/* We don't want to restart sched scan anymore. */
- local->sched_scan_req = NULL;
+ RCU_INIT_POINTER(local->sched_scan_req, NULL);
if (rcu_access_pointer(local->sched_scan_sdata)) {
ret = drv_sched_scan_stop(local, sdata);
@@ -1125,7 +1172,7 @@ void ieee80211_sched_scan_end(struct ieee80211_local *local)
RCU_INIT_POINTER(local->sched_scan_sdata, NULL);
/* If sched scan was aborted by the driver. */
- local->sched_scan_req = NULL;
+ RCU_INIT_POINTER(local->sched_scan_req, NULL);
mutex_unlock(&local->mtx);
diff --git a/net/mac80211/spectmgmt.c b/net/mac80211/spectmgmt.c
index 6ab009070084..efeba56c913b 100644
--- a/net/mac80211/spectmgmt.c
+++ b/net/mac80211/spectmgmt.c
@@ -22,7 +22,7 @@
#include "wme.h"
int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
- struct ieee802_11_elems *elems, bool beacon,
+ struct ieee802_11_elems *elems,
enum ieee80211_band current_band,
u32 sta_flags, u8 *bssid,
struct ieee80211_csa_ie *csa_ie)
@@ -91,19 +91,13 @@ int ieee80211_parse_ch_switch_ie(struct ieee80211_sub_if_data *sdata,
return -EINVAL;
}
- if (!beacon && sec_chan_offs) {
+ if (sec_chan_offs) {
secondary_channel_offset = sec_chan_offs->sec_chan_offs;
- } else if (beacon && ht_oper) {
- secondary_channel_offset =
- ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET;
} else if (!(sta_flags & IEEE80211_STA_DISABLE_HT)) {
- /* If it's not a beacon, HT is enabled and the IE not present,
- * it's 20 MHz, 802.11-2012 8.5.2.6:
- * This element [the Secondary Channel Offset Element] is
- * present when switching to a 40 MHz channel. It may be
- * present when switching to a 20 MHz channel (in which
- * case the secondary channel offset is set to SCN).
- */
+ /* If the secondary channel offset IE is not present,
+ * we can't know what's the post-CSA offset, so the
+ * best we can do is use 20MHz.
+ */
secondary_channel_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE;
}
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index de494df3bab8..a42f5b2b024d 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -351,6 +351,9 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
sta->sta_state = IEEE80211_STA_NONE;
+ /* Mark TID as unreserved */
+ sta->reserved_tid = IEEE80211_TID_UNRESERVED;
+
ktime_get_ts(&uptime);
sta->last_connected = uptime.tv_sec;
ewma_init(&sta->avg_signal, 1024, 8);
@@ -501,7 +504,7 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
/* make the station visible */
sta_info_hash_add(local, sta);
- list_add_rcu(&sta->list, &local->sta_list);
+ list_add_tail_rcu(&sta->list, &local->sta_list);
/* notify driver */
err = sta_info_insert_drv_state(local, sdata, sta);
@@ -847,6 +850,15 @@ static int __must_check __sta_info_destroy_part1(struct sta_info *sta)
if (WARN_ON(ret))
return ret;
+ /*
+ * for TDLS peers, make sure to return to the base channel before
+ * removal.
+ */
+ if (test_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL)) {
+ drv_tdls_cancel_channel_switch(local, sdata, &sta->sta);
+ clear_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL);
+ }
+
list_del_rcu(&sta->list);
drv_sta_pre_rcu_remove(local, sta->sdata, sta);
@@ -1249,7 +1261,8 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
return;
}
- ieee80211_xmit(sdata, skb, chanctx_conf->def.chan->band);
+ info->band = chanctx_conf->def.chan->band;
+ ieee80211_xmit(sdata, skb);
rcu_read_unlock();
}
@@ -1531,7 +1544,7 @@ void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta)
break;
case 0:
/* XXX: what is a good value? */
- n_frames = 8;
+ n_frames = 128;
break;
}
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index bcda2ac7d844..4f052bb2a5ad 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -49,6 +49,9 @@
* packets. This means the link is enabled.
* @WLAN_STA_TDLS_INITIATOR: We are the initiator of the TDLS link with this
* station.
+ * @WLAN_STA_TDLS_CHAN_SWITCH: This TDLS peer supports TDLS channel-switching
+ * @WLAN_STA_TDLS_OFF_CHANNEL: The local STA is currently off-channel with this
+ * TDLS peer
* @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was
* keeping station in power-save mode, reply when the driver
* unblocks the station.
@@ -78,6 +81,8 @@ enum ieee80211_sta_info_flags {
WLAN_STA_TDLS_PEER,
WLAN_STA_TDLS_PEER_AUTH,
WLAN_STA_TDLS_INITIATOR,
+ WLAN_STA_TDLS_CHAN_SWITCH,
+ WLAN_STA_TDLS_OFF_CHANNEL,
WLAN_STA_UAPSD,
WLAN_STA_SP,
WLAN_STA_4ADDR_EVENT,
@@ -249,6 +254,9 @@ struct ieee80211_tx_latency_stat {
u32 bin_count;
};
+/* Value to indicate no TID reservation */
+#define IEEE80211_TID_UNRESERVED 0xff
+
/**
* struct sta_info - STA information
*
@@ -337,6 +345,7 @@ struct ieee80211_tx_latency_stat {
* AP only.
* @cipher_scheme: optional cipher scheme for this station
* @last_tdls_pkt_time: holds the time in jiffies of last TDLS pkt ACKed
+ * @reserved_tid: reserved TID (if any, otherwise IEEE80211_TID_UNRESERVED)
*/
struct sta_info {
/* General information, mostly static */
@@ -454,6 +463,8 @@ struct sta_info {
/* TDLS timeout data */
unsigned long last_tdls_pkt_time;
+ u8 reserved_tid;
+
/* keep last! */
struct ieee80211_sta sta;
};
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 89290e33dafe..bb146f377ee4 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -390,6 +390,46 @@ ieee80211_add_tx_radiotap_header(struct ieee80211_local *local,
}
}
+/*
+ * Handles the tx for TDLS teardown frames.
+ * If the frame wasn't ACKed by the peer - it will be re-sent through the AP
+ */
+static void ieee80211_tdls_td_tx_handle(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, u32 flags)
+{
+ struct sk_buff *teardown_skb;
+ struct sk_buff *orig_teardown_skb;
+ bool is_teardown = false;
+
+ /* Get the teardown data we need and free the lock */
+ spin_lock(&sdata->u.mgd.teardown_lock);
+ teardown_skb = sdata->u.mgd.teardown_skb;
+ orig_teardown_skb = sdata->u.mgd.orig_teardown_skb;
+ if ((skb == orig_teardown_skb) && teardown_skb) {
+ sdata->u.mgd.teardown_skb = NULL;
+ sdata->u.mgd.orig_teardown_skb = NULL;
+ is_teardown = true;
+ }
+ spin_unlock(&sdata->u.mgd.teardown_lock);
+
+ if (is_teardown) {
+ /* This mechanism relies on being able to get ACKs */
+ WARN_ON(!(local->hw.flags &
+ IEEE80211_HW_REPORTS_TX_ACK_STATUS));
+
+ /* Check if peer has ACKed */
+ if (flags & IEEE80211_TX_STAT_ACK) {
+ dev_kfree_skb_any(teardown_skb);
+ } else {
+ tdls_dbg(sdata,
+ "TDLS Resending teardown through AP\n");
+
+ ieee80211_subif_start_xmit(teardown_skb, skb->dev);
+ }
+ }
+}
+
static void ieee80211_report_used_skb(struct ieee80211_local *local,
struct sk_buff *skb, bool dropped)
{
@@ -426,8 +466,19 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
if (!sdata) {
skb->dev = NULL;
} else if (info->flags & IEEE80211_TX_INTFL_MLME_CONN_TX) {
- ieee80211_mgd_conn_tx_status(sdata, hdr->frame_control,
- acked);
+ unsigned int hdr_size =
+ ieee80211_hdrlen(hdr->frame_control);
+
+ /* Check to see if packet is a TDLS teardown packet */
+ if (ieee80211_is_data(hdr->frame_control) &&
+ (ieee80211_get_tdls_action(skb, hdr_size) ==
+ WLAN_TDLS_TEARDOWN))
+ ieee80211_tdls_td_tx_handle(local, sdata, skb,
+ info->flags);
+ else
+ ieee80211_mgd_conn_tx_status(sdata,
+ hdr->frame_control,
+ acked);
} else if (ieee80211_is_nullfunc(hdr->frame_control) ||
ieee80211_is_qos_nullfunc(hdr->frame_control)) {
cfg80211_probe_status(sdata->dev, hdr->addr1,
@@ -541,10 +592,9 @@ static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local,
#define STA_LOST_TDLS_PKT_THRESHOLD 10
#define STA_LOST_TDLS_PKT_TIME (10*HZ) /* 10secs since last ACK */
-static void ieee80211_lost_packet(struct sta_info *sta, struct sk_buff *skb)
+static void ieee80211_lost_packet(struct sta_info *sta,
+ struct ieee80211_tx_info *info)
{
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
-
/* This packet was aggregated but doesn't carry status info */
if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
!(info->flags & IEEE80211_TX_STAT_AMPDU))
@@ -571,24 +621,13 @@ static void ieee80211_lost_packet(struct sta_info *sta, struct sk_buff *skb)
sta->lost_packets = 0;
}
-void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
+static int ieee80211_tx_get_rates(struct ieee80211_hw *hw,
+ struct ieee80211_tx_info *info,
+ int *retry_count)
{
- struct sk_buff *skb2;
- struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
- struct ieee80211_local *local = hw_to_local(hw);
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
- __le16 fc;
- struct ieee80211_supported_band *sband;
- struct ieee80211_sub_if_data *sdata;
- struct net_device *prev_dev = NULL;
- struct sta_info *sta, *tmp;
- int retry_count = -1, i;
int rates_idx = -1;
- bool send_to_cooked;
- bool acked;
- struct ieee80211_bar *bar;
- int rtap_len;
- int shift = 0;
+ int count = -1;
+ int i;
for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
if ((info->flags & IEEE80211_TX_CTL_AMPDU) &&
@@ -606,12 +645,91 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
break;
}
- retry_count += info->status.rates[i].count;
+ count += info->status.rates[i].count;
}
rates_idx = i - 1;
- if (retry_count < 0)
- retry_count = 0;
+ if (count < 0)
+ count = 0;
+
+ *retry_count = count;
+ return rates_idx;
+}
+
+void ieee80211_tx_status_noskb(struct ieee80211_hw *hw,
+ struct ieee80211_sta *pubsta,
+ struct ieee80211_tx_info *info)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_supported_band *sband;
+ int retry_count;
+ int rates_idx;
+ bool acked;
+
+ rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
+
+ sband = hw->wiphy->bands[info->band];
+
+ acked = !!(info->flags & IEEE80211_TX_STAT_ACK);
+ if (pubsta) {
+ struct sta_info *sta;
+
+ sta = container_of(pubsta, struct sta_info, sta);
+
+ if (!acked)
+ sta->tx_retry_failed++;
+ sta->tx_retry_count += retry_count;
+
+ if (acked) {
+ sta->last_rx = jiffies;
+
+ if (sta->lost_packets)
+ sta->lost_packets = 0;
+
+ /* Track when last TDLS packet was ACKed */
+ if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH))
+ sta->last_tdls_pkt_time = jiffies;
+ } else {
+ ieee80211_lost_packet(sta, info);
+ }
+
+ rate_control_tx_status_noskb(local, sband, sta, info);
+ }
+
+ if (acked) {
+ local->dot11TransmittedFrameCount++;
+ if (!pubsta)
+ local->dot11MulticastTransmittedFrameCount++;
+ if (retry_count > 0)
+ local->dot11RetryCount++;
+ if (retry_count > 1)
+ local->dot11MultipleRetryCount++;
+ } else {
+ local->dot11FailedCount++;
+ }
+}
+EXPORT_SYMBOL(ieee80211_tx_status_noskb);
+
+void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+ struct sk_buff *skb2;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ __le16 fc;
+ struct ieee80211_supported_band *sband;
+ struct ieee80211_sub_if_data *sdata;
+ struct net_device *prev_dev = NULL;
+ struct sta_info *sta, *tmp;
+ int retry_count;
+ int rates_idx;
+ bool send_to_cooked;
+ bool acked;
+ struct ieee80211_bar *bar;
+ int rtap_len;
+ int shift = 0;
+
+ rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
rcu_read_lock();
@@ -704,7 +822,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
if ((sta->sdata->vif.type == NL80211_IFTYPE_STATION) &&
(local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS))
- ieee80211_sta_tx_notify(sta->sdata, (void *) skb->data, acked);
+ ieee80211_sta_tx_notify(sta->sdata, (void *) skb->data,
+ acked, info->status.tx_time);
if (local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) {
if (info->flags & IEEE80211_TX_STAT_ACK) {
@@ -715,7 +834,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
if (test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH))
sta->last_tdls_pkt_time = jiffies;
} else {
- ieee80211_lost_packet(sta, skb);
+ ieee80211_lost_packet(sta, info);
}
}
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index 4ea25dec0698..55ddd77b865d 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -35,19 +35,101 @@ void ieee80211_tdls_peer_del_work(struct work_struct *wk)
mutex_unlock(&local->mtx);
}
-static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb)
+static void ieee80211_tdls_add_ext_capab(struct ieee80211_local *local,
+ struct sk_buff *skb)
{
u8 *pos = (void *)skb_put(skb, 7);
+ bool chan_switch = local->hw.wiphy->features &
+ NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
*pos++ = WLAN_EID_EXT_CAPABILITY;
*pos++ = 5; /* len */
*pos++ = 0x0;
*pos++ = 0x0;
*pos++ = 0x0;
- *pos++ = 0x0;
+ *pos++ = chan_switch ? WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH : 0;
*pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
}
+static u8
+ieee80211_tdls_add_subband(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, u16 start, u16 end,
+ u16 spacing)
+{
+ u8 subband_cnt = 0, ch_cnt = 0;
+ struct ieee80211_channel *ch;
+ struct cfg80211_chan_def chandef;
+ int i, subband_start;
+
+ for (i = start; i <= end; i += spacing) {
+ if (!ch_cnt)
+ subband_start = i;
+
+ ch = ieee80211_get_channel(sdata->local->hw.wiphy, i);
+ if (ch) {
+ /* we will be active on the channel */
+ u32 flags = IEEE80211_CHAN_DISABLED |
+ IEEE80211_CHAN_NO_IR;
+ cfg80211_chandef_create(&chandef, ch,
+ NL80211_CHAN_HT20);
+ if (cfg80211_chandef_usable(sdata->local->hw.wiphy,
+ &chandef, flags)) {
+ ch_cnt++;
+ continue;
+ }
+ }
+
+ if (ch_cnt) {
+ u8 *pos = skb_put(skb, 2);
+ *pos++ = ieee80211_frequency_to_channel(subband_start);
+ *pos++ = ch_cnt;
+
+ subband_cnt++;
+ ch_cnt = 0;
+ }
+ }
+
+ return subband_cnt;
+}
+
+static void
+ieee80211_tdls_add_supp_channels(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb)
+{
+ /*
+ * Add possible channels for TDLS. These are channels that are allowed
+ * to be active.
+ */
+ u8 subband_cnt;
+ u8 *pos = skb_put(skb, 2);
+
+ *pos++ = WLAN_EID_SUPPORTED_CHANNELS;
+
+ /*
+ * 5GHz and 2GHz channels numbers can overlap. Ignore this for now, as
+ * this doesn't happen in real world scenarios.
+ */
+
+ /* 2GHz, with 5MHz spacing */
+ subband_cnt = ieee80211_tdls_add_subband(sdata, skb, 2412, 2472, 5);
+
+ /* 5GHz, with 20MHz spacing */
+ subband_cnt += ieee80211_tdls_add_subband(sdata, skb, 5000, 5825, 20);
+
+ /* length */
+ *pos = 2 * subband_cnt;
+}
+
+static void ieee80211_tdls_add_bss_coex_ie(struct sk_buff *skb)
+{
+ u8 *pos = (void *)skb_put(skb, 3);
+
+ *pos++ = WLAN_EID_BSS_COEX_2040;
+ *pos++ = 1; /* len */
+
+ *pos++ = WLAN_BSS_COEX_INFORMATION_REQUEST;
+}
+
static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata,
u16 status_code)
{
@@ -190,6 +272,7 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
ieee80211_add_srates_ie(sdata, skb, false, band);
ieee80211_add_ext_srates_ie(sdata, skb, false, band);
+ ieee80211_tdls_add_supp_channels(sdata, skb);
/* add any custom IEs that go before Extended Capabilities */
if (extra_ies_len) {
@@ -209,7 +292,7 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
offset = noffset;
}
- ieee80211_tdls_add_ext_capab(skb);
+ ieee80211_tdls_add_ext_capab(local, skb);
/* add the QoS element if we support it */
if (local->hw.queues >= IEEE80211_NUM_ACS &&
@@ -271,6 +354,10 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
rcu_read_unlock();
+ if (ht_cap.ht_supported &&
+ (ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
+ ieee80211_tdls_add_bss_coex_ie(skb);
+
/* add any remaining IEs */
if (extra_ies_len) {
noffset = extra_ies_len;
@@ -362,11 +449,68 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
}
+static void
+ieee80211_tdls_add_chan_switch_req_ies(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, const u8 *peer,
+ bool initiator, const u8 *extra_ies,
+ size_t extra_ies_len, u8 oper_class,
+ struct cfg80211_chan_def *chandef)
+{
+ struct ieee80211_tdls_data *tf;
+ size_t offset = 0, noffset;
+ u8 *pos;
+
+ if (WARN_ON_ONCE(!chandef))
+ return;
+
+ tf = (void *)skb->data;
+ tf->u.chan_switch_req.target_channel =
+ ieee80211_frequency_to_channel(chandef->chan->center_freq);
+ tf->u.chan_switch_req.oper_class = oper_class;
+
+ if (extra_ies_len) {
+ static const u8 before_lnkie[] = {
+ WLAN_EID_SECONDARY_CHANNEL_OFFSET,
+ };
+ noffset = ieee80211_ie_split(extra_ies, extra_ies_len,
+ before_lnkie,
+ ARRAY_SIZE(before_lnkie),
+ offset);
+ pos = skb_put(skb, noffset - offset);
+ memcpy(pos, extra_ies + offset, noffset - offset);
+ offset = noffset;
+ }
+
+ ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+
+ /* add any remaining IEs */
+ if (extra_ies_len) {
+ noffset = extra_ies_len;
+ pos = skb_put(skb, noffset - offset);
+ memcpy(pos, extra_ies + offset, noffset - offset);
+ }
+}
+
+static void
+ieee80211_tdls_add_chan_switch_resp_ies(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, const u8 *peer,
+ u16 status_code, bool initiator,
+ const u8 *extra_ies,
+ size_t extra_ies_len)
+{
+ if (status_code == 0)
+ ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+
+ if (extra_ies_len)
+ memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);
+}
+
static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, const u8 *peer,
u8 action_code, u16 status_code,
bool initiator, const u8 *extra_ies,
- size_t extra_ies_len)
+ size_t extra_ies_len, u8 oper_class,
+ struct cfg80211_chan_def *chandef)
{
switch (action_code) {
case WLAN_TDLS_SETUP_REQUEST:
@@ -393,6 +537,18 @@ static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
if (status_code == 0 || action_code == WLAN_TDLS_TEARDOWN)
ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
break;
+ case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
+ ieee80211_tdls_add_chan_switch_req_ies(sdata, skb, peer,
+ initiator, extra_ies,
+ extra_ies_len,
+ oper_class, chandef);
+ break;
+ case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
+ ieee80211_tdls_add_chan_switch_resp_ies(sdata, skb, peer,
+ status_code,
+ initiator, extra_ies,
+ extra_ies_len);
+ break;
}
}
@@ -459,6 +615,19 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
skb_put(skb, sizeof(tf->u.discover_req));
tf->u.discover_req.dialog_token = dialog_token;
break;
+ case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
+ tf->category = WLAN_CATEGORY_TDLS;
+ tf->action_code = WLAN_TDLS_CHANNEL_SWITCH_REQUEST;
+
+ skb_put(skb, sizeof(tf->u.chan_switch_req));
+ break;
+ case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
+ tf->category = WLAN_CATEGORY_TDLS;
+ tf->action_code = WLAN_TDLS_CHANNEL_SWITCH_RESPONSE;
+
+ skb_put(skb, sizeof(tf->u.chan_switch_resp));
+ tf->u.chan_switch_resp.status_code = cpu_to_le16(status_code);
+ break;
default:
return -EINVAL;
}
@@ -502,32 +671,33 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
return 0;
}
-static int
-ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
- const u8 *peer, u8 action_code,
- u8 dialog_token, u16 status_code,
- u32 peer_capability, bool initiator,
- const u8 *extra_ies, size_t extra_ies_len)
+static struct sk_buff *
+ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata,
+ const u8 *peer, u8 action_code,
+ u8 dialog_token, u16 status_code,
+ bool initiator, const u8 *extra_ies,
+ size_t extra_ies_len, u8 oper_class,
+ struct cfg80211_chan_def *chandef)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
- struct sk_buff *skb = NULL;
- bool send_direct;
- struct sta_info *sta;
+ struct sk_buff *skb;
int ret;
- skb = dev_alloc_skb(local->hw.extra_tx_headroom +
- max(sizeof(struct ieee80211_mgmt),
- sizeof(struct ieee80211_tdls_data)) +
- 50 + /* supported rates */
- 7 + /* ext capab */
- 26 + /* max(WMM-info, WMM-param) */
- 2 + max(sizeof(struct ieee80211_ht_cap),
- sizeof(struct ieee80211_ht_operation)) +
- extra_ies_len +
- sizeof(struct ieee80211_tdls_lnkie));
+ skb = netdev_alloc_skb(sdata->dev,
+ local->hw.extra_tx_headroom +
+ max(sizeof(struct ieee80211_mgmt),
+ sizeof(struct ieee80211_tdls_data)) +
+ 50 + /* supported rates */
+ 7 + /* ext capab */
+ 26 + /* max(WMM-info, WMM-param) */
+ 2 + max(sizeof(struct ieee80211_ht_cap),
+ sizeof(struct ieee80211_ht_operation)) +
+ 50 + /* supported channels */
+ 3 + /* 40/20 BSS coex */
+ extra_ies_len +
+ sizeof(struct ieee80211_tdls_lnkie));
if (!skb)
- return -ENOMEM;
+ return NULL;
skb_reserve(skb, local->hw.extra_tx_headroom);
@@ -537,16 +707,18 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
case WLAN_TDLS_SETUP_CONFIRM:
case WLAN_TDLS_TEARDOWN:
case WLAN_TDLS_DISCOVERY_REQUEST:
- ret = ieee80211_prep_tdls_encap_data(wiphy, dev, peer,
+ case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
+ case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
+ ret = ieee80211_prep_tdls_encap_data(local->hw.wiphy,
+ sdata->dev, peer,
action_code, dialog_token,
status_code, skb);
- send_direct = false;
break;
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
- ret = ieee80211_prep_tdls_direct(wiphy, dev, peer, action_code,
+ ret = ieee80211_prep_tdls_direct(local->hw.wiphy, sdata->dev,
+ peer, action_code,
dialog_token, status_code,
skb);
- send_direct = true;
break;
default:
ret = -ENOTSUPP;
@@ -556,14 +728,40 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
if (ret < 0)
goto fail;
+ ieee80211_tdls_add_ies(sdata, skb, peer, action_code, status_code,
+ initiator, extra_ies, extra_ies_len, oper_class,
+ chandef);
+ return skb;
+
+fail:
+ dev_kfree_skb(skb);
+ return NULL;
+}
+
+static int
+ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *peer, u8 action_code, u8 dialog_token,
+ u16 status_code, u32 peer_capability,
+ bool initiator, const u8 *extra_ies,
+ size_t extra_ies_len, u8 oper_class,
+ struct cfg80211_chan_def *chandef)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct sk_buff *skb = NULL;
+ struct sta_info *sta;
+ u32 flags = 0;
+ int ret = 0;
+
rcu_read_lock();
sta = sta_info_get(sdata, peer);
/* infer the initiator if we can, to support old userspace */
switch (action_code) {
case WLAN_TDLS_SETUP_REQUEST:
- if (sta)
+ if (sta) {
set_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
+ sta->sta.tdls_initiator = false;
+ }
/* fall-through */
case WLAN_TDLS_SETUP_CONFIRM:
case WLAN_TDLS_DISCOVERY_REQUEST:
@@ -575,13 +773,17 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
* Make the last packet sent take effect for the initiator
* value.
*/
- if (sta)
+ if (sta) {
clear_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
+ sta->sta.tdls_initiator = true;
+ }
/* fall-through */
case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
initiator = false;
break;
case WLAN_TDLS_TEARDOWN:
+ case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
+ case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
/* any value is ok */
break;
default:
@@ -596,9 +798,17 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
if (ret < 0)
goto fail;
- ieee80211_tdls_add_ies(sdata, skb, peer, action_code, status_code,
- initiator, extra_ies, extra_ies_len);
- if (send_direct) {
+ skb = ieee80211_tdls_build_mgmt_packet_data(sdata, peer, action_code,
+ dialog_token, status_code,
+ initiator, extra_ies,
+ extra_ies_len, oper_class,
+ chandef);
+ if (!skb) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) {
ieee80211_tx_skb(sdata, skb);
return 0;
}
@@ -619,9 +829,44 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
break;
}
+ /*
+ * Set the WLAN_TDLS_TEARDOWN flag to indicate a teardown in progress.
+ * Later, if no ACK is returned from peer, we will re-send the teardown
+ * packet through the AP.
+ */
+ if ((action_code == WLAN_TDLS_TEARDOWN) &&
+ (sdata->local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS)) {
+ struct sta_info *sta = NULL;
+ bool try_resend; /* Should we keep skb for possible resend */
+
+ /* If not sending directly to peer - no point in keeping skb */
+ rcu_read_lock();
+ sta = sta_info_get(sdata, peer);
+ try_resend = sta && test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
+ rcu_read_unlock();
+
+ spin_lock_bh(&sdata->u.mgd.teardown_lock);
+ if (try_resend && !sdata->u.mgd.teardown_skb) {
+ /* Mark it as requiring TX status callback */
+ flags |= IEEE80211_TX_CTL_REQ_TX_STATUS |
+ IEEE80211_TX_INTFL_MLME_CONN_TX;
+
+ /*
+ * skb is copied since mac80211 will later set
+ * properties that might not be the same as the AP,
+ * such as encryption, QoS, addresses, etc.
+ *
+ * No problem if skb_copy() fails, so no need to check.
+ */
+ sdata->u.mgd.teardown_skb = skb_copy(skb, GFP_ATOMIC);
+ sdata->u.mgd.orig_teardown_skb = skb;
+ }
+ spin_unlock_bh(&sdata->u.mgd.teardown_lock);
+ }
+
/* disable bottom halves when entering the Tx path */
local_bh_disable();
- ret = ieee80211_subif_start_xmit(skb, dev);
+ __ieee80211_subif_start_xmit(skb, dev, flags);
local_bh_enable();
return ret;
@@ -672,7 +917,8 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
dialog_token, status_code,
peer_capability, initiator,
- extra_ies, extra_ies_len);
+ extra_ies, extra_ies_len, 0,
+ NULL);
if (ret < 0)
goto exit;
@@ -711,7 +957,8 @@ ieee80211_tdls_mgmt_teardown(struct wiphy *wiphy, struct net_device *dev,
ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
dialog_token, status_code,
peer_capability, initiator,
- extra_ies, extra_ies_len);
+ extra_ies, extra_ies_len, 0,
+ NULL);
if (ret < 0)
sdata_err(sdata, "Failed sending TDLS teardown packet %d\n",
ret);
@@ -781,7 +1028,7 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
status_code,
peer_capability,
initiator, extra_ies,
- extra_ies_len);
+ extra_ies_len, 0, NULL);
break;
default:
ret = -EOPNOTSUPP;
@@ -884,3 +1131,480 @@ void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer,
cfg80211_tdls_oper_request(sdata->dev, peer, oper, reason_code, gfp);
}
EXPORT_SYMBOL(ieee80211_tdls_oper_request);
+
+static void
+iee80211_tdls_add_ch_switch_timing(u8 *buf, u16 switch_time, u16 switch_timeout)
+{
+ struct ieee80211_ch_switch_timing *ch_sw;
+
+ *buf++ = WLAN_EID_CHAN_SWITCH_TIMING;
+ *buf++ = sizeof(struct ieee80211_ch_switch_timing);
+
+ ch_sw = (void *)buf;
+ ch_sw->switch_time = cpu_to_le16(switch_time);
+ ch_sw->switch_timeout = cpu_to_le16(switch_timeout);
+}
+
+/* find switch timing IE in SKB ready for Tx */
+static const u8 *ieee80211_tdls_find_sw_timing_ie(struct sk_buff *skb)
+{
+ struct ieee80211_tdls_data *tf;
+ const u8 *ie_start;
+
+ /*
+ * Get the offset for the new location of the switch timing IE.
+ * The SKB network header will now point to the "payload_type"
+ * element of the TDLS data frame struct.
+ */
+ tf = container_of(skb->data + skb_network_offset(skb),
+ struct ieee80211_tdls_data, payload_type);
+ ie_start = tf->u.chan_switch_req.variable;
+ return cfg80211_find_ie(WLAN_EID_CHAN_SWITCH_TIMING, ie_start,
+ skb->len - (ie_start - skb->data));
+}
+
+static struct sk_buff *
+ieee80211_tdls_ch_sw_tmpl_get(struct sta_info *sta, u8 oper_class,
+ struct cfg80211_chan_def *chandef,
+ u32 *ch_sw_tm_ie_offset)
+{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ u8 extra_ies[2 + sizeof(struct ieee80211_sec_chan_offs_ie) +
+ 2 + sizeof(struct ieee80211_ch_switch_timing)];
+ int extra_ies_len = 2 + sizeof(struct ieee80211_ch_switch_timing);
+ u8 *pos = extra_ies;
+ struct sk_buff *skb;
+
+ /*
+ * if chandef points to a wide channel add a Secondary-Channel
+ * Offset information element
+ */
+ if (chandef->width == NL80211_CHAN_WIDTH_40) {
+ struct ieee80211_sec_chan_offs_ie *sec_chan_ie;
+ bool ht40plus;
+
+ *pos++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;
+ *pos++ = sizeof(*sec_chan_ie);
+ sec_chan_ie = (void *)pos;
+
+ ht40plus = cfg80211_get_chandef_type(chandef) ==
+ NL80211_CHAN_HT40PLUS;
+ sec_chan_ie->sec_chan_offs = ht40plus ?
+ IEEE80211_HT_PARAM_CHA_SEC_ABOVE :
+ IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+ pos += sizeof(*sec_chan_ie);
+
+ extra_ies_len += 2 + sizeof(struct ieee80211_sec_chan_offs_ie);
+ }
+
+ /* just set the values to 0, this is a template */
+ iee80211_tdls_add_ch_switch_timing(pos, 0, 0);
+
+ skb = ieee80211_tdls_build_mgmt_packet_data(sdata, sta->sta.addr,
+ WLAN_TDLS_CHANNEL_SWITCH_REQUEST,
+ 0, 0, !sta->sta.tdls_initiator,
+ extra_ies, extra_ies_len,
+ oper_class, chandef);
+ if (!skb)
+ return NULL;
+
+ skb = ieee80211_build_data_template(sdata, skb, 0);
+ if (IS_ERR(skb)) {
+ tdls_dbg(sdata, "Failed building TDLS channel switch frame\n");
+ return NULL;
+ }
+
+ if (ch_sw_tm_ie_offset) {
+ const u8 *tm_ie = ieee80211_tdls_find_sw_timing_ie(skb);
+
+ if (!tm_ie) {
+ tdls_dbg(sdata, "No switch timing IE in TDLS switch\n");
+ dev_kfree_skb_any(skb);
+ return NULL;
+ }
+
+ *ch_sw_tm_ie_offset = tm_ie - skb->data;
+ }
+
+ tdls_dbg(sdata,
+ "TDLS channel switch request template for %pM ch %d width %d\n",
+ sta->sta.addr, chandef->chan->center_freq, chandef->width);
+ return skb;
+}
+
+int
+ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev,
+ const u8 *addr, u8 oper_class,
+ struct cfg80211_chan_def *chandef)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
+ struct sta_info *sta;
+ struct sk_buff *skb = NULL;
+ u32 ch_sw_tm_ie;
+ int ret;
+
+ mutex_lock(&local->sta_mtx);
+ sta = sta_info_get(sdata, addr);
+ if (!sta) {
+ tdls_dbg(sdata,
+ "Invalid TDLS peer %pM for channel switch request\n",
+ addr);
+ ret = -ENOENT;
+ goto out;
+ }
+
+ if (!test_sta_flag(sta, WLAN_STA_TDLS_CHAN_SWITCH)) {
+ tdls_dbg(sdata, "TDLS channel switch unsupported by %pM\n",
+ addr);
+ ret = -ENOTSUPP;
+ goto out;
+ }
+
+ skb = ieee80211_tdls_ch_sw_tmpl_get(sta, oper_class, chandef,
+ &ch_sw_tm_ie);
+ if (!skb) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ ret = drv_tdls_channel_switch(local, sdata, &sta->sta, oper_class,
+ chandef, skb, ch_sw_tm_ie);
+ if (!ret)
+ set_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL);
+
+out:
+ mutex_unlock(&local->sta_mtx);
+ dev_kfree_skb_any(skb);
+ return ret;
+}
+
+void
+ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy,
+ struct net_device *dev,
+ const u8 *addr)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
+ struct sta_info *sta;
+
+ mutex_lock(&local->sta_mtx);
+ sta = sta_info_get(sdata, addr);
+ if (!sta) {
+ tdls_dbg(sdata,
+ "Invalid TDLS peer %pM for channel switch cancel\n",
+ addr);
+ goto out;
+ }
+
+ if (!test_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL)) {
+ tdls_dbg(sdata, "TDLS channel switch not initiated by %pM\n",
+ addr);
+ goto out;
+ }
+
+ drv_tdls_cancel_channel_switch(local, sdata, &sta->sta);
+ clear_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL);
+
+out:
+ mutex_unlock(&local->sta_mtx);
+}
+
+static struct sk_buff *
+ieee80211_tdls_ch_sw_resp_tmpl_get(struct sta_info *sta,
+ u32 *ch_sw_tm_ie_offset)
+{
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct sk_buff *skb;
+ u8 extra_ies[2 + sizeof(struct ieee80211_ch_switch_timing)];
+
+ /* initial timing are always zero in the template */
+ iee80211_tdls_add_ch_switch_timing(extra_ies, 0, 0);
+
+ skb = ieee80211_tdls_build_mgmt_packet_data(sdata, sta->sta.addr,
+ WLAN_TDLS_CHANNEL_SWITCH_RESPONSE,
+ 0, 0, !sta->sta.tdls_initiator,
+ extra_ies, sizeof(extra_ies), 0, NULL);
+ if (!skb)
+ return NULL;
+
+ skb = ieee80211_build_data_template(sdata, skb, 0);
+ if (IS_ERR(skb)) {
+ tdls_dbg(sdata,
+ "Failed building TDLS channel switch resp frame\n");
+ return NULL;
+ }
+
+ if (ch_sw_tm_ie_offset) {
+ const u8 *tm_ie = ieee80211_tdls_find_sw_timing_ie(skb);
+
+ if (!tm_ie) {
+ tdls_dbg(sdata,
+ "No switch timing IE in TDLS switch resp\n");
+ dev_kfree_skb_any(skb);
+ return NULL;
+ }
+
+ *ch_sw_tm_ie_offset = tm_ie - skb->data;
+ }
+
+ tdls_dbg(sdata, "TDLS get channel switch response template for %pM\n",
+ sta->sta.addr);
+ return skb;
+}
+
+static int
+ieee80211_process_tdls_channel_switch_resp(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee802_11_elems elems;
+ struct sta_info *sta;
+ struct ieee80211_tdls_data *tf = (void *)skb->data;
+ bool local_initiator;
+ struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
+ int baselen = offsetof(typeof(*tf), u.chan_switch_resp.variable);
+ struct ieee80211_tdls_ch_sw_params params = {};
+ int ret;
+
+ params.action_code = WLAN_TDLS_CHANNEL_SWITCH_RESPONSE;
+ params.timestamp = rx_status->device_timestamp;
+
+ if (skb->len < baselen) {
+ tdls_dbg(sdata, "TDLS channel switch resp too short: %d\n",
+ skb->len);
+ return -EINVAL;
+ }
+
+ mutex_lock(&local->sta_mtx);
+ sta = sta_info_get(sdata, tf->sa);
+ if (!sta || !test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) {
+ tdls_dbg(sdata, "TDLS chan switch from non-peer sta %pM\n",
+ tf->sa);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ params.sta = &sta->sta;
+ params.status = le16_to_cpu(tf->u.chan_switch_resp.status_code);
+ if (params.status != 0) {
+ ret = 0;
+ goto call_drv;
+ }
+
+ ieee802_11_parse_elems(tf->u.chan_switch_resp.variable,
+ skb->len - baselen, false, &elems);
+ if (elems.parse_error) {
+ tdls_dbg(sdata, "Invalid IEs in TDLS channel switch resp\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!elems.ch_sw_timing || !elems.lnk_id) {
+ tdls_dbg(sdata, "TDLS channel switch resp - missing IEs\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* validate the initiator is set correctly */
+ local_initiator =
+ !memcmp(elems.lnk_id->init_sta, sdata->vif.addr, ETH_ALEN);
+ if (local_initiator == sta->sta.tdls_initiator) {
+ tdls_dbg(sdata, "TDLS chan switch invalid lnk-id initiator\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ params.switch_time = le16_to_cpu(elems.ch_sw_timing->switch_time);
+ params.switch_timeout = le16_to_cpu(elems.ch_sw_timing->switch_timeout);
+
+ params.tmpl_skb =
+ ieee80211_tdls_ch_sw_resp_tmpl_get(sta, &params.ch_sw_tm_ie);
+ if (!params.tmpl_skb) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+call_drv:
+ drv_tdls_recv_channel_switch(sdata->local, sdata, &params);
+
+ tdls_dbg(sdata,
+ "TDLS channel switch response received from %pM status %d\n",
+ tf->sa, params.status);
+
+out:
+ mutex_unlock(&local->sta_mtx);
+ dev_kfree_skb_any(params.tmpl_skb);
+ return ret;
+}
+
+static int
+ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee802_11_elems elems;
+ struct cfg80211_chan_def chandef;
+ struct ieee80211_channel *chan;
+ enum nl80211_channel_type chan_type;
+ int freq;
+ u8 target_channel, oper_class;
+ bool local_initiator;
+ struct sta_info *sta;
+ enum ieee80211_band band;
+ struct ieee80211_tdls_data *tf = (void *)skb->data;
+ struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
+ int baselen = offsetof(typeof(*tf), u.chan_switch_req.variable);
+ struct ieee80211_tdls_ch_sw_params params = {};
+ int ret = 0;
+
+ params.action_code = WLAN_TDLS_CHANNEL_SWITCH_REQUEST;
+ params.timestamp = rx_status->device_timestamp;
+
+ if (skb->len < baselen) {
+ tdls_dbg(sdata, "TDLS channel switch req too short: %d\n",
+ skb->len);
+ return -EINVAL;
+ }
+
+ target_channel = tf->u.chan_switch_req.target_channel;
+ oper_class = tf->u.chan_switch_req.oper_class;
+
+ /*
+ * We can't easily infer the channel band. The operating class is
+ * ambiguous - there are multiple tables (US/Europe/JP/Global). The
+ * solution here is to treat channels with number >14 as 5GHz ones,
+ * and specifically check for the (oper_class, channel) combinations
+ * where this doesn't hold. These are thankfully unique according to
+ * IEEE802.11-2012.
+ * We consider only the 2GHz and 5GHz bands and 20MHz+ channels as
+ * valid here.
+ */
+ if ((oper_class == 112 || oper_class == 2 || oper_class == 3 ||
+ oper_class == 4 || oper_class == 5 || oper_class == 6) &&
+ target_channel < 14)
+ band = IEEE80211_BAND_5GHZ;
+ else
+ band = target_channel < 14 ? IEEE80211_BAND_2GHZ :
+ IEEE80211_BAND_5GHZ;
+
+ freq = ieee80211_channel_to_frequency(target_channel, band);
+ if (freq == 0) {
+ tdls_dbg(sdata, "Invalid channel in TDLS chan switch: %d\n",
+ target_channel);
+ return -EINVAL;
+ }
+
+ chan = ieee80211_get_channel(sdata->local->hw.wiphy, freq);
+ if (!chan) {
+ tdls_dbg(sdata,
+ "Unsupported channel for TDLS chan switch: %d\n",
+ target_channel);
+ return -EINVAL;
+ }
+
+ ieee802_11_parse_elems(tf->u.chan_switch_req.variable,
+ skb->len - baselen, false, &elems);
+ if (elems.parse_error) {
+ tdls_dbg(sdata, "Invalid IEs in TDLS channel switch req\n");
+ return -EINVAL;
+ }
+
+ if (!elems.ch_sw_timing || !elems.lnk_id) {
+ tdls_dbg(sdata, "TDLS channel switch req - missing IEs\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&local->sta_mtx);
+ sta = sta_info_get(sdata, tf->sa);
+ if (!sta || !test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) {
+ tdls_dbg(sdata, "TDLS chan switch from non-peer sta %pM\n",
+ tf->sa);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ params.sta = &sta->sta;
+
+ /* validate the initiator is set correctly */
+ local_initiator =
+ !memcmp(elems.lnk_id->init_sta, sdata->vif.addr, ETH_ALEN);
+ if (local_initiator == sta->sta.tdls_initiator) {
+ tdls_dbg(sdata, "TDLS chan switch invalid lnk-id initiator\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!sta->sta.ht_cap.ht_supported) {
+ chan_type = NL80211_CHAN_NO_HT;
+ } else if (!elems.sec_chan_offs) {
+ chan_type = NL80211_CHAN_HT20;
+ } else {
+ switch (elems.sec_chan_offs->sec_chan_offs) {
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
+ chan_type = NL80211_CHAN_HT40PLUS;
+ break;
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
+ chan_type = NL80211_CHAN_HT40MINUS;
+ break;
+ default:
+ chan_type = NL80211_CHAN_HT20;
+ break;
+ }
+ }
+
+ cfg80211_chandef_create(&chandef, chan, chan_type);
+ params.chandef = &chandef;
+
+ params.switch_time = le16_to_cpu(elems.ch_sw_timing->switch_time);
+ params.switch_timeout = le16_to_cpu(elems.ch_sw_timing->switch_timeout);
+
+ params.tmpl_skb =
+ ieee80211_tdls_ch_sw_resp_tmpl_get(sta,
+ &params.ch_sw_tm_ie);
+ if (!params.tmpl_skb) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ drv_tdls_recv_channel_switch(sdata->local, sdata, &params);
+
+ tdls_dbg(sdata,
+ "TDLS ch switch request received from %pM ch %d width %d\n",
+ tf->sa, params.chandef->chan->center_freq,
+ params.chandef->width);
+out:
+ mutex_unlock(&local->sta_mtx);
+ dev_kfree_skb_any(params.tmpl_skb);
+ return ret;
+}
+
+void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb)
+{
+ struct ieee80211_tdls_data *tf = (void *)skb->data;
+ struct wiphy *wiphy = sdata->local->hw.wiphy;
+
+ /* make sure the driver supports it */
+ if (!(wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH))
+ return;
+
+ /* we want to access the entire packet */
+ if (skb_linearize(skb))
+ return;
+ /*
+ * The packet/size was already validated by mac80211 Rx path, only look
+ * at the action type.
+ */
+ switch (tf->action_code) {
+ case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
+ ieee80211_process_tdls_channel_switch_req(sdata, skb);
+ break;
+ case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
+ ieee80211_process_tdls_channel_switch_resp(sdata, skb);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return;
+ }
+}
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index 38fae7ebe984..8e461a02c6a8 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -16,6 +16,7 @@
#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_NAMED_ASSIGN(s) memcpy(__entry->sta_addr, (s)->addr, ETH_ALEN)
#define STA_PR_FMT " sta:%pM"
#define STA_PR_ARG __entry->sta_addr
@@ -595,14 +596,33 @@ DEFINE_EVENT(local_sdata_evt, drv_sched_scan_stop,
TP_ARGS(local, sdata)
);
-DEFINE_EVENT(local_only_evt, drv_sw_scan_start,
- TP_PROTO(struct ieee80211_local *local),
- TP_ARGS(local)
+TRACE_EVENT(drv_sw_scan_start,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ const u8 *mac_addr),
+
+ TP_ARGS(local, sdata, mac_addr),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ __array(char, mac_addr, ETH_ALEN)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ memcpy(__entry->mac_addr, mac_addr, ETH_ALEN);
+ ),
+
+ TP_printk(LOCAL_PR_FMT ", " VIF_PR_FMT ", addr:%pM",
+ LOCAL_PR_ARG, VIF_PR_ARG, __entry->mac_addr)
);
-DEFINE_EVENT(local_only_evt, drv_sw_scan_complete,
- TP_PROTO(struct ieee80211_local *local),
- TP_ARGS(local)
+DEFINE_EVENT(local_sdata_evt, drv_sw_scan_complete,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata),
+ TP_ARGS(local, sdata)
);
TRACE_EVENT(drv_get_stats,
@@ -826,6 +846,13 @@ DEFINE_EVENT(sta_event, drv_sta_pre_rcu_remove,
TP_ARGS(local, sdata, sta)
);
+DEFINE_EVENT(sta_event, drv_sta_rate_tbl_update,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta),
+ TP_ARGS(local, sdata, sta)
+);
+
TRACE_EVENT(drv_conf_tx,
TP_PROTO(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
@@ -987,29 +1014,34 @@ TRACE_EVENT(drv_flush,
TRACE_EVENT(drv_channel_switch,
TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel_switch *ch_switch),
- TP_ARGS(local, ch_switch),
+ TP_ARGS(local, sdata, ch_switch),
TP_STRUCT__entry(
LOCAL_ENTRY
+ VIF_ENTRY
CHANDEF_ENTRY
__field(u64, timestamp)
+ __field(u32, device_timestamp)
__field(bool, block_tx)
__field(u8, count)
),
TP_fast_assign(
LOCAL_ASSIGN;
+ VIF_ASSIGN;
CHANDEF_ASSIGN(&ch_switch->chandef)
__entry->timestamp = ch_switch->timestamp;
+ __entry->device_timestamp = ch_switch->device_timestamp;
__entry->block_tx = ch_switch->block_tx;
__entry->count = ch_switch->count;
),
TP_printk(
- LOCAL_PR_FMT " new " CHANDEF_PR_FMT " count:%d",
- LOCAL_PR_ARG, CHANDEF_PR_ARG, __entry->count
+ LOCAL_PR_FMT VIF_PR_FMT " new " CHANDEF_PR_FMT " count:%d",
+ LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG, __entry->count
)
);
@@ -1557,9 +1589,26 @@ DEFINE_EVENT(local_sdata_evt, drv_stop_ap,
TP_ARGS(local, sdata)
);
-DEFINE_EVENT(local_only_evt, drv_restart_complete,
- TP_PROTO(struct ieee80211_local *local),
- TP_ARGS(local)
+TRACE_EVENT(drv_reconfig_complete,
+ TP_PROTO(struct ieee80211_local *local,
+ enum ieee80211_reconfig_type reconfig_type),
+ TP_ARGS(local, reconfig_type),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ __field(u8, reconfig_type)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ __entry->reconfig_type = reconfig_type;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT " reconfig_type:%d",
+ LOCAL_PR_ARG, __entry->reconfig_type
+ )
+
);
#if IS_ENABLED(CONFIG_IPV6)
@@ -1780,6 +1829,12 @@ TRACE_EVENT(api_cqm_rssi_notify,
)
);
+DEFINE_EVENT(local_sdata_evt, api_cqm_beacon_loss_notify,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata),
+ TP_ARGS(local, sdata)
+);
+
TRACE_EVENT(api_scan_completed,
TP_PROTO(struct ieee80211_local *local, bool aborted),
@@ -2106,6 +2161,175 @@ TRACE_EVENT(drv_channel_switch_beacon,
)
);
+TRACE_EVENT(drv_pre_channel_switch,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_channel_switch *ch_switch),
+
+ TP_ARGS(local, sdata, ch_switch),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ CHANDEF_ENTRY
+ __field(u64, timestamp)
+ __field(u32, device_timestamp)
+ __field(bool, block_tx)
+ __field(u8, count)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ CHANDEF_ASSIGN(&ch_switch->chandef)
+ __entry->timestamp = ch_switch->timestamp;
+ __entry->device_timestamp = ch_switch->device_timestamp;
+ __entry->block_tx = ch_switch->block_tx;
+ __entry->count = ch_switch->count;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT " prepare channel switch to "
+ CHANDEF_PR_FMT " count:%d block_tx:%d timestamp:%llu",
+ LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG, __entry->count,
+ __entry->block_tx, __entry->timestamp
+ )
+);
+
+DEFINE_EVENT(local_sdata_evt, drv_post_channel_switch,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata),
+ TP_ARGS(local, sdata)
+);
+
+TRACE_EVENT(drv_get_txpower,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ int dbm, int ret),
+
+ TP_ARGS(local, sdata, dbm, ret),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ __field(int, dbm)
+ __field(int, ret)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ __entry->dbm = dbm;
+ __entry->ret = ret;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT " dbm:%d ret:%d",
+ LOCAL_PR_ARG, VIF_PR_ARG, __entry->dbm, __entry->ret
+ )
+);
+
+TRACE_EVENT(drv_tdls_channel_switch,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta, u8 oper_class,
+ struct cfg80211_chan_def *chandef),
+
+ TP_ARGS(local, sdata, sta, oper_class, chandef),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ STA_ENTRY
+ __field(u8, oper_class)
+ CHANDEF_ENTRY
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ STA_ASSIGN;
+ __entry->oper_class = oper_class;
+ CHANDEF_ASSIGN(chandef)
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT " tdls channel switch to"
+ CHANDEF_PR_FMT " oper_class:%d " STA_PR_FMT,
+ LOCAL_PR_ARG, VIF_PR_ARG, CHANDEF_PR_ARG, __entry->oper_class,
+ STA_PR_ARG
+ )
+);
+
+TRACE_EVENT(drv_tdls_cancel_channel_switch,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_sta *sta),
+
+ TP_ARGS(local, sdata, sta),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ STA_ENTRY
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ STA_ASSIGN;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT
+ " tdls cancel channel switch with " STA_PR_FMT,
+ LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG
+ )
+);
+
+TRACE_EVENT(drv_tdls_recv_channel_switch,
+ TP_PROTO(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_tdls_ch_sw_params *params),
+
+ TP_ARGS(local, sdata, params),
+
+ TP_STRUCT__entry(
+ LOCAL_ENTRY
+ VIF_ENTRY
+ __field(u8, action_code)
+ STA_ENTRY
+ CHANDEF_ENTRY
+ __field(u32, status)
+ __field(bool, peer_initiator)
+ __field(u32, timestamp)
+ __field(u16, switch_time)
+ __field(u16, switch_timeout)
+ ),
+
+ TP_fast_assign(
+ LOCAL_ASSIGN;
+ VIF_ASSIGN;
+ STA_NAMED_ASSIGN(params->sta);
+ CHANDEF_ASSIGN(params->chandef)
+ __entry->peer_initiator = params->sta->tdls_initiator;
+ __entry->action_code = params->action_code;
+ __entry->status = params->status;
+ __entry->timestamp = params->timestamp;
+ __entry->switch_time = params->switch_time;
+ __entry->switch_timeout = params->switch_timeout;
+ ),
+
+ TP_printk(
+ LOCAL_PR_FMT VIF_PR_FMT " received tdls channel switch packet"
+ " action:%d status:%d time:%d switch time:%d switch"
+ " timeout:%d initiator: %d chan:" CHANDEF_PR_FMT STA_PR_FMT,
+ LOCAL_PR_ARG, VIF_PR_ARG, __entry->action_code, __entry->status,
+ __entry->timestamp, __entry->switch_time,
+ __entry->switch_timeout, __entry->peer_initiator,
+ CHANDEF_PR_ARG, STA_PR_ARG
+ )
+);
#ifdef CONFIG_MAC80211_MESSAGE_TRACING
#undef TRACE_SYSTEM
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 900632a250ec..058686a721a1 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -60,7 +60,7 @@ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx,
rcu_read_unlock();
/* assume HW handles this */
- if (tx->rate.flags & IEEE80211_TX_RC_MCS)
+ if (tx->rate.flags & (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS))
return 0;
/* uh huh? */
@@ -296,6 +296,9 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
*/
return TX_DROP;
+ if (tx->sdata->vif.type == NL80211_IFTYPE_OCB)
+ return TX_CONTINUE;
+
if (tx->sdata->vif.type == NL80211_IFTYPE_WDS)
return TX_CONTINUE;
@@ -1423,8 +1426,7 @@ EXPORT_SYMBOL(ieee80211_tx_prepare_skb);
* Returns false if the frame couldn't be transmitted but was queued instead.
*/
static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, bool txpending,
- enum ieee80211_band band)
+ struct sk_buff *skb, bool txpending)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_data tx;
@@ -1449,8 +1451,6 @@ static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata,
return true;
}
- info->band = band;
-
/* set up hw_queue value early */
if (!(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) ||
!(local->hw.flags & IEEE80211_HW_QUEUE_CONTROL))
@@ -1498,8 +1498,7 @@ static int ieee80211_skb_resize(struct ieee80211_sub_if_data *sdata,
return 0;
}
-void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
- enum ieee80211_band band)
+void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -1534,7 +1533,7 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
}
ieee80211_set_qos_hdr(sdata, skb);
- ieee80211_tx(sdata, skb, false, band);
+ ieee80211_tx(sdata, skb, false);
}
static bool ieee80211_parse_tx_radiotap(struct sk_buff *skb)
@@ -1754,7 +1753,8 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
sdata->vif.type))
goto fail_rcu;
- ieee80211_xmit(sdata, skb, chandef->chan->band);
+ info->band = chandef->chan->band;
+ ieee80211_xmit(sdata, skb);
rcu_read_unlock();
return NETDEV_TX_OK;
@@ -1784,23 +1784,26 @@ static void ieee80211_tx_latency_start_msrmnt(struct ieee80211_local *local,
}
/**
- * ieee80211_subif_start_xmit - netif start_xmit function for Ethernet-type
- * subinterfaces (wlan#, WDS, and VLAN interfaces)
- * @skb: packet to be sent
- * @dev: incoming interface
+ * ieee80211_build_hdr - build 802.11 header in the given frame
+ * @sdata: virtual interface to build the header for
+ * @skb: the skb to build the header in
+ * @info_flags: skb flags to set
+ *
+ * This function takes the skb with 802.3 header and reformats the header to
+ * the appropriate IEEE 802.11 header based on which interface the packet is
+ * being transmitted on.
+ *
+ * Note that this function also takes care of the TX status request and
+ * potential unsharing of the SKB - this needs to be interleaved with the
+ * header building.
*
- * Returns: NETDEV_TX_OK both on success and on failure. On failure skb will
- * be freed.
+ * The function requires the read-side RCU lock held
*
- * This function takes in an Ethernet header and encapsulates it with suitable
- * IEEE 802.11 header based on which interface the packet is coming in. The
- * encapsulated packet will then be passed to master interface, wlan#.11, for
- * transmission (through low-level driver).
+ * Returns: the (possibly reallocated) skb or an ERR_PTR() code
*/
-netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
- struct net_device *dev)
+static struct sk_buff *ieee80211_build_hdr(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, u32 info_flags)
{
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
struct ieee80211_tx_info *info;
int head_need;
@@ -1816,25 +1819,17 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
bool wme_sta = false, authorized = false, tdls_auth = false;
bool tdls_peer = false, tdls_setup_frame = false;
bool multicast;
- u32 info_flags = 0;
u16 info_id = 0;
struct ieee80211_chanctx_conf *chanctx_conf;
struct ieee80211_sub_if_data *ap_sdata;
enum ieee80211_band band;
-
- if (unlikely(skb->len < ETH_HLEN))
- goto fail;
+ int ret;
/* convert Ethernet header to proper 802.11 header (based on
* operation mode) */
ethertype = (skb->data[12] << 8) | skb->data[13];
fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA);
- rcu_read_lock();
-
- /* Measure frame arrival for Tx latency statistics calculation */
- ieee80211_tx_latency_start_msrmnt(local, skb);
-
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP_VLAN:
sta = rcu_dereference(sdata->u.vlan.sta);
@@ -1852,8 +1847,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
ap_sdata = container_of(sdata->bss, struct ieee80211_sub_if_data,
u.ap);
chanctx_conf = rcu_dereference(ap_sdata->vif.chanctx_conf);
- if (!chanctx_conf)
- goto fail_rcu;
+ if (!chanctx_conf) {
+ ret = -ENOTCONN;
+ goto free;
+ }
band = chanctx_conf->def.chan->band;
if (sta)
break;
@@ -1861,8 +1858,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
case NL80211_IFTYPE_AP:
if (sdata->vif.type == NL80211_IFTYPE_AP)
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
- if (!chanctx_conf)
- goto fail_rcu;
+ if (!chanctx_conf) {
+ ret = -ENOTCONN;
+ goto free;
+ }
fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
/* DA BSSID SA */
memcpy(hdr.addr1, skb->data, ETH_ALEN);
@@ -1949,8 +1948,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
}
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
- if (!chanctx_conf)
- goto fail_rcu;
+ if (!chanctx_conf) {
+ ret = -ENOTCONN;
+ goto free;
+ }
band = chanctx_conf->def.chan->band;
break;
#endif
@@ -1980,8 +1981,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
* of a link teardown after a TDLS sta is removed due to being
* unreachable.
*/
- if (tdls_peer && !tdls_auth && !tdls_setup_frame)
- goto fail_rcu;
+ if (tdls_peer && !tdls_auth && !tdls_setup_frame) {
+ ret = -EINVAL;
+ goto free;
+ }
/* send direct packets to authorized TDLS peers */
if (tdls_peer && tdls_auth) {
@@ -2009,8 +2012,23 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
hdrlen = 24;
}
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
- if (!chanctx_conf)
- goto fail_rcu;
+ if (!chanctx_conf) {
+ ret = -ENOTCONN;
+ goto free;
+ }
+ band = chanctx_conf->def.chan->band;
+ break;
+ case NL80211_IFTYPE_OCB:
+ /* DA SA BSSID */
+ memcpy(hdr.addr1, skb->data, ETH_ALEN);
+ memcpy(hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN);
+ eth_broadcast_addr(hdr.addr3);
+ hdrlen = 24;
+ chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+ if (!chanctx_conf) {
+ ret = -ENOTCONN;
+ goto free;
+ }
band = chanctx_conf->def.chan->band;
break;
case NL80211_IFTYPE_ADHOC:
@@ -2020,12 +2038,15 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
memcpy(hdr.addr3, sdata->u.ibss.bssid, ETH_ALEN);
hdrlen = 24;
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
- if (!chanctx_conf)
- goto fail_rcu;
+ if (!chanctx_conf) {
+ ret = -ENOTCONN;
+ goto free;
+ }
band = chanctx_conf->def.chan->band;
break;
default:
- goto fail_rcu;
+ ret = -EINVAL;
+ goto free;
}
/*
@@ -2057,17 +2078,19 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
* EAPOL frames from the local station.
*/
if (unlikely(!ieee80211_vif_is_mesh(&sdata->vif) &&
+ (sdata->vif.type != NL80211_IFTYPE_OCB) &&
!multicast && !authorized &&
(cpu_to_be16(ethertype) != sdata->control_port_protocol ||
!ether_addr_equal(sdata->vif.addr, skb->data + ETH_ALEN)))) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
net_info_ratelimited("%s: dropped frame to %pM (unauthorized port)\n",
- dev->name, hdr.addr1);
+ sdata->name, hdr.addr1);
#endif
I802_DEBUG_INC(local->tx_handlers_drop_unauth_port);
- goto fail_rcu;
+ ret = -EPERM;
+ goto free;
}
if (unlikely(!multicast && skb->sk &&
@@ -2104,8 +2127,10 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
skb = skb_clone(skb, GFP_ATOMIC);
kfree_skb(tmp_skb);
- if (!skb)
- goto fail_rcu;
+ if (!skb) {
+ ret = -ENOMEM;
+ goto free;
+ }
}
hdr.frame_control = fc;
@@ -2154,7 +2179,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
if (ieee80211_skb_resize(sdata, skb, head_need, true)) {
ieee80211_free_txskb(&local->hw, skb);
skb = NULL;
- goto fail_rcu;
+ return ERR_PTR(-ENOMEM);
}
}
@@ -2188,9 +2213,6 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
nh_pos += hdrlen;
h_pos += hdrlen;
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += skb->len;
-
/* Update skb pointers to various headers since this modified frame
* is going to go through Linux networking code that may potentially
* need things like pointer to IP header. */
@@ -2201,23 +2223,90 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
info = IEEE80211_SKB_CB(skb);
memset(info, 0, sizeof(*info));
- dev->trans_start = jiffies;
-
info->flags = info_flags;
info->ack_frame_id = info_id;
+ info->band = band;
- ieee80211_xmit(sdata, skb, band);
- rcu_read_unlock();
+ return skb;
+ free:
+ kfree_skb(skb);
+ return ERR_PTR(ret);
+}
- return NETDEV_TX_OK;
+void __ieee80211_subif_start_xmit(struct sk_buff *skb,
+ struct net_device *dev,
+ u32 info_flags)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
+
+ if (unlikely(skb->len < ETH_HLEN)) {
+ kfree_skb(skb);
+ return;
+ }
+
+ rcu_read_lock();
+
+ /* Measure frame arrival for Tx latency statistics calculation */
+ ieee80211_tx_latency_start_msrmnt(local, skb);
+
+ skb = ieee80211_build_hdr(sdata, skb, info_flags);
+ if (IS_ERR(skb))
+ goto out;
- fail_rcu:
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
+ dev->trans_start = jiffies;
+
+ ieee80211_xmit(sdata, skb);
+ out:
rcu_read_unlock();
- fail:
- dev_kfree_skb(skb);
+}
+
+/**
+ * ieee80211_subif_start_xmit - netif start_xmit function for 802.3 vifs
+ * @skb: packet to be sent
+ * @dev: incoming interface
+ *
+ * On failure skb will be freed.
+ */
+netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ __ieee80211_subif_start_xmit(skb, dev, 0);
return NETDEV_TX_OK;
}
+struct sk_buff *
+ieee80211_build_data_template(struct ieee80211_sub_if_data *sdata,
+ struct sk_buff *skb, u32 info_flags)
+{
+ struct ieee80211_hdr *hdr;
+ struct ieee80211_tx_data tx = {
+ .local = sdata->local,
+ .sdata = sdata,
+ };
+
+ rcu_read_lock();
+
+ skb = ieee80211_build_hdr(sdata, skb, info_flags);
+ if (IS_ERR(skb))
+ goto out;
+
+ hdr = (void *)skb->data;
+ tx.sta = sta_info_get(sdata, hdr->addr1);
+ tx.skb = skb;
+
+ if (ieee80211_tx_h_select_key(&tx) != TX_CONTINUE) {
+ rcu_read_unlock();
+ kfree_skb(skb);
+ return ERR_PTR(-EINVAL);
+ }
+
+out:
+ rcu_read_unlock();
+ return skb;
+}
/*
* ieee80211_clear_tx_pending may not be called in a context where
@@ -2257,8 +2346,8 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,
dev_kfree_skb(skb);
return true;
}
- result = ieee80211_tx(sdata, skb, true,
- chanctx_conf->def.chan->band);
+ info->band = chanctx_conf->def.chan->band;
+ result = ieee80211_tx(sdata, skb, true);
} else {
struct sk_buff_head skbs;
@@ -2872,19 +2961,16 @@ struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw,
EXPORT_SYMBOL(ieee80211_nullfunc_get);
struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif,
+ const u8 *src_addr,
const u8 *ssid, size_t ssid_len,
size_t tailroom)
{
- struct ieee80211_sub_if_data *sdata;
- struct ieee80211_local *local;
+ struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_hdr_3addr *hdr;
struct sk_buff *skb;
size_t ie_ssid_len;
u8 *pos;
- sdata = vif_to_sdata(vif);
- local = sdata->local;
ie_ssid_len = 2 + ssid_len;
skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*hdr) +
@@ -2899,7 +2985,7 @@ struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw,
hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_PROBE_REQ);
eth_broadcast_addr(hdr->addr1);
- memcpy(hdr->addr2, vif->addr, ETH_ALEN);
+ memcpy(hdr->addr2, src_addr, ETH_ALEN);
eth_broadcast_addr(hdr->addr3);
pos = skb_put(skb, ie_ssid_len);
@@ -3018,6 +3104,97 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw,
}
EXPORT_SYMBOL(ieee80211_get_buffered_bc);
+int ieee80211_reserve_tid(struct ieee80211_sta *pubsta, u8 tid)
+{
+ struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+ struct ieee80211_local *local = sdata->local;
+ int ret;
+ u32 queues;
+
+ lockdep_assert_held(&local->sta_mtx);
+
+ /* only some cases are supported right now */
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ break;
+ default:
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ if (WARN_ON(tid >= IEEE80211_NUM_UPS))
+ return -EINVAL;
+
+ if (sta->reserved_tid == tid) {
+ ret = 0;
+ goto out;
+ }
+
+ if (sta->reserved_tid != IEEE80211_TID_UNRESERVED) {
+ sdata_err(sdata, "TID reservation already active\n");
+ ret = -EALREADY;
+ goto out;
+ }
+
+ ieee80211_stop_vif_queues(sdata->local, sdata,
+ IEEE80211_QUEUE_STOP_REASON_RESERVE_TID);
+
+ synchronize_net();
+
+ /* Tear down BA sessions so we stop aggregating on this TID */
+ if (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION) {
+ set_sta_flag(sta, WLAN_STA_BLOCK_BA);
+ __ieee80211_stop_tx_ba_session(sta, tid,
+ AGG_STOP_LOCAL_REQUEST);
+ }
+
+ queues = BIT(sdata->vif.hw_queue[ieee802_1d_to_ac[tid]]);
+ __ieee80211_flush_queues(local, sdata, queues);
+
+ sta->reserved_tid = tid;
+
+ ieee80211_wake_vif_queues(local, sdata,
+ IEEE80211_QUEUE_STOP_REASON_RESERVE_TID);
+
+ if (local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION)
+ clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
+
+ ret = 0;
+ out:
+ return ret;
+}
+EXPORT_SYMBOL(ieee80211_reserve_tid);
+
+void ieee80211_unreserve_tid(struct ieee80211_sta *pubsta, u8 tid)
+{
+ struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
+
+ lockdep_assert_held(&sdata->local->sta_mtx);
+
+ /* only some cases are supported right now */
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ break;
+ default:
+ WARN_ON(1);
+ return;
+ }
+
+ if (tid != sta->reserved_tid) {
+ sdata_err(sdata, "TID to unreserve (%d) isn't reserved\n", tid);
+ return;
+ }
+
+ sta->reserved_tid = IEEE80211_TID_UNRESERVED;
+}
+EXPORT_SYMBOL(ieee80211_unreserve_tid);
+
void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb, int tid,
enum ieee80211_band band)
@@ -3039,6 +3216,7 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
* requirements are that we do not come into tx with bhs on.
*/
local_bh_disable();
- ieee80211_xmit(sdata, skb, band);
+ IEEE80211_SKB_CB(skb)->band = band;
+ ieee80211_xmit(sdata, skb);
local_bh_enable();
}
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 3c61060a4d2b..974ebe70f5b0 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -576,15 +576,19 @@ ieee80211_get_vif_queues(struct ieee80211_local *local,
return queues;
}
-void ieee80211_flush_queues(struct ieee80211_local *local,
- struct ieee80211_sub_if_data *sdata)
+void __ieee80211_flush_queues(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata,
+ unsigned int queues)
{
- unsigned int queues;
-
if (!local->ops->flush)
return;
- queues = ieee80211_get_vif_queues(local, sdata);
+ /*
+ * If no queue was set, or if the HW doesn't support
+ * IEEE80211_HW_QUEUE_CONTROL - flush all queues
+ */
+ if (!queues || !(local->hw.flags & IEEE80211_HW_QUEUE_CONTROL))
+ queues = ieee80211_get_vif_queues(local, sdata);
ieee80211_stop_queues_by_reason(&local->hw, queues,
IEEE80211_QUEUE_STOP_REASON_FLUSH,
@@ -597,6 +601,12 @@ void ieee80211_flush_queues(struct ieee80211_local *local,
false);
}
+void ieee80211_flush_queues(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *sdata)
+{
+ __ieee80211_flush_queues(local, sdata, 0);
+}
+
void ieee80211_stop_vif_queues(struct ieee80211_local *local,
struct ieee80211_sub_if_data *sdata,
enum queue_stop_reason reason)
@@ -693,6 +703,34 @@ void ieee80211_iterate_active_interfaces_rtnl(
}
EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces_rtnl);
+static void __iterate_stations(struct ieee80211_local *local,
+ void (*iterator)(void *data,
+ struct ieee80211_sta *sta),
+ void *data)
+{
+ struct sta_info *sta;
+
+ list_for_each_entry_rcu(sta, &local->sta_list, list) {
+ if (!sta->uploaded)
+ continue;
+
+ iterator(data, &sta->sta);
+ }
+}
+
+void ieee80211_iterate_stations_atomic(struct ieee80211_hw *hw,
+ void (*iterator)(void *data,
+ struct ieee80211_sta *sta),
+ void *data)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+
+ rcu_read_lock();
+ __iterate_stations(local, iterator, data);
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL_GPL(ieee80211_iterate_stations_atomic);
+
struct ieee80211_vif *wdev_to_ieee80211_vif(struct wireless_dev *wdev)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
@@ -803,6 +841,9 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
case WLAN_EID_SECONDARY_CHANNEL_OFFSET:
case WLAN_EID_WIDE_BW_CHANNEL_SWITCH:
case WLAN_EID_CHAN_SWITCH_PARAM:
+ case WLAN_EID_EXT_CAPABILITY:
+ case WLAN_EID_CHAN_SWITCH_TIMING:
+ case WLAN_EID_LINK_ID:
/*
* not listing WLAN_EID_CHANNEL_SWITCH_WRAPPER -- it seems possible
* that if the content gets bigger it might be needed more than once
@@ -822,6 +863,24 @@ u32 ieee802_11_parse_elems_crc(const u8 *start, size_t len, bool action,
elem_parse_failed = false;
switch (id) {
+ case WLAN_EID_LINK_ID:
+ if (elen + 2 != sizeof(struct ieee80211_tdls_lnkie)) {
+ elem_parse_failed = true;
+ break;
+ }
+ elems->lnk_id = (void *)(pos - 2);
+ break;
+ case WLAN_EID_CHAN_SWITCH_TIMING:
+ if (elen != sizeof(struct ieee80211_ch_switch_timing)) {
+ elem_parse_failed = true;
+ break;
+ }
+ elems->ch_sw_timing = (void *)pos;
+ break;
+ case WLAN_EID_EXT_CAPABILITY:
+ elems->ext_capab = pos;
+ elems->ext_capab_len = elen;
+ break;
case WLAN_EID_SSID:
elems->ssid = pos;
elems->ssid_len = elen;
@@ -1073,6 +1132,7 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
struct ieee80211_chanctx_conf *chanctx_conf;
int ac;
bool use_11b, enable_qos;
+ bool is_ocb; /* Use another EDCA parameters if dot11OCBActivated=true */
int aCWmin, aCWmax;
if (!local->ops->conf_tx)
@@ -1097,6 +1157,8 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
*/
enable_qos = (sdata->vif.type != NL80211_IFTYPE_STATION);
+ is_ocb = (sdata->vif.type == NL80211_IFTYPE_OCB);
+
/* Set defaults according to 802.11-2007 Table 7-37 */
aCWmax = 1023;
if (use_11b)
@@ -1118,7 +1180,10 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
qparam.cw_max = aCWmax;
qparam.cw_min = aCWmin;
qparam.txop = 0;
- qparam.aifs = 7;
+ if (is_ocb)
+ qparam.aifs = 9;
+ else
+ qparam.aifs = 7;
break;
/* never happens but let's not leave undefined */
default:
@@ -1126,21 +1191,32 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata,
qparam.cw_max = aCWmax;
qparam.cw_min = aCWmin;
qparam.txop = 0;
- qparam.aifs = 3;
+ if (is_ocb)
+ qparam.aifs = 6;
+ else
+ qparam.aifs = 3;
break;
case IEEE80211_AC_VI:
qparam.cw_max = aCWmin;
qparam.cw_min = (aCWmin + 1) / 2 - 1;
- if (use_11b)
+ if (is_ocb)
+ qparam.txop = 0;
+ else if (use_11b)
qparam.txop = 6016/32;
else
qparam.txop = 3008/32;
- qparam.aifs = 2;
+
+ if (is_ocb)
+ qparam.aifs = 3;
+ else
+ qparam.aifs = 2;
break;
case IEEE80211_AC_VO:
qparam.cw_max = (aCWmin + 1) / 2 - 1;
qparam.cw_min = (aCWmin + 1) / 4 - 1;
- if (use_11b)
+ if (is_ocb)
+ qparam.txop = 0;
+ else if (use_11b)
qparam.txop = 3264/32;
else
qparam.txop = 1504/32;
@@ -1263,6 +1339,7 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_local *local,
int ext_rates_len;
int shift;
u32 rate_flags;
+ bool have_80mhz = false;
*offset = 0;
@@ -1391,7 +1468,15 @@ static int ieee80211_build_preq_ies_band(struct ieee80211_local *local,
*offset = noffset;
}
- if (sband->vht_cap.vht_supported) {
+ /* Check if any channel in this sband supports at least 80 MHz */
+ for (i = 0; i < sband->n_channels; i++) {
+ if (!(sband->channels[i].flags & IEEE80211_CHAN_NO_80MHZ)) {
+ have_80mhz = true;
+ break;
+ }
+ }
+
+ if (sband->vht_cap.vht_supported && have_80mhz) {
if (end - pos < 2 + sizeof(struct ieee80211_vht_cap))
goto out_err;
pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap,
@@ -1447,7 +1532,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
};
struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
- u8 *dst, u32 ratemask,
+ const u8 *src, const u8 *dst,
+ u32 ratemask,
struct ieee80211_channel *chan,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len,
@@ -1472,8 +1558,8 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
else
chandef.chan = chan;
- skb = ieee80211_probereq_get(&local->hw, &sdata->vif,
- ssid, ssid_len, 100 + ie_len);
+ skb = ieee80211_probereq_get(&local->hw, src, ssid, ssid_len,
+ 100 + ie_len);
if (!skb)
return NULL;
@@ -1495,7 +1581,8 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata,
return skb;
}
-void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
+void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata,
+ const u8 *src, const u8 *dst,
const u8 *ssid, size_t ssid_len,
const u8 *ie, size_t ie_len,
u32 ratemask, bool directed, u32 tx_flags,
@@ -1503,7 +1590,7 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
{
struct sk_buff *skb;
- skb = ieee80211_build_probe_req(sdata, dst, ratemask, channel,
+ skb = ieee80211_build_probe_req(sdata, src, dst, ratemask, channel,
ssid, ssid_len,
ie, ie_len, directed);
if (skb) {
@@ -1645,6 +1732,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
int res, i;
bool reconfig_due_to_wowlan = false;
struct ieee80211_sub_if_data *sched_scan_sdata;
+ struct cfg80211_sched_scan_request *sched_scan_req;
bool sched_scan_stopped = false;
#ifdef CONFIG_PM
@@ -1813,6 +1901,10 @@ int ieee80211_reconfig(struct ieee80211_local *local)
ieee80211_bss_info_change_notify(sdata, changed);
sdata_unlock(sdata);
break;
+ case NL80211_IFTYPE_OCB:
+ changed |= BSS_CHANGED_OCB;
+ ieee80211_bss_info_change_notify(sdata, changed);
+ break;
case NL80211_IFTYPE_ADHOC:
changed |= BSS_CHANGED_IBSS;
/* fall through */
@@ -1931,13 +2023,15 @@ int ieee80211_reconfig(struct ieee80211_local *local)
mutex_lock(&local->mtx);
sched_scan_sdata = rcu_dereference_protected(local->sched_scan_sdata,
lockdep_is_held(&local->mtx));
- if (sched_scan_sdata && local->sched_scan_req)
+ sched_scan_req = rcu_dereference_protected(local->sched_scan_req,
+ lockdep_is_held(&local->mtx));
+ if (sched_scan_sdata && sched_scan_req)
/*
* Sched scan stopped, but we don't want to report it. Instead,
* we're trying to reschedule.
*/
if (__ieee80211_request_sched_scan_start(sched_scan_sdata,
- local->sched_scan_req))
+ sched_scan_req))
sched_scan_stopped = true;
mutex_unlock(&local->mtx);
@@ -1949,7 +2043,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
* We may want to change that later, however.
*/
if (!local->suspended || reconfig_due_to_wowlan)
- drv_restart_complete(local);
+ drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_RESTART);
if (!local->suspended)
return 0;
@@ -1960,6 +2054,9 @@ int ieee80211_reconfig(struct ieee80211_local *local)
mb();
local->resuming = false;
+ if (!reconfig_due_to_wowlan)
+ drv_reconfig_complete(local, IEEE80211_RECONFIG_TYPE_SUSPEND);
+
list_for_each_entry(sdata, &local->interfaces, list) {
if (!ieee80211_sdata_running(sdata))
continue;
@@ -2052,42 +2149,36 @@ static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
return false;
}
-/**
- * ieee80211_ie_split - split an IE buffer according to ordering
- *
- * @ies: the IE buffer
- * @ielen: the length of the IE buffer
- * @ids: an array with element IDs that are allowed before
- * the split
- * @n_ids: the size of the element ID array
- * @offset: offset where to start splitting in the buffer
- *
- * This function splits an IE buffer by updating the @offset
- * variable to point to the location where the buffer should be
- * split.
- *
- * It assumes that the given IE buffer is well-formed, this
- * has to be guaranteed by the caller!
- *
- * It also assumes that the IEs in the buffer are ordered
- * correctly, if not the result of using this function will not
- * be ordered correctly either, i.e. it does no reordering.
- *
- * The function returns the offset where the next part of the
- * buffer starts, which may be @ielen if the entire (remainder)
- * of the buffer should be used.
- */
-size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
- const u8 *ids, int n_ids, size_t offset)
+size_t ieee80211_ie_split_ric(const u8 *ies, size_t ielen,
+ const u8 *ids, int n_ids,
+ const u8 *after_ric, int n_after_ric,
+ size_t offset)
{
size_t pos = offset;
- while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos]))
- pos += 2 + ies[pos + 1];
+ while (pos < ielen && ieee80211_id_in_list(ids, n_ids, ies[pos])) {
+ if (ies[pos] == WLAN_EID_RIC_DATA && n_after_ric) {
+ pos += 2 + ies[pos + 1];
+
+ while (pos < ielen &&
+ !ieee80211_id_in_list(after_ric, n_after_ric,
+ ies[pos]))
+ pos += 2 + ies[pos + 1];
+ } else {
+ pos += 2 + ies[pos + 1];
+ }
+ }
return pos;
}
+size_t ieee80211_ie_split(const u8 *ies, size_t ielen,
+ const u8 *ids, int n_ids, size_t offset)
+{
+ return ieee80211_ie_split_ric(ies, ielen, ids, n_ids, NULL, 0, offset);
+}
+EXPORT_SYMBOL(ieee80211_ie_split);
+
size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset)
{
size_t pos = offset;
@@ -2526,11 +2617,23 @@ void ieee80211_dfs_radar_detected_work(struct work_struct *work)
struct ieee80211_local *local =
container_of(work, struct ieee80211_local, radar_detected_work);
struct cfg80211_chan_def chandef = local->hw.conf.chandef;
+ struct ieee80211_chanctx *ctx;
+ int num_chanctx = 0;
+
+ mutex_lock(&local->chanctx_mtx);
+ list_for_each_entry(ctx, &local->chanctx_list, list) {
+ if (ctx->replace_state == IEEE80211_CHANCTX_REPLACES_OTHER)
+ continue;
+
+ num_chanctx++;
+ chandef = ctx->conf.def;
+ }
+ mutex_unlock(&local->chanctx_mtx);
ieee80211_dfs_cac_cancel(local);
- if (local->use_chanctx)
- /* currently not handled */
+ if (num_chanctx > 1)
+ /* XXX: multi-channel is not supported yet */
WARN_ON(1);
else
cfg80211_radar_event(local->hw.wiphy, &chandef, GFP_KERNEL);
diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c
index 671ce0d27a80..bc9e8fc48785 100644
--- a/net/mac80211/vht.c
+++ b/net/mac80211/vht.c
@@ -287,6 +287,8 @@ enum ieee80211_sta_rx_bandwidth ieee80211_sta_cur_vht_bw(struct sta_info *sta)
/* fall through */
case NL80211_CHAN_WIDTH_20_NOHT:
case NL80211_CHAN_WIDTH_20:
+ bw = IEEE80211_STA_RX_BW_20;
+ break;
case NL80211_CHAN_WIDTH_40:
bw = sta->sta.ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 ?
IEEE80211_STA_RX_BW_40 : IEEE80211_STA_RX_BW_20;
diff --git a/net/mac80211/wep.c b/net/mac80211/wep.c
index 9181fb6d6437..a4220e92f0cc 100644
--- a/net/mac80211/wep.c
+++ b/net/mac80211/wep.c
@@ -111,8 +111,6 @@ static u8 *ieee80211_wep_add_iv(struct ieee80211_local *local,
(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE))
return newhdr + hdrlen;
- skb_set_network_header(skb, skb_network_offset(skb) +
- IEEE80211_WEP_IV_LEN);
ieee80211_wep_get_iv(local, keylen, keyidx, newhdr + hdrlen);
return newhdr + hdrlen;
}
diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c
index 3b873989992c..9eb0aee9105b 100644
--- a/net/mac80211/wme.c
+++ b/net/mac80211/wme.c
@@ -53,11 +53,49 @@ static int wme_downgrade_ac(struct sk_buff *skb)
}
}
+/**
+ * ieee80211_fix_reserved_tid - return the TID to use if this one is reserved
+ * @tid: the assumed-reserved TID
+ *
+ * Returns: the alternative TID to use, or 0 on error
+ */
+static inline u8 ieee80211_fix_reserved_tid(u8 tid)
+{
+ switch (tid) {
+ case 0:
+ return 3;
+ case 1:
+ return 2;
+ case 2:
+ return 1;
+ case 3:
+ return 0;
+ case 4:
+ return 5;
+ case 5:
+ return 4;
+ case 6:
+ return 7;
+ case 7:
+ return 6;
+ }
+
+ return 0;
+}
+
static u16 ieee80211_downgrade_queue(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb)
+ struct sta_info *sta, struct sk_buff *skb)
{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
/* in case we are a client verify acm is not set for this ac */
- while (unlikely(sdata->wmm_acm & BIT(skb->priority))) {
+ while (sdata->wmm_acm & BIT(skb->priority)) {
+ int ac = ieee802_1d_to_ac[skb->priority];
+
+ if (ifmgd->tx_tspec[ac].admitted_time &&
+ skb->priority == ifmgd->tx_tspec[ac].up)
+ return ac;
+
if (wme_downgrade_ac(skb)) {
/*
* This should not really happen. The AP has marked all
@@ -69,6 +107,10 @@ static u16 ieee80211_downgrade_queue(struct ieee80211_sub_if_data *sdata,
}
}
+ /* Check to see if this is a reserved TID */
+ if (sta && sta->reserved_tid == skb->priority)
+ skb->priority = ieee80211_fix_reserved_tid(skb->priority);
+
/* look up which queue to use for frames with this 1d tag */
return ieee802_1d_to_ac[skb->priority];
}
@@ -96,7 +138,7 @@ u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata,
p = ieee80211_get_qos_ctl(hdr);
skb->priority = *p & IEEE80211_QOS_CTL_TAG1D_MASK;
- return ieee80211_downgrade_queue(sdata, skb);
+ return ieee80211_downgrade_queue(sdata, NULL, skb);
}
/* Indicate which queue to use. */
@@ -108,6 +150,7 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
const u8 *ra = NULL;
bool qos = false;
struct mac80211_qos_map *qos_map;
+ u16 ret;
if (local->hw.queues < IEEE80211_NUM_ACS || skb->len < 6) {
skb->priority = 0; /* required for correct WPA/11i MIC */
@@ -134,11 +177,20 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
break;
#endif
case NL80211_IFTYPE_STATION:
+ /* might be a TDLS station */
+ sta = sta_info_get(sdata, skb->data);
+ if (sta)
+ qos = sta->sta.wme;
+
ra = sdata->u.mgd.bssid;
break;
case NL80211_IFTYPE_ADHOC:
ra = skb->data;
break;
+ case NL80211_IFTYPE_OCB:
+ /* all stations are required to support WME */
+ qos = true;
+ break;
default:
break;
}
@@ -148,27 +200,29 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata,
if (sta)
qos = sta->sta.wme;
}
- rcu_read_unlock();
if (!qos) {
skb->priority = 0; /* required for correct WPA/11i MIC */
- return IEEE80211_AC_BE;
+ ret = IEEE80211_AC_BE;
+ goto out;
}
if (skb->protocol == sdata->control_port_protocol) {
skb->priority = 7;
- return ieee80211_downgrade_queue(sdata, skb);
+ goto downgrade;
}
/* use the data classifier to determine what 802.1d tag the
* data frame has */
- rcu_read_lock();
qos_map = rcu_dereference(sdata->qos_map);
skb->priority = cfg80211_classify8021d(skb, qos_map ?
&qos_map->qos_map : NULL);
- rcu_read_unlock();
- return ieee80211_downgrade_queue(sdata, skb);
+ downgrade:
+ ret = ieee80211_downgrade_queue(sdata, sta, skb);
+ out:
+ rcu_read_unlock();
+ return ret;
}
/**
diff --git a/net/mac80211/wme.h b/net/mac80211/wme.h
index 7fea4bb8acbc..80151edc5195 100644
--- a/net/mac80211/wme.h
+++ b/net/mac80211/wme.h
@@ -13,8 +13,6 @@
#include <linux/netdevice.h>
#include "ieee80211_i.h"
-extern const int ieee802_1d_to_ac[8];
-
u16 ieee80211_select_queue_80211(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb,
struct ieee80211_hdr *hdr);
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index 983527a4c1ab..12398fde02e8 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -209,8 +209,6 @@ static int tkip_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
pos = skb_push(skb, IEEE80211_TKIP_IV_LEN);
memmove(pos, pos + IEEE80211_TKIP_IV_LEN, hdrlen);
- skb_set_network_header(skb, skb_network_offset(skb) +
- IEEE80211_TKIP_IV_LEN);
pos += hdrlen;
/* the HW only needs room for the IV, but not the actual IV */
@@ -434,8 +432,6 @@ static int ccmp_encrypt_skb(struct ieee80211_tx_data *tx, struct sk_buff *skb)
pos = skb_push(skb, IEEE80211_CCMP_HDR_LEN);
memmove(pos, pos + IEEE80211_CCMP_HDR_LEN, hdrlen);
- skb_set_network_header(skb, skb_network_offset(skb) +
- IEEE80211_CCMP_HDR_LEN);
/* the HW only needs room for the IV, but not the actual IV */
if (info->control.hw_key &&
@@ -575,7 +571,6 @@ ieee80211_crypto_cs_encrypt(struct ieee80211_tx_data *tx,
pos = skb_push(skb, cs->hdr_len);
memmove(pos, pos + cs->hdr_len, hdrlen);
- skb_set_network_header(skb, skb_network_offset(skb) + cs->hdr_len);
return TX_CONTINUE;
}
diff --git a/net/mac802154/Kconfig b/net/mac802154/Kconfig
index 1818a99b3081..aa462b480a39 100644
--- a/net/mac802154/Kconfig
+++ b/net/mac802154/Kconfig
@@ -16,5 +16,5 @@ config MAC802154
been tested yet!
If you plan to use HardMAC IEEE 802.15.4 devices, you can
- say N here. Alternatievly you can say M to compile it as
+ say N here. Alternatively you can say M to compile it as
module.
diff --git a/net/mac802154/Makefile b/net/mac802154/Makefile
index 9723d6f3f3e5..702d8b466317 100644
--- a/net/mac802154/Makefile
+++ b/net/mac802154/Makefile
@@ -1,5 +1,5 @@
obj-$(CONFIG_MAC802154) += mac802154.o
-mac802154-objs := ieee802154_dev.o rx.o tx.o mac_cmd.o mib.o \
- monitor.o wpan.o llsec.o
+mac802154-objs := main.o rx.o tx.o mac_cmd.o mib.o \
+ iface.o llsec.o util.o cfg.o
ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/mac802154/cfg.c b/net/mac802154/cfg.c
new file mode 100644
index 000000000000..c035708ada16
--- /dev/null
+++ b/net/mac802154/cfg.c
@@ -0,0 +1,210 @@
+/* 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.
+ *
+ * Authors:
+ * Alexander Aring <aar@pengutronix.de>
+ *
+ * Based on: net/mac80211/cfg.c
+ */
+
+#include <net/rtnetlink.h>
+#include <net/cfg802154.h>
+
+#include "ieee802154_i.h"
+#include "driver-ops.h"
+#include "cfg.h"
+
+static struct net_device *
+ieee802154_add_iface_deprecated(struct wpan_phy *wpan_phy,
+ const char *name, int type)
+{
+ struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
+ struct net_device *dev;
+
+ rtnl_lock();
+ dev = ieee802154_if_add(local, name, type,
+ cpu_to_le64(0x0000000000000000ULL));
+ rtnl_unlock();
+
+ return dev;
+}
+
+static void ieee802154_del_iface_deprecated(struct wpan_phy *wpan_phy,
+ struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+
+ ieee802154_if_remove(sdata);
+}
+
+static int
+ieee802154_add_iface(struct wpan_phy *phy, const char *name,
+ enum nl802154_iftype type, __le64 extended_addr)
+{
+ struct ieee802154_local *local = wpan_phy_priv(phy);
+ struct net_device *err;
+
+ err = ieee802154_if_add(local, name, type, extended_addr);
+ if (IS_ERR(err))
+ return PTR_ERR(err);
+
+ return 0;
+}
+
+static int
+ieee802154_del_iface(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev)
+{
+ ieee802154_if_remove(IEEE802154_WPAN_DEV_TO_SUB_IF(wpan_dev));
+
+ return 0;
+}
+
+static int
+ieee802154_set_channel(struct wpan_phy *wpan_phy, u8 page, u8 channel)
+{
+ struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
+ int ret;
+
+ ASSERT_RTNL();
+
+ /* check if phy support this setting */
+ if (!(wpan_phy->channels_supported[page] & BIT(channel)))
+ return -EINVAL;
+
+ ret = drv_set_channel(local, page, channel);
+ if (!ret) {
+ wpan_phy->current_page = page;
+ wpan_phy->current_channel = channel;
+ }
+
+ return ret;
+}
+
+static int
+ieee802154_set_pan_id(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+ __le16 pan_id)
+{
+ ASSERT_RTNL();
+
+ /* TODO
+ * I am not sure about to check here on broadcast pan_id.
+ * Broadcast is a valid setting, comment from 802.15.4:
+ * If this value is 0xffff, the device is not associated.
+ *
+ * This could useful to simple deassociate an device.
+ */
+ if (pan_id == cpu_to_le16(IEEE802154_PAN_ID_BROADCAST))
+ return -EINVAL;
+
+ wpan_dev->pan_id = pan_id;
+ return 0;
+}
+
+static int
+ieee802154_set_backoff_exponent(struct wpan_phy *wpan_phy,
+ struct wpan_dev *wpan_dev,
+ u8 min_be, u8 max_be)
+{
+ struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
+
+ ASSERT_RTNL();
+
+ if (!(local->hw.flags & IEEE802154_HW_CSMA_PARAMS))
+ return -EOPNOTSUPP;
+
+ wpan_dev->min_be = min_be;
+ wpan_dev->max_be = max_be;
+ return 0;
+}
+
+static int
+ieee802154_set_short_addr(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+ __le16 short_addr)
+{
+ ASSERT_RTNL();
+
+ /* TODO
+ * I am not sure about to check here on broadcast short_addr.
+ * Broadcast is a valid setting, comment from 802.15.4:
+ * A value of 0xfffe indicates that the device has
+ * associated but has not been allocated an address. A
+ * value of 0xffff indicates that the device does not
+ * have a short address.
+ *
+ * I think we should allow to set these settings but
+ * don't allow to allow socket communication with it.
+ */
+ if (short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC) ||
+ short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_BROADCAST))
+ return -EINVAL;
+
+ wpan_dev->short_addr = short_addr;
+ return 0;
+}
+
+static int
+ieee802154_set_max_csma_backoffs(struct wpan_phy *wpan_phy,
+ struct wpan_dev *wpan_dev,
+ u8 max_csma_backoffs)
+{
+ struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
+
+ ASSERT_RTNL();
+
+ if (!(local->hw.flags & IEEE802154_HW_CSMA_PARAMS))
+ return -EOPNOTSUPP;
+
+ wpan_dev->csma_retries = max_csma_backoffs;
+ return 0;
+}
+
+static int
+ieee802154_set_max_frame_retries(struct wpan_phy *wpan_phy,
+ struct wpan_dev *wpan_dev,
+ s8 max_frame_retries)
+{
+ struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
+
+ ASSERT_RTNL();
+
+ if (!(local->hw.flags & IEEE802154_HW_FRAME_RETRIES))
+ return -EOPNOTSUPP;
+
+ wpan_dev->frame_retries = max_frame_retries;
+ return 0;
+}
+
+static int
+ieee802154_set_lbt_mode(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+ bool mode)
+{
+ struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
+
+ ASSERT_RTNL();
+
+ if (!(local->hw.flags & IEEE802154_HW_LBT))
+ return -EOPNOTSUPP;
+
+ wpan_dev->lbt = mode;
+ return 0;
+}
+
+const struct cfg802154_ops mac802154_config_ops = {
+ .add_virtual_intf_deprecated = ieee802154_add_iface_deprecated,
+ .del_virtual_intf_deprecated = ieee802154_del_iface_deprecated,
+ .add_virtual_intf = ieee802154_add_iface,
+ .del_virtual_intf = ieee802154_del_iface,
+ .set_channel = ieee802154_set_channel,
+ .set_pan_id = ieee802154_set_pan_id,
+ .set_short_addr = ieee802154_set_short_addr,
+ .set_backoff_exponent = ieee802154_set_backoff_exponent,
+ .set_max_csma_backoffs = ieee802154_set_max_csma_backoffs,
+ .set_max_frame_retries = ieee802154_set_max_frame_retries,
+ .set_lbt_mode = ieee802154_set_lbt_mode,
+};
diff --git a/net/mac802154/cfg.h b/net/mac802154/cfg.h
new file mode 100644
index 000000000000..e2718f981e82
--- /dev/null
+++ b/net/mac802154/cfg.h
@@ -0,0 +1,9 @@
+/* mac802154 configuration hooks for cfg802154
+ */
+
+#ifndef __CFG_H
+#define __CFG_H
+
+extern const struct cfg802154_ops mac802154_config_ops;
+
+#endif /* __CFG_H */
diff --git a/net/mac802154/driver-ops.h b/net/mac802154/driver-ops.h
new file mode 100644
index 000000000000..f21e864613d0
--- /dev/null
+++ b/net/mac802154/driver-ops.h
@@ -0,0 +1,222 @@
+#ifndef __MAC802154_DRVIER_OPS
+#define __MAC802154_DRIVER_OPS
+
+#include <linux/types.h>
+#include <linux/rtnetlink.h>
+
+#include <net/mac802154.h>
+
+#include "ieee802154_i.h"
+
+static inline int
+drv_xmit_async(struct ieee802154_local *local, struct sk_buff *skb)
+{
+ return local->ops->xmit_async(&local->hw, skb);
+}
+
+static inline int
+drv_xmit_sync(struct ieee802154_local *local, struct sk_buff *skb)
+{
+ /* don't allow other operations while sync xmit */
+ ASSERT_RTNL();
+
+ might_sleep();
+
+ return local->ops->xmit_sync(&local->hw, skb);
+}
+
+static inline int drv_start(struct ieee802154_local *local)
+{
+ might_sleep();
+
+ local->started = true;
+ smp_mb();
+
+ return local->ops->start(&local->hw);
+}
+
+static inline void drv_stop(struct ieee802154_local *local)
+{
+ might_sleep();
+
+ local->ops->stop(&local->hw);
+
+ /* 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_set_channel(struct ieee802154_local *local, u8 page, u8 channel)
+{
+ might_sleep();
+
+ return local->ops->set_channel(&local->hw, page, channel);
+}
+
+static inline int drv_set_tx_power(struct ieee802154_local *local, s8 dbm)
+{
+ might_sleep();
+
+ if (!local->ops->set_txpower) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ return local->ops->set_txpower(&local->hw, dbm);
+}
+
+static inline int drv_set_cca_mode(struct ieee802154_local *local, u8 cca_mode)
+{
+ might_sleep();
+
+ if (!local->ops->set_cca_mode) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ return local->ops->set_cca_mode(&local->hw, cca_mode);
+}
+
+static inline int drv_set_lbt_mode(struct ieee802154_local *local, bool mode)
+{
+ might_sleep();
+
+ if (!local->ops->set_lbt) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ return local->ops->set_lbt(&local->hw, mode);
+}
+
+static inline int
+drv_set_cca_ed_level(struct ieee802154_local *local, s32 ed_level)
+{
+ might_sleep();
+
+ if (!local->ops->set_cca_ed_level) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ return local->ops->set_cca_ed_level(&local->hw, ed_level);
+}
+
+static inline int drv_set_pan_id(struct ieee802154_local *local, __le16 pan_id)
+{
+ struct ieee802154_hw_addr_filt filt;
+
+ might_sleep();
+
+ if (!local->ops->set_hw_addr_filt) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ filt.pan_id = pan_id;
+
+ return local->ops->set_hw_addr_filt(&local->hw, &filt,
+ IEEE802154_AFILT_PANID_CHANGED);
+}
+
+static inline int
+drv_set_extended_addr(struct ieee802154_local *local, __le64 extended_addr)
+{
+ struct ieee802154_hw_addr_filt filt;
+
+ might_sleep();
+
+ if (!local->ops->set_hw_addr_filt) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ filt.ieee_addr = extended_addr;
+
+ return local->ops->set_hw_addr_filt(&local->hw, &filt,
+ IEEE802154_AFILT_IEEEADDR_CHANGED);
+}
+
+static inline int
+drv_set_short_addr(struct ieee802154_local *local, __le16 short_addr)
+{
+ struct ieee802154_hw_addr_filt filt;
+
+ might_sleep();
+
+ if (!local->ops->set_hw_addr_filt) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ filt.short_addr = short_addr;
+
+ return local->ops->set_hw_addr_filt(&local->hw, &filt,
+ IEEE802154_AFILT_SADDR_CHANGED);
+}
+
+static inline int
+drv_set_pan_coord(struct ieee802154_local *local, bool is_coord)
+{
+ struct ieee802154_hw_addr_filt filt;
+
+ might_sleep();
+
+ if (!local->ops->set_hw_addr_filt) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ filt.pan_coord = is_coord;
+
+ return local->ops->set_hw_addr_filt(&local->hw, &filt,
+ IEEE802154_AFILT_PANC_CHANGED);
+}
+
+static inline int
+drv_set_csma_params(struct ieee802154_local *local, u8 min_be, u8 max_be,
+ u8 max_csma_backoffs)
+{
+ might_sleep();
+
+ if (!local->ops->set_csma_params) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ return local->ops->set_csma_params(&local->hw, min_be, max_be,
+ max_csma_backoffs);
+}
+
+static inline int
+drv_set_max_frame_retries(struct ieee802154_local *local, s8 max_frame_retries)
+{
+ might_sleep();
+
+ if (!local->ops->set_frame_retries) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ return local->ops->set_frame_retries(&local->hw, max_frame_retries);
+}
+
+static inline int
+drv_set_promiscuous_mode(struct ieee802154_local *local, bool on)
+{
+ might_sleep();
+
+ if (!local->ops->set_promiscuous_mode) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ return local->ops->set_promiscuous_mode(&local->hw, on);
+}
+
+#endif /* __MAC802154_DRVIER_OPS */
diff --git a/net/mac802154/ieee802154_dev.c b/net/mac802154/ieee802154_dev.c
deleted file mode 100644
index b36b2b996578..000000000000
--- a/net/mac802154/ieee802154_dev.c
+++ /dev/null
@@ -1,415 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Siemens AG
- *
- * Written by:
- * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
- *
- * Based on the code from 'linux-zigbee.sourceforge.net' project.
- *
- * 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/netdevice.h>
-
-#include <net/netlink.h>
-#include <linux/nl802154.h>
-#include <net/mac802154.h>
-#include <net/ieee802154_netdev.h>
-#include <net/route.h>
-#include <net/wpan-phy.h>
-
-#include "mac802154.h"
-
-int mac802154_slave_open(struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct mac802154_sub_if_data *subif;
- struct mac802154_priv *ipriv = priv->hw;
- int res = 0;
-
- ASSERT_RTNL();
-
- if (priv->type == IEEE802154_DEV_WPAN) {
- mutex_lock(&priv->hw->slaves_mtx);
- list_for_each_entry(subif, &priv->hw->slaves, list) {
- if (subif != priv && subif->type == priv->type &&
- subif->running) {
- mutex_unlock(&priv->hw->slaves_mtx);
- return -EBUSY;
- }
- }
- mutex_unlock(&priv->hw->slaves_mtx);
- }
-
- mutex_lock(&priv->hw->slaves_mtx);
- priv->running = true;
- mutex_unlock(&priv->hw->slaves_mtx);
-
- if (ipriv->open_count++ == 0) {
- res = ipriv->ops->start(&ipriv->hw);
- WARN_ON(res);
- if (res)
- goto err;
- }
-
- if (ipriv->ops->ieee_addr) {
- __le64 addr = ieee802154_devaddr_from_raw(dev->dev_addr);
-
- res = ipriv->ops->ieee_addr(&ipriv->hw, addr);
- WARN_ON(res);
- if (res)
- goto err;
- mac802154_dev_set_ieee_addr(dev);
- }
-
- netif_start_queue(dev);
- return 0;
-err:
- priv->hw->open_count--;
-
- return res;
-}
-
-int mac802154_slave_close(struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct mac802154_priv *ipriv = priv->hw;
-
- ASSERT_RTNL();
-
- netif_stop_queue(dev);
-
- mutex_lock(&priv->hw->slaves_mtx);
- priv->running = false;
- mutex_unlock(&priv->hw->slaves_mtx);
-
- if (!--ipriv->open_count)
- ipriv->ops->stop(&ipriv->hw);
-
- return 0;
-}
-
-static int
-mac802154_netdev_register(struct wpan_phy *phy, struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv;
- struct mac802154_priv *ipriv;
- int err;
-
- ipriv = wpan_phy_priv(phy);
-
- priv = netdev_priv(dev);
- priv->dev = dev;
- priv->hw = ipriv;
-
- dev->needed_headroom = ipriv->hw.extra_tx_headroom;
-
- SET_NETDEV_DEV(dev, &ipriv->phy->dev);
-
- mutex_lock(&ipriv->slaves_mtx);
- if (!ipriv->running) {
- mutex_unlock(&ipriv->slaves_mtx);
- return -ENODEV;
- }
- mutex_unlock(&ipriv->slaves_mtx);
-
- err = register_netdev(dev);
- if (err < 0)
- return err;
-
- rtnl_lock();
- mutex_lock(&ipriv->slaves_mtx);
- list_add_tail_rcu(&priv->list, &ipriv->slaves);
- mutex_unlock(&ipriv->slaves_mtx);
- rtnl_unlock();
-
- return 0;
-}
-
-static void
-mac802154_del_iface(struct wpan_phy *phy, struct net_device *dev)
-{
- struct mac802154_sub_if_data *sdata;
-
- ASSERT_RTNL();
-
- sdata = netdev_priv(dev);
-
- BUG_ON(sdata->hw->phy != phy);
-
- mutex_lock(&sdata->hw->slaves_mtx);
- list_del_rcu(&sdata->list);
- mutex_unlock(&sdata->hw->slaves_mtx);
-
- synchronize_rcu();
- unregister_netdevice(sdata->dev);
-}
-
-static struct net_device *
-mac802154_add_iface(struct wpan_phy *phy, const char *name, int type)
-{
- struct net_device *dev;
- int err = -ENOMEM;
-
- switch (type) {
- case IEEE802154_DEV_MONITOR:
- dev = alloc_netdev(sizeof(struct mac802154_sub_if_data),
- name, NET_NAME_UNKNOWN,
- mac802154_monitor_setup);
- break;
- case IEEE802154_DEV_WPAN:
- dev = alloc_netdev(sizeof(struct mac802154_sub_if_data),
- name, NET_NAME_UNKNOWN,
- mac802154_wpan_setup);
- break;
- default:
- dev = NULL;
- err = -EINVAL;
- break;
- }
- if (!dev)
- goto err;
-
- err = mac802154_netdev_register(phy, dev);
- if (err)
- goto err_free;
-
- dev_hold(dev); /* we return an incremented device refcount */
- return dev;
-
-err_free:
- free_netdev(dev);
-err:
- return ERR_PTR(err);
-}
-
-static int mac802154_set_txpower(struct wpan_phy *phy, int db)
-{
- struct mac802154_priv *priv = wpan_phy_priv(phy);
-
- return priv->ops->set_txpower(&priv->hw, db);
-}
-
-static int mac802154_set_lbt(struct wpan_phy *phy, bool on)
-{
- struct mac802154_priv *priv = wpan_phy_priv(phy);
-
- return priv->ops->set_lbt(&priv->hw, on);
-}
-
-static int mac802154_set_cca_mode(struct wpan_phy *phy, u8 mode)
-{
- struct mac802154_priv *priv = wpan_phy_priv(phy);
-
- return priv->ops->set_cca_mode(&priv->hw, mode);
-}
-
-static int mac802154_set_cca_ed_level(struct wpan_phy *phy, s32 level)
-{
- struct mac802154_priv *priv = wpan_phy_priv(phy);
-
- return priv->ops->set_cca_ed_level(&priv->hw, level);
-}
-
-static int mac802154_set_csma_params(struct wpan_phy *phy, u8 min_be,
- u8 max_be, u8 retries)
-{
- struct mac802154_priv *priv = wpan_phy_priv(phy);
-
- return priv->ops->set_csma_params(&priv->hw, min_be, max_be, retries);
-}
-
-static int mac802154_set_frame_retries(struct wpan_phy *phy, s8 retries)
-{
- struct mac802154_priv *priv = wpan_phy_priv(phy);
-
- return priv->ops->set_frame_retries(&priv->hw, retries);
-}
-
-struct ieee802154_dev *
-ieee802154_alloc_device(size_t priv_data_len, struct ieee802154_ops *ops)
-{
- struct wpan_phy *phy;
- struct mac802154_priv *priv;
- size_t priv_size;
-
- if (!ops || !ops->xmit || !ops->ed || !ops->start ||
- !ops->stop || !ops->set_channel) {
- pr_err("undefined IEEE802.15.4 device operations\n");
- return NULL;
- }
-
- /* Ensure 32-byte alignment of our private data and hw private data.
- * We use the wpan_phy priv data for both our mac802154_priv and for
- * the driver's private data
- *
- * in memory it'll be like this:
- *
- * +-----------------------+
- * | struct wpan_phy |
- * +-----------------------+
- * | struct mac802154_priv |
- * +-----------------------+
- * | driver's private data |
- * +-----------------------+
- *
- * Due to ieee802154 layer isn't aware of driver and MAC structures,
- * so lets allign them here.
- */
-
- priv_size = ALIGN(sizeof(*priv), NETDEV_ALIGN) + priv_data_len;
-
- phy = wpan_phy_alloc(priv_size);
- if (!phy) {
- pr_err("failure to allocate master IEEE802.15.4 device\n");
- return NULL;
- }
-
- priv = wpan_phy_priv(phy);
- priv->phy = phy;
- priv->hw.phy = priv->phy;
- priv->hw.priv = (char *)priv + ALIGN(sizeof(*priv), NETDEV_ALIGN);
- priv->ops = ops;
-
- INIT_LIST_HEAD(&priv->slaves);
- mutex_init(&priv->slaves_mtx);
-
- return &priv->hw;
-}
-EXPORT_SYMBOL(ieee802154_alloc_device);
-
-void ieee802154_free_device(struct ieee802154_dev *hw)
-{
- struct mac802154_priv *priv = mac802154_to_priv(hw);
-
- BUG_ON(!list_empty(&priv->slaves));
-
- mutex_destroy(&priv->slaves_mtx);
-
- wpan_phy_free(priv->phy);
-}
-EXPORT_SYMBOL(ieee802154_free_device);
-
-int ieee802154_register_device(struct ieee802154_dev *dev)
-{
- struct mac802154_priv *priv = mac802154_to_priv(dev);
- int rc = -ENOSYS;
-
- if (dev->flags & IEEE802154_HW_TXPOWER) {
- if (!priv->ops->set_txpower)
- goto out;
-
- priv->phy->set_txpower = mac802154_set_txpower;
- }
-
- if (dev->flags & IEEE802154_HW_LBT) {
- if (!priv->ops->set_lbt)
- goto out;
-
- priv->phy->set_lbt = mac802154_set_lbt;
- }
-
- if (dev->flags & IEEE802154_HW_CCA_MODE) {
- if (!priv->ops->set_cca_mode)
- goto out;
-
- priv->phy->set_cca_mode = mac802154_set_cca_mode;
- }
-
- if (dev->flags & IEEE802154_HW_CCA_ED_LEVEL) {
- if (!priv->ops->set_cca_ed_level)
- goto out;
-
- priv->phy->set_cca_ed_level = mac802154_set_cca_ed_level;
- }
-
- if (dev->flags & IEEE802154_HW_CSMA_PARAMS) {
- if (!priv->ops->set_csma_params)
- goto out;
-
- priv->phy->set_csma_params = mac802154_set_csma_params;
- }
-
- if (dev->flags & IEEE802154_HW_FRAME_RETRIES) {
- if (!priv->ops->set_frame_retries)
- goto out;
-
- priv->phy->set_frame_retries = mac802154_set_frame_retries;
- }
-
- priv->dev_workqueue =
- create_singlethread_workqueue(wpan_phy_name(priv->phy));
- if (!priv->dev_workqueue) {
- rc = -ENOMEM;
- goto out;
- }
-
- wpan_phy_set_dev(priv->phy, priv->hw.parent);
-
- priv->phy->add_iface = mac802154_add_iface;
- priv->phy->del_iface = mac802154_del_iface;
-
- rc = wpan_phy_register(priv->phy);
- if (rc < 0)
- goto out_wq;
-
- rtnl_lock();
-
- mutex_lock(&priv->slaves_mtx);
- priv->running = MAC802154_DEVICE_RUN;
- mutex_unlock(&priv->slaves_mtx);
-
- rtnl_unlock();
-
- return 0;
-
-out_wq:
- destroy_workqueue(priv->dev_workqueue);
-out:
- return rc;
-}
-EXPORT_SYMBOL(ieee802154_register_device);
-
-void ieee802154_unregister_device(struct ieee802154_dev *dev)
-{
- struct mac802154_priv *priv = mac802154_to_priv(dev);
- struct mac802154_sub_if_data *sdata, *next;
-
- flush_workqueue(priv->dev_workqueue);
- destroy_workqueue(priv->dev_workqueue);
-
- rtnl_lock();
-
- mutex_lock(&priv->slaves_mtx);
- priv->running = MAC802154_DEVICE_STOPPED;
- mutex_unlock(&priv->slaves_mtx);
-
- list_for_each_entry_safe(sdata, next, &priv->slaves, list) {
- mutex_lock(&sdata->hw->slaves_mtx);
- list_del(&sdata->list);
- mutex_unlock(&sdata->hw->slaves_mtx);
-
- unregister_netdevice(sdata->dev);
- }
-
- rtnl_unlock();
-
- wpan_phy_unregister(priv->phy);
-}
-EXPORT_SYMBOL(ieee802154_unregister_device);
-
-MODULE_DESCRIPTION("IEEE 802.15.4 implementation");
-MODULE_LICENSE("GPL v2");
diff --git a/net/mac802154/mac802154.h b/net/mac802154/ieee802154_i.h
index 762a6f849c6b..bebd70ffc7a3 100644
--- a/net/mac802154/mac802154.h
+++ b/net/mac802154/ieee802154_i.h
@@ -10,29 +10,28 @@
* 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.
- *
* Written by:
* Pavel Smolenskiy <pavel.smolenskiy@gmail.com>
* Maxim Gorbachyov <maxim.gorbachev@siemens.com>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
* Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
*/
-#ifndef MAC802154_H
-#define MAC802154_H
+#ifndef __IEEE802154_I_H
+#define __IEEE802154_I_H
#include <linux/mutex.h>
+#include <linux/hrtimer.h>
+#include <net/cfg802154.h>
#include <net/mac802154.h>
+#include <net/nl802154.h>
#include <net/ieee802154_netdev.h>
#include "llsec.h"
/* mac802154 device private data */
-struct mac802154_priv {
- struct ieee802154_dev hw;
- struct ieee802154_ops *ops;
+struct ieee802154_local {
+ struct ieee802154_hw hw;
+ const struct ieee802154_ops *ops;
/* ieee802154 phy */
struct wpan_phy *phy;
@@ -46,23 +45,29 @@ struct mac802154_priv {
*
* So atomic readers can use any of this protection methods.
*/
- struct list_head slaves;
- struct mutex slaves_mtx;
+ struct list_head interfaces;
+ struct mutex iflist_mtx;
/* This one is used for scanning and other jobs not to be interfered
* with serial driver.
*/
- struct workqueue_struct *dev_workqueue;
+ struct workqueue_struct *workqueue;
- /* SoftMAC device is registered and running. One can add subinterfaces.
- * This flag should be modified under slaves_mtx and RTNL, so you can
- * read them using any of protection methods.
- */
- bool running;
+ struct hrtimer ifs_timer;
+
+ bool started;
+
+ struct tasklet_struct tasklet;
+ struct sk_buff_head skb_queue;
+};
+
+enum {
+ IEEE802154_RX_MSG = 1,
};
-#define MAC802154_DEVICE_STOPPED 0x00
-#define MAC802154_DEVICE_RUN 0x01
+enum ieee802154_sdata_state_bits {
+ SDATA_STATE_RUNNING,
+};
/* Slave interface definition.
*
@@ -70,72 +75,74 @@ struct mac802154_priv {
* Each ieee802154 device/transceiver may have several slaves and able
* to be associated with several networks at the same time.
*/
-struct mac802154_sub_if_data {
+struct ieee802154_sub_if_data {
struct list_head list; /* the ieee802154_priv->slaves list */
- struct mac802154_priv *hw;
+ struct wpan_dev wpan_dev;
+
+ struct ieee802154_local *local;
struct net_device *dev;
- int type;
- bool running;
+ unsigned long state;
+ char name[IFNAMSIZ];
spinlock_t mib_lock;
- __le16 pan_id;
- __le16 short_addr;
- __le64 extended_addr;
-
- u8 chan;
- u8 page;
-
- struct ieee802154_mac_params mac_params;
-
- /* MAC BSN field */
- u8 bsn;
- /* MAC DSN field */
- u8 dsn;
-
/* protects sec from concurrent access by netlink. access by
* encrypt/decrypt/header_create safe without additional protection.
*/
struct mutex sec_mtx;
struct mac802154_llsec sec;
+ /* must be last, dynamically sized area in this! */
+ struct ieee802154_vif vif;
};
-#define mac802154_to_priv(_hw) container_of(_hw, struct mac802154_priv, hw)
-
#define MAC802154_CHAN_NONE 0xff /* No channel is assigned */
-extern struct ieee802154_reduced_mlme_ops mac802154_mlme_reduced;
-extern struct ieee802154_mlme_ops mac802154_mlme_wpan;
-
-int mac802154_slave_open(struct net_device *dev);
-int mac802154_slave_close(struct net_device *dev);
+/* utility functions/constants */
+extern const void *const mac802154_wpan_phy_privid; /* for wpan_phy privid */
+
+static inline struct ieee802154_local *
+hw_to_local(struct ieee802154_hw *hw)
+{
+ return container_of(hw, struct ieee802154_local, hw);
+}
+
+static inline struct ieee802154_sub_if_data *
+IEEE802154_DEV_TO_SUB_IF(const struct net_device *dev)
+{
+ return netdev_priv(dev);
+}
+
+static inline struct ieee802154_sub_if_data *
+IEEE802154_WPAN_DEV_TO_SUB_IF(struct wpan_dev *wpan_dev)
+{
+ return container_of(wpan_dev, struct ieee802154_sub_if_data, wpan_dev);
+}
+
+static inline bool
+ieee802154_sdata_running(struct ieee802154_sub_if_data *sdata)
+{
+ return test_bit(SDATA_STATE_RUNNING, &sdata->state);
+}
-void mac802154_monitors_rx(struct mac802154_priv *priv, struct sk_buff *skb);
-void mac802154_monitor_setup(struct net_device *dev);
-
-void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb);
-void mac802154_wpan_setup(struct net_device *dev);
+extern struct ieee802154_mlme_ops mac802154_mlme_wpan;
-netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb,
- u8 page, u8 chan);
+netdev_tx_t
+ieee802154_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev);
+netdev_tx_t
+ieee802154_subif_start_xmit(struct sk_buff *skb, struct net_device *dev);
+enum hrtimer_restart ieee802154_xmit_ifs_timer(struct hrtimer *timer);
/* MIB callbacks */
void mac802154_dev_set_short_addr(struct net_device *dev, __le16 val);
__le16 mac802154_dev_get_short_addr(const struct net_device *dev);
-void mac802154_dev_set_ieee_addr(struct net_device *dev);
__le16 mac802154_dev_get_pan_id(const struct net_device *dev);
void mac802154_dev_set_pan_id(struct net_device *dev, __le16 val);
void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan);
u8 mac802154_dev_get_dsn(const struct net_device *dev);
-int mac802154_set_mac_params(struct net_device *dev,
- const struct ieee802154_mac_params *params);
-void mac802154_get_mac_params(struct net_device *dev,
- struct ieee802154_mac_params *params);
-
int mac802154_get_params(struct net_device *dev,
struct ieee802154_llsec_params *params);
int mac802154_set_params(struct net_device *dev,
@@ -169,4 +176,13 @@ void mac802154_get_table(struct net_device *dev,
struct ieee802154_llsec_table **t);
void mac802154_unlock_table(struct net_device *dev);
-#endif /* MAC802154_H */
+/* interface handling */
+int ieee802154_iface_init(void);
+void ieee802154_iface_exit(void);
+void ieee802154_if_remove(struct ieee802154_sub_if_data *sdata);
+struct net_device *
+ieee802154_if_add(struct ieee802154_local *local, const char *name,
+ enum nl802154_iftype type, __le64 extended_addr);
+void ieee802154_remove_interfaces(struct ieee802154_local *local);
+
+#endif /* __IEEE802154_I_H */
diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c
new file mode 100644
index 000000000000..9ae893057dd7
--- /dev/null
+++ b/net/mac802154/iface.c
@@ -0,0 +1,586 @@
+/*
+ * Copyright 2007-2012 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.
+ *
+ * Written by:
+ * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+ * Sergey Lapin <slapin@ossfans.org>
+ * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
+ * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
+ */
+
+#include <linux/netdevice.h>
+#include <linux/module.h>
+#include <linux/if_arp.h>
+#include <linux/ieee802154.h>
+
+#include <net/nl802154.h>
+#include <net/mac802154.h>
+#include <net/ieee802154_netdev.h>
+#include <net/cfg802154.h>
+
+#include "ieee802154_i.h"
+#include "driver-ops.h"
+
+static int mac802154_wpan_update_llsec(struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
+ struct wpan_dev *wpan_dev = &sdata->wpan_dev;
+ int rc = 0;
+
+ if (ops->llsec) {
+ struct ieee802154_llsec_params params;
+ int changed = 0;
+
+ params.pan_id = wpan_dev->pan_id;
+ changed |= IEEE802154_LLSEC_PARAM_PAN_ID;
+
+ params.hwaddr = wpan_dev->extended_addr;
+ changed |= IEEE802154_LLSEC_PARAM_HWADDR;
+
+ rc = ops->llsec->set_params(dev, &params, changed);
+ }
+
+ return rc;
+}
+
+static int
+mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct wpan_dev *wpan_dev = &sdata->wpan_dev;
+ struct sockaddr_ieee802154 *sa =
+ (struct sockaddr_ieee802154 *)&ifr->ifr_addr;
+ int err = -ENOIOCTLCMD;
+
+ ASSERT_RTNL();
+
+ spin_lock_bh(&sdata->mib_lock);
+
+ switch (cmd) {
+ case SIOCGIFADDR:
+ {
+ u16 pan_id, short_addr;
+
+ pan_id = le16_to_cpu(wpan_dev->pan_id);
+ short_addr = le16_to_cpu(wpan_dev->short_addr);
+ if (pan_id == IEEE802154_PANID_BROADCAST ||
+ short_addr == IEEE802154_ADDR_BROADCAST) {
+ err = -EADDRNOTAVAIL;
+ break;
+ }
+
+ sa->family = AF_IEEE802154;
+ sa->addr.addr_type = IEEE802154_ADDR_SHORT;
+ sa->addr.pan_id = pan_id;
+ sa->addr.short_addr = short_addr;
+
+ err = 0;
+ break;
+ }
+ case SIOCSIFADDR:
+ if (netif_running(dev)) {
+ spin_unlock_bh(&sdata->mib_lock);
+ return -EBUSY;
+ }
+
+ dev_warn(&dev->dev,
+ "Using DEBUGing ioctl SIOCSIFADDR isn't recommended!\n");
+ if (sa->family != AF_IEEE802154 ||
+ sa->addr.addr_type != IEEE802154_ADDR_SHORT ||
+ sa->addr.pan_id == IEEE802154_PANID_BROADCAST ||
+ sa->addr.short_addr == IEEE802154_ADDR_BROADCAST ||
+ sa->addr.short_addr == IEEE802154_ADDR_UNDEF) {
+ err = -EINVAL;
+ break;
+ }
+
+ wpan_dev->pan_id = cpu_to_le16(sa->addr.pan_id);
+ wpan_dev->short_addr = cpu_to_le16(sa->addr.short_addr);
+
+ err = mac802154_wpan_update_llsec(dev);
+ break;
+ }
+
+ spin_unlock_bh(&sdata->mib_lock);
+ return err;
+}
+
+static int mac802154_wpan_mac_addr(struct net_device *dev, void *p)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct sockaddr *addr = p;
+ __le64 extended_addr;
+
+ if (netif_running(dev))
+ return -EBUSY;
+
+ ieee802154_be64_to_le64(&extended_addr, addr->sa_data);
+ if (!ieee802154_is_valid_extended_addr(extended_addr))
+ return -EINVAL;
+
+ memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+ sdata->wpan_dev.extended_addr = extended_addr;
+
+ return mac802154_wpan_update_llsec(dev);
+}
+
+static int mac802154_slave_open(struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct ieee802154_sub_if_data *subif;
+ struct ieee802154_local *local = sdata->local;
+ int res = 0;
+
+ ASSERT_RTNL();
+
+ if (sdata->vif.type == NL802154_IFTYPE_NODE) {
+ mutex_lock(&sdata->local->iflist_mtx);
+ list_for_each_entry(subif, &sdata->local->interfaces, list) {
+ if (subif != sdata &&
+ subif->vif.type == sdata->vif.type &&
+ ieee802154_sdata_running(subif)) {
+ mutex_unlock(&sdata->local->iflist_mtx);
+ return -EBUSY;
+ }
+ }
+ mutex_unlock(&sdata->local->iflist_mtx);
+ }
+
+ set_bit(SDATA_STATE_RUNNING, &sdata->state);
+
+ if (!local->open_count) {
+ res = drv_start(local);
+ WARN_ON(res);
+ if (res)
+ goto err;
+ }
+
+ local->open_count++;
+ netif_start_queue(dev);
+ return 0;
+err:
+ /* might already be clear but that doesn't matter */
+ clear_bit(SDATA_STATE_RUNNING, &sdata->state);
+
+ return res;
+}
+
+static int mac802154_wpan_open(struct net_device *dev)
+{
+ int rc;
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct ieee802154_local *local = sdata->local;
+ struct wpan_dev *wpan_dev = &sdata->wpan_dev;
+ struct wpan_phy *phy = sdata->local->phy;
+
+ rc = mac802154_slave_open(dev);
+ if (rc < 0)
+ return rc;
+
+ mutex_lock(&phy->pib_lock);
+
+ if (local->hw.flags & IEEE802154_HW_PROMISCUOUS) {
+ rc = drv_set_promiscuous_mode(local,
+ wpan_dev->promiscuous_mode);
+ if (rc < 0)
+ goto out;
+ }
+
+ if (local->hw.flags & IEEE802154_HW_AFILT) {
+ rc = drv_set_pan_id(local, wpan_dev->pan_id);
+ if (rc < 0)
+ goto out;
+
+ rc = drv_set_extended_addr(local, wpan_dev->extended_addr);
+ if (rc < 0)
+ goto out;
+
+ rc = drv_set_short_addr(local, wpan_dev->short_addr);
+ if (rc < 0)
+ goto out;
+ }
+
+ if (local->hw.flags & IEEE802154_HW_LBT) {
+ rc = drv_set_lbt_mode(local, wpan_dev->lbt);
+ if (rc < 0)
+ goto out;
+ }
+
+ if (local->hw.flags & IEEE802154_HW_CSMA_PARAMS) {
+ rc = drv_set_csma_params(local, wpan_dev->min_be,
+ wpan_dev->max_be,
+ wpan_dev->csma_retries);
+ if (rc < 0)
+ goto out;
+ }
+
+ if (local->hw.flags & IEEE802154_HW_FRAME_RETRIES) {
+ rc = drv_set_max_frame_retries(local, wpan_dev->frame_retries);
+ if (rc < 0)
+ goto out;
+ }
+
+ mutex_unlock(&phy->pib_lock);
+ return 0;
+
+out:
+ mutex_unlock(&phy->pib_lock);
+ return rc;
+}
+
+static int mac802154_slave_close(struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct ieee802154_local *local = sdata->local;
+
+ ASSERT_RTNL();
+
+ hrtimer_cancel(&local->ifs_timer);
+
+ netif_stop_queue(dev);
+ local->open_count--;
+
+ clear_bit(SDATA_STATE_RUNNING, &sdata->state);
+
+ if (!local->open_count)
+ drv_stop(local);
+
+ return 0;
+}
+
+static int mac802154_set_header_security(struct ieee802154_sub_if_data *sdata,
+ struct ieee802154_hdr *hdr,
+ const struct ieee802154_mac_cb *cb)
+{
+ struct ieee802154_llsec_params params;
+ u8 level;
+
+ mac802154_llsec_get_params(&sdata->sec, &params);
+
+ if (!params.enabled && cb->secen_override && cb->secen)
+ return -EINVAL;
+ if (!params.enabled ||
+ (cb->secen_override && !cb->secen) ||
+ !params.out_level)
+ return 0;
+ if (cb->seclevel_override && !cb->seclevel)
+ return -EINVAL;
+
+ level = cb->seclevel_override ? cb->seclevel : params.out_level;
+
+ hdr->fc.security_enabled = 1;
+ hdr->sec.level = level;
+ hdr->sec.key_id_mode = params.out_key.mode;
+ if (params.out_key.mode == IEEE802154_SCF_KEY_SHORT_INDEX)
+ hdr->sec.short_src = params.out_key.short_source;
+ else if (params.out_key.mode == IEEE802154_SCF_KEY_HW_INDEX)
+ hdr->sec.extended_src = params.out_key.extended_source;
+ hdr->sec.key_id = params.out_key.id;
+
+ return 0;
+}
+
+static int mac802154_header_create(struct sk_buff *skb,
+ struct net_device *dev,
+ unsigned short type,
+ const void *daddr,
+ const void *saddr,
+ unsigned len)
+{
+ struct ieee802154_hdr hdr;
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct wpan_dev *wpan_dev = &sdata->wpan_dev;
+ struct ieee802154_mac_cb *cb = mac_cb(skb);
+ int hlen;
+
+ if (!daddr)
+ return -EINVAL;
+
+ memset(&hdr.fc, 0, sizeof(hdr.fc));
+ hdr.fc.type = cb->type;
+ hdr.fc.security_enabled = cb->secen;
+ hdr.fc.ack_request = cb->ackreq;
+ hdr.seq = ieee802154_mlme_ops(dev)->get_dsn(dev);
+
+ if (mac802154_set_header_security(sdata, &hdr, cb) < 0)
+ return -EINVAL;
+
+ if (!saddr) {
+ spin_lock_bh(&sdata->mib_lock);
+
+ if (wpan_dev->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST) ||
+ wpan_dev->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) ||
+ wpan_dev->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) {
+ hdr.source.mode = IEEE802154_ADDR_LONG;
+ hdr.source.extended_addr = wpan_dev->extended_addr;
+ } else {
+ hdr.source.mode = IEEE802154_ADDR_SHORT;
+ hdr.source.short_addr = wpan_dev->short_addr;
+ }
+
+ hdr.source.pan_id = wpan_dev->pan_id;
+
+ spin_unlock_bh(&sdata->mib_lock);
+ } else {
+ hdr.source = *(const struct ieee802154_addr *)saddr;
+ }
+
+ hdr.dest = *(const struct ieee802154_addr *)daddr;
+
+ hlen = ieee802154_hdr_push(skb, &hdr);
+ if (hlen < 0)
+ return -EINVAL;
+
+ skb_reset_mac_header(skb);
+ skb->mac_len = hlen;
+
+ if (len > ieee802154_max_payload(&hdr))
+ return -EMSGSIZE;
+
+ return hlen;
+}
+
+static int
+mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr)
+{
+ struct ieee802154_hdr hdr;
+ struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr;
+
+ if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) {
+ pr_debug("malformed packet\n");
+ return 0;
+ }
+
+ *addr = hdr.source;
+ return sizeof(*addr);
+}
+
+static struct header_ops mac802154_header_ops = {
+ .create = mac802154_header_create,
+ .parse = mac802154_header_parse,
+};
+
+static const struct net_device_ops mac802154_wpan_ops = {
+ .ndo_open = mac802154_wpan_open,
+ .ndo_stop = mac802154_slave_close,
+ .ndo_start_xmit = ieee802154_subif_start_xmit,
+ .ndo_do_ioctl = mac802154_wpan_ioctl,
+ .ndo_set_mac_address = mac802154_wpan_mac_addr,
+};
+
+static const struct net_device_ops mac802154_monitor_ops = {
+ .ndo_open = mac802154_wpan_open,
+ .ndo_stop = mac802154_slave_close,
+ .ndo_start_xmit = ieee802154_monitor_start_xmit,
+};
+
+static void mac802154_wpan_free(struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+
+ mac802154_llsec_destroy(&sdata->sec);
+
+ free_netdev(dev);
+}
+
+static void ieee802154_if_setup(struct net_device *dev)
+{
+ dev->addr_len = IEEE802154_EXTENDED_ADDR_LEN;
+ memset(dev->broadcast, 0xff, IEEE802154_EXTENDED_ADDR_LEN);
+
+ dev->hard_header_len = MAC802154_FRAME_HARD_HEADER_LEN;
+ dev->needed_tailroom = 2 + 16; /* FCS + MIC */
+ dev->mtu = IEEE802154_MTU;
+ dev->tx_queue_len = 300;
+ dev->flags = IFF_NOARP | IFF_BROADCAST;
+}
+
+static int
+ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata,
+ enum nl802154_iftype type)
+{
+ struct wpan_dev *wpan_dev = &sdata->wpan_dev;
+
+ /* set some type-dependent values */
+ sdata->vif.type = type;
+ sdata->wpan_dev.iftype = type;
+
+ get_random_bytes(&wpan_dev->bsn, 1);
+ get_random_bytes(&wpan_dev->dsn, 1);
+
+ /* defaults per 802.15.4-2011 */
+ wpan_dev->min_be = 3;
+ wpan_dev->max_be = 5;
+ wpan_dev->csma_retries = 4;
+ /* for compatibility, actual default is 3 */
+ wpan_dev->frame_retries = -1;
+
+ wpan_dev->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
+ wpan_dev->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
+
+ switch (type) {
+ case NL802154_IFTYPE_NODE:
+ ieee802154_be64_to_le64(&wpan_dev->extended_addr,
+ sdata->dev->dev_addr);
+
+ sdata->dev->header_ops = &mac802154_header_ops;
+ sdata->dev->destructor = mac802154_wpan_free;
+ sdata->dev->netdev_ops = &mac802154_wpan_ops;
+ sdata->dev->ml_priv = &mac802154_mlme_wpan;
+ wpan_dev->promiscuous_mode = false;
+
+ spin_lock_init(&sdata->mib_lock);
+ mutex_init(&sdata->sec_mtx);
+
+ mac802154_llsec_init(&sdata->sec);
+ break;
+ case NL802154_IFTYPE_MONITOR:
+ sdata->dev->destructor = free_netdev;
+ sdata->dev->netdev_ops = &mac802154_monitor_ops;
+ wpan_dev->promiscuous_mode = true;
+ break;
+ default:
+ BUG();
+ }
+
+ return 0;
+}
+
+struct net_device *
+ieee802154_if_add(struct ieee802154_local *local, const char *name,
+ enum nl802154_iftype type, __le64 extended_addr)
+{
+ struct net_device *ndev = NULL;
+ struct ieee802154_sub_if_data *sdata = NULL;
+ int ret = -ENOMEM;
+
+ ASSERT_RTNL();
+
+ ndev = alloc_netdev(sizeof(*sdata) + local->hw.vif_data_size, name,
+ NET_NAME_UNKNOWN, ieee802154_if_setup);
+ if (!ndev)
+ return ERR_PTR(-ENOMEM);
+
+ ndev->needed_headroom = local->hw.extra_tx_headroom;
+
+ ret = dev_alloc_name(ndev, ndev->name);
+ if (ret < 0)
+ goto err;
+
+ ieee802154_le64_to_be64(ndev->perm_addr,
+ &local->hw.phy->perm_extended_addr);
+ switch (type) {
+ case NL802154_IFTYPE_NODE:
+ ndev->type = ARPHRD_IEEE802154;
+ if (ieee802154_is_valid_extended_addr(extended_addr))
+ ieee802154_le64_to_be64(ndev->dev_addr, &extended_addr);
+ else
+ memcpy(ndev->dev_addr, ndev->perm_addr,
+ IEEE802154_EXTENDED_ADDR_LEN);
+ break;
+ case NL802154_IFTYPE_MONITOR:
+ ndev->type = ARPHRD_IEEE802154_MONITOR;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* TODO check this */
+ SET_NETDEV_DEV(ndev, &local->phy->dev);
+ sdata = netdev_priv(ndev);
+ ndev->ieee802154_ptr = &sdata->wpan_dev;
+ memcpy(sdata->name, ndev->name, IFNAMSIZ);
+ sdata->dev = ndev;
+ sdata->wpan_dev.wpan_phy = local->hw.phy;
+ sdata->local = local;
+
+ /* setup type-dependent data */
+ ret = ieee802154_setup_sdata(sdata, type);
+ if (ret)
+ goto err;
+
+ ret = register_netdevice(ndev);
+ if (ret < 0)
+ goto err;
+
+ mutex_lock(&local->iflist_mtx);
+ list_add_tail_rcu(&sdata->list, &local->interfaces);
+ mutex_unlock(&local->iflist_mtx);
+
+ return ndev;
+
+err:
+ free_netdev(ndev);
+ return ERR_PTR(ret);
+}
+
+void ieee802154_if_remove(struct ieee802154_sub_if_data *sdata)
+{
+ ASSERT_RTNL();
+
+ mutex_lock(&sdata->local->iflist_mtx);
+ list_del_rcu(&sdata->list);
+ mutex_unlock(&sdata->local->iflist_mtx);
+
+ synchronize_rcu();
+ unregister_netdevice(sdata->dev);
+}
+
+void ieee802154_remove_interfaces(struct ieee802154_local *local)
+{
+ struct ieee802154_sub_if_data *sdata, *tmp;
+
+ mutex_lock(&local->iflist_mtx);
+ list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) {
+ list_del(&sdata->list);
+
+ unregister_netdevice(sdata->dev);
+ }
+ mutex_unlock(&local->iflist_mtx);
+}
+
+static int netdev_notify(struct notifier_block *nb,
+ unsigned long state, void *ptr)
+{
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+ struct ieee802154_sub_if_data *sdata;
+
+ if (state != NETDEV_CHANGENAME)
+ return NOTIFY_DONE;
+
+ if (!dev->ieee802154_ptr || !dev->ieee802154_ptr->wpan_phy)
+ return NOTIFY_DONE;
+
+ if (dev->ieee802154_ptr->wpan_phy->privid != mac802154_wpan_phy_privid)
+ return NOTIFY_DONE;
+
+ sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ memcpy(sdata->name, dev->name, IFNAMSIZ);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block mac802154_netdev_notifier = {
+ .notifier_call = netdev_notify,
+};
+
+int ieee802154_iface_init(void)
+{
+ return register_netdevice_notifier(&mac802154_netdev_notifier);
+}
+
+void ieee802154_iface_exit(void)
+{
+ unregister_netdevice_notifier(&mac802154_netdev_notifier);
+}
diff --git a/net/mac802154/llsec.c b/net/mac802154/llsec.c
index 457058142098..dcf73958133a 100644
--- a/net/mac802154/llsec.c
+++ b/net/mac802154/llsec.c
@@ -17,10 +17,10 @@
#include <linux/err.h>
#include <linux/bug.h>
#include <linux/completion.h>
-#include <net/ieee802154.h>
+#include <linux/ieee802154.h>
#include <crypto/algapi.h>
-#include "mac802154.h"
+#include "ieee802154_i.h"
#include "llsec.h"
static void llsec_key_put(struct mac802154_llsec_key *key);
@@ -75,8 +75,6 @@ void mac802154_llsec_destroy(struct mac802154_llsec *sec)
}
}
-
-
int mac802154_llsec_get_params(struct mac802154_llsec *sec,
struct ieee802154_llsec_params *params)
{
@@ -117,8 +115,6 @@ int mac802154_llsec_set_params(struct mac802154_llsec *sec,
return 0;
}
-
-
static struct mac802154_llsec_key*
llsec_key_alloc(const struct ieee802154_llsec_key *template)
{
@@ -294,8 +290,6 @@ int mac802154_llsec_key_del(struct mac802154_llsec *sec,
return -ENOENT;
}
-
-
static bool llsec_dev_use_shortaddr(__le16 short_addr)
{
return short_addr != cpu_to_le16(IEEE802154_ADDR_UNDEF) &&
@@ -304,12 +298,12 @@ static bool llsec_dev_use_shortaddr(__le16 short_addr)
static u32 llsec_dev_hash_short(__le16 short_addr, __le16 pan_id)
{
- return ((__force u16) short_addr) << 16 | (__force u16) pan_id;
+ return ((__force u16)short_addr) << 16 | (__force u16)pan_id;
}
static u64 llsec_dev_hash_long(__le64 hwaddr)
{
- return (__force u64) hwaddr;
+ return (__force u64)hwaddr;
}
static struct mac802154_llsec_device*
@@ -411,8 +405,6 @@ int mac802154_llsec_dev_del(struct mac802154_llsec *sec, __le64 device_addr)
return 0;
}
-
-
static struct mac802154_llsec_device_key*
llsec_devkey_find(struct mac802154_llsec_device *dev,
const struct ieee802154_llsec_key_id *key)
@@ -475,8 +467,6 @@ int mac802154_llsec_devkey_del(struct mac802154_llsec *sec,
return 0;
}
-
-
static struct mac802154_llsec_seclevel*
llsec_find_seclevel(const struct mac802154_llsec *sec,
const struct ieee802154_llsec_seclevel *sl)
@@ -532,8 +522,6 @@ int mac802154_llsec_seclevel_del(struct mac802154_llsec *sec,
return 0;
}
-
-
static int llsec_recover_addr(struct mac802154_llsec *sec,
struct ieee802154_addr *addr)
{
@@ -609,7 +597,6 @@ found:
return llsec_key_get(key);
}
-
static void llsec_geniv(u8 iv[16], __le64 addr,
const struct ieee802154_sechdr *sec)
{
@@ -786,8 +773,6 @@ fail:
return rc;
}
-
-
static struct mac802154_llsec_device*
llsec_lookup_dev(struct mac802154_llsec *sec,
const struct ieee802154_addr *addr)
diff --git a/net/mac802154/mac_cmd.c b/net/mac802154/mac_cmd.c
index bf809131eef7..6aacb1816889 100644
--- a/net/mac802154/mac_cmd.c
+++ b/net/mac802154/mac_cmd.c
@@ -12,10 +12,6 @@
* 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.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
@@ -24,14 +20,14 @@
#include <linux/skbuff.h>
#include <linux/if_arp.h>
+#include <linux/ieee802154.h>
-#include <net/ieee802154.h>
#include <net/ieee802154_netdev.h>
-#include <net/wpan-phy.h>
+#include <net/cfg802154.h>
#include <net/mac802154.h>
-#include <net/nl802154.h>
-#include "mac802154.h"
+#include "ieee802154_i.h"
+#include "driver-ops.h"
static int mac802154_mlme_start_req(struct net_device *dev,
struct ieee802154_addr *addr,
@@ -43,11 +39,12 @@ static int mac802154_mlme_start_req(struct net_device *dev,
struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
int rc = 0;
+ ASSERT_RTNL();
+
BUG_ON(addr->mode != IEEE802154_ADDR_SHORT);
mac802154_dev_set_pan_id(dev, addr->pan_id);
mac802154_dev_set_short_addr(dev, addr->short_addr);
- mac802154_dev_set_ieee_addr(dev);
mac802154_dev_set_page_channel(dev, page, channel);
if (ops->llsec) {
@@ -69,21 +66,71 @@ static int mac802154_mlme_start_req(struct net_device *dev,
rc = ops->llsec->set_params(dev, &params, changed);
}
- /* FIXME: add validation for unused parameters to be sane
- * for SoftMAC
- */
- ieee802154_nl_start_confirm(dev, IEEE802154_SUCCESS);
-
return rc;
}
-static struct wpan_phy *mac802154_get_phy(const struct net_device *dev)
+static int mac802154_set_mac_params(struct net_device *dev,
+ const struct ieee802154_mac_params *params)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct ieee802154_local *local = sdata->local;
+ struct wpan_dev *wpan_dev = &sdata->wpan_dev;
+ int ret;
+
+ ASSERT_RTNL();
+
+ /* PHY */
+ wpan_dev->wpan_phy->transmit_power = params->transmit_power;
+ wpan_dev->wpan_phy->cca_mode = params->cca_mode;
+ wpan_dev->wpan_phy->cca_ed_level = params->cca_ed_level;
+
+ /* MAC */
+ wpan_dev->min_be = params->min_be;
+ wpan_dev->max_be = params->max_be;
+ wpan_dev->csma_retries = params->csma_retries;
+ wpan_dev->frame_retries = params->frame_retries;
+ wpan_dev->lbt = params->lbt;
+
+ if (local->hw.flags & IEEE802154_HW_TXPOWER) {
+ ret = drv_set_tx_power(local, params->transmit_power);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (local->hw.flags & IEEE802154_HW_CCA_MODE) {
+ ret = drv_set_cca_mode(local, params->cca_mode);
+ if (ret < 0)
+ return ret;
+ }
- BUG_ON(dev->type != ARPHRD_IEEE802154);
+ if (local->hw.flags & IEEE802154_HW_CCA_ED_LEVEL) {
+ ret = drv_set_cca_ed_level(local, params->cca_ed_level);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
- return to_phy(get_device(&priv->hw->phy->dev));
+static void mac802154_get_mac_params(struct net_device *dev,
+ struct ieee802154_mac_params *params)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct wpan_dev *wpan_dev = &sdata->wpan_dev;
+
+ ASSERT_RTNL();
+
+ /* PHY */
+ params->transmit_power = wpan_dev->wpan_phy->transmit_power;
+ params->cca_mode = wpan_dev->wpan_phy->cca_mode;
+ params->cca_ed_level = wpan_dev->wpan_phy->cca_ed_level;
+
+ /* MAC */
+ params->min_be = wpan_dev->min_be;
+ params->max_be = wpan_dev->max_be;
+ params->csma_retries = wpan_dev->csma_retries;
+ params->frame_retries = wpan_dev->frame_retries;
+ params->lbt = wpan_dev->lbt;
}
static struct ieee802154_llsec_ops mac802154_llsec_ops = {
@@ -102,12 +149,7 @@ static struct ieee802154_llsec_ops mac802154_llsec_ops = {
.unlock_table = mac802154_unlock_table,
};
-struct ieee802154_reduced_mlme_ops mac802154_mlme_reduced = {
- .get_phy = mac802154_get_phy,
-};
-
struct ieee802154_mlme_ops mac802154_mlme_wpan = {
- .get_phy = mac802154_get_phy,
.start_req = mac802154_mlme_start_req,
.get_pan_id = mac802154_dev_get_pan_id,
.get_short_addr = mac802154_dev_get_short_addr,
diff --git a/net/mac802154/main.c b/net/mac802154/main.c
new file mode 100644
index 000000000000..8500378c8318
--- /dev/null
+++ b/net/mac802154/main.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2007-2012 Siemens AG
+ *
+ * Written by:
+ * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+
+#include <net/netlink.h>
+#include <net/nl802154.h>
+#include <net/mac802154.h>
+#include <net/ieee802154_netdev.h>
+#include <net/route.h>
+#include <net/cfg802154.h>
+
+#include "ieee802154_i.h"
+#include "cfg.h"
+
+static void ieee802154_tasklet_handler(unsigned long data)
+{
+ struct ieee802154_local *local = (struct ieee802154_local *)data;
+ struct sk_buff *skb;
+
+ while ((skb = skb_dequeue(&local->skb_queue))) {
+ switch (skb->pkt_type) {
+ case IEEE802154_RX_MSG:
+ /* Clear skb->pkt_type in order to not confuse kernel
+ * netstack.
+ */
+ skb->pkt_type = 0;
+ ieee802154_rx(&local->hw, skb);
+ break;
+ default:
+ WARN(1, "mac802154: Packet is of unknown type %d\n",
+ skb->pkt_type);
+ kfree_skb(skb);
+ break;
+ }
+ }
+}
+
+struct ieee802154_hw *
+ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops)
+{
+ struct wpan_phy *phy;
+ struct ieee802154_local *local;
+ size_t priv_size;
+
+ if (!ops || !(ops->xmit_async || ops->xmit_sync) || !ops->ed ||
+ !ops->start || !ops->stop || !ops->set_channel) {
+ pr_err("undefined IEEE802.15.4 device operations\n");
+ return NULL;
+ }
+
+ /* Ensure 32-byte alignment of our private data and hw private data.
+ * We use the wpan_phy priv data for both our ieee802154_local and for
+ * the driver's private data
+ *
+ * in memory it'll be like this:
+ *
+ * +-------------------------+
+ * | struct wpan_phy |
+ * +-------------------------+
+ * | struct ieee802154_local |
+ * +-------------------------+
+ * | driver's private data |
+ * +-------------------------+
+ *
+ * Due to ieee802154 layer isn't aware of driver and MAC structures,
+ * so lets align them here.
+ */
+
+ priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len;
+
+ phy = wpan_phy_new(&mac802154_config_ops, priv_size);
+ if (!phy) {
+ pr_err("failure to allocate master IEEE802.15.4 device\n");
+ return NULL;
+ }
+
+ phy->privid = mac802154_wpan_phy_privid;
+
+ local = wpan_phy_priv(phy);
+ local->phy = phy;
+ local->hw.phy = local->phy;
+ local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN);
+ local->ops = ops;
+
+ INIT_LIST_HEAD(&local->interfaces);
+ mutex_init(&local->iflist_mtx);
+
+ tasklet_init(&local->tasklet,
+ ieee802154_tasklet_handler,
+ (unsigned long)local);
+
+ skb_queue_head_init(&local->skb_queue);
+
+ return &local->hw;
+}
+EXPORT_SYMBOL(ieee802154_alloc_hw);
+
+void ieee802154_free_hw(struct ieee802154_hw *hw)
+{
+ struct ieee802154_local *local = hw_to_local(hw);
+
+ BUG_ON(!list_empty(&local->interfaces));
+
+ mutex_destroy(&local->iflist_mtx);
+
+ wpan_phy_free(local->phy);
+}
+EXPORT_SYMBOL(ieee802154_free_hw);
+
+static void ieee802154_setup_wpan_phy_pib(struct wpan_phy *wpan_phy)
+{
+ /* TODO warn on empty symbol_duration
+ * Should be done when all drivers sets this value.
+ */
+
+ wpan_phy->lifs_period = IEEE802154_LIFS_PERIOD *
+ wpan_phy->symbol_duration;
+ wpan_phy->sifs_period = IEEE802154_SIFS_PERIOD *
+ wpan_phy->symbol_duration;
+}
+
+int ieee802154_register_hw(struct ieee802154_hw *hw)
+{
+ struct ieee802154_local *local = hw_to_local(hw);
+ struct net_device *dev;
+ int rc = -ENOSYS;
+
+ local->workqueue =
+ create_singlethread_workqueue(wpan_phy_name(local->phy));
+ if (!local->workqueue) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ hrtimer_init(&local->ifs_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ local->ifs_timer.function = ieee802154_xmit_ifs_timer;
+
+ wpan_phy_set_dev(local->phy, local->hw.parent);
+
+ ieee802154_setup_wpan_phy_pib(local->phy);
+
+ rc = wpan_phy_register(local->phy);
+ if (rc < 0)
+ goto out_wq;
+
+ rtnl_lock();
+
+ dev = ieee802154_if_add(local, "wpan%d", NL802154_IFTYPE_NODE,
+ cpu_to_le64(0x0000000000000000ULL));
+ if (IS_ERR(dev)) {
+ rtnl_unlock();
+ rc = PTR_ERR(dev);
+ goto out_wq;
+ }
+
+ rtnl_unlock();
+
+ return 0;
+
+out_wq:
+ destroy_workqueue(local->workqueue);
+out:
+ return rc;
+}
+EXPORT_SYMBOL(ieee802154_register_hw);
+
+void ieee802154_unregister_hw(struct ieee802154_hw *hw)
+{
+ struct ieee802154_local *local = hw_to_local(hw);
+
+ tasklet_kill(&local->tasklet);
+ flush_workqueue(local->workqueue);
+ destroy_workqueue(local->workqueue);
+
+ rtnl_lock();
+
+ ieee802154_remove_interfaces(local);
+
+ rtnl_unlock();
+
+ wpan_phy_unregister(local->phy);
+}
+EXPORT_SYMBOL(ieee802154_unregister_hw);
+
+static int __init ieee802154_init(void)
+{
+ return ieee802154_iface_init();
+}
+
+static void __exit ieee802154_exit(void)
+{
+ ieee802154_iface_exit();
+
+ rcu_barrier();
+}
+
+subsys_initcall(ieee802154_init);
+module_exit(ieee802154_exit);
+
+MODULE_DESCRIPTION("IEEE 802.15.4 subsystem");
+MODULE_LICENSE("GPL v2");
diff --git a/net/mac802154/mib.c b/net/mac802154/mib.c
index 868a040fd422..5cf019a57fd7 100644
--- a/net/mac802154/mib.c
+++ b/net/mac802154/mib.c
@@ -10,10 +10,6 @@
* 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.
- *
* Written by:
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
* Sergey Lapin <slapin@ossfans.org>
@@ -25,208 +21,100 @@
#include <net/mac802154.h>
#include <net/ieee802154_netdev.h>
-#include <net/wpan-phy.h>
-
-#include "mac802154.h"
-
-struct phy_chan_notify_work {
- struct work_struct work;
- struct net_device *dev;
-};
-
-struct hw_addr_filt_notify_work {
- struct work_struct work;
- struct net_device *dev;
- unsigned long changed;
-};
-
-static struct mac802154_priv *mac802154_slave_get_priv(struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
-
- BUG_ON(dev->type != ARPHRD_IEEE802154);
-
- return priv->hw;
-}
-
-static void hw_addr_notify(struct work_struct *work)
-{
- struct hw_addr_filt_notify_work *nw = container_of(work,
- struct hw_addr_filt_notify_work, work);
- struct mac802154_priv *hw = mac802154_slave_get_priv(nw->dev);
- int res;
+#include <net/cfg802154.h>
- res = hw->ops->set_hw_addr_filt(&hw->hw,
- &hw->hw.hw_filt,
- nw->changed);
- if (res)
- pr_debug("failed changed mask %lx\n", nw->changed);
-
- kfree(nw);
-}
-
-static void set_hw_addr_filt(struct net_device *dev, unsigned long changed)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct hw_addr_filt_notify_work *work;
-
- work = kzalloc(sizeof(*work), GFP_ATOMIC);
- if (!work)
- return;
-
- INIT_WORK(&work->work, hw_addr_notify);
- work->dev = dev;
- work->changed = changed;
- queue_work(priv->hw->dev_workqueue, &work->work);
-}
+#include "ieee802154_i.h"
+#include "driver-ops.h"
void mac802154_dev_set_short_addr(struct net_device *dev, __le16 val)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
BUG_ON(dev->type != ARPHRD_IEEE802154);
- spin_lock_bh(&priv->mib_lock);
- priv->short_addr = val;
- spin_unlock_bh(&priv->mib_lock);
-
- if ((priv->hw->ops->set_hw_addr_filt) &&
- (priv->hw->hw.hw_filt.short_addr != priv->short_addr)) {
- priv->hw->hw.hw_filt.short_addr = priv->short_addr;
- set_hw_addr_filt(dev, IEEE802515_AFILT_SADDR_CHANGED);
- }
+ spin_lock_bh(&sdata->mib_lock);
+ sdata->wpan_dev.short_addr = val;
+ spin_unlock_bh(&sdata->mib_lock);
}
__le16 mac802154_dev_get_short_addr(const struct net_device *dev)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
__le16 ret;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- spin_lock_bh(&priv->mib_lock);
- ret = priv->short_addr;
- spin_unlock_bh(&priv->mib_lock);
+ spin_lock_bh(&sdata->mib_lock);
+ ret = sdata->wpan_dev.short_addr;
+ spin_unlock_bh(&sdata->mib_lock);
return ret;
}
-void mac802154_dev_set_ieee_addr(struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct mac802154_priv *mac = priv->hw;
-
- priv->extended_addr = ieee802154_devaddr_from_raw(dev->dev_addr);
-
- if (mac->ops->set_hw_addr_filt &&
- mac->hw.hw_filt.ieee_addr != priv->extended_addr) {
- mac->hw.hw_filt.ieee_addr = priv->extended_addr;
- set_hw_addr_filt(dev, IEEE802515_AFILT_IEEEADDR_CHANGED);
- }
-}
-
__le16 mac802154_dev_get_pan_id(const struct net_device *dev)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
__le16 ret;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- spin_lock_bh(&priv->mib_lock);
- ret = priv->pan_id;
- spin_unlock_bh(&priv->mib_lock);
+ spin_lock_bh(&sdata->mib_lock);
+ ret = sdata->wpan_dev.pan_id;
+ spin_unlock_bh(&sdata->mib_lock);
return ret;
}
void mac802154_dev_set_pan_id(struct net_device *dev, __le16 val)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
BUG_ON(dev->type != ARPHRD_IEEE802154);
- spin_lock_bh(&priv->mib_lock);
- priv->pan_id = val;
- spin_unlock_bh(&priv->mib_lock);
-
- if ((priv->hw->ops->set_hw_addr_filt) &&
- (priv->hw->hw.hw_filt.pan_id != priv->pan_id)) {
- priv->hw->hw.hw_filt.pan_id = priv->pan_id;
- set_hw_addr_filt(dev, IEEE802515_AFILT_PANID_CHANGED);
- }
+ spin_lock_bh(&sdata->mib_lock);
+ sdata->wpan_dev.pan_id = val;
+ spin_unlock_bh(&sdata->mib_lock);
}
u8 mac802154_dev_get_dsn(const struct net_device *dev)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
BUG_ON(dev->type != ARPHRD_IEEE802154);
- return priv->dsn++;
-}
-
-static void phy_chan_notify(struct work_struct *work)
-{
- struct phy_chan_notify_work *nw = container_of(work,
- struct phy_chan_notify_work, work);
- struct mac802154_priv *hw = mac802154_slave_get_priv(nw->dev);
- struct mac802154_sub_if_data *priv = netdev_priv(nw->dev);
- int res;
-
- mutex_lock(&priv->hw->phy->pib_lock);
- res = hw->ops->set_channel(&hw->hw, priv->page, priv->chan);
- if (res) {
- pr_debug("set_channel failed\n");
- } else {
- priv->hw->phy->current_channel = priv->chan;
- priv->hw->phy->current_page = priv->page;
- }
- mutex_unlock(&priv->hw->phy->pib_lock);
-
- kfree(nw);
+ return sdata->wpan_dev.dsn++;
}
void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct phy_chan_notify_work *work;
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct ieee802154_local *local = sdata->local;
+ int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- spin_lock_bh(&priv->mib_lock);
- priv->page = page;
- priv->chan = chan;
- spin_unlock_bh(&priv->mib_lock);
-
- mutex_lock(&priv->hw->phy->pib_lock);
- if (priv->hw->phy->current_channel != priv->chan ||
- priv->hw->phy->current_page != priv->page) {
- mutex_unlock(&priv->hw->phy->pib_lock);
-
- work = kzalloc(sizeof(*work), GFP_ATOMIC);
- if (!work)
- return;
-
- INIT_WORK(&work->work, phy_chan_notify);
- work->dev = dev;
- queue_work(priv->hw->dev_workqueue, &work->work);
+ res = drv_set_channel(local, page, chan);
+ if (res) {
+ pr_debug("set_channel failed\n");
} else {
- mutex_unlock(&priv->hw->phy->pib_lock);
+ mutex_lock(&local->phy->pib_lock);
+ local->phy->current_channel = chan;
+ local->phy->current_page = page;
+ mutex_unlock(&local->phy->pib_lock);
}
}
-
int mac802154_get_params(struct net_device *dev,
struct ieee802154_llsec_params *params)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_get_params(&priv->sec, params);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_get_params(&sdata->sec, params);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
@@ -235,31 +123,30 @@ int mac802154_set_params(struct net_device *dev,
const struct ieee802154_llsec_params *params,
int changed)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_set_params(&priv->sec, params, changed);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_set_params(&sdata->sec, params, changed);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
-
int mac802154_add_key(struct net_device *dev,
const struct ieee802154_llsec_key_id *id,
const struct ieee802154_llsec_key *key)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_key_add(&priv->sec, id, key);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_key_add(&sdata->sec, id, key);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
@@ -267,61 +154,59 @@ int mac802154_add_key(struct net_device *dev,
int mac802154_del_key(struct net_device *dev,
const struct ieee802154_llsec_key_id *id)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_key_del(&priv->sec, id);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_key_del(&sdata->sec, id);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
-
int mac802154_add_dev(struct net_device *dev,
const struct ieee802154_llsec_device *llsec_dev)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_dev_add(&priv->sec, llsec_dev);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_dev_add(&sdata->sec, llsec_dev);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
int mac802154_del_dev(struct net_device *dev, __le64 dev_addr)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_dev_del(&priv->sec, dev_addr);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_dev_del(&sdata->sec, dev_addr);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
-
int mac802154_add_devkey(struct net_device *dev,
__le64 device_addr,
const struct ieee802154_llsec_device_key *key)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_devkey_add(&priv->sec, device_addr, key);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_devkey_add(&sdata->sec, device_addr, key);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
@@ -330,30 +215,29 @@ int mac802154_del_devkey(struct net_device *dev,
__le64 device_addr,
const struct ieee802154_llsec_device_key *key)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_devkey_del(&priv->sec, device_addr, key);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_devkey_del(&sdata->sec, device_addr, key);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
-
int mac802154_add_seclevel(struct net_device *dev,
const struct ieee802154_llsec_seclevel *sl)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_seclevel_add(&priv->sec, sl);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_seclevel_add(&sdata->sec, sl);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
@@ -361,43 +245,42 @@ int mac802154_add_seclevel(struct net_device *dev,
int mac802154_del_seclevel(struct net_device *dev,
const struct ieee802154_llsec_seclevel *sl)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_seclevel_del(&priv->sec, sl);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_seclevel_del(&sdata->sec, sl);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
-
void mac802154_lock_table(struct net_device *dev)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
}
void mac802154_get_table(struct net_device *dev,
struct ieee802154_llsec_table **t)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
BUG_ON(dev->type != ARPHRD_IEEE802154);
- *t = &priv->sec.table;
+ *t = &sdata->sec.table;
}
void mac802154_unlock_table(struct net_device *dev)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_unlock(&priv->sec_mtx);
+ mutex_unlock(&sdata->sec_mtx);
}
diff --git a/net/mac802154/monitor.c b/net/mac802154/monitor.c
deleted file mode 100644
index a68230e2b25f..000000000000
--- a/net/mac802154/monitor.c
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright 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.
- *
- * Written by:
- * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
- * Sergey Lapin <slapin@ossfans.org>
- * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
- * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
- */
-
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/crc-ccitt.h>
-
-#include <net/ieee802154.h>
-#include <net/mac802154.h>
-#include <net/netlink.h>
-#include <net/wpan-phy.h>
-#include <linux/nl802154.h>
-
-#include "mac802154.h"
-
-static netdev_tx_t mac802154_monitor_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv;
- u8 chan, page;
-
- priv = netdev_priv(dev);
-
- /* FIXME: locking */
- chan = priv->hw->phy->current_channel;
- page = priv->hw->phy->current_page;
-
- if (chan == MAC802154_CHAN_NONE) /* not initialized */
- return NETDEV_TX_OK;
-
- if (WARN_ON(page >= WPAN_NUM_PAGES) ||
- WARN_ON(chan >= WPAN_NUM_CHANNELS))
- return NETDEV_TX_OK;
-
- skb->skb_iif = dev->ifindex;
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += skb->len;
-
- return mac802154_tx(priv->hw, skb, page, chan);
-}
-
-
-void mac802154_monitors_rx(struct mac802154_priv *priv, struct sk_buff *skb)
-{
- struct sk_buff *skb2;
- struct mac802154_sub_if_data *sdata;
- u16 crc = crc_ccitt(0, skb->data, skb->len);
- u8 *data;
-
- rcu_read_lock();
- list_for_each_entry_rcu(sdata, &priv->slaves, list) {
- if (sdata->type != IEEE802154_DEV_MONITOR ||
- !netif_running(sdata->dev))
- continue;
-
- skb2 = skb_clone(skb, GFP_ATOMIC);
- skb2->dev = sdata->dev;
- skb2->pkt_type = PACKET_HOST;
- data = skb_put(skb2, 2);
- data[0] = crc & 0xff;
- data[1] = crc >> 8;
-
- netif_rx_ni(skb2);
- }
- rcu_read_unlock();
-}
-
-static const struct net_device_ops mac802154_monitor_ops = {
- .ndo_open = mac802154_slave_open,
- .ndo_stop = mac802154_slave_close,
- .ndo_start_xmit = mac802154_monitor_xmit,
-};
-
-void mac802154_monitor_setup(struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv;
-
- dev->addr_len = 0;
- dev->hard_header_len = 0;
- dev->needed_tailroom = 2; /* room for FCS */
- dev->mtu = IEEE802154_MTU;
- dev->tx_queue_len = 10;
- dev->type = ARPHRD_IEEE802154_MONITOR;
- dev->flags = IFF_NOARP | IFF_BROADCAST;
- dev->watchdog_timeo = 0;
-
- dev->destructor = free_netdev;
- dev->netdev_ops = &mac802154_monitor_ops;
- dev->ml_priv = &mac802154_mlme_reduced;
-
- priv = netdev_priv(dev);
- priv->type = IEEE802154_DEV_MONITOR;
-
- priv->chan = MAC802154_CHAN_NONE; /* not initialized */
- priv->page = 0;
-}
diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c
index a14cf9ede171..c0d67b2b4132 100644
--- a/net/mac802154/rx.c
+++ b/net/mac802154/rx.c
@@ -10,10 +10,6 @@
* 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.
- *
* Written by:
* Pavel Smolenskiy <pavel.smolenskiy@gmail.com>
* Maxim Gorbachyov <maxim.gorbachev@siemens.com>
@@ -23,92 +19,284 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/workqueue.h>
#include <linux/netdevice.h>
#include <linux/crc-ccitt.h>
+#include <asm/unaligned.h>
#include <net/mac802154.h>
#include <net/ieee802154_netdev.h>
+#include <net/nl802154.h>
-#include "mac802154.h"
+#include "ieee802154_i.h"
-/* The IEEE 802.15.4 standard defines 4 MAC packet types:
- * - beacon frame
- * - MAC command frame
- * - acknowledgement frame
- * - data frame
- *
- * and only the data frame should be pushed to the upper layers, other types
- * are just internal MAC layer management information. So only data packets
- * are going to be sent to the networking queue, all other will be processed
- * right here by using the device workqueue.
- */
-struct rx_work {
- struct sk_buff *skb;
- struct work_struct work;
- struct ieee802154_dev *dev;
- u8 lqi;
-};
+static int ieee802154_deliver_skb(struct sk_buff *skb)
+{
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->protocol = htons(ETH_P_IEEE802154);
-static void
-mac802154_subif_rx(struct ieee802154_dev *hw, struct sk_buff *skb, u8 lqi)
+ return netif_receive_skb(skb);
+}
+
+static int
+ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,
+ struct sk_buff *skb, const struct ieee802154_hdr *hdr)
{
- struct mac802154_priv *priv = mac802154_to_priv(hw);
+ struct wpan_dev *wpan_dev = &sdata->wpan_dev;
+ __le16 span, sshort;
+ int rc;
- mac_cb(skb)->lqi = lqi;
- skb->protocol = htons(ETH_P_IEEE802154);
- skb_reset_mac_header(skb);
+ pr_debug("getting packet via slave interface %s\n", sdata->dev->name);
- if (!(priv->hw.flags & IEEE802154_HW_OMIT_CKSUM)) {
- u16 crc;
+ spin_lock_bh(&sdata->mib_lock);
- if (skb->len < 2) {
- pr_debug("got invalid frame\n");
- goto fail;
- }
- crc = crc_ccitt(0, skb->data, skb->len);
- if (crc) {
- pr_debug("CRC mismatch\n");
- goto fail;
- }
- skb_trim(skb, skb->len - 2); /* CRC */
+ span = wpan_dev->pan_id;
+ sshort = wpan_dev->short_addr;
+
+ switch (mac_cb(skb)->dest.mode) {
+ case IEEE802154_ADDR_NONE:
+ if (mac_cb(skb)->dest.mode != IEEE802154_ADDR_NONE)
+ /* FIXME: check if we are PAN coordinator */
+ skb->pkt_type = PACKET_OTHERHOST;
+ else
+ /* ACK comes with both addresses empty */
+ skb->pkt_type = PACKET_HOST;
+ break;
+ case IEEE802154_ADDR_LONG:
+ if (mac_cb(skb)->dest.pan_id != span &&
+ mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST))
+ skb->pkt_type = PACKET_OTHERHOST;
+ else if (mac_cb(skb)->dest.extended_addr == wpan_dev->extended_addr)
+ skb->pkt_type = PACKET_HOST;
+ else
+ skb->pkt_type = PACKET_OTHERHOST;
+ break;
+ case IEEE802154_ADDR_SHORT:
+ if (mac_cb(skb)->dest.pan_id != span &&
+ mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST))
+ skb->pkt_type = PACKET_OTHERHOST;
+ else if (mac_cb(skb)->dest.short_addr == sshort)
+ skb->pkt_type = PACKET_HOST;
+ else if (mac_cb(skb)->dest.short_addr ==
+ cpu_to_le16(IEEE802154_ADDR_BROADCAST))
+ skb->pkt_type = PACKET_BROADCAST;
+ else
+ skb->pkt_type = PACKET_OTHERHOST;
+ break;
+ default:
+ spin_unlock_bh(&sdata->mib_lock);
+ pr_debug("invalid dest mode\n");
+ goto fail;
}
- mac802154_monitors_rx(priv, skb);
- mac802154_wpans_rx(priv, skb);
+ spin_unlock_bh(&sdata->mib_lock);
- return;
+ skb->dev = sdata->dev;
+
+ rc = mac802154_llsec_decrypt(&sdata->sec, skb);
+ if (rc) {
+ pr_debug("decryption failed: %i\n", rc);
+ goto fail;
+ }
+
+ sdata->dev->stats.rx_packets++;
+ sdata->dev->stats.rx_bytes += skb->len;
+
+ switch (mac_cb(skb)->type) {
+ case IEEE802154_FC_TYPE_DATA:
+ return ieee802154_deliver_skb(skb);
+ default:
+ pr_warn("ieee802154: bad frame received (type = %d)\n",
+ mac_cb(skb)->type);
+ goto fail;
+ }
fail:
kfree_skb(skb);
+ return NET_RX_DROP;
}
-static void mac802154_rx_worker(struct work_struct *work)
+static void
+ieee802154_print_addr(const char *name, const struct ieee802154_addr *addr)
{
- struct rx_work *rw = container_of(work, struct rx_work, work);
+ if (addr->mode == IEEE802154_ADDR_NONE)
+ pr_debug("%s not present\n", name);
+
+ pr_debug("%s PAN ID: %04x\n", name, le16_to_cpu(addr->pan_id));
+ if (addr->mode == IEEE802154_ADDR_SHORT) {
+ pr_debug("%s is short: %04x\n", name,
+ le16_to_cpu(addr->short_addr));
+ } else {
+ u64 hw = swab64((__force u64)addr->extended_addr);
- mac802154_subif_rx(rw->dev, rw->skb, rw->lqi);
- kfree(rw);
+ pr_debug("%s is hardware: %8phC\n", name, &hw);
+ }
}
-void
-ieee802154_rx_irqsafe(struct ieee802154_dev *dev, struct sk_buff *skb, u8 lqi)
+static int
+ieee802154_parse_frame_start(struct sk_buff *skb, struct ieee802154_hdr *hdr)
{
- struct mac802154_priv *priv = mac802154_to_priv(dev);
- struct rx_work *work;
+ int hlen;
+ struct ieee802154_mac_cb *cb = mac_cb_init(skb);
- if (!skb)
- return;
+ skb_reset_mac_header(skb);
+
+ hlen = ieee802154_hdr_pull(skb, hdr);
+ if (hlen < 0)
+ return -EINVAL;
+
+ skb->mac_len = hlen;
+
+ pr_debug("fc: %04x dsn: %02x\n", le16_to_cpup((__le16 *)&hdr->fc),
+ hdr->seq);
+
+ cb->type = hdr->fc.type;
+ cb->ackreq = hdr->fc.ack_request;
+ cb->secen = hdr->fc.security_enabled;
+
+ ieee802154_print_addr("destination", &hdr->dest);
+ ieee802154_print_addr("source", &hdr->source);
+
+ cb->source = hdr->source;
+ cb->dest = hdr->dest;
+
+ if (hdr->fc.security_enabled) {
+ u64 key;
+
+ pr_debug("seclevel %i\n", hdr->sec.level);
+
+ switch (hdr->sec.key_id_mode) {
+ case IEEE802154_SCF_KEY_IMPLICIT:
+ pr_debug("implicit key\n");
+ break;
+
+ case IEEE802154_SCF_KEY_INDEX:
+ pr_debug("key %02x\n", hdr->sec.key_id);
+ break;
+
+ case IEEE802154_SCF_KEY_SHORT_INDEX:
+ pr_debug("key %04x:%04x %02x\n",
+ le32_to_cpu(hdr->sec.short_src) >> 16,
+ le32_to_cpu(hdr->sec.short_src) & 0xffff,
+ hdr->sec.key_id);
+ break;
+
+ case IEEE802154_SCF_KEY_HW_INDEX:
+ key = swab64((__force u64)hdr->sec.extended_src);
+ pr_debug("key source %8phC %02x\n", &key,
+ hdr->sec.key_id);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void
+__ieee802154_rx_handle_packet(struct ieee802154_local *local,
+ struct sk_buff *skb)
+{
+ int ret;
+ struct ieee802154_sub_if_data *sdata;
+ struct ieee802154_hdr hdr;
- work = kzalloc(sizeof(*work), GFP_ATOMIC);
- if (!work)
+ ret = ieee802154_parse_frame_start(skb, &hdr);
+ if (ret) {
+ pr_debug("got invalid frame\n");
+ kfree_skb(skb);
return;
+ }
+
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (sdata->vif.type != NL802154_IFTYPE_NODE ||
+ !netif_running(sdata->dev))
+ continue;
+
+ ieee802154_subif_frame(sdata, skb, &hdr);
+ skb = NULL;
+ break;
+ }
+
+ if (skb)
+ kfree_skb(skb);
+}
+
+static void
+ieee802154_monitors_rx(struct ieee802154_local *local, struct sk_buff *skb)
+{
+ struct sk_buff *skb2;
+ struct ieee802154_sub_if_data *sdata;
+
+ skb_reset_mac_header(skb);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->protocol = htons(ETH_P_IEEE802154);
+
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (sdata->vif.type != NL802154_IFTYPE_MONITOR)
+ continue;
+
+ if (!ieee802154_sdata_running(sdata))
+ continue;
+
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2) {
+ skb2->dev = sdata->dev;
+ ieee802154_deliver_skb(skb2);
+
+ sdata->dev->stats.rx_packets++;
+ sdata->dev->stats.rx_bytes += skb->len;
+ }
+ }
+}
+
+void ieee802154_rx(struct ieee802154_hw *hw, struct sk_buff *skb)
+{
+ struct ieee802154_local *local = hw_to_local(hw);
+ u16 crc;
- INIT_WORK(&work->work, mac802154_rx_worker);
- work->skb = skb;
- work->dev = dev;
- work->lqi = lqi;
+ WARN_ON_ONCE(softirq_count() == 0);
- queue_work(priv->dev_workqueue, &work->work);
+ /* TODO: When a transceiver omits the checksum here, we
+ * add an own calculated one. This is currently an ugly
+ * solution because the monitor needs a crc here.
+ */
+ if (local->hw.flags & IEEE802154_HW_RX_OMIT_CKSUM) {
+ crc = crc_ccitt(0, skb->data, skb->len);
+ put_unaligned_le16(crc, skb_put(skb, 2));
+ }
+
+ rcu_read_lock();
+
+ ieee802154_monitors_rx(local, skb);
+
+ /* Check if transceiver doesn't validate the checksum.
+ * If not we validate the checksum here.
+ */
+ if (local->hw.flags & IEEE802154_HW_RX_DROP_BAD_CKSUM) {
+ crc = crc_ccitt(0, skb->data, skb->len);
+ if (crc) {
+ rcu_read_unlock();
+ kfree_skb(skb);
+ return;
+ }
+ }
+ /* remove crc */
+ skb_trim(skb, skb->len - 2);
+
+ __ieee802154_rx_handle_packet(local, skb);
+
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL(ieee802154_rx);
+
+void
+ieee802154_rx_irqsafe(struct ieee802154_hw *hw, struct sk_buff *skb, u8 lqi)
+{
+ struct ieee802154_local *local = hw_to_local(hw);
+
+ mac_cb(skb)->lqi = lqi;
+ skb->pkt_type = IEEE802154_RX_MSG;
+ skb_queue_tail(&local->skb_queue, skb);
+ tasklet_schedule(&local->tasklet);
}
EXPORT_SYMBOL(ieee802154_rx_irqsafe);
diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c
index fdf4c0e67259..c62e95695c78 100644
--- a/net/mac802154/tx.c
+++ b/net/mac802154/tx.c
@@ -10,10 +10,6 @@
* 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.
- *
* Written by:
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
* Sergey Lapin <slapin@ossfans.org>
@@ -24,106 +20,98 @@
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/crc-ccitt.h>
+#include <asm/unaligned.h>
+#include <net/rtnetlink.h>
#include <net/ieee802154_netdev.h>
#include <net/mac802154.h>
-#include <net/wpan-phy.h>
+#include <net/cfg802154.h>
-#include "mac802154.h"
+#include "ieee802154_i.h"
+#include "driver-ops.h"
/* IEEE 802.15.4 transceivers can sleep during the xmit session, so process
* packets through the workqueue.
*/
-struct xmit_work {
+struct ieee802154_xmit_cb {
struct sk_buff *skb;
struct work_struct work;
- struct mac802154_priv *priv;
- u8 chan;
- u8 page;
+ struct ieee802154_local *local;
};
-static void mac802154_xmit_worker(struct work_struct *work)
+static struct ieee802154_xmit_cb ieee802154_xmit_cb;
+
+static void ieee802154_xmit_worker(struct work_struct *work)
{
- struct xmit_work *xw = container_of(work, struct xmit_work, work);
- struct mac802154_sub_if_data *sdata;
+ struct ieee802154_xmit_cb *cb =
+ container_of(work, struct ieee802154_xmit_cb, work);
+ struct ieee802154_local *local = cb->local;
+ struct sk_buff *skb = cb->skb;
+ struct net_device *dev = skb->dev;
int res;
- mutex_lock(&xw->priv->phy->pib_lock);
- if (xw->priv->phy->current_channel != xw->chan ||
- xw->priv->phy->current_page != xw->page) {
- res = xw->priv->ops->set_channel(&xw->priv->hw,
- xw->page,
- xw->chan);
- if (res) {
- pr_debug("set_channel failed\n");
- goto out;
- }
+ rtnl_lock();
- xw->priv->phy->current_channel = xw->chan;
- xw->priv->phy->current_page = xw->page;
- }
+ /* check if ifdown occurred while schedule */
+ if (!netif_running(dev))
+ goto err_tx;
- res = xw->priv->ops->xmit(&xw->priv->hw, xw->skb);
+ res = drv_xmit_sync(local, skb);
if (res)
- pr_debug("transmission failed\n");
+ goto err_tx;
-out:
- mutex_unlock(&xw->priv->phy->pib_lock);
+ ieee802154_xmit_complete(&local->hw, skb, false);
- /* Restart the netif queue on each sub_if_data object. */
- rcu_read_lock();
- list_for_each_entry_rcu(sdata, &xw->priv->slaves, list)
- netif_wake_queue(sdata->dev);
- rcu_read_unlock();
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
- dev_kfree_skb(xw->skb);
+ rtnl_unlock();
- kfree(xw);
+ return;
+
+err_tx:
+ /* Restart the netif queue on each sub_if_data object. */
+ ieee802154_wake_queue(&local->hw);
+ rtnl_unlock();
+ kfree_skb(skb);
+ netdev_dbg(dev, "transmission failed\n");
}
-netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb,
- u8 page, u8 chan)
+static netdev_tx_t
+ieee802154_tx(struct ieee802154_local *local, struct sk_buff *skb)
{
- struct xmit_work *work;
- struct mac802154_sub_if_data *sdata;
-
- if (!(priv->phy->channels_supported[page] & (1 << chan))) {
- WARN_ON(1);
- goto err_tx;
- }
-
- mac802154_monitors_rx(mac802154_to_priv(&priv->hw), skb);
+ struct net_device *dev = skb->dev;
+ int ret;
- if (!(priv->hw.flags & IEEE802154_HW_OMIT_CKSUM)) {
+ if (!(local->hw.flags & IEEE802154_HW_TX_OMIT_CKSUM)) {
u16 crc = crc_ccitt(0, skb->data, skb->len);
- u8 *data = skb_put(skb, 2);
- data[0] = crc & 0xff;
- data[1] = crc >> 8;
+ put_unaligned_le16(crc, skb_put(skb, 2));
}
- if (skb_cow_head(skb, priv->hw.extra_tx_headroom))
+ if (skb_cow_head(skb, local->hw.extra_tx_headroom))
goto err_tx;
- work = kzalloc(sizeof(*work), GFP_ATOMIC);
- if (!work) {
- kfree_skb(skb);
- return NETDEV_TX_BUSY;
- }
-
/* Stop the netif queue on each sub_if_data object. */
- rcu_read_lock();
- list_for_each_entry_rcu(sdata, &priv->slaves, list)
- netif_stop_queue(sdata->dev);
- rcu_read_unlock();
+ ieee802154_stop_queue(&local->hw);
+
+ /* async is priority, otherwise sync is fallback */
+ if (local->ops->xmit_async) {
+ ret = drv_xmit_async(local, skb);
+ if (ret) {
+ ieee802154_wake_queue(&local->hw);
+ goto err_tx;
+ }
- INIT_WORK(&work->work, mac802154_xmit_worker);
- work->skb = skb;
- work->priv = priv;
- work->page = page;
- work->chan = chan;
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
+ } else {
+ INIT_WORK(&ieee802154_xmit_cb.work, ieee802154_xmit_worker);
+ ieee802154_xmit_cb.skb = skb;
+ ieee802154_xmit_cb.local = local;
- queue_work(priv->dev_workqueue, &work->work);
+ queue_work(local->workqueue, &ieee802154_xmit_cb.work);
+ }
return NETDEV_TX_OK;
@@ -131,3 +119,31 @@ err_tx:
kfree_skb(skb);
return NETDEV_TX_OK;
}
+
+netdev_tx_t
+ieee802154_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+
+ skb->skb_iif = dev->ifindex;
+
+ return ieee802154_tx(sdata->local, skb);
+}
+
+netdev_tx_t
+ieee802154_subif_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ int rc;
+
+ rc = mac802154_llsec_encrypt(&sdata->sec, skb);
+ if (rc) {
+ netdev_warn(dev, "encryption failed: %i\n", rc);
+ kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ skb->skb_iif = dev->ifindex;
+
+ return ieee802154_tx(sdata->local, skb);
+}
diff --git a/net/mac802154/util.c b/net/mac802154/util.c
new file mode 100644
index 000000000000..5fc979027919
--- /dev/null
+++ b/net/mac802154/util.c
@@ -0,0 +1,84 @@
+/* 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.
+ *
+ * Authors:
+ * Alexander Aring <aar@pengutronix.de>
+ *
+ * Based on: net/mac80211/util.c
+ */
+
+#include "ieee802154_i.h"
+
+/* privid for wpan_phys to determine whether they belong to us or not */
+const void *const mac802154_wpan_phy_privid = &mac802154_wpan_phy_privid;
+
+void ieee802154_wake_queue(struct ieee802154_hw *hw)
+{
+ struct ieee802154_local *local = hw_to_local(hw);
+ struct ieee802154_sub_if_data *sdata;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (!sdata->dev)
+ continue;
+
+ netif_wake_queue(sdata->dev);
+ }
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL(ieee802154_wake_queue);
+
+void ieee802154_stop_queue(struct ieee802154_hw *hw)
+{
+ struct ieee802154_local *local = hw_to_local(hw);
+ struct ieee802154_sub_if_data *sdata;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (!sdata->dev)
+ continue;
+
+ netif_stop_queue(sdata->dev);
+ }
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL(ieee802154_stop_queue);
+
+enum hrtimer_restart ieee802154_xmit_ifs_timer(struct hrtimer *timer)
+{
+ struct ieee802154_local *local =
+ container_of(timer, struct ieee802154_local, ifs_timer);
+
+ ieee802154_wake_queue(&local->hw);
+
+ return HRTIMER_NORESTART;
+}
+
+void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb,
+ bool ifs_handling)
+{
+ if (ifs_handling) {
+ struct ieee802154_local *local = hw_to_local(hw);
+
+ if (skb->len > 18)
+ hrtimer_start(&local->ifs_timer,
+ ktime_set(0, hw->phy->lifs_period * NSEC_PER_USEC),
+ HRTIMER_MODE_REL);
+ else
+ hrtimer_start(&local->ifs_timer,
+ ktime_set(0, hw->phy->sifs_period * NSEC_PER_USEC),
+ HRTIMER_MODE_REL);
+
+ consume_skb(skb);
+ } else {
+ ieee802154_wake_queue(hw);
+ consume_skb(skb);
+ }
+}
+EXPORT_SYMBOL(ieee802154_xmit_complete);
diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c
deleted file mode 100644
index 4ab86a57dca5..000000000000
--- a/net/mac802154/wpan.c
+++ /dev/null
@@ -1,599 +0,0 @@
-/*
- * Copyright 2007-2012 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.
- *
- * Written by:
- * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
- * Sergey Lapin <slapin@ossfans.org>
- * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
- * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
- */
-
-#include <linux/netdevice.h>
-#include <linux/module.h>
-#include <linux/if_arp.h>
-
-#include <net/rtnetlink.h>
-#include <linux/nl802154.h>
-#include <net/af_ieee802154.h>
-#include <net/mac802154.h>
-#include <net/ieee802154_netdev.h>
-#include <net/ieee802154.h>
-#include <net/wpan-phy.h>
-
-#include "mac802154.h"
-
-static int mac802154_wpan_update_llsec(struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
- int rc = 0;
-
- if (ops->llsec) {
- struct ieee802154_llsec_params params;
- int changed = 0;
-
- params.pan_id = priv->pan_id;
- changed |= IEEE802154_LLSEC_PARAM_PAN_ID;
-
- params.hwaddr = priv->extended_addr;
- changed |= IEEE802154_LLSEC_PARAM_HWADDR;
-
- rc = ops->llsec->set_params(dev, &params, changed);
- }
-
- return rc;
-}
-
-static int
-mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct sockaddr_ieee802154 *sa =
- (struct sockaddr_ieee802154 *)&ifr->ifr_addr;
- int err = -ENOIOCTLCMD;
-
- spin_lock_bh(&priv->mib_lock);
-
- switch (cmd) {
- case SIOCGIFADDR:
- {
- u16 pan_id, short_addr;
-
- pan_id = le16_to_cpu(priv->pan_id);
- short_addr = le16_to_cpu(priv->short_addr);
- if (pan_id == IEEE802154_PANID_BROADCAST ||
- short_addr == IEEE802154_ADDR_BROADCAST) {
- err = -EADDRNOTAVAIL;
- break;
- }
-
- sa->family = AF_IEEE802154;
- sa->addr.addr_type = IEEE802154_ADDR_SHORT;
- sa->addr.pan_id = pan_id;
- sa->addr.short_addr = short_addr;
-
- err = 0;
- break;
- }
- case SIOCSIFADDR:
- dev_warn(&dev->dev,
- "Using DEBUGing ioctl SIOCSIFADDR isn't recommended!\n");
- if (sa->family != AF_IEEE802154 ||
- sa->addr.addr_type != IEEE802154_ADDR_SHORT ||
- sa->addr.pan_id == IEEE802154_PANID_BROADCAST ||
- sa->addr.short_addr == IEEE802154_ADDR_BROADCAST ||
- sa->addr.short_addr == IEEE802154_ADDR_UNDEF) {
- err = -EINVAL;
- break;
- }
-
- priv->pan_id = cpu_to_le16(sa->addr.pan_id);
- priv->short_addr = cpu_to_le16(sa->addr.short_addr);
-
- err = mac802154_wpan_update_llsec(dev);
- break;
- }
-
- spin_unlock_bh(&priv->mib_lock);
- return err;
-}
-
-static int mac802154_wpan_mac_addr(struct net_device *dev, void *p)
-{
- struct sockaddr *addr = p;
-
- if (netif_running(dev))
- return -EBUSY;
-
- /* FIXME: validate addr */
- memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
- mac802154_dev_set_ieee_addr(dev);
- return mac802154_wpan_update_llsec(dev);
-}
-
-int mac802154_set_mac_params(struct net_device *dev,
- const struct ieee802154_mac_params *params)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
-
- mutex_lock(&priv->hw->slaves_mtx);
- priv->mac_params = *params;
- mutex_unlock(&priv->hw->slaves_mtx);
-
- return 0;
-}
-
-void mac802154_get_mac_params(struct net_device *dev,
- struct ieee802154_mac_params *params)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
-
- mutex_lock(&priv->hw->slaves_mtx);
- *params = priv->mac_params;
- mutex_unlock(&priv->hw->slaves_mtx);
-}
-
-static int mac802154_wpan_open(struct net_device *dev)
-{
- int rc;
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct wpan_phy *phy = priv->hw->phy;
-
- rc = mac802154_slave_open(dev);
- if (rc < 0)
- return rc;
-
- mutex_lock(&phy->pib_lock);
-
- if (phy->set_txpower) {
- rc = phy->set_txpower(phy, priv->mac_params.transmit_power);
- if (rc < 0)
- goto out;
- }
-
- if (phy->set_lbt) {
- rc = phy->set_lbt(phy, priv->mac_params.lbt);
- if (rc < 0)
- goto out;
- }
-
- if (phy->set_cca_mode) {
- rc = phy->set_cca_mode(phy, priv->mac_params.cca_mode);
- if (rc < 0)
- goto out;
- }
-
- if (phy->set_cca_ed_level) {
- rc = phy->set_cca_ed_level(phy, priv->mac_params.cca_ed_level);
- if (rc < 0)
- goto out;
- }
-
- if (phy->set_csma_params) {
- rc = phy->set_csma_params(phy, priv->mac_params.min_be,
- priv->mac_params.max_be,
- priv->mac_params.csma_retries);
- if (rc < 0)
- goto out;
- }
-
- if (phy->set_frame_retries) {
- rc = phy->set_frame_retries(phy,
- priv->mac_params.frame_retries);
- if (rc < 0)
- goto out;
- }
-
- mutex_unlock(&phy->pib_lock);
- return 0;
-
-out:
- mutex_unlock(&phy->pib_lock);
- return rc;
-}
-
-static int mac802154_set_header_security(struct mac802154_sub_if_data *priv,
- struct ieee802154_hdr *hdr,
- const struct ieee802154_mac_cb *cb)
-{
- struct ieee802154_llsec_params params;
- u8 level;
-
- mac802154_llsec_get_params(&priv->sec, &params);
-
- if (!params.enabled && cb->secen_override && cb->secen)
- return -EINVAL;
- if (!params.enabled ||
- (cb->secen_override && !cb->secen) ||
- !params.out_level)
- return 0;
- if (cb->seclevel_override && !cb->seclevel)
- return -EINVAL;
-
- level = cb->seclevel_override ? cb->seclevel : params.out_level;
-
- hdr->fc.security_enabled = 1;
- hdr->sec.level = level;
- hdr->sec.key_id_mode = params.out_key.mode;
- if (params.out_key.mode == IEEE802154_SCF_KEY_SHORT_INDEX)
- hdr->sec.short_src = params.out_key.short_source;
- else if (params.out_key.mode == IEEE802154_SCF_KEY_HW_INDEX)
- hdr->sec.extended_src = params.out_key.extended_source;
- hdr->sec.key_id = params.out_key.id;
-
- return 0;
-}
-
-static int mac802154_header_create(struct sk_buff *skb,
- struct net_device *dev,
- unsigned short type,
- const void *daddr,
- const void *saddr,
- unsigned len)
-{
- struct ieee802154_hdr hdr;
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct ieee802154_mac_cb *cb = mac_cb(skb);
- int hlen;
-
- if (!daddr)
- return -EINVAL;
-
- memset(&hdr.fc, 0, sizeof(hdr.fc));
- hdr.fc.type = cb->type;
- hdr.fc.security_enabled = cb->secen;
- hdr.fc.ack_request = cb->ackreq;
- hdr.seq = ieee802154_mlme_ops(dev)->get_dsn(dev);
-
- if (mac802154_set_header_security(priv, &hdr, cb) < 0)
- return -EINVAL;
-
- if (!saddr) {
- spin_lock_bh(&priv->mib_lock);
-
- if (priv->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST) ||
- priv->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) ||
- priv->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) {
- hdr.source.mode = IEEE802154_ADDR_LONG;
- hdr.source.extended_addr = priv->extended_addr;
- } else {
- hdr.source.mode = IEEE802154_ADDR_SHORT;
- hdr.source.short_addr = priv->short_addr;
- }
-
- hdr.source.pan_id = priv->pan_id;
-
- spin_unlock_bh(&priv->mib_lock);
- } else {
- hdr.source = *(const struct ieee802154_addr *)saddr;
- }
-
- hdr.dest = *(const struct ieee802154_addr *)daddr;
-
- hlen = ieee802154_hdr_push(skb, &hdr);
- if (hlen < 0)
- return -EINVAL;
-
- skb_reset_mac_header(skb);
- skb->mac_len = hlen;
-
- if (len > ieee802154_max_payload(&hdr))
- return -EMSGSIZE;
-
- return hlen;
-}
-
-static int
-mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr)
-{
- struct ieee802154_hdr hdr;
- struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr;
-
- if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) {
- pr_debug("malformed packet\n");
- return 0;
- }
-
- *addr = hdr.source;
- return sizeof(*addr);
-}
-
-static netdev_tx_t
-mac802154_wpan_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv;
- u8 chan, page;
- int rc;
-
- priv = netdev_priv(dev);
-
- spin_lock_bh(&priv->mib_lock);
- chan = priv->chan;
- page = priv->page;
- spin_unlock_bh(&priv->mib_lock);
-
- if (chan == MAC802154_CHAN_NONE ||
- page >= WPAN_NUM_PAGES ||
- chan >= WPAN_NUM_CHANNELS) {
- kfree_skb(skb);
- return NETDEV_TX_OK;
- }
-
- rc = mac802154_llsec_encrypt(&priv->sec, skb);
- if (rc) {
- pr_warn("encryption failed: %i\n", rc);
- kfree_skb(skb);
- return NETDEV_TX_OK;
- }
-
- skb->skb_iif = dev->ifindex;
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += skb->len;
-
- return mac802154_tx(priv->hw, skb, page, chan);
-}
-
-static struct header_ops mac802154_header_ops = {
- .create = mac802154_header_create,
- .parse = mac802154_header_parse,
-};
-
-static const struct net_device_ops mac802154_wpan_ops = {
- .ndo_open = mac802154_wpan_open,
- .ndo_stop = mac802154_slave_close,
- .ndo_start_xmit = mac802154_wpan_xmit,
- .ndo_do_ioctl = mac802154_wpan_ioctl,
- .ndo_set_mac_address = mac802154_wpan_mac_addr,
-};
-
-static void mac802154_wpan_free(struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
-
- mac802154_llsec_destroy(&priv->sec);
-
- free_netdev(dev);
-}
-
-void mac802154_wpan_setup(struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv;
-
- dev->addr_len = IEEE802154_ADDR_LEN;
- memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN);
-
- dev->hard_header_len = MAC802154_FRAME_HARD_HEADER_LEN;
- dev->header_ops = &mac802154_header_ops;
- dev->needed_tailroom = 2 + 16; /* FCS + MIC */
- dev->mtu = IEEE802154_MTU;
- dev->tx_queue_len = 300;
- dev->type = ARPHRD_IEEE802154;
- dev->flags = IFF_NOARP | IFF_BROADCAST;
- dev->watchdog_timeo = 0;
-
- dev->destructor = mac802154_wpan_free;
- dev->netdev_ops = &mac802154_wpan_ops;
- dev->ml_priv = &mac802154_mlme_wpan;
-
- priv = netdev_priv(dev);
- priv->type = IEEE802154_DEV_WPAN;
-
- priv->chan = MAC802154_CHAN_NONE;
- priv->page = 0;
-
- spin_lock_init(&priv->mib_lock);
- mutex_init(&priv->sec_mtx);
-
- get_random_bytes(&priv->bsn, 1);
- get_random_bytes(&priv->dsn, 1);
-
- /* defaults per 802.15.4-2011 */
- priv->mac_params.min_be = 3;
- priv->mac_params.max_be = 5;
- priv->mac_params.csma_retries = 4;
- priv->mac_params.frame_retries = -1; /* for compatibility, actual default is 3 */
-
- priv->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
- priv->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
-
- mac802154_llsec_init(&priv->sec);
-}
-
-static int mac802154_process_data(struct net_device *dev, struct sk_buff *skb)
-{
- return netif_rx_ni(skb);
-}
-
-static int
-mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb,
- const struct ieee802154_hdr *hdr)
-{
- __le16 span, sshort;
- int rc;
-
- pr_debug("getting packet via slave interface %s\n", sdata->dev->name);
-
- spin_lock_bh(&sdata->mib_lock);
-
- span = sdata->pan_id;
- sshort = sdata->short_addr;
-
- switch (mac_cb(skb)->dest.mode) {
- case IEEE802154_ADDR_NONE:
- if (mac_cb(skb)->dest.mode != IEEE802154_ADDR_NONE)
- /* FIXME: check if we are PAN coordinator */
- skb->pkt_type = PACKET_OTHERHOST;
- else
- /* ACK comes with both addresses empty */
- skb->pkt_type = PACKET_HOST;
- break;
- case IEEE802154_ADDR_LONG:
- if (mac_cb(skb)->dest.pan_id != span &&
- mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST))
- skb->pkt_type = PACKET_OTHERHOST;
- else if (mac_cb(skb)->dest.extended_addr == sdata->extended_addr)
- skb->pkt_type = PACKET_HOST;
- else
- skb->pkt_type = PACKET_OTHERHOST;
- break;
- case IEEE802154_ADDR_SHORT:
- if (mac_cb(skb)->dest.pan_id != span &&
- mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST))
- skb->pkt_type = PACKET_OTHERHOST;
- else if (mac_cb(skb)->dest.short_addr == sshort)
- skb->pkt_type = PACKET_HOST;
- else if (mac_cb(skb)->dest.short_addr ==
- cpu_to_le16(IEEE802154_ADDR_BROADCAST))
- skb->pkt_type = PACKET_BROADCAST;
- else
- skb->pkt_type = PACKET_OTHERHOST;
- break;
- default:
- spin_unlock_bh(&sdata->mib_lock);
- pr_debug("invalid dest mode\n");
- kfree_skb(skb);
- return NET_RX_DROP;
- }
-
- spin_unlock_bh(&sdata->mib_lock);
-
- skb->dev = sdata->dev;
-
- rc = mac802154_llsec_decrypt(&sdata->sec, skb);
- if (rc) {
- pr_debug("decryption failed: %i\n", rc);
- goto fail;
- }
-
- sdata->dev->stats.rx_packets++;
- sdata->dev->stats.rx_bytes += skb->len;
-
- switch (mac_cb(skb)->type) {
- case IEEE802154_FC_TYPE_DATA:
- return mac802154_process_data(sdata->dev, skb);
- default:
- pr_warn("ieee802154: bad frame received (type = %d)\n",
- mac_cb(skb)->type);
- goto fail;
- }
-
-fail:
- kfree_skb(skb);
- return NET_RX_DROP;
-}
-
-static void mac802154_print_addr(const char *name,
- const struct ieee802154_addr *addr)
-{
- if (addr->mode == IEEE802154_ADDR_NONE)
- pr_debug("%s not present\n", name);
-
- pr_debug("%s PAN ID: %04x\n", name, le16_to_cpu(addr->pan_id));
- if (addr->mode == IEEE802154_ADDR_SHORT) {
- pr_debug("%s is short: %04x\n", name,
- le16_to_cpu(addr->short_addr));
- } else {
- u64 hw = swab64((__force u64) addr->extended_addr);
-
- pr_debug("%s is hardware: %8phC\n", name, &hw);
- }
-}
-
-static int mac802154_parse_frame_start(struct sk_buff *skb,
- struct ieee802154_hdr *hdr)
-{
- int hlen;
- struct ieee802154_mac_cb *cb = mac_cb_init(skb);
-
- hlen = ieee802154_hdr_pull(skb, hdr);
- if (hlen < 0)
- return -EINVAL;
-
- skb->mac_len = hlen;
-
- pr_debug("fc: %04x dsn: %02x\n", le16_to_cpup((__le16 *)&hdr->fc),
- hdr->seq);
-
- cb->type = hdr->fc.type;
- cb->ackreq = hdr->fc.ack_request;
- cb->secen = hdr->fc.security_enabled;
-
- mac802154_print_addr("destination", &hdr->dest);
- mac802154_print_addr("source", &hdr->source);
-
- cb->source = hdr->source;
- cb->dest = hdr->dest;
-
- if (hdr->fc.security_enabled) {
- u64 key;
-
- pr_debug("seclevel %i\n", hdr->sec.level);
-
- switch (hdr->sec.key_id_mode) {
- case IEEE802154_SCF_KEY_IMPLICIT:
- pr_debug("implicit key\n");
- break;
-
- case IEEE802154_SCF_KEY_INDEX:
- pr_debug("key %02x\n", hdr->sec.key_id);
- break;
-
- case IEEE802154_SCF_KEY_SHORT_INDEX:
- pr_debug("key %04x:%04x %02x\n",
- le32_to_cpu(hdr->sec.short_src) >> 16,
- le32_to_cpu(hdr->sec.short_src) & 0xffff,
- hdr->sec.key_id);
- break;
-
- case IEEE802154_SCF_KEY_HW_INDEX:
- key = swab64((__force u64) hdr->sec.extended_src);
- pr_debug("key source %8phC %02x\n", &key,
- hdr->sec.key_id);
- break;
- }
- }
-
- return 0;
-}
-
-void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb)
-{
- int ret;
- struct mac802154_sub_if_data *sdata;
- struct ieee802154_hdr hdr;
-
- ret = mac802154_parse_frame_start(skb, &hdr);
- if (ret) {
- pr_debug("got invalid frame\n");
- kfree_skb(skb);
- return;
- }
-
- rcu_read_lock();
- list_for_each_entry_rcu(sdata, &priv->slaves, list) {
- if (sdata->type != IEEE802154_DEV_WPAN ||
- !netif_running(sdata->dev))
- continue;
-
- mac802154_subif_frame(sdata, skb, &hdr);
- skb = NULL;
- break;
- }
- rcu_read_unlock();
-
- if (skb)
- kfree_skb(skb);
-}
diff --git a/net/mpls/mpls_gso.c b/net/mpls/mpls_gso.c
index e3545f21a099..ca27837974fe 100644
--- a/net/mpls/mpls_gso.c
+++ b/net/mpls/mpls_gso.c
@@ -34,8 +34,7 @@ static struct sk_buff *mpls_gso_segment(struct sk_buff *skb,
SKB_GSO_TCP_ECN |
SKB_GSO_GRE |
SKB_GSO_GRE_CSUM |
- SKB_GSO_IPIP |
- SKB_GSO_MPLS)))
+ SKB_GSO_IPIP)))
goto out;
/* Setup inner SKB. */
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index ae5096ab65eb..b02660fa9eb0 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -411,6 +411,13 @@ config NF_NAT_TFTP
depends on NF_CONNTRACK && NF_NAT
default NF_NAT && NF_CONNTRACK_TFTP
+config NF_NAT_REDIRECT
+ tristate "IPv4/IPv6 redirect support"
+ depends on NF_NAT
+ help
+ This is the kernel functionality to redirect packets to local
+ machine through NAT.
+
config NETFILTER_SYNPROXY
tristate
@@ -505,6 +512,15 @@ config NFT_MASQ
This option adds the "masquerade" expression that you can use
to perform NAT in the masquerade flavour.
+config NFT_REDIR
+ depends on NF_TABLES
+ depends on NF_CONNTRACK
+ depends on NF_NAT
+ tristate "Netfilter nf_tables redirect support"
+ help
+ This options adds the "redirect" expression that you can use
+ to perform NAT in the redirect flavour.
+
config NFT_NAT
depends on NF_TABLES
depends on NF_CONNTRACK
@@ -835,6 +851,7 @@ config NETFILTER_XT_TARGET_RATEEST
config NETFILTER_XT_TARGET_REDIRECT
tristate "REDIRECT target support"
depends on NF_NAT
+ select NF_NAT_REDIRECT
---help---
REDIRECT is a special case of NAT: all incoming connections are
mapped onto the incoming interface's address, causing the packets to
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index a9571be3f791..89f73a9e9874 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -51,6 +51,7 @@ nf_nat-y := nf_nat_core.o nf_nat_proto_unknown.o nf_nat_proto_common.o \
obj-$(CONFIG_NF_LOG_COMMON) += nf_log_common.o
obj-$(CONFIG_NF_NAT) += nf_nat.o
+obj-$(CONFIG_NF_NAT_REDIRECT) += nf_nat_redirect.o
# NAT protocols (nf_nat)
obj-$(CONFIG_NF_NAT_PROTO_DCCP) += nf_nat_proto_dccp.o
@@ -88,6 +89,7 @@ obj-$(CONFIG_NFT_HASH) += nft_hash.o
obj-$(CONFIG_NFT_COUNTER) += nft_counter.o
obj-$(CONFIG_NFT_LOG) += nft_log.o
obj-$(CONFIG_NFT_MASQ) += nft_masq.o
+obj-$(CONFIG_NFT_REDIR) += nft_redir.o
# generic X tables
obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o
diff --git a/net/netfilter/core.c b/net/netfilter/core.c
index 024a2e25c8a4..fea9ef566427 100644
--- a/net/netfilter/core.c
+++ b/net/netfilter/core.c
@@ -17,6 +17,7 @@
#include <linux/interrupt.h>
#include <linux/if.h>
#include <linux/netdevice.h>
+#include <linux/netfilter_ipv6.h>
#include <linux/inetdevice.h>
#include <linux/proc_fs.h>
#include <linux/mutex.h>
diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
index 86f9d76b1464..d259da3ce67a 100644
--- a/net/netfilter/ipset/ip_set_core.c
+++ b/net/netfilter/ipset/ip_set_core.c
@@ -1863,6 +1863,12 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
if (*op < IP_SET_OP_VERSION) {
/* Check the version at the beginning of operations */
struct ip_set_req_version *req_version = data;
+
+ if (*len < sizeof(struct ip_set_req_version)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
if (req_version->version != IPSET_PROTOCOL) {
ret = -EPROTO;
goto done;
diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h
index fee7c64e4dd1..974ff386db0f 100644
--- a/net/netfilter/ipset/ip_set_hash_gen.h
+++ b/net/netfilter/ipset/ip_set_hash_gen.h
@@ -147,16 +147,22 @@ hbucket_elem_add(struct hbucket *n, u8 ahash_max, size_t dsize)
#else
#define __CIDR(cidr, i) (cidr)
#endif
+
+/* cidr + 1 is stored in net_prefixes to support /0 */
+#define SCIDR(cidr, i) (__CIDR(cidr, i) + 1)
+
#ifdef IP_SET_HASH_WITH_NETS_PACKED
-/* When cidr is packed with nomatch, cidr - 1 is stored in the entry */
-#define CIDR(cidr, i) (__CIDR(cidr, i) + 1)
+/* When cidr is packed with nomatch, cidr - 1 is stored in the data entry */
+#define GCIDR(cidr, i) (__CIDR(cidr, i) + 1)
+#define NCIDR(cidr) (cidr)
#else
-#define CIDR(cidr, i) (__CIDR(cidr, i))
+#define GCIDR(cidr, i) (__CIDR(cidr, i))
+#define NCIDR(cidr) (cidr - 1)
#endif
#define SET_HOST_MASK(family) (family == AF_INET ? 32 : 128)
-#ifdef IP_SET_HASH_WITH_MULTI
+#ifdef IP_SET_HASH_WITH_NET0
#define NLEN(family) (SET_HOST_MASK(family) + 1)
#else
#define NLEN(family) SET_HOST_MASK(family)
@@ -292,24 +298,22 @@ mtype_add_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n)
int i, j;
/* Add in increasing prefix order, so larger cidr first */
- for (i = 0, j = -1; i < nets_length && h->nets[i].nets[n]; i++) {
+ for (i = 0, j = -1; i < nets_length && h->nets[i].cidr[n]; i++) {
if (j != -1)
continue;
else if (h->nets[i].cidr[n] < cidr)
j = i;
else if (h->nets[i].cidr[n] == cidr) {
- h->nets[i].nets[n]++;
+ h->nets[cidr - 1].nets[n]++;
return;
}
}
if (j != -1) {
- for (; i > j; i--) {
+ for (; i > j; i--)
h->nets[i].cidr[n] = h->nets[i - 1].cidr[n];
- h->nets[i].nets[n] = h->nets[i - 1].nets[n];
- }
}
h->nets[i].cidr[n] = cidr;
- h->nets[i].nets[n] = 1;
+ h->nets[cidr - 1].nets[n] = 1;
}
static void
@@ -320,16 +324,12 @@ mtype_del_cidr(struct htype *h, u8 cidr, u8 nets_length, u8 n)
for (i = 0; i < nets_length; i++) {
if (h->nets[i].cidr[n] != cidr)
continue;
- if (h->nets[i].nets[n] > 1 || i == net_end ||
- h->nets[i + 1].nets[n] == 0) {
- h->nets[i].nets[n]--;
+ h->nets[cidr -1].nets[n]--;
+ if (h->nets[cidr -1].nets[n] > 0)
return;
- }
- for (j = i; j < net_end && h->nets[j].nets[n]; j++) {
+ for (j = i; j < net_end && h->nets[j].cidr[n]; j++)
h->nets[j].cidr[n] = h->nets[j + 1].cidr[n];
- h->nets[j].nets[n] = h->nets[j + 1].nets[n];
- }
- h->nets[j].nets[n] = 0;
+ h->nets[j].cidr[n] = 0;
return;
}
}
@@ -486,7 +486,7 @@ mtype_expire(struct ip_set *set, struct htype *h, u8 nets_length, size_t dsize)
pr_debug("expired %u/%u\n", i, j);
#ifdef IP_SET_HASH_WITH_NETS
for (k = 0; k < IPSET_NET_COUNT; k++)
- mtype_del_cidr(h, CIDR(data->cidr, k),
+ mtype_del_cidr(h, SCIDR(data->cidr, k),
nets_length, k);
#endif
ip_set_ext_destroy(set, data);
@@ -633,29 +633,6 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
bool flag_exist = flags & IPSET_FLAG_EXIST;
u32 key, multi = 0;
- if (h->elements >= h->maxelem && SET_WITH_FORCEADD(set)) {
- rcu_read_lock_bh();
- t = rcu_dereference_bh(h->table);
- key = HKEY(value, h->initval, t->htable_bits);
- n = hbucket(t,key);
- if (n->pos) {
- /* Choosing the first entry in the array to replace */
- j = 0;
- goto reuse_slot;
- }
- rcu_read_unlock_bh();
- }
- if (SET_WITH_TIMEOUT(set) && h->elements >= h->maxelem)
- /* FIXME: when set is full, we slow down here */
- mtype_expire(set, h, NLEN(set->family), set->dsize);
-
- if (h->elements >= h->maxelem) {
- if (net_ratelimit())
- pr_warn("Set %s is full, maxelem %u reached\n",
- set->name, h->maxelem);
- return -IPSET_ERR_HASH_FULL;
- }
-
rcu_read_lock_bh();
t = rcu_dereference_bh(h->table);
key = HKEY(value, h->initval, t->htable_bits);
@@ -680,15 +657,32 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
j != AHASH_MAX(h) + 1)
j = i;
}
+ if (h->elements >= h->maxelem && SET_WITH_FORCEADD(set) && n->pos) {
+ /* Choosing the first entry in the array to replace */
+ j = 0;
+ goto reuse_slot;
+ }
+ if (SET_WITH_TIMEOUT(set) && h->elements >= h->maxelem)
+ /* FIXME: when set is full, we slow down here */
+ mtype_expire(set, h, NLEN(set->family), set->dsize);
+
+ if (h->elements >= h->maxelem) {
+ if (net_ratelimit())
+ pr_warn("Set %s is full, maxelem %u reached\n",
+ set->name, h->maxelem);
+ ret = -IPSET_ERR_HASH_FULL;
+ goto out;
+ }
+
reuse_slot:
if (j != AHASH_MAX(h) + 1) {
/* Fill out reused slot */
data = ahash_data(n, j, set->dsize);
#ifdef IP_SET_HASH_WITH_NETS
for (i = 0; i < IPSET_NET_COUNT; i++) {
- mtype_del_cidr(h, CIDR(data->cidr, i),
+ mtype_del_cidr(h, SCIDR(data->cidr, i),
NLEN(set->family), i);
- mtype_add_cidr(h, CIDR(d->cidr, i),
+ mtype_add_cidr(h, SCIDR(d->cidr, i),
NLEN(set->family), i);
}
#endif
@@ -705,7 +699,7 @@ reuse_slot:
data = ahash_data(n, n->pos++, set->dsize);
#ifdef IP_SET_HASH_WITH_NETS
for (i = 0; i < IPSET_NET_COUNT; i++)
- mtype_add_cidr(h, CIDR(d->cidr, i), NLEN(set->family),
+ mtype_add_cidr(h, SCIDR(d->cidr, i), NLEN(set->family),
i);
#endif
h->elements++;
@@ -766,7 +760,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
h->elements--;
#ifdef IP_SET_HASH_WITH_NETS
for (j = 0; j < IPSET_NET_COUNT; j++)
- mtype_del_cidr(h, CIDR(d->cidr, j), NLEN(set->family),
+ mtype_del_cidr(h, SCIDR(d->cidr, j), NLEN(set->family),
j);
#endif
ip_set_ext_destroy(set, data);
@@ -827,15 +821,15 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d,
u8 nets_length = NLEN(set->family);
pr_debug("test by nets\n");
- for (; j < nets_length && h->nets[j].nets[0] && !multi; j++) {
+ for (; j < nets_length && h->nets[j].cidr[0] && !multi; j++) {
#if IPSET_NET_COUNT == 2
mtype_data_reset_elem(d, &orig);
- mtype_data_netmask(d, h->nets[j].cidr[0], false);
- for (k = 0; k < nets_length && h->nets[k].nets[1] && !multi;
+ mtype_data_netmask(d, NCIDR(h->nets[j].cidr[0]), false);
+ for (k = 0; k < nets_length && h->nets[k].cidr[1] && !multi;
k++) {
- mtype_data_netmask(d, h->nets[k].cidr[1], true);
+ mtype_data_netmask(d, NCIDR(h->nets[k].cidr[1]), true);
#else
- mtype_data_netmask(d, h->nets[j].cidr[0]);
+ mtype_data_netmask(d, NCIDR(h->nets[j].cidr[0]));
#endif
key = HKEY(d, h->initval, t->htable_bits);
n = hbucket(t, key);
@@ -883,7 +877,7 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext,
/* If we test an IP address and not a network address,
* try all possible network sizes */
for (i = 0; i < IPSET_NET_COUNT; i++)
- if (CIDR(d->cidr, i) != SET_HOST_MASK(set->family))
+ if (GCIDR(d->cidr, i) != SET_HOST_MASK(set->family))
break;
if (i == IPSET_NET_COUNT) {
ret = mtype_test_cidrs(set, d, ext, mext, flags);
@@ -1107,8 +1101,7 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set,
hsize = sizeof(*h);
#ifdef IP_SET_HASH_WITH_NETS
- hsize += sizeof(struct net_prefixes) *
- (set->family == NFPROTO_IPV4 ? 32 : 128);
+ hsize += sizeof(struct net_prefixes) * NLEN(set->family);
#endif
h = kzalloc(hsize, GFP_KERNEL);
if (!h)
diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c
index 35dd35873442..758b002130d9 100644
--- a/net/netfilter/ipset/ip_set_hash_netiface.c
+++ b/net/netfilter/ipset/ip_set_hash_netiface.c
@@ -115,6 +115,7 @@ iface_add(struct rb_root *root, const char **iface)
#define IP_SET_HASH_WITH_NETS
#define IP_SET_HASH_WITH_RBTREE
#define IP_SET_HASH_WITH_MULTI
+#define IP_SET_HASH_WITH_NET0
#define STREQ(a, b) (strcmp(a, b) == 0)
diff --git a/net/netfilter/ipset/ip_set_hash_netnet.c b/net/netfilter/ipset/ip_set_hash_netnet.c
index da00284b3571..ea8772afb6e7 100644
--- a/net/netfilter/ipset/ip_set_hash_netnet.c
+++ b/net/netfilter/ipset/ip_set_hash_netnet.c
@@ -46,6 +46,7 @@ struct hash_netnet4_elem {
__be64 ipcmp;
};
u8 nomatch;
+ u8 padding;
union {
u8 cidr[2];
u16 ccmp;
@@ -271,6 +272,7 @@ hash_netnet4_uadt(struct ip_set *set, struct nlattr *tb[],
struct hash_netnet6_elem {
union nf_inet_addr ip[2];
u8 nomatch;
+ u8 padding;
union {
u8 cidr[2];
u16 ccmp;
diff --git a/net/netfilter/ipset/ip_set_hash_netportnet.c b/net/netfilter/ipset/ip_set_hash_netportnet.c
index b8053d675fc3..bfaa94c7baa7 100644
--- a/net/netfilter/ipset/ip_set_hash_netportnet.c
+++ b/net/netfilter/ipset/ip_set_hash_netportnet.c
@@ -53,6 +53,7 @@ struct hash_netportnet4_elem {
u8 cidr[2];
u16 ccmp;
};
+ u16 padding;
u8 nomatch:1;
u8 proto;
};
@@ -324,6 +325,7 @@ struct hash_netportnet6_elem {
u8 cidr[2];
u16 ccmp;
};
+ u16 padding;
u8 nomatch:1;
u8 proto;
};
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index ac7ba689efe7..b8295a430a56 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -465,8 +465,7 @@ __ip_vs_bind_svc(struct ip_vs_dest *dest, struct ip_vs_service *svc)
static void ip_vs_service_free(struct ip_vs_service *svc)
{
- if (svc->stats.cpustats)
- free_percpu(svc->stats.cpustats);
+ free_percpu(svc->stats.cpustats);
kfree(svc);
}
diff --git a/net/netfilter/ipvs/ip_vs_pe.c b/net/netfilter/ipvs/ip_vs_pe.c
index 1a82b29ce8ea..0df17caa8af6 100644
--- a/net/netfilter/ipvs/ip_vs_pe.c
+++ b/net/netfilter/ipvs/ip_vs_pe.c
@@ -37,8 +37,7 @@ struct ip_vs_pe *__ip_vs_pe_getbyname(const char *pe_name)
rcu_read_unlock();
return pe;
}
- if (pe->module)
- module_put(pe->module);
+ module_put(pe->module);
}
rcu_read_unlock();
diff --git a/net/netfilter/ipvs/ip_vs_sched.c b/net/netfilter/ipvs/ip_vs_sched.c
index 4dbcda6258bc..199760c71f39 100644
--- a/net/netfilter/ipvs/ip_vs_sched.c
+++ b/net/netfilter/ipvs/ip_vs_sched.c
@@ -104,8 +104,7 @@ static struct ip_vs_scheduler *ip_vs_sched_getbyname(const char *sched_name)
mutex_unlock(&ip_vs_sched_mutex);
return sched;
}
- if (sched->module)
- module_put(sched->module);
+ module_put(sched->module);
}
mutex_unlock(&ip_vs_sched_mutex);
diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c
index 7162c86fd50d..c47ffd7a0a70 100644
--- a/net/netfilter/ipvs/ip_vs_sync.c
+++ b/net/netfilter/ipvs/ip_vs_sync.c
@@ -820,8 +820,7 @@ ip_vs_conn_fill_param_sync(struct net *net, int af, union ip_vs_sync_conn *sc,
p->pe_data = kmemdup(pe_data, pe_data_len, GFP_ATOMIC);
if (!p->pe_data) {
- if (p->pe->module)
- module_put(p->pe->module);
+ module_put(p->pe->module);
return -ENOMEM;
}
p->pe_data_len = pe_data_len;
diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
index 437a3663ad03..3aedbda7658a 100644
--- a/net/netfilter/ipvs/ip_vs_xmit.c
+++ b/net/netfilter/ipvs/ip_vs_xmit.c
@@ -293,7 +293,6 @@ __ip_vs_get_out_rt(int skb_af, struct sk_buff *skb, struct ip_vs_dest *dest,
&dest->addr.ip, &dest_dst->dst_saddr.ip,
atomic_read(&rt->dst.__refcnt));
}
- daddr = dest->addr.ip;
if (ret_saddr)
*ret_saddr = dest_dst->dst_saddr.ip;
} else {
@@ -344,7 +343,7 @@ __ip_vs_get_out_rt(int skb_af, struct sk_buff *skb, struct ip_vs_dest *dest,
skb_dst_drop(skb);
if (noref) {
if (!local)
- skb_dst_set_noref_force(skb, &rt->dst);
+ skb_dst_set_noref(skb, &rt->dst);
else
skb_dst_set(skb, dst_clone(&rt->dst));
} else
@@ -488,7 +487,7 @@ __ip_vs_get_out_rt_v6(int skb_af, struct sk_buff *skb, struct ip_vs_dest *dest,
skb_dst_drop(skb);
if (noref) {
if (!local)
- skb_dst_set_noref_force(skb, &rt->dst);
+ skb_dst_set_noref(skb, &rt->dst);
else
skb_dst_set(skb, dst_clone(&rt->dst));
} else
@@ -846,6 +845,8 @@ ip_vs_prepare_tunneled_skb(struct sk_buff *skb, int skb_af,
new_skb = skb_realloc_headroom(skb, max_headroom);
if (!new_skb)
goto error;
+ if (skb->sk)
+ skb_set_owner_w(new_skb, skb->sk);
consume_skb(skb);
skb = new_skb;
}
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index 5016a6929085..a11674806707 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -824,22 +824,19 @@ __nf_conntrack_alloc(struct net *net, u16 zone,
atomic_dec(&net->ct.count);
return ERR_PTR(-ENOMEM);
}
- /*
- * Let ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode.next
- * and ct->tuplehash[IP_CT_DIR_REPLY].hnnode.next unchanged.
- */
- memset(&ct->tuplehash[IP_CT_DIR_MAX], 0,
- offsetof(struct nf_conn, proto) -
- offsetof(struct nf_conn, tuplehash[IP_CT_DIR_MAX]));
spin_lock_init(&ct->lock);
ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *orig;
ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode.pprev = NULL;
ct->tuplehash[IP_CT_DIR_REPLY].tuple = *repl;
/* save hash for reusing when confirming */
*(unsigned long *)(&ct->tuplehash[IP_CT_DIR_REPLY].hnnode.pprev) = hash;
+ ct->status = 0;
/* Don't set timer yet: wait for confirmation */
setup_timer(&ct->timeout, death_by_timeout, (unsigned long)ct);
write_pnet(&ct->ct_net, net);
+ memset(&ct->__nfct_init_offset[0], 0,
+ offsetof(struct nf_conn, proto) -
+ offsetof(struct nf_conn, __nfct_init_offset[0]));
#ifdef CONFIG_NF_CONNTRACK_ZONES
if (zone) {
struct nf_conntrack_zone *nf_ct_zone;
diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c
index 3a3a60b126e0..1d69f5b9748f 100644
--- a/net/netfilter/nf_conntrack_h323_main.c
+++ b/net/netfilter/nf_conntrack_h323_main.c
@@ -728,7 +728,8 @@ static int expect_h245(struct sk_buff *skb, struct nf_conn *ct,
/* If the calling party is on the same side of the forward-to party,
* we don't need to track the second call */
-static int callforward_do_filter(const union nf_inet_addr *src,
+static int callforward_do_filter(struct net *net,
+ const union nf_inet_addr *src,
const union nf_inet_addr *dst,
u_int8_t family)
{
@@ -750,9 +751,9 @@ static int callforward_do_filter(const union nf_inet_addr *src,
memset(&fl2, 0, sizeof(fl2));
fl2.daddr = dst->ip;
- if (!afinfo->route(&init_net, (struct dst_entry **)&rt1,
+ if (!afinfo->route(net, (struct dst_entry **)&rt1,
flowi4_to_flowi(&fl1), false)) {
- if (!afinfo->route(&init_net, (struct dst_entry **)&rt2,
+ if (!afinfo->route(net, (struct dst_entry **)&rt2,
flowi4_to_flowi(&fl2), false)) {
if (rt_nexthop(rt1, fl1.daddr) ==
rt_nexthop(rt2, fl2.daddr) &&
@@ -774,9 +775,9 @@ static int callforward_do_filter(const union nf_inet_addr *src,
memset(&fl2, 0, sizeof(fl2));
fl2.daddr = dst->in6;
- if (!afinfo->route(&init_net, (struct dst_entry **)&rt1,
+ if (!afinfo->route(net, (struct dst_entry **)&rt1,
flowi6_to_flowi(&fl1), false)) {
- if (!afinfo->route(&init_net, (struct dst_entry **)&rt2,
+ if (!afinfo->route(net, (struct dst_entry **)&rt2,
flowi6_to_flowi(&fl2), false)) {
if (ipv6_addr_equal(rt6_nexthop(rt1),
rt6_nexthop(rt2)) &&
@@ -807,6 +808,7 @@ static int expect_callforwarding(struct sk_buff *skb,
__be16 port;
union nf_inet_addr addr;
struct nf_conntrack_expect *exp;
+ struct net *net = nf_ct_net(ct);
typeof(nat_callforwarding_hook) nat_callforwarding;
/* Read alternativeAddress */
@@ -816,7 +818,7 @@ static int expect_callforwarding(struct sk_buff *skb,
/* If the calling party is on the same side of the forward-to party,
* we don't need to track the second call */
if (callforward_filter &&
- callforward_do_filter(&addr, &ct->tuplehash[!dir].tuple.src.u3,
+ callforward_do_filter(net, &addr, &ct->tuplehash[!dir].tuple.src.u3,
nf_ct_l3num(ct))) {
pr_debug("nf_ct_q931: Call Forwarding not tracked\n");
return 0;
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c
index 5b3eae7d4c9a..bd9d31537905 100644
--- a/net/netfilter/nf_conntrack_helper.c
+++ b/net/netfilter/nf_conntrack_helper.c
@@ -250,7 +250,7 @@ out:
}
EXPORT_SYMBOL_GPL(__nf_ct_try_assign_helper);
-/* appropiate ct lock protecting must be taken by caller */
+/* appropriate ct lock protecting must be taken by caller */
static inline int unhelp(struct nf_conntrack_tuple_hash *i,
const struct nf_conntrack_helper *me)
{
diff --git a/net/netfilter/nf_conntrack_l3proto_generic.c b/net/netfilter/nf_conntrack_l3proto_generic.c
index e7eb807fe07d..cf9ace70bece 100644
--- a/net/netfilter/nf_conntrack_l3proto_generic.c
+++ b/net/netfilter/nf_conntrack_l3proto_generic.c
@@ -49,10 +49,9 @@ static bool generic_invert_tuple(struct nf_conntrack_tuple *tuple,
return true;
}
-static int generic_print_tuple(struct seq_file *s,
- const struct nf_conntrack_tuple *tuple)
+static void generic_print_tuple(struct seq_file *s,
+ const struct nf_conntrack_tuple *tuple)
{
- return 0;
}
static int generic_get_l4proto(const struct sk_buff *skb, unsigned int nhoff,
diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c
index cb372f96f10d..6dd995c7c72b 100644
--- a/net/netfilter/nf_conntrack_proto_dccp.c
+++ b/net/netfilter/nf_conntrack_proto_dccp.c
@@ -618,17 +618,17 @@ out_invalid:
return -NF_ACCEPT;
}
-static int dccp_print_tuple(struct seq_file *s,
- const struct nf_conntrack_tuple *tuple)
+static void dccp_print_tuple(struct seq_file *s,
+ const struct nf_conntrack_tuple *tuple)
{
- return seq_printf(s, "sport=%hu dport=%hu ",
- ntohs(tuple->src.u.dccp.port),
- ntohs(tuple->dst.u.dccp.port));
+ seq_printf(s, "sport=%hu dport=%hu ",
+ ntohs(tuple->src.u.dccp.port),
+ ntohs(tuple->dst.u.dccp.port));
}
-static int dccp_print_conntrack(struct seq_file *s, struct nf_conn *ct)
+static void dccp_print_conntrack(struct seq_file *s, struct nf_conn *ct)
{
- return seq_printf(s, "%s ", dccp_state_names[ct->proto.dccp.state]);
+ seq_printf(s, "%s ", dccp_state_names[ct->proto.dccp.state]);
}
#if IS_ENABLED(CONFIG_NF_CT_NETLINK)
diff --git a/net/netfilter/nf_conntrack_proto_generic.c b/net/netfilter/nf_conntrack_proto_generic.c
index 957c1db66652..60865f110309 100644
--- a/net/netfilter/nf_conntrack_proto_generic.c
+++ b/net/netfilter/nf_conntrack_proto_generic.c
@@ -63,10 +63,9 @@ static bool generic_invert_tuple(struct nf_conntrack_tuple *tuple,
}
/* Print out the per-protocol part of the tuple. */
-static int generic_print_tuple(struct seq_file *s,
- const struct nf_conntrack_tuple *tuple)
+static void generic_print_tuple(struct seq_file *s,
+ const struct nf_conntrack_tuple *tuple)
{
- return 0;
}
static unsigned int *generic_get_timeouts(struct net *net)
diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c
index d5665739e3b1..7648674f29c3 100644
--- a/net/netfilter/nf_conntrack_proto_gre.c
+++ b/net/netfilter/nf_conntrack_proto_gre.c
@@ -226,20 +226,20 @@ static bool gre_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff,
}
/* print gre part of tuple */
-static int gre_print_tuple(struct seq_file *s,
- const struct nf_conntrack_tuple *tuple)
+static void gre_print_tuple(struct seq_file *s,
+ const struct nf_conntrack_tuple *tuple)
{
- return seq_printf(s, "srckey=0x%x dstkey=0x%x ",
- ntohs(tuple->src.u.gre.key),
- ntohs(tuple->dst.u.gre.key));
+ seq_printf(s, "srckey=0x%x dstkey=0x%x ",
+ ntohs(tuple->src.u.gre.key),
+ ntohs(tuple->dst.u.gre.key));
}
/* print private data for conntrack */
-static int gre_print_conntrack(struct seq_file *s, struct nf_conn *ct)
+static void gre_print_conntrack(struct seq_file *s, struct nf_conn *ct)
{
- return seq_printf(s, "timeout=%u, stream_timeout=%u ",
- (ct->proto.gre.timeout / HZ),
- (ct->proto.gre.stream_timeout / HZ));
+ seq_printf(s, "timeout=%u, stream_timeout=%u ",
+ (ct->proto.gre.timeout / HZ),
+ (ct->proto.gre.stream_timeout / HZ));
}
static unsigned int *gre_get_timeouts(struct net *net)
diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c
index 1314d33f6bcf..b45da90fad32 100644
--- a/net/netfilter/nf_conntrack_proto_sctp.c
+++ b/net/netfilter/nf_conntrack_proto_sctp.c
@@ -166,16 +166,16 @@ static bool sctp_invert_tuple(struct nf_conntrack_tuple *tuple,
}
/* Print out the per-protocol part of the tuple. */
-static int sctp_print_tuple(struct seq_file *s,
- const struct nf_conntrack_tuple *tuple)
+static void sctp_print_tuple(struct seq_file *s,
+ const struct nf_conntrack_tuple *tuple)
{
- return seq_printf(s, "sport=%hu dport=%hu ",
- ntohs(tuple->src.u.sctp.port),
- ntohs(tuple->dst.u.sctp.port));
+ seq_printf(s, "sport=%hu dport=%hu ",
+ ntohs(tuple->src.u.sctp.port),
+ ntohs(tuple->dst.u.sctp.port));
}
/* Print out the private part of the conntrack. */
-static int sctp_print_conntrack(struct seq_file *s, struct nf_conn *ct)
+static void sctp_print_conntrack(struct seq_file *s, struct nf_conn *ct)
{
enum sctp_conntrack state;
@@ -183,7 +183,7 @@ static int sctp_print_conntrack(struct seq_file *s, struct nf_conn *ct)
state = ct->proto.sctp.state;
spin_unlock_bh(&ct->lock);
- return seq_printf(s, "%s ", sctp_conntrack_names[state]);
+ seq_printf(s, "%s ", sctp_conntrack_names[state]);
}
#define for_each_sctp_chunk(skb, sch, _sch, offset, dataoff, count) \
diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c
index d87b6423ffb2..5caa0c41bf26 100644
--- a/net/netfilter/nf_conntrack_proto_tcp.c
+++ b/net/netfilter/nf_conntrack_proto_tcp.c
@@ -302,16 +302,16 @@ static bool tcp_invert_tuple(struct nf_conntrack_tuple *tuple,
}
/* Print out the per-protocol part of the tuple. */
-static int tcp_print_tuple(struct seq_file *s,
- const struct nf_conntrack_tuple *tuple)
+static void tcp_print_tuple(struct seq_file *s,
+ const struct nf_conntrack_tuple *tuple)
{
- return seq_printf(s, "sport=%hu dport=%hu ",
- ntohs(tuple->src.u.tcp.port),
- ntohs(tuple->dst.u.tcp.port));
+ seq_printf(s, "sport=%hu dport=%hu ",
+ ntohs(tuple->src.u.tcp.port),
+ ntohs(tuple->dst.u.tcp.port));
}
/* Print out the private part of the conntrack. */
-static int tcp_print_conntrack(struct seq_file *s, struct nf_conn *ct)
+static void tcp_print_conntrack(struct seq_file *s, struct nf_conn *ct)
{
enum tcp_conntrack state;
@@ -319,7 +319,7 @@ static int tcp_print_conntrack(struct seq_file *s, struct nf_conn *ct)
state = ct->proto.tcp.state;
spin_unlock_bh(&ct->lock);
- return seq_printf(s, "%s ", tcp_conntrack_names[state]);
+ seq_printf(s, "%s ", tcp_conntrack_names[state]);
}
static unsigned int get_conntrack_index(const struct tcphdr *tcph)
diff --git a/net/netfilter/nf_conntrack_proto_udp.c b/net/netfilter/nf_conntrack_proto_udp.c
index 9d7721cbce4b..6957281ffee5 100644
--- a/net/netfilter/nf_conntrack_proto_udp.c
+++ b/net/netfilter/nf_conntrack_proto_udp.c
@@ -63,12 +63,12 @@ static bool udp_invert_tuple(struct nf_conntrack_tuple *tuple,
}
/* Print out the per-protocol part of the tuple. */
-static int udp_print_tuple(struct seq_file *s,
- const struct nf_conntrack_tuple *tuple)
+static void udp_print_tuple(struct seq_file *s,
+ const struct nf_conntrack_tuple *tuple)
{
- return seq_printf(s, "sport=%hu dport=%hu ",
- ntohs(tuple->src.u.udp.port),
- ntohs(tuple->dst.u.udp.port));
+ seq_printf(s, "sport=%hu dport=%hu ",
+ ntohs(tuple->src.u.udp.port),
+ ntohs(tuple->dst.u.udp.port));
}
static unsigned int *udp_get_timeouts(struct net *net)
diff --git a/net/netfilter/nf_conntrack_proto_udplite.c b/net/netfilter/nf_conntrack_proto_udplite.c
index 2750e6c69f82..c5903d1649f9 100644
--- a/net/netfilter/nf_conntrack_proto_udplite.c
+++ b/net/netfilter/nf_conntrack_proto_udplite.c
@@ -71,12 +71,12 @@ static bool udplite_invert_tuple(struct nf_conntrack_tuple *tuple,
}
/* Print out the per-protocol part of the tuple. */
-static int udplite_print_tuple(struct seq_file *s,
- const struct nf_conntrack_tuple *tuple)
+static void udplite_print_tuple(struct seq_file *s,
+ const struct nf_conntrack_tuple *tuple)
{
- return seq_printf(s, "sport=%hu dport=%hu ",
- ntohs(tuple->src.u.udp.port),
- ntohs(tuple->dst.u.udp.port));
+ seq_printf(s, "sport=%hu dport=%hu ",
+ ntohs(tuple->src.u.udp.port),
+ ntohs(tuple->dst.u.udp.port));
}
static unsigned int *udplite_get_timeouts(struct net *net)
diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c
index cf65a1e040dd..fc823fa5dcf5 100644
--- a/net/netfilter/nf_conntrack_standalone.c
+++ b/net/netfilter/nf_conntrack_standalone.c
@@ -36,12 +36,13 @@
MODULE_LICENSE("GPL");
#ifdef CONFIG_NF_CONNTRACK_PROCFS
-int
+void
print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple,
const struct nf_conntrack_l3proto *l3proto,
const struct nf_conntrack_l4proto *l4proto)
{
- return l3proto->print_tuple(s, tuple) || l4proto->print_tuple(s, tuple);
+ l3proto->print_tuple(s, tuple);
+ l4proto->print_tuple(s, tuple);
}
EXPORT_SYMBOL_GPL(print_tuple);
@@ -119,7 +120,7 @@ static void ct_seq_stop(struct seq_file *s, void *v)
}
#ifdef CONFIG_NF_CONNTRACK_SECMARK
-static int ct_show_secctx(struct seq_file *s, const struct nf_conn *ct)
+static void ct_show_secctx(struct seq_file *s, const struct nf_conn *ct)
{
int ret;
u32 len;
@@ -127,22 +128,20 @@ static int ct_show_secctx(struct seq_file *s, const struct nf_conn *ct)
ret = security_secid_to_secctx(ct->secmark, &secctx, &len);
if (ret)
- return 0;
+ return;
- ret = seq_printf(s, "secctx=%s ", secctx);
+ seq_printf(s, "secctx=%s ", secctx);
security_release_secctx(secctx, len);
- return ret;
}
#else
-static inline int ct_show_secctx(struct seq_file *s, const struct nf_conn *ct)
+static inline void ct_show_secctx(struct seq_file *s, const struct nf_conn *ct)
{
- return 0;
}
#endif
#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP
-static int ct_show_delta_time(struct seq_file *s, const struct nf_conn *ct)
+static void ct_show_delta_time(struct seq_file *s, const struct nf_conn *ct)
{
struct ct_iter_state *st = s->private;
struct nf_conn_tstamp *tstamp;
@@ -156,16 +155,15 @@ static int ct_show_delta_time(struct seq_file *s, const struct nf_conn *ct)
else
delta_time = 0;
- return seq_printf(s, "delta-time=%llu ",
- (unsigned long long)delta_time);
+ seq_printf(s, "delta-time=%llu ",
+ (unsigned long long)delta_time);
}
- return 0;
+ return;
}
#else
-static inline int
+static inline void
ct_show_delta_time(struct seq_file *s, const struct nf_conn *ct)
{
- return 0;
}
#endif
@@ -192,55 +190,54 @@ static int ct_seq_show(struct seq_file *s, void *v)
NF_CT_ASSERT(l4proto);
ret = -ENOSPC;
- if (seq_printf(s, "%-8s %u %-8s %u %ld ",
- l3proto->name, nf_ct_l3num(ct),
- l4proto->name, nf_ct_protonum(ct),
- timer_pending(&ct->timeout)
- ? (long)(ct->timeout.expires - jiffies)/HZ : 0) != 0)
- goto release;
+ seq_printf(s, "%-8s %u %-8s %u %ld ",
+ l3proto->name, nf_ct_l3num(ct),
+ l4proto->name, nf_ct_protonum(ct),
+ timer_pending(&ct->timeout)
+ ? (long)(ct->timeout.expires - jiffies)/HZ : 0);
- if (l4proto->print_conntrack && l4proto->print_conntrack(s, ct))
- goto release;
+ if (l4proto->print_conntrack)
+ l4proto->print_conntrack(s, ct);
+
+ print_tuple(s, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
+ l3proto, l4proto);
- if (print_tuple(s, &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple,
- l3proto, l4proto))
+ if (seq_has_overflowed(s))
goto release;
if (seq_print_acct(s, ct, IP_CT_DIR_ORIGINAL))
goto release;
if (!(test_bit(IPS_SEEN_REPLY_BIT, &ct->status)))
- if (seq_printf(s, "[UNREPLIED] "))
- goto release;
+ seq_printf(s, "[UNREPLIED] ");
- if (print_tuple(s, &ct->tuplehash[IP_CT_DIR_REPLY].tuple,
- l3proto, l4proto))
- goto release;
+ print_tuple(s, &ct->tuplehash[IP_CT_DIR_REPLY].tuple,
+ l3proto, l4proto);
if (seq_print_acct(s, ct, IP_CT_DIR_REPLY))
goto release;
if (test_bit(IPS_ASSURED_BIT, &ct->status))
- if (seq_printf(s, "[ASSURED] "))
- goto release;
+ seq_printf(s, "[ASSURED] ");
-#if defined(CONFIG_NF_CONNTRACK_MARK)
- if (seq_printf(s, "mark=%u ", ct->mark))
+ if (seq_has_overflowed(s))
goto release;
+
+#if defined(CONFIG_NF_CONNTRACK_MARK)
+ seq_printf(s, "mark=%u ", ct->mark);
#endif
- if (ct_show_secctx(s, ct))
- goto release;
+ ct_show_secctx(s, ct);
#ifdef CONFIG_NF_CONNTRACK_ZONES
- if (seq_printf(s, "zone=%u ", nf_ct_zone(ct)))
- goto release;
+ seq_printf(s, "zone=%u ", nf_ct_zone(ct));
#endif
- if (ct_show_delta_time(s, ct))
- goto release;
+ ct_show_delta_time(s, ct);
+
+ seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use));
- if (seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use)))
+ if (seq_has_overflowed(s))
goto release;
ret = 0;
diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c
index d7197649dba6..43c926cae9c0 100644
--- a/net/netfilter/nf_log.c
+++ b/net/netfilter/nf_log.c
@@ -19,6 +19,9 @@
static struct nf_logger __rcu *loggers[NFPROTO_NUMPROTO][NF_LOG_TYPE_MAX] __read_mostly;
static DEFINE_MUTEX(nf_log_mutex);
+#define nft_log_dereference(logger) \
+ rcu_dereference_protected(logger, lockdep_is_held(&nf_log_mutex))
+
static struct nf_logger *__find_logger(int pf, const char *str_logger)
{
struct nf_logger *log;
@@ -28,8 +31,7 @@ static struct nf_logger *__find_logger(int pf, const char *str_logger)
if (loggers[pf][i] == NULL)
continue;
- log = rcu_dereference_protected(loggers[pf][i],
- lockdep_is_held(&nf_log_mutex));
+ log = nft_log_dereference(loggers[pf][i]);
if (!strncasecmp(str_logger, log->name, strlen(log->name)))
return log;
}
@@ -45,8 +47,7 @@ void nf_log_set(struct net *net, u_int8_t pf, const struct nf_logger *logger)
return;
mutex_lock(&nf_log_mutex);
- log = rcu_dereference_protected(net->nf.nf_loggers[pf],
- lockdep_is_held(&nf_log_mutex));
+ log = nft_log_dereference(net->nf.nf_loggers[pf]);
if (log == NULL)
rcu_assign_pointer(net->nf.nf_loggers[pf], logger);
@@ -61,8 +62,7 @@ void nf_log_unset(struct net *net, const struct nf_logger *logger)
mutex_lock(&nf_log_mutex);
for (i = 0; i < NFPROTO_NUMPROTO; i++) {
- log = rcu_dereference_protected(net->nf.nf_loggers[i],
- lockdep_is_held(&nf_log_mutex));
+ log = nft_log_dereference(net->nf.nf_loggers[i]);
if (log == logger)
RCU_INIT_POINTER(net->nf.nf_loggers[i], NULL);
}
@@ -75,6 +75,7 @@ EXPORT_SYMBOL(nf_log_unset);
int nf_log_register(u_int8_t pf, struct nf_logger *logger)
{
int i;
+ int ret = 0;
if (pf >= ARRAY_SIZE(init_net.nf.nf_loggers))
return -EINVAL;
@@ -82,16 +83,25 @@ int nf_log_register(u_int8_t pf, struct nf_logger *logger)
mutex_lock(&nf_log_mutex);
if (pf == NFPROTO_UNSPEC) {
+ for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++) {
+ if (rcu_access_pointer(loggers[i][logger->type])) {
+ ret = -EEXIST;
+ goto unlock;
+ }
+ }
for (i = NFPROTO_UNSPEC; i < NFPROTO_NUMPROTO; i++)
rcu_assign_pointer(loggers[i][logger->type], logger);
} else {
- /* register at end of list to honor first register win */
+ if (rcu_access_pointer(loggers[pf][logger->type])) {
+ ret = -EEXIST;
+ goto unlock;
+ }
rcu_assign_pointer(loggers[pf][logger->type], logger);
}
+unlock:
mutex_unlock(&nf_log_mutex);
-
- return 0;
+ return ret;
}
EXPORT_SYMBOL(nf_log_register);
@@ -144,8 +154,7 @@ int nf_logger_find_get(int pf, enum nf_log_type type)
struct nf_logger *logger;
int ret = -ENOENT;
- logger = loggers[pf][type];
- if (logger == NULL)
+ if (rcu_access_pointer(loggers[pf][type]) == NULL)
request_module("nf-logger-%u-%u", pf, type);
rcu_read_lock();
@@ -294,37 +303,37 @@ static int seq_show(struct seq_file *s, void *v)
{
loff_t *pos = v;
const struct nf_logger *logger;
- int i, ret;
+ int i;
struct net *net = seq_file_net(s);
- logger = rcu_dereference_protected(net->nf.nf_loggers[*pos],
- lockdep_is_held(&nf_log_mutex));
+ logger = nft_log_dereference(net->nf.nf_loggers[*pos]);
if (!logger)
- ret = seq_printf(s, "%2lld NONE (", *pos);
+ seq_printf(s, "%2lld NONE (", *pos);
else
- ret = seq_printf(s, "%2lld %s (", *pos, logger->name);
+ seq_printf(s, "%2lld %s (", *pos, logger->name);
- if (ret < 0)
- return ret;
+ if (seq_has_overflowed(s))
+ return -ENOSPC;
for (i = 0; i < NF_LOG_TYPE_MAX; i++) {
if (loggers[*pos][i] == NULL)
continue;
- logger = rcu_dereference_protected(loggers[*pos][i],
- lockdep_is_held(&nf_log_mutex));
- ret = seq_printf(s, "%s", logger->name);
- if (ret < 0)
- return ret;
- if (i == 0 && loggers[*pos][i + 1] != NULL) {
- ret = seq_printf(s, ",");
- if (ret < 0)
- return ret;
- }
+ logger = nft_log_dereference(loggers[*pos][i]);
+ seq_printf(s, "%s", logger->name);
+ if (i == 0 && loggers[*pos][i + 1] != NULL)
+ seq_printf(s, ",");
+
+ if (seq_has_overflowed(s))
+ return -ENOSPC;
}
- return seq_printf(s, ")\n");
+ seq_printf(s, ")\n");
+
+ if (seq_has_overflowed(s))
+ return -ENOSPC;
+ return 0;
}
static const struct seq_operations nflog_seq_ops = {
@@ -385,8 +394,7 @@ static int nf_log_proc_dostring(struct ctl_table *table, int write,
mutex_unlock(&nf_log_mutex);
} else {
mutex_lock(&nf_log_mutex);
- logger = rcu_dereference_protected(net->nf.nf_loggers[tindex],
- lockdep_is_held(&nf_log_mutex));
+ logger = nft_log_dereference(net->nf.nf_loggers[tindex]);
if (!logger)
table->data = "NONE";
else
diff --git a/net/netfilter/nf_nat_redirect.c b/net/netfilter/nf_nat_redirect.c
new file mode 100644
index 000000000000..97b75f9bfbcd
--- /dev/null
+++ b/net/netfilter/nf_nat_redirect.c
@@ -0,0 +1,127 @@
+/*
+ * (C) 1999-2001 Paul `Rusty' Russell
+ * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
+ * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
+ *
+ * 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.
+ *
+ * Based on Rusty Russell's IPv4 REDIRECT target. Development of IPv6
+ * NAT funded by Astaro.
+ */
+
+#include <linux/if.h>
+#include <linux/inetdevice.h>
+#include <linux/ip.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/netfilter.h>
+#include <linux/types.h>
+#include <linux/netfilter_ipv4.h>
+#include <linux/netfilter_ipv6.h>
+#include <linux/netfilter/x_tables.h>
+#include <net/addrconf.h>
+#include <net/checksum.h>
+#include <net/protocol.h>
+#include <net/netfilter/nf_nat.h>
+#include <net/netfilter/nf_nat_redirect.h>
+
+unsigned int
+nf_nat_redirect_ipv4(struct sk_buff *skb,
+ const struct nf_nat_ipv4_multi_range_compat *mr,
+ unsigned int hooknum)
+{
+ struct nf_conn *ct;
+ enum ip_conntrack_info ctinfo;
+ __be32 newdst;
+ struct nf_nat_range newrange;
+
+ NF_CT_ASSERT(hooknum == NF_INET_PRE_ROUTING ||
+ hooknum == NF_INET_LOCAL_OUT);
+
+ ct = nf_ct_get(skb, &ctinfo);
+ NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));
+
+ /* Local packets: make them go to loopback */
+ if (hooknum == NF_INET_LOCAL_OUT) {
+ newdst = htonl(0x7F000001);
+ } else {
+ struct in_device *indev;
+ struct in_ifaddr *ifa;
+
+ newdst = 0;
+
+ rcu_read_lock();
+ indev = __in_dev_get_rcu(skb->dev);
+ if (indev != NULL) {
+ ifa = indev->ifa_list;
+ newdst = ifa->ifa_local;
+ }
+ rcu_read_unlock();
+
+ if (!newdst)
+ return NF_DROP;
+ }
+
+ /* Transfer from original range. */
+ memset(&newrange.min_addr, 0, sizeof(newrange.min_addr));
+ memset(&newrange.max_addr, 0, sizeof(newrange.max_addr));
+ newrange.flags = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS;
+ newrange.min_addr.ip = newdst;
+ newrange.max_addr.ip = newdst;
+ newrange.min_proto = mr->range[0].min;
+ newrange.max_proto = mr->range[0].max;
+
+ /* Hand modified range to generic setup. */
+ return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
+}
+EXPORT_SYMBOL_GPL(nf_nat_redirect_ipv4);
+
+static const struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT;
+
+unsigned int
+nf_nat_redirect_ipv6(struct sk_buff *skb, const struct nf_nat_range *range,
+ unsigned int hooknum)
+{
+ struct nf_nat_range newrange;
+ struct in6_addr newdst;
+ enum ip_conntrack_info ctinfo;
+ struct nf_conn *ct;
+
+ ct = nf_ct_get(skb, &ctinfo);
+ if (hooknum == NF_INET_LOCAL_OUT) {
+ newdst = loopback_addr;
+ } else {
+ struct inet6_dev *idev;
+ struct inet6_ifaddr *ifa;
+ bool addr = false;
+
+ rcu_read_lock();
+ idev = __in6_dev_get(skb->dev);
+ if (idev != NULL) {
+ list_for_each_entry(ifa, &idev->addr_list, if_list) {
+ newdst = ifa->addr;
+ addr = true;
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ if (!addr)
+ return NF_DROP;
+ }
+
+ newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS;
+ newrange.min_addr.in6 = newdst;
+ newrange.max_addr.in6 = newdst;
+ newrange.min_proto = range->min_proto;
+ newrange.max_proto = range->max_proto;
+
+ return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
+}
+EXPORT_SYMBOL_GPL(nf_nat_redirect_ipv6);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 11ab4b078f3b..129a8daa4abf 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -2477,7 +2477,7 @@ static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb,
const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
int err;
- /* Verify existance before starting dump */
+ /* Verify existence before starting dump */
err = nft_ctx_init_from_setattr(&ctx, skb, nlh, nla);
if (err < 0)
return err;
@@ -3484,13 +3484,8 @@ static void nft_chain_commit_update(struct nft_trans *trans)
}
}
-/* Schedule objects for release via rcu to make sure no packets are accesing
- * removed rules.
- */
-static void nf_tables_commit_release_rcu(struct rcu_head *rt)
+static void nf_tables_commit_release(struct nft_trans *trans)
{
- struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head);
-
switch (trans->msg_type) {
case NFT_MSG_DELTABLE:
nf_tables_table_destroy(&trans->ctx);
@@ -3612,10 +3607,11 @@ static int nf_tables_commit(struct sk_buff *skb)
}
}
+ synchronize_rcu();
+
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
list_del(&trans->list);
- trans->ctx.nla = NULL;
- call_rcu(&trans->rcu_head, nf_tables_commit_release_rcu);
+ nf_tables_commit_release(trans);
}
nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN);
@@ -3623,13 +3619,8 @@ static int nf_tables_commit(struct sk_buff *skb)
return 0;
}
-/* Schedule objects for release via rcu to make sure no packets are accesing
- * aborted rules.
- */
-static void nf_tables_abort_release_rcu(struct rcu_head *rt)
+static void nf_tables_abort_release(struct nft_trans *trans)
{
- struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head);
-
switch (trans->msg_type) {
case NFT_MSG_NEWTABLE:
nf_tables_table_destroy(&trans->ctx);
@@ -3674,8 +3665,7 @@ static int nf_tables_abort(struct sk_buff *skb)
break;
case NFT_MSG_NEWCHAIN:
if (nft_trans_chain_update(trans)) {
- if (nft_trans_chain_stats(trans))
- free_percpu(nft_trans_chain_stats(trans));
+ free_percpu(nft_trans_chain_stats(trans));
nft_trans_destroy(trans);
} else {
@@ -3725,11 +3715,12 @@ static int nf_tables_abort(struct sk_buff *skb)
}
}
+ synchronize_rcu();
+
list_for_each_entry_safe_reverse(trans, next,
&net->nft.commit_list, list) {
list_del(&trans->list);
- trans->ctx.nla = NULL;
- call_rcu(&trans->rcu_head, nf_tables_abort_release_rcu);
+ nf_tables_abort_release(trans);
}
return 0;
diff --git a/net/netfilter/nfnetlink.c b/net/netfilter/nfnetlink.c
index 6c5a915cfa75..13c2e17bbe27 100644
--- a/net/netfilter/nfnetlink.c
+++ b/net/netfilter/nfnetlink.c
@@ -47,6 +47,8 @@ static const int nfnl_group2type[NFNLGRP_MAX+1] = {
[NFNLGRP_CONNTRACK_EXP_NEW] = NFNL_SUBSYS_CTNETLINK_EXP,
[NFNLGRP_CONNTRACK_EXP_UPDATE] = NFNL_SUBSYS_CTNETLINK_EXP,
[NFNLGRP_CONNTRACK_EXP_DESTROY] = NFNL_SUBSYS_CTNETLINK_EXP,
+ [NFNLGRP_NFTABLES] = NFNL_SUBSYS_NFTABLES,
+ [NFNLGRP_ACCT_QUOTA] = NFNL_SUBSYS_ACCT,
};
void nfnl_lock(__u8 subsys_id)
@@ -464,7 +466,12 @@ static void nfnetlink_rcv(struct sk_buff *skb)
static int nfnetlink_bind(int group)
{
const struct nfnetlink_subsystem *ss;
- int type = nfnl_group2type[group];
+ int type;
+
+ if (group <= NFNLGRP_NONE || group > NFNLGRP_MAX)
+ return -EINVAL;
+
+ type = nfnl_group2type[group];
rcu_read_lock();
ss = nfnetlink_get_subsys(type);
@@ -514,6 +521,9 @@ static int __init nfnetlink_init(void)
{
int i;
+ for (i = NFNLGRP_NONE + 1; i <= NFNLGRP_MAX; i++)
+ BUG_ON(nfnl_group2type[i] == NFNL_SUBSYS_NONE);
+
for (i=0; i<NFNL_SUBSYS_COUNT; i++)
mutex_init(&table[i].mutex);
diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
index 5f1be5ba3559..11d85b3813f2 100644
--- a/net/netfilter/nfnetlink_log.c
+++ b/net/netfilter/nfnetlink_log.c
@@ -12,6 +12,9 @@
* 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/module.h>
#include <linux/skbuff.h>
#include <linux/if_arp.h>
@@ -337,9 +340,6 @@ nfulnl_alloc_skb(struct net *net, u32 peer_portid, unsigned int inst_size,
skb = nfnetlink_alloc_skb(net, pkt_size,
peer_portid, GFP_ATOMIC);
- if (!skb)
- pr_err("nfnetlink_log: can't even alloc %u bytes\n",
- pkt_size);
}
}
@@ -570,10 +570,8 @@ __build_packet_message(struct nfnl_log_net *log,
struct nlattr *nla;
int size = nla_attr_size(data_len);
- if (skb_tailroom(inst->skb) < nla_total_size(data_len)) {
- printk(KERN_WARNING "nfnetlink_log: no tailroom!\n");
- return -1;
- }
+ if (skb_tailroom(inst->skb) < nla_total_size(data_len))
+ goto nla_put_failure;
nla = (struct nlattr *)skb_put(inst->skb, nla_total_size(data_len));
nla->nla_type = NFULA_PAYLOAD;
@@ -1069,19 +1067,19 @@ static int __init nfnetlink_log_init(void)
netlink_register_notifier(&nfulnl_rtnl_notifier);
status = nfnetlink_subsys_register(&nfulnl_subsys);
if (status < 0) {
- pr_err("log: failed to create netlink socket\n");
+ pr_err("failed to create netlink socket\n");
goto cleanup_netlink_notifier;
}
status = nf_log_register(NFPROTO_UNSPEC, &nfulnl_logger);
if (status < 0) {
- pr_err("log: failed to register logger\n");
+ pr_err("failed to register logger\n");
goto cleanup_subsys;
}
status = register_pernet_subsys(&nfnl_log_net_ops);
if (status < 0) {
- pr_err("log: failed to register pernet ops\n");
+ pr_err("failed to register pernet ops\n");
goto cleanup_logger;
}
return status;
diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c
index 7c60ccd61a3e..0db8515e76da 100644
--- a/net/netfilter/nfnetlink_queue_core.c
+++ b/net/netfilter/nfnetlink_queue_core.c
@@ -1242,12 +1242,13 @@ static int seq_show(struct seq_file *s, void *v)
{
const struct nfqnl_instance *inst = v;
- return seq_printf(s, "%5d %6d %5d %1d %5d %5d %5d %8d %2d\n",
- inst->queue_num,
- inst->peer_portid, inst->queue_total,
- inst->copy_mode, inst->copy_range,
- inst->queue_dropped, inst->queue_user_dropped,
- inst->id_sequence, 1);
+ seq_printf(s, "%5d %6d %5d %1d %5d %5d %5d %8d %2d\n",
+ inst->queue_num,
+ inst->peer_portid, inst->queue_total,
+ inst->copy_mode, inst->copy_range,
+ inst->queue_dropped, inst->queue_user_dropped,
+ inst->id_sequence, 1);
+ return seq_has_overflowed(s);
}
static const struct seq_operations nfqnl_seq_ops = {
diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c
index 9d6d6f60a80f..265e190f2218 100644
--- a/net/netfilter/nft_compat.c
+++ b/net/netfilter/nft_compat.c
@@ -21,45 +21,17 @@
#include <linux/netfilter_ipv6/ip6_tables.h>
#include <net/netfilter/nf_tables.h>
-static const struct {
- const char *name;
- u8 type;
-} table_to_chaintype[] = {
- { "filter", NFT_CHAIN_T_DEFAULT },
- { "raw", NFT_CHAIN_T_DEFAULT },
- { "security", NFT_CHAIN_T_DEFAULT },
- { "mangle", NFT_CHAIN_T_ROUTE },
- { "nat", NFT_CHAIN_T_NAT },
- { },
-};
-
-static int nft_compat_table_to_chaintype(const char *table)
-{
- int i;
-
- for (i = 0; table_to_chaintype[i].name != NULL; i++) {
- if (strcmp(table_to_chaintype[i].name, table) == 0)
- return table_to_chaintype[i].type;
- }
-
- return -1;
-}
-
static int nft_compat_chain_validate_dependency(const char *tablename,
const struct nft_chain *chain)
{
- enum nft_chain_type type;
const struct nft_base_chain *basechain;
if (!tablename || !(chain->flags & NFT_BASE_CHAIN))
return 0;
- type = nft_compat_table_to_chaintype(tablename);
- if (type < 0)
- return -EINVAL;
-
basechain = nft_base_chain(chain);
- if (basechain->type->type != type)
+ if (strcmp(tablename, "nat") == 0 &&
+ basechain->type->type != NFT_CHAIN_T_NAT)
return -EINVAL;
return 0;
@@ -117,7 +89,7 @@ nft_target_set_tgchk_param(struct xt_tgchk_param *par,
struct xt_target *target, void *info,
union nft_entry *entry, u8 proto, bool inv)
{
- par->net = &init_net;
+ par->net = ctx->net;
par->table = ctx->table->name;
switch (ctx->afi->family) {
case AF_INET:
@@ -324,7 +296,7 @@ nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx,
struct xt_match *match, void *info,
union nft_entry *entry, u8 proto, bool inv)
{
- par->net = &init_net;
+ par->net = ctx->net;
par->table = ctx->table->name;
switch (ctx->afi->family) {
case AF_INET:
@@ -374,7 +346,7 @@ nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
union nft_entry e = {};
int ret;
- ret = nft_compat_chain_validate_dependency(match->name, ctx->chain);
+ ret = nft_compat_chain_validate_dependency(match->table, ctx->chain);
if (ret < 0)
goto err;
@@ -448,7 +420,7 @@ static int nft_match_validate(const struct nft_ctx *ctx,
if (!(hook_mask & match->hooks))
return -EINVAL;
- ret = nft_compat_chain_validate_dependency(match->name,
+ ret = nft_compat_chain_validate_dependency(match->table,
ctx->chain);
if (ret < 0)
return ret;
diff --git a/net/netfilter/nft_hash.c b/net/netfilter/nft_hash.c
index 8892b7b6184a..1e316ce4cb5d 100644
--- a/net/netfilter/nft_hash.c
+++ b/net/netfilter/nft_hash.c
@@ -65,7 +65,7 @@ static int nft_hash_insert(const struct nft_set *set,
if (set->flags & NFT_SET_MAP)
nft_data_copy(he->data, &elem->data);
- rhashtable_insert(priv, &he->node, GFP_KERNEL);
+ rhashtable_insert(priv, &he->node);
return 0;
}
@@ -88,7 +88,7 @@ static void nft_hash_remove(const struct nft_set *set,
pprev = elem->cookie;
he = rht_dereference((*pprev), priv);
- rhashtable_remove_pprev(priv, he, pprev, GFP_KERNEL);
+ rhashtable_remove_pprev(priv, he, pprev);
synchronize_rcu();
kfree(he);
@@ -153,10 +153,12 @@ static unsigned int nft_hash_privsize(const struct nlattr * const nla[])
return sizeof(struct rhashtable);
}
-static int lockdep_nfnl_lock_is_held(void)
+#ifdef CONFIG_PROVE_LOCKING
+static int lockdep_nfnl_lock_is_held(void *parent)
{
return lockdep_nfnl_is_held(NFNL_SUBSYS_NFTABLES);
}
+#endif
static int nft_hash_init(const struct nft_set *set,
const struct nft_set_desc *desc,
@@ -171,7 +173,9 @@ static int nft_hash_init(const struct nft_set *set,
.hashfn = jhash,
.grow_decision = rht_grow_above_75,
.shrink_decision = rht_shrink_below_30,
+#ifdef CONFIG_PROVE_LOCKING
.mutex_is_held = lockdep_nfnl_lock_is_held,
+#endif
};
return rhashtable_init(priv, &params);
diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c
index 1e7c076ca63a..e99911eda915 100644
--- a/net/netfilter/nft_meta.c
+++ b/net/netfilter/nft_meta.c
@@ -165,6 +165,12 @@ void nft_meta_get_eval(const struct nft_expr *expr,
goto err;
dest->data[0] = out->group;
break;
+ case NFT_META_CGROUP:
+ if (skb->sk == NULL)
+ break;
+
+ dest->data[0] = skb->sk->sk_classid;
+ break;
default:
WARN_ON(1);
goto err;
@@ -240,6 +246,7 @@ int nft_meta_get_init(const struct nft_ctx *ctx,
case NFT_META_CPU:
case NFT_META_IIFGROUP:
case NFT_META_OIFGROUP:
+ case NFT_META_CGROUP:
break;
default:
return -EOPNOTSUPP;
diff --git a/net/netfilter/nft_redir.c b/net/netfilter/nft_redir.c
new file mode 100644
index 000000000000..9e8093f28311
--- /dev/null
+++ b/net/netfilter/nft_redir.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2014 Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_nat.h>
+#include <net/netfilter/nf_tables.h>
+#include <net/netfilter/nft_redir.h>
+
+const struct nla_policy nft_redir_policy[NFTA_REDIR_MAX + 1] = {
+ [NFTA_REDIR_REG_PROTO_MIN] = { .type = NLA_U32 },
+ [NFTA_REDIR_REG_PROTO_MAX] = { .type = NLA_U32 },
+ [NFTA_REDIR_FLAGS] = { .type = NLA_U32 },
+};
+EXPORT_SYMBOL_GPL(nft_redir_policy);
+
+int nft_redir_init(const struct nft_ctx *ctx,
+ const struct nft_expr *expr,
+ const struct nlattr * const tb[])
+{
+ struct nft_redir *priv = nft_expr_priv(expr);
+ int err;
+
+ err = nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT);
+ if (err < 0)
+ return err;
+
+ if (tb[NFTA_REDIR_REG_PROTO_MIN]) {
+ priv->sreg_proto_min =
+ ntohl(nla_get_be32(tb[NFTA_REDIR_REG_PROTO_MIN]));
+
+ err = nft_validate_input_register(priv->sreg_proto_min);
+ if (err < 0)
+ return err;
+
+ if (tb[NFTA_REDIR_REG_PROTO_MAX]) {
+ priv->sreg_proto_max =
+ ntohl(nla_get_be32(tb[NFTA_REDIR_REG_PROTO_MAX]));
+
+ err = nft_validate_input_register(priv->sreg_proto_max);
+ if (err < 0)
+ return err;
+ } else {
+ priv->sreg_proto_max = priv->sreg_proto_min;
+ }
+ }
+
+ if (tb[NFTA_REDIR_FLAGS]) {
+ priv->flags = ntohl(nla_get_be32(tb[NFTA_REDIR_FLAGS]));
+ if (priv->flags & ~NF_NAT_RANGE_MASK)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(nft_redir_init);
+
+int nft_redir_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+ const struct nft_redir *priv = nft_expr_priv(expr);
+
+ if (priv->sreg_proto_min) {
+ if (nla_put_be32(skb, NFTA_REDIR_REG_PROTO_MIN,
+ htonl(priv->sreg_proto_min)))
+ goto nla_put_failure;
+ if (nla_put_be32(skb, NFTA_REDIR_REG_PROTO_MAX,
+ htonl(priv->sreg_proto_max)))
+ goto nla_put_failure;
+ }
+
+ if (priv->flags != 0 &&
+ nla_put_be32(skb, NFTA_REDIR_FLAGS, htonl(priv->flags)))
+ goto nla_put_failure;
+
+ return 0;
+
+nla_put_failure:
+ return -1;
+}
+EXPORT_SYMBOL_GPL(nft_redir_dump);
+
+int nft_redir_validate(const struct nft_ctx *ctx, const struct nft_expr *expr,
+ const struct nft_data **data)
+{
+ return nft_chain_validate_dependency(ctx->chain, NFT_CHAIN_T_NAT);
+}
+EXPORT_SYMBOL_GPL(nft_redir_validate);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Arturo Borrero Gonzalez <arturo.borrero.glez@gmail.com>");
diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c
index 133eb4772f12..51a459c3c649 100644
--- a/net/netfilter/x_tables.c
+++ b/net/netfilter/x_tables.c
@@ -947,9 +947,10 @@ static int xt_table_seq_show(struct seq_file *seq, void *v)
{
struct xt_table *table = list_entry(v, struct xt_table, list);
- if (strlen(table->name))
- return seq_printf(seq, "%s\n", table->name);
- else
+ if (strlen(table->name)) {
+ seq_printf(seq, "%s\n", table->name);
+ return seq_has_overflowed(seq);
+ } else
return 0;
}
@@ -1086,8 +1087,10 @@ static int xt_match_seq_show(struct seq_file *seq, void *v)
if (trav->curr == trav->head)
return 0;
match = list_entry(trav->curr, struct xt_match, list);
- return (*match->name == '\0') ? 0 :
- seq_printf(seq, "%s\n", match->name);
+ if (*match->name == '\0')
+ return 0;
+ seq_printf(seq, "%s\n", match->name);
+ return seq_has_overflowed(seq);
}
return 0;
}
@@ -1139,8 +1142,10 @@ static int xt_target_seq_show(struct seq_file *seq, void *v)
if (trav->curr == trav->head)
return 0;
target = list_entry(trav->curr, struct xt_target, list);
- return (*target->name == '\0') ? 0 :
- seq_printf(seq, "%s\n", target->name);
+ if (*target->name == '\0')
+ return 0;
+ seq_printf(seq, "%s\n", target->name);
+ return seq_has_overflowed(seq);
}
return 0;
}
diff --git a/net/netfilter/xt_DSCP.c b/net/netfilter/xt_DSCP.c
index ae8271652efa..3f83d38c4e5b 100644
--- a/net/netfilter/xt_DSCP.c
+++ b/net/netfilter/xt_DSCP.c
@@ -37,7 +37,8 @@ dscp_tg(struct sk_buff *skb, const struct xt_action_param *par)
if (!skb_make_writable(skb, sizeof(struct iphdr)))
return NF_DROP;
- ipv4_change_dsfield(ip_hdr(skb), (__u8)(~XT_DSCP_MASK),
+ ipv4_change_dsfield(ip_hdr(skb),
+ (__force __u8)(~XT_DSCP_MASK),
dinfo->dscp << XT_DSCP_SHIFT);
}
@@ -54,7 +55,8 @@ dscp_tg6(struct sk_buff *skb, const struct xt_action_param *par)
if (!skb_make_writable(skb, sizeof(struct ipv6hdr)))
return NF_DROP;
- ipv6_change_dsfield(ipv6_hdr(skb), (__u8)(~XT_DSCP_MASK),
+ ipv6_change_dsfield(ipv6_hdr(skb),
+ (__force __u8)(~XT_DSCP_MASK),
dinfo->dscp << XT_DSCP_SHIFT);
}
return XT_CONTINUE;
diff --git a/net/netfilter/xt_REDIRECT.c b/net/netfilter/xt_REDIRECT.c
index 22a10309297c..03f0b370e178 100644
--- a/net/netfilter/xt_REDIRECT.c
+++ b/net/netfilter/xt_REDIRECT.c
@@ -26,48 +26,12 @@
#include <net/checksum.h>
#include <net/protocol.h>
#include <net/netfilter/nf_nat.h>
-
-static const struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT;
+#include <net/netfilter/nf_nat_redirect.h>
static unsigned int
redirect_tg6(struct sk_buff *skb, const struct xt_action_param *par)
{
- const struct nf_nat_range *range = par->targinfo;
- struct nf_nat_range newrange;
- struct in6_addr newdst;
- enum ip_conntrack_info ctinfo;
- struct nf_conn *ct;
-
- ct = nf_ct_get(skb, &ctinfo);
- if (par->hooknum == NF_INET_LOCAL_OUT)
- newdst = loopback_addr;
- else {
- struct inet6_dev *idev;
- struct inet6_ifaddr *ifa;
- bool addr = false;
-
- rcu_read_lock();
- idev = __in6_dev_get(skb->dev);
- if (idev != NULL) {
- list_for_each_entry(ifa, &idev->addr_list, if_list) {
- newdst = ifa->addr;
- addr = true;
- break;
- }
- }
- rcu_read_unlock();
-
- if (!addr)
- return NF_DROP;
- }
-
- newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS;
- newrange.min_addr.in6 = newdst;
- newrange.max_addr.in6 = newdst;
- newrange.min_proto = range->min_proto;
- newrange.max_proto = range->max_proto;
-
- return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
+ return nf_nat_redirect_ipv6(skb, par->targinfo, par->hooknum);
}
static int redirect_tg6_checkentry(const struct xt_tgchk_param *par)
@@ -98,48 +62,7 @@ static int redirect_tg4_check(const struct xt_tgchk_param *par)
static unsigned int
redirect_tg4(struct sk_buff *skb, const struct xt_action_param *par)
{
- struct nf_conn *ct;
- enum ip_conntrack_info ctinfo;
- __be32 newdst;
- const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo;
- struct nf_nat_range newrange;
-
- NF_CT_ASSERT(par->hooknum == NF_INET_PRE_ROUTING ||
- par->hooknum == NF_INET_LOCAL_OUT);
-
- ct = nf_ct_get(skb, &ctinfo);
- NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));
-
- /* Local packets: make them go to loopback */
- if (par->hooknum == NF_INET_LOCAL_OUT)
- newdst = htonl(0x7F000001);
- else {
- struct in_device *indev;
- struct in_ifaddr *ifa;
-
- newdst = 0;
-
- rcu_read_lock();
- indev = __in_dev_get_rcu(skb->dev);
- if (indev && (ifa = indev->ifa_list))
- newdst = ifa->ifa_local;
- rcu_read_unlock();
-
- if (!newdst)
- return NF_DROP;
- }
-
- /* Transfer from original range. */
- memset(&newrange.min_addr, 0, sizeof(newrange.min_addr));
- memset(&newrange.max_addr, 0, sizeof(newrange.max_addr));
- newrange.flags = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS;
- newrange.min_addr.ip = newdst;
- newrange.max_addr.ip = newdst;
- newrange.min_proto = mr->range[0].min;
- newrange.max_proto = mr->range[0].max;
-
- /* Hand modified range to generic setup. */
- return nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
+ return nf_nat_redirect_ipv4(skb, par->targinfo, par->hooknum);
}
static struct xt_target redirect_tg_reg[] __read_mostly = {
diff --git a/net/netfilter/xt_connlimit.c b/net/netfilter/xt_connlimit.c
index fbc66bb250d5..29ba6218a820 100644
--- a/net/netfilter/xt_connlimit.c
+++ b/net/netfilter/xt_connlimit.c
@@ -134,6 +134,7 @@ static bool add_hlist(struct hlist_head *head,
static unsigned int check_hlist(struct net *net,
struct hlist_head *head,
const struct nf_conntrack_tuple *tuple,
+ u16 zone,
bool *addit)
{
const struct nf_conntrack_tuple_hash *found;
@@ -147,8 +148,7 @@ static unsigned int check_hlist(struct net *net,
/* check the saved connections */
hlist_for_each_entry_safe(conn, n, head, node) {
- found = nf_conntrack_find_get(net, NF_CT_DEFAULT_ZONE,
- &conn->tuple);
+ found = nf_conntrack_find_get(net, zone, &conn->tuple);
if (found == NULL) {
hlist_del(&conn->node);
kmem_cache_free(connlimit_conn_cachep, conn);
@@ -201,7 +201,7 @@ static unsigned int
count_tree(struct net *net, struct rb_root *root,
const struct nf_conntrack_tuple *tuple,
const union nf_inet_addr *addr, const union nf_inet_addr *mask,
- u8 family)
+ u8 family, u16 zone)
{
struct xt_connlimit_rb *gc_nodes[CONNLIMIT_GC_MAX_NODES];
struct rb_node **rbnode, *parent;
@@ -229,7 +229,7 @@ count_tree(struct net *net, struct rb_root *root,
} else {
/* same source network -> be counted! */
unsigned int count;
- count = check_hlist(net, &rbconn->hhead, tuple, &addit);
+ count = check_hlist(net, &rbconn->hhead, tuple, zone, &addit);
tree_nodes_free(root, gc_nodes, gc_count);
if (!addit)
@@ -245,7 +245,7 @@ count_tree(struct net *net, struct rb_root *root,
continue;
/* only used for GC on hhead, retval and 'addit' ignored */
- check_hlist(net, &rbconn->hhead, tuple, &addit);
+ check_hlist(net, &rbconn->hhead, tuple, zone, &addit);
if (hlist_empty(&rbconn->hhead))
gc_nodes[gc_count++] = rbconn;
}
@@ -290,7 +290,7 @@ static int count_them(struct net *net,
const struct nf_conntrack_tuple *tuple,
const union nf_inet_addr *addr,
const union nf_inet_addr *mask,
- u_int8_t family)
+ u_int8_t family, u16 zone)
{
struct rb_root *root;
int count;
@@ -306,7 +306,7 @@ static int count_them(struct net *net,
spin_lock_bh(&xt_connlimit_locks[hash % CONNLIMIT_LOCK_SLOTS]);
- count = count_tree(net, root, tuple, addr, mask, family);
+ count = count_tree(net, root, tuple, addr, mask, family, zone);
spin_unlock_bh(&xt_connlimit_locks[hash % CONNLIMIT_LOCK_SLOTS]);
@@ -324,13 +324,16 @@ connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
enum ip_conntrack_info ctinfo;
const struct nf_conn *ct;
unsigned int connections;
+ u16 zone = NF_CT_DEFAULT_ZONE;
ct = nf_ct_get(skb, &ctinfo);
- if (ct != NULL)
+ if (ct != NULL) {
tuple_ptr = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
- else if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb),
- par->family, &tuple))
+ zone = nf_ct_zone(ct);
+ } else if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb),
+ par->family, &tuple)) {
goto hotdrop;
+ }
if (par->family == NFPROTO_IPV6) {
const struct ipv6hdr *iph = ipv6_hdr(skb);
@@ -343,7 +346,7 @@ connlimit_mt(const struct sk_buff *skb, struct xt_action_param *par)
}
connections = count_them(net, info->data, tuple_ptr, &addr,
- &info->mask, par->family);
+ &info->mask, par->family, zone);
if (connections == 0)
/* kmalloc failed, drop it entirely */
goto hotdrop;
diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c
index 05fbc2a0be46..178696852bde 100644
--- a/net/netfilter/xt_hashlimit.c
+++ b/net/netfilter/xt_hashlimit.c
@@ -789,7 +789,6 @@ static void dl_seq_stop(struct seq_file *s, void *v)
static int dl_seq_real_show(struct dsthash_ent *ent, u_int8_t family,
struct seq_file *s)
{
- int res;
const struct xt_hashlimit_htable *ht = s->private;
spin_lock(&ent->lock);
@@ -798,33 +797,32 @@ static int dl_seq_real_show(struct dsthash_ent *ent, u_int8_t family,
switch (family) {
case NFPROTO_IPV4:
- res = seq_printf(s, "%ld %pI4:%u->%pI4:%u %u %u %u\n",
- (long)(ent->expires - jiffies)/HZ,
- &ent->dst.ip.src,
- ntohs(ent->dst.src_port),
- &ent->dst.ip.dst,
- ntohs(ent->dst.dst_port),
- ent->rateinfo.credit, ent->rateinfo.credit_cap,
- ent->rateinfo.cost);
+ seq_printf(s, "%ld %pI4:%u->%pI4:%u %u %u %u\n",
+ (long)(ent->expires - jiffies)/HZ,
+ &ent->dst.ip.src,
+ ntohs(ent->dst.src_port),
+ &ent->dst.ip.dst,
+ ntohs(ent->dst.dst_port),
+ ent->rateinfo.credit, ent->rateinfo.credit_cap,
+ ent->rateinfo.cost);
break;
#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES)
case NFPROTO_IPV6:
- res = seq_printf(s, "%ld %pI6:%u->%pI6:%u %u %u %u\n",
- (long)(ent->expires - jiffies)/HZ,
- &ent->dst.ip6.src,
- ntohs(ent->dst.src_port),
- &ent->dst.ip6.dst,
- ntohs(ent->dst.dst_port),
- ent->rateinfo.credit, ent->rateinfo.credit_cap,
- ent->rateinfo.cost);
+ seq_printf(s, "%ld %pI6:%u->%pI6:%u %u %u %u\n",
+ (long)(ent->expires - jiffies)/HZ,
+ &ent->dst.ip6.src,
+ ntohs(ent->dst.src_port),
+ &ent->dst.ip6.dst,
+ ntohs(ent->dst.dst_port),
+ ent->rateinfo.credit, ent->rateinfo.credit_cap,
+ ent->rateinfo.cost);
break;
#endif
default:
BUG();
- res = 0;
}
spin_unlock(&ent->lock);
- return res;
+ return seq_has_overflowed(s);
}
static int dl_seq_show(struct seq_file *s, void *v)
diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c
index a9faae89f955..30dbe34915ae 100644
--- a/net/netfilter/xt_recent.c
+++ b/net/netfilter/xt_recent.c
@@ -43,25 +43,29 @@ MODULE_LICENSE("GPL");
MODULE_ALIAS("ipt_recent");
MODULE_ALIAS("ip6t_recent");
-static unsigned int ip_list_tot = 100;
-static unsigned int ip_pkt_list_tot = 20;
-static unsigned int ip_list_hash_size = 0;
-static unsigned int ip_list_perms = 0644;
-static unsigned int ip_list_uid = 0;
-static unsigned int ip_list_gid = 0;
+static unsigned int ip_list_tot __read_mostly = 100;
+static unsigned int ip_list_hash_size __read_mostly;
+static unsigned int ip_list_perms __read_mostly = 0644;
+static unsigned int ip_list_uid __read_mostly;
+static unsigned int ip_list_gid __read_mostly;
module_param(ip_list_tot, uint, 0400);
-module_param(ip_pkt_list_tot, uint, 0400);
module_param(ip_list_hash_size, uint, 0400);
module_param(ip_list_perms, uint, 0400);
module_param(ip_list_uid, uint, S_IRUGO | S_IWUSR);
module_param(ip_list_gid, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(ip_list_tot, "number of IPs to remember per list");
-MODULE_PARM_DESC(ip_pkt_list_tot, "number of packets per IP address to remember (max. 255)");
MODULE_PARM_DESC(ip_list_hash_size, "size of hash table used to look up IPs");
MODULE_PARM_DESC(ip_list_perms, "permissions on /proc/net/xt_recent/* files");
MODULE_PARM_DESC(ip_list_uid, "default owner of /proc/net/xt_recent/* files");
MODULE_PARM_DESC(ip_list_gid, "default owning group of /proc/net/xt_recent/* files");
+/* retained for backwards compatibility */
+static unsigned int ip_pkt_list_tot __read_mostly;
+module_param(ip_pkt_list_tot, uint, 0400);
+MODULE_PARM_DESC(ip_pkt_list_tot, "number of packets per IP address to remember (max. 255)");
+
+#define XT_RECENT_MAX_NSTAMPS 256
+
struct recent_entry {
struct list_head list;
struct list_head lru_list;
@@ -79,6 +83,7 @@ struct recent_table {
union nf_inet_addr mask;
unsigned int refcnt;
unsigned int entries;
+ u8 nstamps_max_mask;
struct list_head lru_list;
struct list_head iphash[0];
};
@@ -90,7 +95,8 @@ struct recent_net {
#endif
};
-static int recent_net_id;
+static int recent_net_id __read_mostly;
+
static inline struct recent_net *recent_pernet(struct net *net)
{
return net_generic(net, recent_net_id);
@@ -171,12 +177,15 @@ recent_entry_init(struct recent_table *t, const union nf_inet_addr *addr,
u_int16_t family, u_int8_t ttl)
{
struct recent_entry *e;
+ unsigned int nstamps_max = t->nstamps_max_mask;
if (t->entries >= ip_list_tot) {
e = list_entry(t->lru_list.next, struct recent_entry, lru_list);
recent_entry_remove(t, e);
}
- e = kmalloc(sizeof(*e) + sizeof(e->stamps[0]) * ip_pkt_list_tot,
+
+ nstamps_max += 1;
+ e = kmalloc(sizeof(*e) + sizeof(e->stamps[0]) * nstamps_max,
GFP_ATOMIC);
if (e == NULL)
return NULL;
@@ -197,7 +206,7 @@ recent_entry_init(struct recent_table *t, const union nf_inet_addr *addr,
static void recent_entry_update(struct recent_table *t, struct recent_entry *e)
{
- e->index %= ip_pkt_list_tot;
+ e->index &= t->nstamps_max_mask;
e->stamps[e->index++] = jiffies;
if (e->index > e->nstamps)
e->nstamps = e->index;
@@ -326,6 +335,7 @@ static int recent_mt_check(const struct xt_mtchk_param *par,
kuid_t uid;
kgid_t gid;
#endif
+ unsigned int nstamp_mask;
unsigned int i;
int ret = -EINVAL;
size_t sz;
@@ -349,19 +359,33 @@ static int recent_mt_check(const struct xt_mtchk_param *par,
return -EINVAL;
if ((info->check_set & XT_RECENT_REAP) && !info->seconds)
return -EINVAL;
- if (info->hit_count > ip_pkt_list_tot) {
- pr_info("hitcount (%u) is larger than "
- "packets to be remembered (%u)\n",
- info->hit_count, ip_pkt_list_tot);
+ if (info->hit_count >= XT_RECENT_MAX_NSTAMPS) {
+ pr_info("hitcount (%u) is larger than allowed maximum (%u)\n",
+ info->hit_count, XT_RECENT_MAX_NSTAMPS - 1);
return -EINVAL;
}
if (info->name[0] == '\0' ||
strnlen(info->name, XT_RECENT_NAME_LEN) == XT_RECENT_NAME_LEN)
return -EINVAL;
+ if (ip_pkt_list_tot && info->hit_count < ip_pkt_list_tot)
+ nstamp_mask = roundup_pow_of_two(ip_pkt_list_tot) - 1;
+ else if (info->hit_count)
+ nstamp_mask = roundup_pow_of_two(info->hit_count) - 1;
+ else
+ nstamp_mask = 32 - 1;
+
mutex_lock(&recent_mutex);
t = recent_table_lookup(recent_net, info->name);
if (t != NULL) {
+ if (info->hit_count > t->nstamps_max_mask) {
+ pr_info("hitcount (%u) is larger than packets to be remembered (%u) for table %s\n",
+ info->hit_count, t->nstamps_max_mask + 1,
+ info->name);
+ ret = -EINVAL;
+ goto out;
+ }
+
t->refcnt++;
ret = 0;
goto out;
@@ -377,6 +401,7 @@ static int recent_mt_check(const struct xt_mtchk_param *par,
goto out;
}
t->refcnt = 1;
+ t->nstamps_max_mask = nstamp_mask;
memcpy(&t->mask, &info->mask, sizeof(t->mask));
strcpy(t->name, info->name);
@@ -497,9 +522,12 @@ static void recent_seq_stop(struct seq_file *s, void *v)
static int recent_seq_show(struct seq_file *seq, void *v)
{
const struct recent_entry *e = v;
+ struct recent_iter_state *st = seq->private;
+ const struct recent_table *t = st->table;
unsigned int i;
- i = (e->index - 1) % ip_pkt_list_tot;
+ i = (e->index - 1) & t->nstamps_max_mask;
+
if (e->family == NFPROTO_IPV4)
seq_printf(seq, "src=%pI4 ttl: %u last_seen: %lu oldest_pkt: %u",
&e->addr.ip, e->ttl, e->stamps[i], e->index);
@@ -717,7 +745,9 @@ static int __init recent_mt_init(void)
{
int err;
- if (!ip_list_tot || !ip_pkt_list_tot || ip_pkt_list_tot > 255)
+ BUILD_BUG_ON_NOT_POWER_OF_2(XT_RECENT_MAX_NSTAMPS);
+
+ if (!ip_list_tot || ip_pkt_list_tot >= XT_RECENT_MAX_NSTAMPS)
return -EINVAL;
ip_list_hash_size = 1 << fls(ip_list_tot);
diff --git a/net/netfilter/xt_set.c b/net/netfilter/xt_set.c
index 5732cd64acc0..0d47afea9682 100644
--- a/net/netfilter/xt_set.c
+++ b/net/netfilter/xt_set.c
@@ -157,7 +157,7 @@ set_match_v1_destroy(const struct xt_mtdtor_param *par)
/* Revision 3 match */
static bool
-match_counter(u64 counter, const struct ip_set_counter_match *info)
+match_counter0(u64 counter, const struct ip_set_counter_match0 *info)
{
switch (info->op) {
case IPSET_COUNTER_NONE:
@@ -192,14 +192,60 @@ set_match_v3(const struct sk_buff *skb, struct xt_action_param *par)
if (!(ret && opt.cmdflags & IPSET_FLAG_MATCH_COUNTERS))
return ret;
- if (!match_counter(opt.ext.packets, &info->packets))
+ if (!match_counter0(opt.ext.packets, &info->packets))
return 0;
- return match_counter(opt.ext.bytes, &info->bytes);
+ return match_counter0(opt.ext.bytes, &info->bytes);
}
#define set_match_v3_checkentry set_match_v1_checkentry
#define set_match_v3_destroy set_match_v1_destroy
+/* Revision 4 match */
+
+static bool
+match_counter(u64 counter, const struct ip_set_counter_match *info)
+{
+ switch (info->op) {
+ case IPSET_COUNTER_NONE:
+ return true;
+ case IPSET_COUNTER_EQ:
+ return counter == info->value;
+ case IPSET_COUNTER_NE:
+ return counter != info->value;
+ case IPSET_COUNTER_LT:
+ return counter < info->value;
+ case IPSET_COUNTER_GT:
+ return counter > info->value;
+ }
+ return false;
+}
+
+static bool
+set_match_v4(const struct sk_buff *skb, struct xt_action_param *par)
+{
+ const struct xt_set_info_match_v4 *info = par->matchinfo;
+ ADT_OPT(opt, par->family, info->match_set.dim,
+ info->match_set.flags, info->flags, UINT_MAX);
+ int ret;
+
+ if (info->packets.op != IPSET_COUNTER_NONE ||
+ info->bytes.op != IPSET_COUNTER_NONE)
+ opt.cmdflags |= IPSET_FLAG_MATCH_COUNTERS;
+
+ ret = match_set(info->match_set.index, skb, par, &opt,
+ info->match_set.flags & IPSET_INV_MATCH);
+
+ if (!(ret && opt.cmdflags & IPSET_FLAG_MATCH_COUNTERS))
+ return ret;
+
+ if (!match_counter(opt.ext.packets, &info->packets))
+ return 0;
+ return match_counter(opt.ext.bytes, &info->bytes);
+}
+
+#define set_match_v4_checkentry set_match_v1_checkentry
+#define set_match_v4_destroy set_match_v1_destroy
+
/* Revision 0 interface: backward compatible with netfilter/iptables */
static unsigned int
@@ -573,6 +619,27 @@ static struct xt_match set_matches[] __read_mostly = {
.destroy = set_match_v3_destroy,
.me = THIS_MODULE
},
+ /* new revision for counters support: update, match */
+ {
+ .name = "set",
+ .family = NFPROTO_IPV4,
+ .revision = 4,
+ .match = set_match_v4,
+ .matchsize = sizeof(struct xt_set_info_match_v4),
+ .checkentry = set_match_v4_checkentry,
+ .destroy = set_match_v4_destroy,
+ .me = THIS_MODULE
+ },
+ {
+ .name = "set",
+ .family = NFPROTO_IPV6,
+ .revision = 4,
+ .match = set_match_v4,
+ .matchsize = sizeof(struct xt_set_info_match_v4),
+ .checkentry = set_match_v4_checkentry,
+ .destroy = set_match_v4_destroy,
+ .me = THIS_MODULE
+ },
};
static struct xt_target set_targets[] __read_mostly = {
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index f1de72de273e..ef5f77b44ec7 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -114,14 +114,14 @@ static atomic_t nl_table_users = ATOMIC_INIT(0);
DEFINE_MUTEX(nl_sk_hash_lock);
EXPORT_SYMBOL_GPL(nl_sk_hash_lock);
-static int lockdep_nl_sk_hash_is_held(void)
+#ifdef CONFIG_PROVE_LOCKING
+static int lockdep_nl_sk_hash_is_held(void *parent)
{
-#ifdef CONFIG_LOCKDEP
if (debug_locks)
return lockdep_is_held(&nl_sk_hash_lock) || lockdep_is_held(&nl_table_lock);
-#endif
return 1;
}
+#endif
static ATOMIC_NOTIFIER_HEAD(netlink_chain);
@@ -142,8 +142,7 @@ int netlink_add_tap(struct netlink_tap *nt)
list_add_rcu(&nt->list, &netlink_tap_all);
spin_unlock(&netlink_tap_lock);
- if (nt->module)
- __module_get(nt->module);
+ __module_get(nt->module);
return 0;
}
@@ -1092,7 +1091,7 @@ static int netlink_insert(struct sock *sk, struct net *net, u32 portid)
nlk_sk(sk)->portid = portid;
sock_hold(sk);
- rhashtable_insert(&table->hash, &nlk_sk(sk)->node, GFP_KERNEL);
+ rhashtable_insert(&table->hash, &nlk_sk(sk)->node);
err = 0;
err:
mutex_unlock(&nl_sk_hash_lock);
@@ -1105,7 +1104,7 @@ static void netlink_remove(struct sock *sk)
mutex_lock(&nl_sk_hash_lock);
table = &nl_table[sk->sk_protocol];
- if (rhashtable_remove(&table->hash, &nlk_sk(sk)->node, GFP_KERNEL)) {
+ if (rhashtable_remove(&table->hash, &nlk_sk(sk)->node)) {
WARN_ON(atomic_read(&sk->sk_refcnt) == 1);
__sock_put(sk);
}
@@ -1440,7 +1439,7 @@ static void netlink_unbind(int group, long unsigned int groups,
return;
for (undo = 0; undo < group; undo++)
- if (test_bit(group, &groups))
+ if (test_bit(undo, &groups))
nlk->netlink_unbind(undo);
}
@@ -1492,7 +1491,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr,
netlink_insert(sk, net, nladdr->nl_pid) :
netlink_autobind(sock);
if (err) {
- netlink_unbind(nlk->ngroups - 1, groups, nlk);
+ netlink_unbind(nlk->ngroups, groups, nlk);
return err;
}
}
@@ -2306,7 +2305,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock,
}
if (netlink_tx_is_mmaped(sk) &&
- msg->msg_iov->iov_base == NULL) {
+ msg->msg_iter.iov->iov_base == NULL) {
err = netlink_mmap_sendmsg(sk, msg, dst_portid, dst_group,
siocb);
goto out;
@@ -2326,7 +2325,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock,
NETLINK_CB(skb).flags = netlink_skb_flags;
err = -EFAULT;
- if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
+ if (memcpy_from_msg(skb_put(skb, len), msg, len)) {
kfree_skb(skb);
goto out;
}
@@ -2401,7 +2400,7 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock,
}
skb_reset_transport_header(data_skb);
- err = skb_copy_datagram_iovec(data_skb, 0, msg->msg_iov, copied);
+ err = skb_copy_datagram_msg(data_skb, 0, msg, copied);
if (msg->msg_name) {
DECLARE_SOCKADDR(struct sockaddr_nl *, addr, msg->msg_name);
@@ -2509,6 +2508,7 @@ __netlink_kernel_create(struct net *net, int unit, struct module *module,
nl_table[unit].module = module;
if (cfg) {
nl_table[unit].bind = cfg->bind;
+ nl_table[unit].unbind = cfg->unbind;
nl_table[unit].flags = cfg->flags;
if (cfg->compare)
nl_table[unit].compare = cfg->compare;
@@ -3129,11 +3129,13 @@ static int __init netlink_proto_init(void)
.head_offset = offsetof(struct netlink_sock, node),
.key_offset = offsetof(struct netlink_sock, portid),
.key_len = sizeof(u32), /* portid */
- .hashfn = arch_fast_hash,
+ .hashfn = jhash,
.max_shift = 16, /* 64K */
.grow_decision = rht_grow_above_75,
.shrink_decision = rht_shrink_below_30,
+#ifdef CONFIG_PROVE_LOCKING
.mutex_is_held = lockdep_nl_sk_hash_is_held,
+#endif
};
if (err != 0)
diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c
index 1b06a1fcf3e8..69f1d5e9959f 100644
--- a/net/netrom/af_netrom.c
+++ b/net/netrom/af_netrom.c
@@ -1113,7 +1113,7 @@ static int nr_sendmsg(struct kiocb *iocb, struct socket *sock,
skb_put(skb, len);
/* User data follows immediately after the NET/ROM transport header */
- if (memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len)) {
+ if (memcpy_from_msg(skb_transport_header(skb), msg, len)) {
kfree_skb(skb);
err = -EFAULT;
goto out;
@@ -1167,7 +1167,7 @@ static int nr_recvmsg(struct kiocb *iocb, struct socket *sock,
msg->msg_flags |= MSG_TRUNC;
}
- er = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ er = skb_copy_datagram_msg(skb, 0, msg, copied);
if (er < 0) {
skb_free_datagram(sk, skb);
release_sock(sk);
diff --git a/net/nfc/digital_dep.c b/net/nfc/digital_dep.c
index b60aa35c074f..f72be7433df3 100644
--- a/net/nfc/digital_dep.c
+++ b/net/nfc/digital_dep.c
@@ -17,6 +17,9 @@
#include "digital.h"
+#define DIGITAL_NFC_DEP_N_RETRY_NACK 2
+#define DIGITAL_NFC_DEP_N_RETRY_ATN 2
+
#define DIGITAL_NFC_DEP_FRAME_DIR_OUT 0xD4
#define DIGITAL_NFC_DEP_FRAME_DIR_IN 0xD5
@@ -32,20 +35,32 @@
#define DIGITAL_ATR_REQ_MIN_SIZE 16
#define DIGITAL_ATR_REQ_MAX_SIZE 64
-#define DIGITAL_LR_BITS_PAYLOAD_SIZE_254B 0x30
-#define DIGITAL_FSL_BITS_PAYLOAD_SIZE_254B \
- (DIGITAL_LR_BITS_PAYLOAD_SIZE_254B >> 4)
+#define DIGITAL_DID_MAX 14
+
+#define DIGITAL_PAYLOAD_SIZE_MAX 254
+#define DIGITAL_PAYLOAD_BITS_TO_PP(s) (((s) & 0x3) << 4)
+#define DIGITAL_PAYLOAD_PP_TO_BITS(s) (((s) >> 4) & 0x3)
+#define DIGITAL_PAYLOAD_BITS_TO_FSL(s) ((s) & 0x3)
+#define DIGITAL_PAYLOAD_FSL_TO_BITS(s) ((s) & 0x3)
+
#define DIGITAL_GB_BIT 0x02
+#define DIGITAL_NFC_DEP_REQ_RES_HEADROOM 2 /* SoD: [SB (NFC-A)] + LEN */
+#define DIGITAL_NFC_DEP_REQ_RES_TAILROOM 2 /* EoD: 2-byte CRC */
+
#define DIGITAL_NFC_DEP_PFB_TYPE(pfb) ((pfb) & 0xE0)
#define DIGITAL_NFC_DEP_PFB_TIMEOUT_BIT 0x10
+#define DIGITAL_NFC_DEP_PFB_MI_BIT 0x10
+#define DIGITAL_NFC_DEP_PFB_NACK_BIT 0x10
+#define DIGITAL_NFC_DEP_PFB_DID_BIT 0x04
#define DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb) \
((pfb) & DIGITAL_NFC_DEP_PFB_TIMEOUT_BIT)
-#define DIGITAL_NFC_DEP_MI_BIT_SET(pfb) ((pfb) & 0x10)
+#define DIGITAL_NFC_DEP_MI_BIT_SET(pfb) ((pfb) & DIGITAL_NFC_DEP_PFB_MI_BIT)
+#define DIGITAL_NFC_DEP_NACK_BIT_SET(pfb) ((pfb) & DIGITAL_NFC_DEP_PFB_NACK_BIT)
#define DIGITAL_NFC_DEP_NAD_BIT_SET(pfb) ((pfb) & 0x08)
-#define DIGITAL_NFC_DEP_DID_BIT_SET(pfb) ((pfb) & 0x04)
+#define DIGITAL_NFC_DEP_DID_BIT_SET(pfb) ((pfb) & DIGITAL_NFC_DEP_PFB_DID_BIT)
#define DIGITAL_NFC_DEP_PFB_PNI(pfb) ((pfb) & 0x03)
#define DIGITAL_NFC_DEP_PFB_I_PDU 0x00
@@ -97,6 +112,34 @@ struct digital_dep_req_res {
static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg,
struct sk_buff *resp);
+static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg,
+ struct sk_buff *resp);
+
+static const u8 digital_payload_bits_map[4] = {
+ [0] = 64,
+ [1] = 128,
+ [2] = 192,
+ [3] = 254
+};
+
+static u8 digital_payload_bits_to_size(u8 payload_bits)
+{
+ if (payload_bits >= ARRAY_SIZE(digital_payload_bits_map))
+ return 0;
+
+ return digital_payload_bits_map[payload_bits];
+}
+
+static u8 digital_payload_size_to_bits(u8 payload_size)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(digital_payload_bits_map); i++)
+ if (digital_payload_bits_map[i] == payload_size)
+ return i;
+
+ return 0xff;
+}
static void digital_skb_push_dep_sod(struct nfc_digital_dev *ddev,
struct sk_buff *skb)
@@ -129,6 +172,106 @@ static int digital_skb_pull_dep_sod(struct nfc_digital_dev *ddev,
return 0;
}
+static struct sk_buff *
+digital_send_dep_data_prep(struct nfc_digital_dev *ddev, struct sk_buff *skb,
+ struct digital_dep_req_res *dep_req_res,
+ struct digital_data_exch *data_exch)
+{
+ struct sk_buff *new_skb;
+
+ if (skb->len > ddev->remote_payload_max) {
+ dep_req_res->pfb |= DIGITAL_NFC_DEP_PFB_MI_BIT;
+
+ new_skb = digital_skb_alloc(ddev, ddev->remote_payload_max);
+ if (!new_skb) {
+ kfree_skb(ddev->chaining_skb);
+ ddev->chaining_skb = NULL;
+
+ return ERR_PTR(-ENOMEM);
+ }
+
+ skb_reserve(new_skb, ddev->tx_headroom + NFC_HEADER_SIZE +
+ DIGITAL_NFC_DEP_REQ_RES_HEADROOM);
+ memcpy(skb_put(new_skb, ddev->remote_payload_max), skb->data,
+ ddev->remote_payload_max);
+ skb_pull(skb, ddev->remote_payload_max);
+
+ ddev->chaining_skb = skb;
+ ddev->data_exch = data_exch;
+ } else {
+ ddev->chaining_skb = NULL;
+ new_skb = skb;
+ }
+
+ return new_skb;
+}
+
+static struct sk_buff *
+digital_recv_dep_data_gather(struct nfc_digital_dev *ddev, u8 pfb,
+ struct sk_buff *resp,
+ int (*send_ack)(struct nfc_digital_dev *ddev,
+ struct digital_data_exch
+ *data_exch),
+ struct digital_data_exch *data_exch)
+{
+ struct sk_buff *new_skb;
+ int rc;
+
+ if (DIGITAL_NFC_DEP_MI_BIT_SET(pfb) && (!ddev->chaining_skb)) {
+ ddev->chaining_skb =
+ nfc_alloc_recv_skb(8 * ddev->local_payload_max,
+ GFP_KERNEL);
+ if (!ddev->chaining_skb) {
+ rc = -ENOMEM;
+ goto error;
+ }
+ }
+
+ if (ddev->chaining_skb) {
+ if (resp->len > skb_tailroom(ddev->chaining_skb)) {
+ new_skb = skb_copy_expand(ddev->chaining_skb,
+ skb_headroom(
+ ddev->chaining_skb),
+ 8 * ddev->local_payload_max,
+ GFP_KERNEL);
+ if (!new_skb) {
+ rc = -ENOMEM;
+ goto error;
+ }
+
+ kfree_skb(ddev->chaining_skb);
+ ddev->chaining_skb = new_skb;
+ }
+
+ memcpy(skb_put(ddev->chaining_skb, resp->len), resp->data,
+ resp->len);
+
+ kfree_skb(resp);
+ resp = NULL;
+
+ if (DIGITAL_NFC_DEP_MI_BIT_SET(pfb)) {
+ rc = send_ack(ddev, data_exch);
+ if (rc)
+ goto error;
+
+ return NULL;
+ }
+
+ resp = ddev->chaining_skb;
+ ddev->chaining_skb = NULL;
+ }
+
+ return resp;
+
+error:
+ kfree_skb(resp);
+
+ kfree_skb(ddev->chaining_skb);
+ ddev->chaining_skb = NULL;
+
+ return ERR_PTR(rc);
+}
+
static void digital_in_recv_psl_res(struct nfc_digital_dev *ddev, void *arg,
struct sk_buff *resp)
{
@@ -198,6 +341,8 @@ static int digital_in_send_psl_req(struct nfc_digital_dev *ddev,
{
struct sk_buff *skb;
struct digital_psl_req *psl_req;
+ int rc;
+ u8 payload_size, payload_bits;
skb = digital_skb_alloc(ddev, sizeof(*psl_req));
if (!skb)
@@ -211,14 +356,24 @@ static int digital_in_send_psl_req(struct nfc_digital_dev *ddev,
psl_req->cmd = DIGITAL_CMD_PSL_REQ;
psl_req->did = 0;
psl_req->brs = (0x2 << 3) | 0x2; /* 424F both directions */
- psl_req->fsl = DIGITAL_FSL_BITS_PAYLOAD_SIZE_254B;
+
+ payload_size = min(ddev->local_payload_max, ddev->remote_payload_max);
+ payload_bits = digital_payload_size_to_bits(payload_size);
+ psl_req->fsl = DIGITAL_PAYLOAD_BITS_TO_FSL(payload_bits);
+
+ ddev->local_payload_max = payload_size;
+ ddev->remote_payload_max = payload_size;
digital_skb_push_dep_sod(ddev, skb);
ddev->skb_add_crc(skb);
- return digital_in_send_cmd(ddev, skb, 500, digital_in_recv_psl_res,
- target);
+ rc = digital_in_send_cmd(ddev, skb, 500, digital_in_recv_psl_res,
+ target);
+ if (rc)
+ kfree_skb(skb);
+
+ return rc;
}
static void digital_in_recv_atr_res(struct nfc_digital_dev *ddev, void *arg,
@@ -226,7 +381,7 @@ static void digital_in_recv_atr_res(struct nfc_digital_dev *ddev, void *arg,
{
struct nfc_target *target = arg;
struct digital_atr_res *atr_res;
- u8 gb_len;
+ u8 gb_len, payload_bits;
int rc;
if (IS_ERR(resp)) {
@@ -256,6 +411,14 @@ static void digital_in_recv_atr_res(struct nfc_digital_dev *ddev, void *arg,
atr_res = (struct digital_atr_res *)resp->data;
+ payload_bits = DIGITAL_PAYLOAD_PP_TO_BITS(atr_res->pp);
+ ddev->remote_payload_max = digital_payload_bits_to_size(payload_bits);
+
+ if (!ddev->remote_payload_max) {
+ rc = -EINVAL;
+ goto exit;
+ }
+
rc = nfc_set_remote_general_bytes(ddev->nfc_dev, atr_res->gb, gb_len);
if (rc)
goto exit;
@@ -286,6 +449,8 @@ int digital_in_send_atr_req(struct nfc_digital_dev *ddev,
struct sk_buff *skb;
struct digital_atr_req *atr_req;
uint size;
+ int rc;
+ u8 payload_bits;
size = DIGITAL_ATR_REQ_MIN_SIZE + gb_len;
@@ -314,7 +479,9 @@ int digital_in_send_atr_req(struct nfc_digital_dev *ddev,
atr_req->bs = 0;
atr_req->br = 0;
- atr_req->pp = DIGITAL_LR_BITS_PAYLOAD_SIZE_254B;
+ ddev->local_payload_max = DIGITAL_PAYLOAD_SIZE_MAX;
+ payload_bits = digital_payload_size_to_bits(ddev->local_payload_max);
+ atr_req->pp = DIGITAL_PAYLOAD_BITS_TO_PP(payload_bits);
if (gb_len) {
atr_req->pp |= DIGITAL_GB_BIT;
@@ -325,8 +492,113 @@ int digital_in_send_atr_req(struct nfc_digital_dev *ddev,
ddev->skb_add_crc(skb);
- return digital_in_send_cmd(ddev, skb, 500, digital_in_recv_atr_res,
- target);
+ rc = digital_in_send_cmd(ddev, skb, 500, digital_in_recv_atr_res,
+ target);
+ if (rc)
+ kfree_skb(skb);
+
+ return rc;
+}
+
+static int digital_in_send_ack(struct nfc_digital_dev *ddev,
+ struct digital_data_exch *data_exch)
+{
+ struct digital_dep_req_res *dep_req;
+ struct sk_buff *skb;
+ int rc;
+
+ skb = digital_skb_alloc(ddev, 1);
+ if (!skb)
+ return -ENOMEM;
+
+ skb_push(skb, sizeof(struct digital_dep_req_res));
+
+ dep_req = (struct digital_dep_req_res *)skb->data;
+
+ dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT;
+ dep_req->cmd = DIGITAL_CMD_DEP_REQ;
+ dep_req->pfb = DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU |
+ ddev->curr_nfc_dep_pni;
+
+ digital_skb_push_dep_sod(ddev, skb);
+
+ ddev->skb_add_crc(skb);
+
+ ddev->saved_skb = skb_get(skb);
+ ddev->saved_skb_len = skb->len;
+
+ rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res,
+ data_exch);
+ if (rc) {
+ kfree_skb(skb);
+ kfree_skb(ddev->saved_skb);
+ ddev->saved_skb = NULL;
+ }
+
+ return rc;
+}
+
+static int digital_in_send_nack(struct nfc_digital_dev *ddev,
+ struct digital_data_exch *data_exch)
+{
+ struct digital_dep_req_res *dep_req;
+ struct sk_buff *skb;
+ int rc;
+
+ skb = digital_skb_alloc(ddev, 1);
+ if (!skb)
+ return -ENOMEM;
+
+ skb_push(skb, sizeof(struct digital_dep_req_res));
+
+ dep_req = (struct digital_dep_req_res *)skb->data;
+
+ dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT;
+ dep_req->cmd = DIGITAL_CMD_DEP_REQ;
+ dep_req->pfb = DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU |
+ DIGITAL_NFC_DEP_PFB_NACK_BIT | ddev->curr_nfc_dep_pni;
+
+ digital_skb_push_dep_sod(ddev, skb);
+
+ ddev->skb_add_crc(skb);
+
+ rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res,
+ data_exch);
+ if (rc)
+ kfree_skb(skb);
+
+ return rc;
+}
+
+static int digital_in_send_atn(struct nfc_digital_dev *ddev,
+ struct digital_data_exch *data_exch)
+{
+ struct digital_dep_req_res *dep_req;
+ struct sk_buff *skb;
+ int rc;
+
+ skb = digital_skb_alloc(ddev, 1);
+ if (!skb)
+ return -ENOMEM;
+
+ skb_push(skb, sizeof(struct digital_dep_req_res));
+
+ dep_req = (struct digital_dep_req_res *)skb->data;
+
+ dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT;
+ dep_req->cmd = DIGITAL_CMD_DEP_REQ;
+ dep_req->pfb = DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU;
+
+ digital_skb_push_dep_sod(ddev, skb);
+
+ ddev->skb_add_crc(skb);
+
+ rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res,
+ data_exch);
+ if (rc)
+ kfree_skb(skb);
+
+ return rc;
}
static int digital_in_send_rtox(struct nfc_digital_dev *ddev,
@@ -355,12 +627,30 @@ static int digital_in_send_rtox(struct nfc_digital_dev *ddev,
ddev->skb_add_crc(skb);
+ ddev->saved_skb = skb_get(skb);
+ ddev->saved_skb_len = skb->len;
+
rc = digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res,
data_exch);
+ if (rc) {
+ kfree_skb(skb);
+ kfree_skb(ddev->saved_skb);
+ ddev->saved_skb = NULL;
+ }
return rc;
}
+static int digital_in_send_saved_skb(struct nfc_digital_dev *ddev,
+ struct digital_data_exch *data_exch)
+{
+ skb_get(ddev->saved_skb);
+ skb_push(ddev->saved_skb, ddev->saved_skb_len);
+
+ return digital_in_send_cmd(ddev, ddev->saved_skb, 1500,
+ digital_in_recv_dep_res, data_exch);
+}
+
static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg,
struct sk_buff *resp)
{
@@ -373,25 +663,67 @@ static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg,
if (IS_ERR(resp)) {
rc = PTR_ERR(resp);
resp = NULL;
+
+ if (((rc != -ETIMEDOUT) || ddev->nack_count) &&
+ (ddev->nack_count++ < DIGITAL_NFC_DEP_N_RETRY_NACK)) {
+ ddev->atn_count = 0;
+
+ rc = digital_in_send_nack(ddev, data_exch);
+ if (rc)
+ goto error;
+
+ return;
+ } else if ((rc == -ETIMEDOUT) &&
+ (ddev->atn_count++ < DIGITAL_NFC_DEP_N_RETRY_ATN)) {
+ ddev->nack_count = 0;
+
+ rc = digital_in_send_atn(ddev, data_exch);
+ if (rc)
+ goto error;
+
+ return;
+ }
+
+ goto exit;
+ }
+
+ rc = digital_skb_pull_dep_sod(ddev, resp);
+ if (rc) {
+ PROTOCOL_ERR("14.4.1.2");
goto exit;
}
rc = ddev->skb_check_crc(resp);
if (rc) {
+ if ((resp->len >= 4) &&
+ (ddev->nack_count++ < DIGITAL_NFC_DEP_N_RETRY_NACK)) {
+ ddev->atn_count = 0;
+
+ rc = digital_in_send_nack(ddev, data_exch);
+ if (rc)
+ goto error;
+
+ kfree_skb(resp);
+
+ return;
+ }
+
PROTOCOL_ERR("14.4.1.6");
goto error;
}
- rc = digital_skb_pull_dep_sod(ddev, resp);
- if (rc) {
- PROTOCOL_ERR("14.4.1.2");
+ ddev->atn_count = 0;
+ ddev->nack_count = 0;
+
+ if (resp->len > ddev->local_payload_max) {
+ rc = -EMSGSIZE;
goto exit;
}
+ size = sizeof(struct digital_dep_req_res);
dep_res = (struct digital_dep_req_res *)resp->data;
- if (resp->len < sizeof(struct digital_dep_req_res) ||
- dep_res->dir != DIGITAL_NFC_DEP_FRAME_DIR_IN ||
+ if (resp->len < size || dep_res->dir != DIGITAL_NFC_DEP_FRAME_DIR_IN ||
dep_res->cmd != DIGITAL_CMD_DEP_RES) {
rc = -EIO;
goto error;
@@ -399,6 +731,24 @@ static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg,
pfb = dep_res->pfb;
+ if (DIGITAL_NFC_DEP_DID_BIT_SET(pfb)) {
+ PROTOCOL_ERR("14.8.2.1");
+ rc = -EIO;
+ goto error;
+ }
+
+ if (DIGITAL_NFC_DEP_NAD_BIT_SET(pfb)) {
+ rc = -EIO;
+ goto exit;
+ }
+
+ if (size > resp->len) {
+ rc = -EIO;
+ goto error;
+ }
+
+ skb_pull(resp, size);
+
switch (DIGITAL_NFC_DEP_PFB_TYPE(pfb)) {
case DIGITAL_NFC_DEP_PFB_I_PDU:
if (DIGITAL_NFC_DEP_PFB_PNI(pfb) != ddev->curr_nfc_dep_pni) {
@@ -409,21 +759,71 @@ static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg,
ddev->curr_nfc_dep_pni =
DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1);
+
+ kfree_skb(ddev->saved_skb);
+ ddev->saved_skb = NULL;
+
+ resp = digital_recv_dep_data_gather(ddev, pfb, resp,
+ digital_in_send_ack,
+ data_exch);
+ if (IS_ERR(resp)) {
+ rc = PTR_ERR(resp);
+ resp = NULL;
+ goto error;
+ }
+
+ /* If resp is NULL then we're still chaining so return and
+ * wait for the next part of the PDU. Else, the PDU is
+ * complete so pass it up.
+ */
+ if (!resp)
+ return;
+
rc = 0;
break;
case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU:
+ if (DIGITAL_NFC_DEP_PFB_PNI(pfb) != ddev->curr_nfc_dep_pni) {
+ PROTOCOL_ERR("14.12.3.3");
+ rc = -EIO;
+ goto exit;
+ }
+
+ ddev->curr_nfc_dep_pni =
+ DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1);
+
+ if (ddev->chaining_skb && !DIGITAL_NFC_DEP_NACK_BIT_SET(pfb)) {
+ kfree_skb(ddev->saved_skb);
+ ddev->saved_skb = NULL;
+
+ rc = digital_in_send_dep_req(ddev, NULL,
+ ddev->chaining_skb,
+ ddev->data_exch);
+ if (rc)
+ goto error;
+
+ return;
+ }
+
pr_err("Received a ACK/NACK PDU\n");
- rc = -EIO;
- goto error;
+ rc = -EINVAL;
+ goto exit;
case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU:
- if (!DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) {
- rc = -EINVAL;
- goto error;
+ if (!DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) { /* ATN */
+ rc = digital_in_send_saved_skb(ddev, data_exch);
+ if (rc) {
+ kfree_skb(ddev->saved_skb);
+ goto error;
+ }
+
+ return;
}
- rc = digital_in_send_rtox(ddev, data_exch, resp->data[3]);
+ kfree_skb(ddev->saved_skb);
+ ddev->saved_skb = NULL;
+
+ rc = digital_in_send_rtox(ddev, data_exch, resp->data[0]);
if (rc)
goto error;
@@ -431,30 +831,18 @@ static void digital_in_recv_dep_res(struct nfc_digital_dev *ddev, void *arg,
return;
}
- if (DIGITAL_NFC_DEP_MI_BIT_SET(pfb)) {
- pr_err("MI bit set. Chained PDU not supported\n");
- rc = -EIO;
- goto error;
- }
-
- size = sizeof(struct digital_dep_req_res);
-
- if (DIGITAL_NFC_DEP_DID_BIT_SET(pfb))
- size++;
-
- if (size > resp->len) {
- rc = -EIO;
- goto error;
- }
-
- skb_pull(resp, size);
-
exit:
data_exch->cb(data_exch->cb_context, resp, rc);
error:
kfree(data_exch);
+ kfree_skb(ddev->chaining_skb);
+ ddev->chaining_skb = NULL;
+
+ kfree_skb(ddev->saved_skb);
+ ddev->saved_skb = NULL;
+
if (rc)
kfree_skb(resp);
}
@@ -464,20 +852,47 @@ int digital_in_send_dep_req(struct nfc_digital_dev *ddev,
struct digital_data_exch *data_exch)
{
struct digital_dep_req_res *dep_req;
+ struct sk_buff *chaining_skb, *tmp_skb;
+ int rc;
skb_push(skb, sizeof(struct digital_dep_req_res));
dep_req = (struct digital_dep_req_res *)skb->data;
+
dep_req->dir = DIGITAL_NFC_DEP_FRAME_DIR_OUT;
dep_req->cmd = DIGITAL_CMD_DEP_REQ;
dep_req->pfb = ddev->curr_nfc_dep_pni;
- digital_skb_push_dep_sod(ddev, skb);
+ ddev->atn_count = 0;
+ ddev->nack_count = 0;
- ddev->skb_add_crc(skb);
+ chaining_skb = ddev->chaining_skb;
+
+ tmp_skb = digital_send_dep_data_prep(ddev, skb, dep_req, data_exch);
+ if (IS_ERR(tmp_skb))
+ return PTR_ERR(tmp_skb);
+
+ digital_skb_push_dep_sod(ddev, tmp_skb);
+
+ ddev->skb_add_crc(tmp_skb);
- return digital_in_send_cmd(ddev, skb, 1500, digital_in_recv_dep_res,
- data_exch);
+ ddev->saved_skb = skb_get(tmp_skb);
+ ddev->saved_skb_len = tmp_skb->len;
+
+ rc = digital_in_send_cmd(ddev, tmp_skb, 1500, digital_in_recv_dep_res,
+ data_exch);
+ if (rc) {
+ if (tmp_skb != skb)
+ kfree_skb(tmp_skb);
+
+ kfree_skb(chaining_skb);
+ ddev->chaining_skb = NULL;
+
+ kfree_skb(ddev->saved_skb);
+ ddev->saved_skb = NULL;
+ }
+
+ return rc;
}
static void digital_tg_set_rf_tech(struct nfc_digital_dev *ddev, u8 rf_tech)
@@ -507,11 +922,106 @@ static void digital_tg_set_rf_tech(struct nfc_digital_dev *ddev, u8 rf_tech)
}
}
+static int digital_tg_send_ack(struct nfc_digital_dev *ddev,
+ struct digital_data_exch *data_exch)
+{
+ struct digital_dep_req_res *dep_res;
+ struct sk_buff *skb;
+ int rc;
+
+ skb = digital_skb_alloc(ddev, 1);
+ if (!skb)
+ return -ENOMEM;
+
+ skb_push(skb, sizeof(struct digital_dep_req_res));
+
+ dep_res = (struct digital_dep_req_res *)skb->data;
+
+ dep_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN;
+ dep_res->cmd = DIGITAL_CMD_DEP_RES;
+ dep_res->pfb = DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU |
+ ddev->curr_nfc_dep_pni;
+
+ if (ddev->did) {
+ dep_res->pfb |= DIGITAL_NFC_DEP_PFB_DID_BIT;
+
+ memcpy(skb_put(skb, sizeof(ddev->did)), &ddev->did,
+ sizeof(ddev->did));
+ }
+
+ ddev->curr_nfc_dep_pni =
+ DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1);
+
+ digital_skb_push_dep_sod(ddev, skb);
+
+ ddev->skb_add_crc(skb);
+
+ ddev->saved_skb = skb_get(skb);
+ ddev->saved_skb_len = skb->len;
+
+ rc = digital_tg_send_cmd(ddev, skb, 1500, digital_tg_recv_dep_req,
+ data_exch);
+ if (rc) {
+ kfree_skb(skb);
+ kfree_skb(ddev->saved_skb);
+ ddev->saved_skb = NULL;
+ }
+
+ return rc;
+}
+
+static int digital_tg_send_atn(struct nfc_digital_dev *ddev)
+{
+ struct digital_dep_req_res *dep_res;
+ struct sk_buff *skb;
+ int rc;
+
+ skb = digital_skb_alloc(ddev, 1);
+ if (!skb)
+ return -ENOMEM;
+
+ skb_push(skb, sizeof(struct digital_dep_req_res));
+
+ dep_res = (struct digital_dep_req_res *)skb->data;
+
+ dep_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN;
+ dep_res->cmd = DIGITAL_CMD_DEP_RES;
+ dep_res->pfb = DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU;
+
+ if (ddev->did) {
+ dep_res->pfb |= DIGITAL_NFC_DEP_PFB_DID_BIT;
+
+ memcpy(skb_put(skb, sizeof(ddev->did)), &ddev->did,
+ sizeof(ddev->did));
+ }
+
+ digital_skb_push_dep_sod(ddev, skb);
+
+ ddev->skb_add_crc(skb);
+
+ rc = digital_tg_send_cmd(ddev, skb, 1500, digital_tg_recv_dep_req,
+ NULL);
+ if (rc)
+ kfree_skb(skb);
+
+ return rc;
+}
+
+static int digital_tg_send_saved_skb(struct nfc_digital_dev *ddev)
+{
+ skb_get(ddev->saved_skb);
+ skb_push(ddev->saved_skb, ddev->saved_skb_len);
+
+ return digital_tg_send_cmd(ddev, ddev->saved_skb, 1500,
+ digital_tg_recv_dep_req, NULL);
+}
+
static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg,
struct sk_buff *resp)
{
int rc;
struct digital_dep_req_res *dep_req;
+ u8 pfb;
size_t size;
if (IS_ERR(resp)) {
@@ -532,6 +1042,11 @@ static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg,
goto exit;
}
+ if (resp->len > ddev->local_payload_max) {
+ rc = -EMSGSIZE;
+ goto exit;
+ }
+
size = sizeof(struct digital_dep_req_res);
dep_req = (struct digital_dep_req_res *)resp->data;
@@ -541,34 +1056,147 @@ static void digital_tg_recv_dep_req(struct nfc_digital_dev *ddev, void *arg,
goto exit;
}
- if (DIGITAL_NFC_DEP_DID_BIT_SET(dep_req->pfb))
- size++;
+ pfb = dep_req->pfb;
- if (resp->len < size) {
+ if (DIGITAL_NFC_DEP_DID_BIT_SET(pfb)) {
+ if (ddev->did && (ddev->did == resp->data[3])) {
+ size++;
+ } else {
+ rc = -EIO;
+ goto exit;
+ }
+ } else if (ddev->did) {
rc = -EIO;
goto exit;
}
- switch (DIGITAL_NFC_DEP_PFB_TYPE(dep_req->pfb)) {
+ if (DIGITAL_NFC_DEP_NAD_BIT_SET(pfb)) {
+ rc = -EIO;
+ goto exit;
+ }
+
+ if (size > resp->len) {
+ rc = -EIO;
+ goto exit;
+ }
+
+ skb_pull(resp, size);
+
+ switch (DIGITAL_NFC_DEP_PFB_TYPE(pfb)) {
case DIGITAL_NFC_DEP_PFB_I_PDU:
pr_debug("DIGITAL_NFC_DEP_PFB_I_PDU\n");
- ddev->curr_nfc_dep_pni = DIGITAL_NFC_DEP_PFB_PNI(dep_req->pfb);
+
+ if ((ddev->atn_count && (DIGITAL_NFC_DEP_PFB_PNI(pfb - 1) !=
+ ddev->curr_nfc_dep_pni)) ||
+ (DIGITAL_NFC_DEP_PFB_PNI(pfb) != ddev->curr_nfc_dep_pni)) {
+ PROTOCOL_ERR("14.12.3.4");
+ rc = -EIO;
+ goto exit;
+ }
+
+ if (ddev->atn_count) {
+ ddev->atn_count = 0;
+
+ rc = digital_tg_send_saved_skb(ddev);
+ if (rc)
+ goto exit;
+
+ return;
+ }
+
+ kfree_skb(ddev->saved_skb);
+ ddev->saved_skb = NULL;
+
+ resp = digital_recv_dep_data_gather(ddev, pfb, resp,
+ digital_tg_send_ack, NULL);
+ if (IS_ERR(resp)) {
+ rc = PTR_ERR(resp);
+ resp = NULL;
+ goto exit;
+ }
+
+ /* If resp is NULL then we're still chaining so return and
+ * wait for the next part of the PDU. Else, the PDU is
+ * complete so pass it up.
+ */
+ if (!resp)
+ return;
+
+ rc = 0;
break;
case DIGITAL_NFC_DEP_PFB_ACK_NACK_PDU:
- pr_err("Received a ACK/NACK PDU\n");
- rc = -EINVAL;
- goto exit;
+ if (!DIGITAL_NFC_DEP_NACK_BIT_SET(pfb)) { /* ACK */
+ if ((ddev->atn_count &&
+ (DIGITAL_NFC_DEP_PFB_PNI(pfb - 1) !=
+ ddev->curr_nfc_dep_pni)) ||
+ (DIGITAL_NFC_DEP_PFB_PNI(pfb) !=
+ ddev->curr_nfc_dep_pni) ||
+ !ddev->chaining_skb || !ddev->saved_skb) {
+ rc = -EIO;
+ goto exit;
+ }
+
+ if (ddev->atn_count) {
+ ddev->atn_count = 0;
+
+ rc = digital_tg_send_saved_skb(ddev);
+ if (rc)
+ goto exit;
+
+ return;
+ }
+
+ kfree_skb(ddev->saved_skb);
+ ddev->saved_skb = NULL;
+
+ rc = digital_tg_send_dep_res(ddev, ddev->chaining_skb);
+ if (rc)
+ goto exit;
+ } else { /* NACK */
+ if ((DIGITAL_NFC_DEP_PFB_PNI(pfb + 1) !=
+ ddev->curr_nfc_dep_pni) ||
+ !ddev->saved_skb) {
+ rc = -EIO;
+ goto exit;
+ }
+
+ ddev->atn_count = 0;
+
+ rc = digital_tg_send_saved_skb(ddev);
+ if (rc) {
+ kfree_skb(ddev->saved_skb);
+ goto exit;
+ }
+ }
+
+ return;
case DIGITAL_NFC_DEP_PFB_SUPERVISOR_PDU:
- pr_err("Received a SUPERVISOR PDU\n");
- rc = -EINVAL;
- goto exit;
- }
+ if (DIGITAL_NFC_DEP_PFB_IS_TIMEOUT(pfb)) {
+ rc = -EINVAL;
+ goto exit;
+ }
- skb_pull(resp, size);
+ rc = digital_tg_send_atn(ddev);
+ if (rc)
+ goto exit;
+
+ ddev->atn_count++;
+
+ kfree_skb(resp);
+ return;
+ }
rc = nfc_tm_data_received(ddev->nfc_dev, resp);
exit:
+ kfree_skb(ddev->chaining_skb);
+ ddev->chaining_skb = NULL;
+
+ ddev->atn_count = 0;
+
+ kfree_skb(ddev->saved_skb);
+ ddev->saved_skb = NULL;
+
if (rc)
kfree_skb(resp);
}
@@ -576,20 +1204,54 @@ exit:
int digital_tg_send_dep_res(struct nfc_digital_dev *ddev, struct sk_buff *skb)
{
struct digital_dep_req_res *dep_res;
+ struct sk_buff *chaining_skb, *tmp_skb;
+ int rc;
skb_push(skb, sizeof(struct digital_dep_req_res));
+
dep_res = (struct digital_dep_req_res *)skb->data;
dep_res->dir = DIGITAL_NFC_DEP_FRAME_DIR_IN;
dep_res->cmd = DIGITAL_CMD_DEP_RES;
dep_res->pfb = ddev->curr_nfc_dep_pni;
- digital_skb_push_dep_sod(ddev, skb);
+ if (ddev->did) {
+ dep_res->pfb |= DIGITAL_NFC_DEP_PFB_DID_BIT;
- ddev->skb_add_crc(skb);
+ memcpy(skb_put(skb, sizeof(ddev->did)), &ddev->did,
+ sizeof(ddev->did));
+ }
+
+ ddev->curr_nfc_dep_pni =
+ DIGITAL_NFC_DEP_PFB_PNI(ddev->curr_nfc_dep_pni + 1);
+
+ chaining_skb = ddev->chaining_skb;
+
+ tmp_skb = digital_send_dep_data_prep(ddev, skb, dep_res, NULL);
+ if (IS_ERR(tmp_skb))
+ return PTR_ERR(tmp_skb);
+
+ digital_skb_push_dep_sod(ddev, tmp_skb);
+
+ ddev->skb_add_crc(tmp_skb);
+
+ ddev->saved_skb = skb_get(tmp_skb);
+ ddev->saved_skb_len = tmp_skb->len;
+
+ rc = digital_tg_send_cmd(ddev, tmp_skb, 1500, digital_tg_recv_dep_req,
+ NULL);
+ if (rc) {
+ if (tmp_skb != skb)
+ kfree_skb(tmp_skb);
+
+ kfree_skb(chaining_skb);
+ ddev->chaining_skb = NULL;
- return digital_tg_send_cmd(ddev, skb, 1500, digital_tg_recv_dep_req,
- NULL);
+ kfree_skb(ddev->saved_skb);
+ ddev->saved_skb = NULL;
+ }
+
+ return rc;
}
static void digital_tg_send_psl_res_complete(struct nfc_digital_dev *ddev,
@@ -632,9 +1294,10 @@ static int digital_tg_send_psl_res(struct nfc_digital_dev *ddev, u8 did,
ddev->skb_add_crc(skb);
+ ddev->curr_nfc_dep_pni = 0;
+
rc = digital_tg_send_cmd(ddev, skb, 0, digital_tg_send_psl_res_complete,
(void *)(unsigned long)rf_tech);
-
if (rc)
kfree_skb(skb);
@@ -647,7 +1310,7 @@ static void digital_tg_recv_psl_req(struct nfc_digital_dev *ddev, void *arg,
int rc;
struct digital_psl_req *psl_req;
u8 rf_tech;
- u8 dsi;
+ u8 dsi, payload_size, payload_bits;
if (IS_ERR(resp)) {
rc = PTR_ERR(resp);
@@ -692,6 +1355,18 @@ static void digital_tg_recv_psl_req(struct nfc_digital_dev *ddev, void *arg,
goto exit;
}
+ payload_bits = DIGITAL_PAYLOAD_FSL_TO_BITS(psl_req->fsl);
+ payload_size = digital_payload_bits_to_size(payload_bits);
+
+ if (!payload_size || (payload_size > min(ddev->local_payload_max,
+ ddev->remote_payload_max))) {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ ddev->local_payload_max = payload_size;
+ ddev->remote_payload_max = payload_size;
+
rc = digital_tg_send_psl_res(ddev, psl_req->did, rf_tech);
exit:
@@ -712,6 +1387,8 @@ static void digital_tg_send_atr_res_complete(struct nfc_digital_dev *ddev,
if (resp->data[0] == DIGITAL_NFC_DEP_NFCA_SOD_SB)
offset++;
+ ddev->atn_count = 0;
+
if (resp->data[offset] == DIGITAL_CMD_PSL_REQ)
digital_tg_recv_psl_req(ddev, arg, resp);
else
@@ -723,7 +1400,7 @@ static int digital_tg_send_atr_res(struct nfc_digital_dev *ddev,
{
struct digital_atr_res *atr_res;
struct sk_buff *skb;
- u8 *gb;
+ u8 *gb, payload_bits;
size_t gb_len;
int rc;
@@ -744,7 +1421,11 @@ static int digital_tg_send_atr_res(struct nfc_digital_dev *ddev,
atr_res->cmd = DIGITAL_CMD_ATR_RES;
memcpy(atr_res->nfcid3, atr_req->nfcid3, sizeof(atr_req->nfcid3));
atr_res->to = 8;
- atr_res->pp = DIGITAL_LR_BITS_PAYLOAD_SIZE_254B;
+
+ ddev->local_payload_max = DIGITAL_PAYLOAD_SIZE_MAX;
+ payload_bits = digital_payload_size_to_bits(ddev->local_payload_max);
+ atr_res->pp = DIGITAL_PAYLOAD_BITS_TO_PP(payload_bits);
+
if (gb_len) {
skb_put(skb, gb_len);
@@ -756,12 +1437,12 @@ static int digital_tg_send_atr_res(struct nfc_digital_dev *ddev,
ddev->skb_add_crc(skb);
+ ddev->curr_nfc_dep_pni = 0;
+
rc = digital_tg_send_cmd(ddev, skb, 999,
digital_tg_send_atr_res_complete, NULL);
- if (rc) {
+ if (rc)
kfree_skb(skb);
- return rc;
- }
return rc;
}
@@ -772,7 +1453,7 @@ void digital_tg_recv_atr_req(struct nfc_digital_dev *ddev, void *arg,
int rc;
struct digital_atr_req *atr_req;
size_t gb_len, min_size;
- u8 poll_tech_count;
+ u8 poll_tech_count, payload_bits;
if (IS_ERR(resp)) {
rc = PTR_ERR(resp);
@@ -815,11 +1496,22 @@ void digital_tg_recv_atr_req(struct nfc_digital_dev *ddev, void *arg,
atr_req = (struct digital_atr_req *)resp->data;
if (atr_req->dir != DIGITAL_NFC_DEP_FRAME_DIR_OUT ||
- atr_req->cmd != DIGITAL_CMD_ATR_REQ) {
+ atr_req->cmd != DIGITAL_CMD_ATR_REQ ||
+ atr_req->did > DIGITAL_DID_MAX) {
rc = -EINVAL;
goto exit;
}
+ payload_bits = DIGITAL_PAYLOAD_PP_TO_BITS(atr_req->pp);
+ ddev->remote_payload_max = digital_payload_bits_to_size(payload_bits);
+
+ if (!ddev->remote_payload_max) {
+ rc = -EINVAL;
+ goto exit;
+ }
+
+ ddev->did = atr_req->did;
+
rc = digital_tg_configure_hw(ddev, NFC_DIGITAL_CONFIG_FRAMING,
NFC_DIGITAL_FRAMING_NFC_DEP_ACTIVATED);
if (rc)
diff --git a/net/nfc/hci/command.c b/net/nfc/hci/command.c
index 677d24bb70f8..91df487aa0a9 100644
--- a/net/nfc/hci/command.c
+++ b/net/nfc/hci/command.c
@@ -345,6 +345,9 @@ int nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate,
pr_debug("\n");
+ if (hdev->gate2pipe[dest_gate] == NFC_HCI_DO_NOT_CREATE_PIPE)
+ return 0;
+
if (hdev->gate2pipe[dest_gate] != NFC_HCI_INVALID_PIPE)
return -EADDRINUSE;
diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c
index 117708263ced..ef50e7716c4a 100644
--- a/net/nfc/hci/core.c
+++ b/net/nfc/hci/core.c
@@ -167,6 +167,48 @@ exit:
void nfc_hci_cmd_received(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd,
struct sk_buff *skb)
{
+ int r = 0;
+ u8 gate = nfc_hci_pipe2gate(hdev, pipe);
+ u8 local_gate, new_pipe;
+ u8 gate_opened = 0x00;
+
+ pr_debug("from gate %x pipe %x cmd %x\n", gate, pipe, cmd);
+
+ switch (cmd) {
+ case NFC_HCI_ADM_NOTIFY_PIPE_CREATED:
+ if (skb->len != 5) {
+ r = -EPROTO;
+ break;
+ }
+
+ local_gate = skb->data[3];
+ new_pipe = skb->data[4];
+ nfc_hci_send_response(hdev, gate, NFC_HCI_ANY_OK, NULL, 0);
+
+ /* save the new created pipe and bind with local gate,
+ * the description for skb->data[3] is destination gate id
+ * but since we received this cmd from host controller, we
+ * are the destination and it is our local gate
+ */
+ hdev->gate2pipe[local_gate] = new_pipe;
+ break;
+ case NFC_HCI_ANY_OPEN_PIPE:
+ /* if the pipe is already created, we allow remote host to
+ * open it
+ */
+ if (gate != 0xff)
+ nfc_hci_send_response(hdev, gate, NFC_HCI_ANY_OK,
+ &gate_opened, 1);
+ break;
+ case NFC_HCI_ADM_NOTIFY_ALL_PIPE_CLEARED:
+ nfc_hci_send_response(hdev, gate, NFC_HCI_ANY_OK, NULL, 0);
+ break;
+ default:
+ pr_info("Discarded unknown cmd %x to gate %x\n", cmd, gate);
+ r = -EINVAL;
+ break;
+ }
+
kfree_skb(skb);
}
@@ -717,6 +759,19 @@ static int hci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx)
return 0;
}
+static int hci_se_io(struct nfc_dev *nfc_dev, u32 se_idx,
+ u8 *apdu, size_t apdu_length,
+ se_io_cb_t cb, void *cb_context)
+{
+ struct nfc_hci_dev *hdev = nfc_get_drvdata(nfc_dev);
+
+ if (hdev->ops->se_io)
+ return hdev->ops->se_io(hdev, se_idx, apdu,
+ apdu_length, cb, cb_context);
+
+ return 0;
+}
+
static void nfc_hci_failure(struct nfc_hci_dev *hdev, int err)
{
mutex_lock(&hdev->msg_tx_mutex);
@@ -830,6 +885,7 @@ static struct nfc_ops hci_nfc_ops = {
.discover_se = hci_discover_se,
.enable_se = hci_enable_se,
.disable_se = hci_disable_se,
+ .se_io = hci_se_io,
};
struct nfc_hci_dev *nfc_hci_allocate_device(struct nfc_hci_ops *ops,
diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c
index a3ad69a4c648..3621a902cb6e 100644
--- a/net/nfc/llcp_commands.c
+++ b/net/nfc/llcp_commands.c
@@ -401,7 +401,8 @@ int nfc_llcp_send_connect(struct nfc_llcp_sock *sock)
u8 *miux_tlv = NULL, miux_tlv_length;
u8 *rw_tlv = NULL, rw_tlv_length, rw;
int err;
- u16 size = 0, miux;
+ u16 size = 0;
+ __be16 miux;
pr_debug("Sending CONNECT\n");
@@ -465,7 +466,8 @@ int nfc_llcp_send_cc(struct nfc_llcp_sock *sock)
u8 *miux_tlv = NULL, miux_tlv_length;
u8 *rw_tlv = NULL, rw_tlv_length, rw;
int err;
- u16 size = 0, miux;
+ u16 size = 0;
+ __be16 miux;
pr_debug("Sending CC\n");
@@ -665,7 +667,7 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock,
if (msg_data == NULL)
return -ENOMEM;
- if (memcpy_fromiovec(msg_data, msg->msg_iov, len)) {
+ if (memcpy_from_msg(msg_data, msg, len)) {
kfree(msg_data);
return -EFAULT;
}
@@ -731,7 +733,7 @@ int nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap,
if (msg_data == NULL)
return -ENOMEM;
- if (memcpy_fromiovec(msg_data, msg->msg_iov, len)) {
+ if (memcpy_from_msg(msg_data, msg, len)) {
kfree(msg_data);
return -EFAULT;
}
diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c
index 51e788797317..b18f07ccb504 100644
--- a/net/nfc/llcp_core.c
+++ b/net/nfc/llcp_core.c
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2011 Intel Corporation. All rights reserved.
+ * Copyright (C) 2014 Marvell International Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1511,8 +1512,10 @@ int nfc_llcp_data_received(struct nfc_dev *dev, struct sk_buff *skb)
struct nfc_llcp_local *local;
local = nfc_llcp_find_local(dev);
- if (local == NULL)
+ if (local == NULL) {
+ kfree_skb(skb);
return -ENODEV;
+ }
__nfc_llcp_recv(local, skb);
diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c
index 51f077a92fa9..e181e290427c 100644
--- a/net/nfc/llcp_sock.c
+++ b/net/nfc/llcp_sock.c
@@ -524,13 +524,13 @@ static int llcp_sock_getname(struct socket *sock, struct sockaddr *uaddr,
static inline unsigned int llcp_accept_poll(struct sock *parent)
{
- struct nfc_llcp_sock *llcp_sock, *n, *parent_sock;
+ struct nfc_llcp_sock *llcp_sock, *parent_sock;
struct sock *sk;
parent_sock = nfc_llcp_sock(parent);
- list_for_each_entry_safe(llcp_sock, n, &parent_sock->accept_queue,
- accept_queue) {
+ list_for_each_entry(llcp_sock, &parent_sock->accept_queue,
+ accept_queue) {
sk = &llcp_sock->sk;
if (sk->sk_state == LLCP_CONNECTED)
@@ -832,7 +832,7 @@ static int llcp_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
copied = min_t(unsigned int, rlen, len);
cskb = skb;
- if (skb_copy_datagram_iovec(cskb, 0, msg->msg_iov, copied)) {
+ if (skb_copy_datagram_msg(cskb, 0, msg, copied)) {
if (!(flags & MSG_PEEK))
skb_queue_head(&sk->sk_receive_queue, skb);
return -EFAULT;
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index 90b16cb40058..51feb5e63008 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -3,6 +3,7 @@
* NFC Controller (NFCC) and a Device Host (DH).
*
* Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2014 Marvell International Ltd.
*
* Written by Ilan Elias <ilane@ti.com>
*
@@ -196,18 +197,24 @@ static void nci_set_config_req(struct nci_dev *ndev, unsigned long opt)
nci_send_cmd(ndev, NCI_OP_CORE_SET_CONFIG_CMD, (3 + param->len), &cmd);
}
+struct nci_rf_discover_param {
+ __u32 im_protocols;
+ __u32 tm_protocols;
+};
+
static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt)
{
+ struct nci_rf_discover_param *param =
+ (struct nci_rf_discover_param *)opt;
struct nci_rf_disc_cmd cmd;
- __u32 protocols = opt;
cmd.num_disc_configs = 0;
if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
- (protocols & NFC_PROTO_JEWEL_MASK ||
- protocols & NFC_PROTO_MIFARE_MASK ||
- protocols & NFC_PROTO_ISO14443_MASK ||
- protocols & NFC_PROTO_NFC_DEP_MASK)) {
+ (param->im_protocols & NFC_PROTO_JEWEL_MASK ||
+ param->im_protocols & NFC_PROTO_MIFARE_MASK ||
+ param->im_protocols & NFC_PROTO_ISO14443_MASK ||
+ param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) {
cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
NCI_NFC_A_PASSIVE_POLL_MODE;
cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
@@ -215,7 +222,7 @@ static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt)
}
if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
- (protocols & NFC_PROTO_ISO14443_B_MASK)) {
+ (param->im_protocols & NFC_PROTO_ISO14443_B_MASK)) {
cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
NCI_NFC_B_PASSIVE_POLL_MODE;
cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
@@ -223,8 +230,8 @@ static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt)
}
if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
- (protocols & NFC_PROTO_FELICA_MASK ||
- protocols & NFC_PROTO_NFC_DEP_MASK)) {
+ (param->im_protocols & NFC_PROTO_FELICA_MASK ||
+ param->im_protocols & NFC_PROTO_NFC_DEP_MASK)) {
cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
NCI_NFC_F_PASSIVE_POLL_MODE;
cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
@@ -232,13 +239,25 @@ static void nci_rf_discover_req(struct nci_dev *ndev, unsigned long opt)
}
if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS) &&
- (protocols & NFC_PROTO_ISO15693_MASK)) {
+ (param->im_protocols & NFC_PROTO_ISO15693_MASK)) {
cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
NCI_NFC_V_PASSIVE_POLL_MODE;
cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
cmd.num_disc_configs++;
}
+ if ((cmd.num_disc_configs < NCI_MAX_NUM_RF_CONFIGS - 1) &&
+ (param->tm_protocols & NFC_PROTO_NFC_DEP_MASK)) {
+ cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
+ NCI_NFC_A_PASSIVE_LISTEN_MODE;
+ cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
+ cmd.num_disc_configs++;
+ cmd.disc_configs[cmd.num_disc_configs].rf_tech_and_mode =
+ NCI_NFC_F_PASSIVE_LISTEN_MODE;
+ cmd.disc_configs[cmd.num_disc_configs].frequency = 1;
+ cmd.num_disc_configs++;
+ }
+
nci_send_cmd(ndev, NCI_OP_RF_DISCOVER_CMD,
(1 + (cmd.num_disc_configs * sizeof(struct disc_config))),
&cmd);
@@ -280,7 +299,7 @@ static void nci_rf_deactivate_req(struct nci_dev *ndev, unsigned long opt)
{
struct nci_rf_deactivate_cmd cmd;
- cmd.type = NCI_DEACTIVATE_TYPE_IDLE_MODE;
+ cmd.type = opt;
nci_send_cmd(ndev, NCI_OP_RF_DEACTIVATE_CMD,
sizeof(struct nci_rf_deactivate_cmd), &cmd);
@@ -441,6 +460,7 @@ static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev)
{
struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
struct nci_set_config_param param;
+ int rc;
param.val = nfc_get_local_general_bytes(nfc_dev, &param.len);
if ((param.val == NULL) || (param.len == 0))
@@ -451,14 +471,45 @@ static int nci_set_local_general_bytes(struct nfc_dev *nfc_dev)
param.id = NCI_PN_ATR_REQ_GEN_BYTES;
+ rc = nci_request(ndev, nci_set_config_req, (unsigned long)&param,
+ msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
+ if (rc)
+ return rc;
+
+ param.id = NCI_LN_ATR_RES_GEN_BYTES;
+
return nci_request(ndev, nci_set_config_req, (unsigned long)&param,
msecs_to_jiffies(NCI_SET_CONFIG_TIMEOUT));
}
+static int nci_set_listen_parameters(struct nfc_dev *nfc_dev)
+{
+ struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+ int rc;
+ __u8 val;
+
+ val = NCI_LA_SEL_INFO_NFC_DEP_MASK;
+
+ rc = nci_set_config(ndev, NCI_LA_SEL_INFO, 1, &val);
+ if (rc)
+ return rc;
+
+ val = NCI_LF_PROTOCOL_TYPE_NFC_DEP_MASK;
+
+ rc = nci_set_config(ndev, NCI_LF_PROTOCOL_TYPE, 1, &val);
+ if (rc)
+ return rc;
+
+ val = NCI_LF_CON_BITR_F_212 | NCI_LF_CON_BITR_F_424;
+
+ return nci_set_config(ndev, NCI_LF_CON_BITR_F, 1, &val);
+}
+
static int nci_start_poll(struct nfc_dev *nfc_dev,
__u32 im_protocols, __u32 tm_protocols)
{
struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+ struct nci_rf_discover_param param;
int rc;
if ((atomic_read(&ndev->state) == NCI_DISCOVERY) ||
@@ -476,13 +527,14 @@ static int nci_start_poll(struct nfc_dev *nfc_dev,
(atomic_read(&ndev->state) == NCI_POLL_ACTIVE)) {
pr_debug("target active or w4 select, implicitly deactivate\n");
- rc = nci_request(ndev, nci_rf_deactivate_req, 0,
+ rc = nci_request(ndev, nci_rf_deactivate_req,
+ NCI_DEACTIVATE_TYPE_IDLE_MODE,
msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
if (rc)
return -EBUSY;
}
- if (im_protocols & NFC_PROTO_NFC_DEP_MASK) {
+ if ((im_protocols | tm_protocols) & NFC_PROTO_NFC_DEP_MASK) {
rc = nci_set_local_general_bytes(nfc_dev);
if (rc) {
pr_err("failed to set local general bytes\n");
@@ -490,7 +542,15 @@ static int nci_start_poll(struct nfc_dev *nfc_dev,
}
}
- rc = nci_request(ndev, nci_rf_discover_req, im_protocols,
+ if (tm_protocols & NFC_PROTO_NFC_DEP_MASK) {
+ rc = nci_set_listen_parameters(nfc_dev);
+ if (rc)
+ pr_err("failed to set listen parameters\n");
+ }
+
+ param.im_protocols = im_protocols;
+ param.tm_protocols = tm_protocols;
+ rc = nci_request(ndev, nci_rf_discover_req, (unsigned long)&param,
msecs_to_jiffies(NCI_RF_DISC_TIMEOUT));
if (!rc)
@@ -509,7 +569,7 @@ static void nci_stop_poll(struct nfc_dev *nfc_dev)
return;
}
- nci_request(ndev, nci_rf_deactivate_req, 0,
+ nci_request(ndev, nci_rf_deactivate_req, NCI_DEACTIVATE_TYPE_IDLE_MODE,
msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
}
@@ -594,7 +654,8 @@ static void nci_deactivate_target(struct nfc_dev *nfc_dev,
ndev->target_active_prot = 0;
if (atomic_read(&ndev->state) == NCI_POLL_ACTIVE) {
- nci_request(ndev, nci_rf_deactivate_req, 0,
+ nci_request(ndev, nci_rf_deactivate_req,
+ NCI_DEACTIVATE_TYPE_SLEEP_MODE,
msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
}
}
@@ -622,9 +683,24 @@ static int nci_dep_link_up(struct nfc_dev *nfc_dev, struct nfc_target *target,
static int nci_dep_link_down(struct nfc_dev *nfc_dev)
{
+ struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+ int rc;
+
pr_debug("entry\n");
- nci_deactivate_target(nfc_dev, NULL);
+ if (nfc_dev->rf_mode == NFC_RF_INITIATOR) {
+ nci_deactivate_target(nfc_dev, NULL);
+ } else {
+ if (atomic_read(&ndev->state) == NCI_LISTEN_ACTIVE ||
+ atomic_read(&ndev->state) == NCI_DISCOVERY) {
+ nci_request(ndev, nci_rf_deactivate_req, 0,
+ msecs_to_jiffies(NCI_RF_DEACTIVATE_TIMEOUT));
+ }
+
+ rc = nfc_tm_deactivated(nfc_dev);
+ if (rc)
+ pr_err("error when signaling tm deactivation\n");
+ }
return 0;
}
@@ -658,18 +734,58 @@ static int nci_transceive(struct nfc_dev *nfc_dev, struct nfc_target *target,
return rc;
}
+static int nci_tm_send(struct nfc_dev *nfc_dev, struct sk_buff *skb)
+{
+ struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+ int rc;
+
+ rc = nci_send_data(ndev, NCI_STATIC_RF_CONN_ID, skb);
+ if (rc)
+ pr_err("unable to send data\n");
+
+ return rc;
+}
+
static int nci_enable_se(struct nfc_dev *nfc_dev, u32 se_idx)
{
+ struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+
+ if (ndev->ops->enable_se)
+ return ndev->ops->enable_se(ndev, se_idx);
+
return 0;
}
static int nci_disable_se(struct nfc_dev *nfc_dev, u32 se_idx)
{
+ struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+
+ if (ndev->ops->disable_se)
+ return ndev->ops->disable_se(ndev, se_idx);
+
return 0;
}
static int nci_discover_se(struct nfc_dev *nfc_dev)
{
+ struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+
+ if (ndev->ops->discover_se)
+ return ndev->ops->discover_se(ndev);
+
+ return 0;
+}
+
+static int nci_se_io(struct nfc_dev *nfc_dev, u32 se_idx,
+ u8 *apdu, size_t apdu_length,
+ se_io_cb_t cb, void *cb_context)
+{
+ struct nci_dev *ndev = nfc_get_drvdata(nfc_dev);
+
+ if (ndev->ops->se_io)
+ return ndev->ops->se_io(ndev, se_idx, apdu,
+ apdu_length, cb, cb_context);
+
return 0;
}
@@ -683,9 +799,11 @@ static struct nfc_ops nci_nfc_ops = {
.activate_target = nci_activate_target,
.deactivate_target = nci_deactivate_target,
.im_transceive = nci_transceive,
+ .tm_send = nci_tm_send,
.enable_se = nci_enable_se,
.disable_se = nci_disable_se,
.discover_se = nci_discover_se,
+ .se_io = nci_se_io,
};
/* ---- Interface to NCI drivers ---- */
diff --git a/net/nfc/nci/data.c b/net/nfc/nci/data.c
index 427ef2c7ab68..a2de2a8cb00e 100644
--- a/net/nfc/nci/data.c
+++ b/net/nfc/nci/data.c
@@ -3,6 +3,7 @@
* NFC Controller (NFCC) and a Device Host (DH).
*
* Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2014 Marvell International Ltd.
*
* Written by Ilan Elias <ilane@ti.com>
*
@@ -184,11 +185,16 @@ exit:
static void nci_add_rx_data_frag(struct nci_dev *ndev,
struct sk_buff *skb,
- __u8 pbf)
+ __u8 pbf, __u8 status)
{
int reassembly_len;
int err = 0;
+ if (status) {
+ err = status;
+ goto exit;
+ }
+
if (ndev->rx_data_reassembly) {
reassembly_len = ndev->rx_data_reassembly->len;
@@ -223,13 +229,24 @@ static void nci_add_rx_data_frag(struct nci_dev *ndev,
}
exit:
- nci_data_exchange_complete(ndev, skb, err);
+ if (ndev->nfc_dev->rf_mode == NFC_RF_INITIATOR) {
+ nci_data_exchange_complete(ndev, skb, err);
+ } else if (ndev->nfc_dev->rf_mode == NFC_RF_TARGET) {
+ /* Data received in Target mode, forward to nfc core */
+ err = nfc_tm_data_received(ndev->nfc_dev, skb);
+ if (err)
+ pr_err("unable to handle received data\n");
+ } else {
+ pr_err("rf mode unknown\n");
+ kfree_skb(skb);
+ }
}
/* Rx Data packet */
void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb)
{
__u8 pbf = nci_pbf(skb->data);
+ __u8 status = 0;
pr_debug("len %d\n", skb->len);
@@ -247,8 +264,9 @@ void nci_rx_data_packet(struct nci_dev *ndev, struct sk_buff *skb)
ndev->target_active_prot == NFC_PROTO_ISO15693) {
/* frame I/F => remove the status byte */
pr_debug("frame I/F => remove the status byte\n");
+ status = skb->data[skb->len - 1];
skb_trim(skb, (skb->len - 1));
}
- nci_add_rx_data_frag(ndev, skb, pbf);
+ nci_add_rx_data_frag(ndev, skb, pbf, nci_to_errno(status));
}
diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c
index 205b35f666db..22e453cb787d 100644
--- a/net/nfc/nci/ntf.c
+++ b/net/nfc/nci/ntf.c
@@ -103,7 +103,7 @@ static __u8 *nci_extract_rf_params_nfca_passive_poll(struct nci_dev *ndev,
struct rf_tech_specific_params_nfca_poll *nfca_poll,
__u8 *data)
{
- nfca_poll->sens_res = __le16_to_cpu(*((__u16 *)data));
+ nfca_poll->sens_res = __le16_to_cpu(*((__le16 *)data));
data += 2;
nfca_poll->nfcid1_len = min_t(__u8, *data++, NFC_NFCID1_MAXSIZE);
@@ -167,7 +167,19 @@ static __u8 *nci_extract_rf_params_nfcv_passive_poll(struct nci_dev *ndev,
return data;
}
-__u32 nci_get_prop_rf_protocol(struct nci_dev *ndev, __u8 rf_protocol)
+static __u8 *nci_extract_rf_params_nfcf_passive_listen(struct nci_dev *ndev,
+ struct rf_tech_specific_params_nfcf_listen *nfcf_listen,
+ __u8 *data)
+{
+ nfcf_listen->local_nfcid2_len = min_t(__u8, *data++,
+ NFC_NFCID2_MAXSIZE);
+ memcpy(nfcf_listen->local_nfcid2, data, nfcf_listen->local_nfcid2_len);
+ data += nfcf_listen->local_nfcid2_len;
+
+ return data;
+}
+
+static __u32 nci_get_prop_rf_protocol(struct nci_dev *ndev, __u8 rf_protocol)
{
if (ndev->ops->get_rfprotocol)
return ndev->ops->get_rfprotocol(ndev, rf_protocol);
@@ -401,17 +413,29 @@ static int nci_extract_activation_params_nfc_dep(struct nci_dev *ndev,
struct nci_rf_intf_activated_ntf *ntf, __u8 *data)
{
struct activation_params_poll_nfc_dep *poll;
+ struct activation_params_listen_nfc_dep *listen;
switch (ntf->activation_rf_tech_and_mode) {
case NCI_NFC_A_PASSIVE_POLL_MODE:
case NCI_NFC_F_PASSIVE_POLL_MODE:
poll = &ntf->activation_params.poll_nfc_dep;
- poll->atr_res_len = min_t(__u8, *data++, 63);
+ poll->atr_res_len = min_t(__u8, *data++,
+ NFC_ATR_RES_MAXSIZE - 2);
pr_debug("atr_res_len %d\n", poll->atr_res_len);
if (poll->atr_res_len > 0)
memcpy(poll->atr_res, data, poll->atr_res_len);
break;
+ case NCI_NFC_A_PASSIVE_LISTEN_MODE:
+ case NCI_NFC_F_PASSIVE_LISTEN_MODE:
+ listen = &ntf->activation_params.listen_nfc_dep;
+ listen->atr_req_len = min_t(__u8, *data++,
+ NFC_ATR_REQ_MAXSIZE - 2);
+ pr_debug("atr_req_len %d\n", listen->atr_req_len);
+ if (listen->atr_req_len > 0)
+ memcpy(listen->atr_req, data, listen->atr_req_len);
+ break;
+
default:
pr_err("unsupported activation_rf_tech_and_mode 0x%x\n",
ntf->activation_rf_tech_and_mode);
@@ -444,6 +468,48 @@ static void nci_target_auto_activated(struct nci_dev *ndev,
nfc_targets_found(ndev->nfc_dev, ndev->targets, ndev->n_targets);
}
+static int nci_store_general_bytes_nfc_dep(struct nci_dev *ndev,
+ struct nci_rf_intf_activated_ntf *ntf)
+{
+ ndev->remote_gb_len = 0;
+
+ if (ntf->activation_params_len <= 0)
+ return NCI_STATUS_OK;
+
+ switch (ntf->activation_rf_tech_and_mode) {
+ case NCI_NFC_A_PASSIVE_POLL_MODE:
+ case NCI_NFC_F_PASSIVE_POLL_MODE:
+ ndev->remote_gb_len = min_t(__u8,
+ (ntf->activation_params.poll_nfc_dep.atr_res_len
+ - NFC_ATR_RES_GT_OFFSET),
+ NFC_ATR_RES_GB_MAXSIZE);
+ memcpy(ndev->remote_gb,
+ (ntf->activation_params.poll_nfc_dep.atr_res
+ + NFC_ATR_RES_GT_OFFSET),
+ ndev->remote_gb_len);
+ break;
+
+ case NCI_NFC_A_PASSIVE_LISTEN_MODE:
+ case NCI_NFC_F_PASSIVE_LISTEN_MODE:
+ ndev->remote_gb_len = min_t(__u8,
+ (ntf->activation_params.listen_nfc_dep.atr_req_len
+ - NFC_ATR_REQ_GT_OFFSET),
+ NFC_ATR_REQ_GB_MAXSIZE);
+ memcpy(ndev->remote_gb,
+ (ntf->activation_params.listen_nfc_dep.atr_req
+ + NFC_ATR_REQ_GT_OFFSET),
+ ndev->remote_gb_len);
+ break;
+
+ default:
+ pr_err("unsupported activation_rf_tech_and_mode 0x%x\n",
+ ntf->activation_rf_tech_and_mode);
+ return NCI_STATUS_RF_PROTOCOL_ERROR;
+ }
+
+ return NCI_STATUS_OK;
+}
+
static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev,
struct sk_buff *skb)
{
@@ -493,6 +559,16 @@ static void nci_rf_intf_activated_ntf_packet(struct nci_dev *ndev,
&(ntf.rf_tech_specific_params.nfcv_poll), data);
break;
+ case NCI_NFC_A_PASSIVE_LISTEN_MODE:
+ /* no RF technology specific parameters */
+ break;
+
+ case NCI_NFC_F_PASSIVE_LISTEN_MODE:
+ data = nci_extract_rf_params_nfcf_passive_listen(ndev,
+ &(ntf.rf_tech_specific_params.nfcf_listen),
+ data);
+ break;
+
default:
pr_err("unsupported activation_rf_tech_and_mode 0x%x\n",
ntf.activation_rf_tech_and_mode);
@@ -546,32 +622,39 @@ exit:
/* store general bytes to be reported later in dep_link_up */
if (ntf.rf_interface == NCI_RF_INTERFACE_NFC_DEP) {
- ndev->remote_gb_len = 0;
-
- if (ntf.activation_params_len > 0) {
- /* ATR_RES general bytes at offset 15 */
- ndev->remote_gb_len = min_t(__u8,
- (ntf.activation_params
- .poll_nfc_dep.atr_res_len
- - NFC_ATR_RES_GT_OFFSET),
- NFC_MAX_GT_LEN);
- memcpy(ndev->remote_gb,
- (ntf.activation_params.poll_nfc_dep
- .atr_res + NFC_ATR_RES_GT_OFFSET),
- ndev->remote_gb_len);
- }
+ err = nci_store_general_bytes_nfc_dep(ndev, &ntf);
+ if (err != NCI_STATUS_OK)
+ pr_err("unable to store general bytes\n");
}
}
- if (atomic_read(&ndev->state) == NCI_DISCOVERY) {
- /* A single target was found and activated automatically */
- atomic_set(&ndev->state, NCI_POLL_ACTIVE);
- if (err == NCI_STATUS_OK)
- nci_target_auto_activated(ndev, &ntf);
- } else { /* ndev->state == NCI_W4_HOST_SELECT */
- /* A selected target was activated, so complete the request */
- atomic_set(&ndev->state, NCI_POLL_ACTIVE);
- nci_req_complete(ndev, err);
+ if (!(ntf.activation_rf_tech_and_mode & NCI_RF_TECH_MODE_LISTEN_MASK)) {
+ /* Poll mode */
+ if (atomic_read(&ndev->state) == NCI_DISCOVERY) {
+ /* A single target was found and activated
+ * automatically */
+ atomic_set(&ndev->state, NCI_POLL_ACTIVE);
+ if (err == NCI_STATUS_OK)
+ nci_target_auto_activated(ndev, &ntf);
+ } else { /* ndev->state == NCI_W4_HOST_SELECT */
+ /* A selected target was activated, so complete the
+ * request */
+ atomic_set(&ndev->state, NCI_POLL_ACTIVE);
+ nci_req_complete(ndev, err);
+ }
+ } else {
+ /* Listen mode */
+ atomic_set(&ndev->state, NCI_LISTEN_ACTIVE);
+ if (err == NCI_STATUS_OK &&
+ ntf.rf_protocol == NCI_RF_PROTOCOL_NFC_DEP) {
+ err = nfc_tm_activated(ndev->nfc_dev,
+ NFC_PROTO_NFC_DEP_MASK,
+ NFC_COMM_PASSIVE,
+ ndev->remote_gb,
+ ndev->remote_gb_len);
+ if (err != NCI_STATUS_OK)
+ pr_err("error when signaling tm activation\n");
+ }
}
}
@@ -595,8 +678,21 @@ static void nci_rf_deactivate_ntf_packet(struct nci_dev *ndev,
if (test_bit(NCI_DATA_EXCHANGE, &ndev->flags))
nci_data_exchange_complete(ndev, NULL, -EIO);
- nci_clear_target_list(ndev);
- atomic_set(&ndev->state, NCI_IDLE);
+ switch (ntf->type) {
+ case NCI_DEACTIVATE_TYPE_IDLE_MODE:
+ nci_clear_target_list(ndev);
+ atomic_set(&ndev->state, NCI_IDLE);
+ break;
+ case NCI_DEACTIVATE_TYPE_SLEEP_MODE:
+ case NCI_DEACTIVATE_TYPE_SLEEP_AF_MODE:
+ atomic_set(&ndev->state, NCI_W4_HOST_SELECT);
+ break;
+ case NCI_DEACTIVATE_TYPE_DISCOVERY:
+ nci_clear_target_list(ndev);
+ atomic_set(&ndev->state, NCI_DISCOVERY);
+ break;
+ }
+
nci_req_complete(ndev, NCI_STATUS_OK);
}
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c
index 43cb1c17e267..44989fc8cddf 100644
--- a/net/nfc/netlink.c
+++ b/net/nfc/netlink.c
@@ -810,6 +810,31 @@ out:
return rc;
}
+static int nfc_genl_activate_target(struct sk_buff *skb, struct genl_info *info)
+{
+ struct nfc_dev *dev;
+ u32 device_idx, target_idx, protocol;
+ int rc;
+
+ if (!info->attrs[NFC_ATTR_DEVICE_INDEX])
+ return -EINVAL;
+
+ device_idx = nla_get_u32(info->attrs[NFC_ATTR_DEVICE_INDEX]);
+
+ dev = nfc_get_device(device_idx);
+ if (!dev)
+ return -ENODEV;
+
+ target_idx = nla_get_u32(info->attrs[NFC_ATTR_TARGET_INDEX]);
+ protocol = nla_get_u32(info->attrs[NFC_ATTR_PROTOCOLS]);
+
+ nfc_deactivate_target(dev, target_idx);
+ rc = nfc_activate_target(dev, target_idx, protocol);
+
+ nfc_put_device(dev);
+ return 0;
+}
+
static int nfc_genl_dep_link_up(struct sk_buff *skb, struct genl_info *info)
{
struct nfc_dev *dev;
@@ -1285,6 +1310,51 @@ static int nfc_genl_dump_ses_done(struct netlink_callback *cb)
return 0;
}
+static int nfc_se_io(struct nfc_dev *dev, u32 se_idx,
+ u8 *apdu, size_t apdu_length,
+ se_io_cb_t cb, void *cb_context)
+{
+ struct nfc_se *se;
+ int rc;
+
+ pr_debug("%s se index %d\n", dev_name(&dev->dev), se_idx);
+
+ device_lock(&dev->dev);
+
+ if (!device_is_registered(&dev->dev)) {
+ rc = -ENODEV;
+ goto error;
+ }
+
+ if (!dev->dev_up) {
+ rc = -ENODEV;
+ goto error;
+ }
+
+ if (!dev->ops->se_io) {
+ rc = -EOPNOTSUPP;
+ goto error;
+ }
+
+ se = nfc_find_se(dev, se_idx);
+ if (!se) {
+ rc = -EINVAL;
+ goto error;
+ }
+
+ if (se->state != NFC_SE_ENABLED) {
+ rc = -ENODEV;
+ goto error;
+ }
+
+ rc = dev->ops->se_io(dev, se_idx, apdu,
+ apdu_length, cb, cb_context);
+
+error:
+ device_unlock(&dev->dev);
+ return rc;
+}
+
struct se_io_ctx {
u32 dev_idx;
u32 se_idx;
@@ -1367,7 +1437,7 @@ static int nfc_genl_se_io(struct sk_buff *skb, struct genl_info *info)
ctx->dev_idx = dev_idx;
ctx->se_idx = se_idx;
- return dev->ops->se_io(dev, se_idx, apdu, apdu_len, se_io_cb, ctx);
+ return nfc_se_io(dev, se_idx, apdu, apdu_len, se_io_cb, ctx);
}
static const struct genl_ops nfc_genl_ops[] = {
@@ -1455,6 +1525,11 @@ static const struct genl_ops nfc_genl_ops[] = {
.doit = nfc_genl_se_io,
.policy = nfc_genl_policy,
},
+ {
+ .cmd = NFC_CMD_ACTIVATE_TARGET,
+ .doit = nfc_genl_activate_target,
+ .policy = nfc_genl_policy,
+ },
};
diff --git a/net/nfc/rawsock.c b/net/nfc/rawsock.c
index 11c3544ea546..373e138c0ab6 100644
--- a/net/nfc/rawsock.c
+++ b/net/nfc/rawsock.c
@@ -231,7 +231,7 @@ static int rawsock_sendmsg(struct kiocb *iocb, struct socket *sock,
if (skb == NULL)
return rc;
- rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
+ rc = memcpy_from_msg(skb_put(skb, len), msg, len);
if (rc < 0) {
kfree_skb(skb);
return rc;
@@ -269,7 +269,7 @@ static int rawsock_recvmsg(struct kiocb *iocb, struct socket *sock,
copied = len;
}
- rc = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ rc = skb_copy_datagram_msg(skb, 0, msg, copied);
skb_free_datagram(sk, skb);
diff --git a/net/openvswitch/Kconfig b/net/openvswitch/Kconfig
index ba3bb8203b99..b7d818c59423 100644
--- a/net/openvswitch/Kconfig
+++ b/net/openvswitch/Kconfig
@@ -4,7 +4,9 @@
config OPENVSWITCH
tristate "Open vSwitch"
+ depends on INET
select LIBCRC32C
+ select NET_MPLS_GSO
---help---
Open vSwitch is a multilayer Ethernet switch targeted at virtualized
environments. In addition to supporting a variety of features
@@ -29,11 +31,10 @@ config OPENVSWITCH
If unsure, say N.
config OPENVSWITCH_GRE
- bool "Open vSwitch GRE tunneling support"
- depends on INET
+ tristate "Open vSwitch GRE tunneling support"
depends on OPENVSWITCH
- depends on NET_IPGRE_DEMUX && !(OPENVSWITCH=y && NET_IPGRE_DEMUX=m)
- default y
+ depends on NET_IPGRE_DEMUX
+ default OPENVSWITCH
---help---
If you say Y here, then the Open vSwitch will be able create GRE
vport.
@@ -43,11 +44,10 @@ config OPENVSWITCH_GRE
If unsure, say Y.
config OPENVSWITCH_VXLAN
- bool "Open vSwitch VXLAN tunneling support"
- depends on INET
+ tristate "Open vSwitch VXLAN tunneling support"
depends on OPENVSWITCH
- depends on VXLAN && !(OPENVSWITCH=y && VXLAN=m)
- default y
+ depends on VXLAN
+ default OPENVSWITCH
---help---
If you say Y here, then the Open vSwitch will be able create vxlan vport.
@@ -56,11 +56,10 @@ config OPENVSWITCH_VXLAN
If unsure, say Y.
config OPENVSWITCH_GENEVE
- bool "Open vSwitch Geneve tunneling support"
- depends on INET
+ tristate "Open vSwitch Geneve tunneling support"
depends on OPENVSWITCH
- depends on GENEVE && !(OPENVSWITCH=y && GENEVE=m)
- default y
+ depends on GENEVE
+ default OPENVSWITCH
---help---
If you say Y here, then the Open vSwitch will be able create geneve vport.
diff --git a/net/openvswitch/Makefile b/net/openvswitch/Makefile
index 9a33a273c375..91b9478413ef 100644
--- a/net/openvswitch/Makefile
+++ b/net/openvswitch/Makefile
@@ -15,14 +15,6 @@ openvswitch-y := \
vport-internal_dev.o \
vport-netdev.o
-ifneq ($(CONFIG_OPENVSWITCH_GENEVE),)
-openvswitch-y += vport-geneve.o
-endif
-
-ifneq ($(CONFIG_OPENVSWITCH_VXLAN),)
-openvswitch-y += vport-vxlan.o
-endif
-
-ifneq ($(CONFIG_OPENVSWITCH_GRE),)
-openvswitch-y += vport-gre.o
-endif
+obj-$(CONFIG_OPENVSWITCH_GENEVE)+= vport-geneve.o
+obj-$(CONFIG_OPENVSWITCH_VXLAN) += vport-vxlan.o
+obj-$(CONFIG_OPENVSWITCH_GRE) += vport-gre.o
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
index 006886dbee36..764fdc39c63b 100644
--- a/net/openvswitch/actions.c
+++ b/net/openvswitch/actions.c
@@ -28,10 +28,12 @@
#include <linux/in6.h>
#include <linux/if_arp.h>
#include <linux/if_vlan.h>
+
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/checksum.h>
#include <net/dsfield.h>
+#include <net/mpls.h>
#include <net/sctp/checksum.h>
#include "datapath.h"
@@ -67,7 +69,7 @@ static void action_fifo_init(struct action_fifo *fifo)
fifo->tail = 0;
}
-static bool action_fifo_is_empty(struct action_fifo *fifo)
+static bool action_fifo_is_empty(const struct action_fifo *fifo)
{
return (fifo->head == fifo->tail);
}
@@ -90,7 +92,7 @@ static struct deferred_action *action_fifo_put(struct action_fifo *fifo)
/* Return true if fifo is not full */
static struct deferred_action *add_deferred_actions(struct sk_buff *skb,
- struct sw_flow_key *key,
+ const struct sw_flow_key *key,
const struct nlattr *attr)
{
struct action_fifo *fifo;
@@ -107,100 +109,131 @@ static struct deferred_action *add_deferred_actions(struct sk_buff *skb,
return da;
}
-static int make_writable(struct sk_buff *skb, int write_len)
+static void invalidate_flow_key(struct sw_flow_key *key)
+{
+ key->eth.type = htons(0);
+}
+
+static bool is_flow_key_valid(const struct sw_flow_key *key)
{
- if (!pskb_may_pull(skb, write_len))
+ return !!key->eth.type;
+}
+
+static int push_mpls(struct sk_buff *skb, struct sw_flow_key *key,
+ const struct ovs_action_push_mpls *mpls)
+{
+ __be32 *new_mpls_lse;
+ struct ethhdr *hdr;
+
+ /* Networking stack do not allow simultaneous Tunnel and MPLS GSO. */
+ if (skb->encapsulation)
+ return -ENOTSUPP;
+
+ if (skb_cow_head(skb, MPLS_HLEN) < 0)
return -ENOMEM;
- if (!skb_cloned(skb) || skb_clone_writable(skb, write_len))
- return 0;
+ skb_push(skb, MPLS_HLEN);
+ memmove(skb_mac_header(skb) - MPLS_HLEN, skb_mac_header(skb),
+ skb->mac_len);
+ skb_reset_mac_header(skb);
+
+ new_mpls_lse = (__be32 *)skb_mpls_header(skb);
+ *new_mpls_lse = mpls->mpls_lse;
- return pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
+ if (skb->ip_summed == CHECKSUM_COMPLETE)
+ skb->csum = csum_add(skb->csum, csum_partial(new_mpls_lse,
+ MPLS_HLEN, 0));
+
+ hdr = eth_hdr(skb);
+ hdr->h_proto = mpls->mpls_ethertype;
+
+ skb_set_inner_protocol(skb, skb->protocol);
+ skb->protocol = mpls->mpls_ethertype;
+
+ invalidate_flow_key(key);
+ return 0;
}
-/* remove VLAN header from packet and update csum accordingly. */
-static int __pop_vlan_tci(struct sk_buff *skb, __be16 *current_tci)
+static int pop_mpls(struct sk_buff *skb, struct sw_flow_key *key,
+ const __be16 ethertype)
{
- struct vlan_hdr *vhdr;
+ struct ethhdr *hdr;
int err;
- err = make_writable(skb, VLAN_ETH_HLEN);
+ err = skb_ensure_writable(skb, skb->mac_len + MPLS_HLEN);
if (unlikely(err))
return err;
- if (skb->ip_summed == CHECKSUM_COMPLETE)
- skb->csum = csum_sub(skb->csum, csum_partial(skb->data
- + (2 * ETH_ALEN), VLAN_HLEN, 0));
+ skb_postpull_rcsum(skb, skb_mpls_header(skb), MPLS_HLEN);
- vhdr = (struct vlan_hdr *)(skb->data + ETH_HLEN);
- *current_tci = vhdr->h_vlan_TCI;
+ memmove(skb_mac_header(skb) + MPLS_HLEN, skb_mac_header(skb),
+ skb->mac_len);
- memmove(skb->data + VLAN_HLEN, skb->data, 2 * ETH_ALEN);
- __skb_pull(skb, VLAN_HLEN);
+ __skb_pull(skb, MPLS_HLEN);
+ skb_reset_mac_header(skb);
- vlan_set_encap_proto(skb, vhdr);
- skb->mac_header += VLAN_HLEN;
- if (skb_network_offset(skb) < ETH_HLEN)
- skb_set_network_header(skb, ETH_HLEN);
- skb_reset_mac_len(skb);
+ /* skb_mpls_header() is used to locate the ethertype
+ * field correctly in the presence of VLAN tags.
+ */
+ hdr = (struct ethhdr *)(skb_mpls_header(skb) - ETH_HLEN);
+ hdr->h_proto = ethertype;
+ if (eth_p_mpls(skb->protocol))
+ skb->protocol = ethertype;
+ invalidate_flow_key(key);
return 0;
}
-static int pop_vlan(struct sk_buff *skb)
+static int set_mpls(struct sk_buff *skb, struct sw_flow_key *key,
+ const __be32 *mpls_lse)
{
- __be16 tci;
+ __be32 *stack;
int err;
- if (likely(vlan_tx_tag_present(skb))) {
- skb->vlan_tci = 0;
- } else {
- if (unlikely(skb->protocol != htons(ETH_P_8021Q) ||
- skb->len < VLAN_ETH_HLEN))
- return 0;
-
- err = __pop_vlan_tci(skb, &tci);
- if (err)
- return err;
- }
- /* move next vlan tag to hw accel tag */
- if (likely(skb->protocol != htons(ETH_P_8021Q) ||
- skb->len < VLAN_ETH_HLEN))
- return 0;
-
- err = __pop_vlan_tci(skb, &tci);
+ err = skb_ensure_writable(skb, skb->mac_len + MPLS_HLEN);
if (unlikely(err))
return err;
- __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ntohs(tci));
+ stack = (__be32 *)skb_mpls_header(skb);
+ if (skb->ip_summed == CHECKSUM_COMPLETE) {
+ __be32 diff[] = { ~(*stack), *mpls_lse };
+ skb->csum = ~csum_partial((char *)diff, sizeof(diff),
+ ~skb->csum);
+ }
+
+ *stack = *mpls_lse;
+ key->mpls.top_lse = *mpls_lse;
return 0;
}
-static int push_vlan(struct sk_buff *skb, const struct ovs_action_push_vlan *vlan)
+static int pop_vlan(struct sk_buff *skb, struct sw_flow_key *key)
{
- if (unlikely(vlan_tx_tag_present(skb))) {
- u16 current_tag;
-
- /* push down current VLAN tag */
- current_tag = vlan_tx_tag_get(skb);
-
- if (!__vlan_put_tag(skb, skb->vlan_proto, current_tag))
- return -ENOMEM;
+ int err;
- if (skb->ip_summed == CHECKSUM_COMPLETE)
- skb->csum = csum_add(skb->csum, csum_partial(skb->data
- + (2 * ETH_ALEN), VLAN_HLEN, 0));
+ err = skb_vlan_pop(skb);
+ if (vlan_tx_tag_present(skb))
+ invalidate_flow_key(key);
+ else
+ key->eth.tci = 0;
+ return err;
+}
- }
- __vlan_hwaccel_put_tag(skb, vlan->vlan_tpid, ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT);
- return 0;
+static int push_vlan(struct sk_buff *skb, struct sw_flow_key *key,
+ const struct ovs_action_push_vlan *vlan)
+{
+ if (vlan_tx_tag_present(skb))
+ invalidate_flow_key(key);
+ else
+ key->eth.tci = vlan->vlan_tci;
+ return skb_vlan_push(skb, vlan->vlan_tpid,
+ ntohs(vlan->vlan_tci) & ~VLAN_TAG_PRESENT);
}
-static int set_eth_addr(struct sk_buff *skb,
+static int set_eth_addr(struct sk_buff *skb, struct sw_flow_key *key,
const struct ovs_key_ethernet *eth_key)
{
int err;
- err = make_writable(skb, ETH_HLEN);
+ err = skb_ensure_writable(skb, ETH_HLEN);
if (unlikely(err))
return err;
@@ -211,11 +244,13 @@ static int set_eth_addr(struct sk_buff *skb,
ovs_skb_postpush_rcsum(skb, eth_hdr(skb), ETH_ALEN * 2);
+ ether_addr_copy(key->eth.src, eth_key->eth_src);
+ ether_addr_copy(key->eth.dst, eth_key->eth_dst);
return 0;
}
static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh,
- __be32 *addr, __be32 new_addr)
+ __be32 *addr, __be32 new_addr)
{
int transport_len = skb->len - skb_transport_offset(skb);
@@ -246,11 +281,11 @@ static void update_ipv6_checksum(struct sk_buff *skb, u8 l4_proto,
{
int transport_len = skb->len - skb_transport_offset(skb);
- if (l4_proto == IPPROTO_TCP) {
+ if (l4_proto == NEXTHDR_TCP) {
if (likely(transport_len >= sizeof(struct tcphdr)))
inet_proto_csum_replace16(&tcp_hdr(skb)->check, skb,
addr, new_addr, 1);
- } else if (l4_proto == IPPROTO_UDP) {
+ } else if (l4_proto == NEXTHDR_UDP) {
if (likely(transport_len >= sizeof(struct udphdr))) {
struct udphdr *uh = udp_hdr(skb);
@@ -261,6 +296,10 @@ static void update_ipv6_checksum(struct sk_buff *skb, u8 l4_proto,
uh->check = CSUM_MANGLED_0;
}
}
+ } else if (l4_proto == NEXTHDR_ICMP) {
+ if (likely(transport_len >= sizeof(struct icmp6hdr)))
+ inet_proto_csum_replace16(&icmp6_hdr(skb)->icmp6_cksum,
+ skb, addr, new_addr, 1);
}
}
@@ -294,42 +333,52 @@ static void set_ip_ttl(struct sk_buff *skb, struct iphdr *nh, u8 new_ttl)
nh->ttl = new_ttl;
}
-static int set_ipv4(struct sk_buff *skb, const struct ovs_key_ipv4 *ipv4_key)
+static int set_ipv4(struct sk_buff *skb, struct sw_flow_key *key,
+ const struct ovs_key_ipv4 *ipv4_key)
{
struct iphdr *nh;
int err;
- err = make_writable(skb, skb_network_offset(skb) +
- sizeof(struct iphdr));
+ err = skb_ensure_writable(skb, skb_network_offset(skb) +
+ sizeof(struct iphdr));
if (unlikely(err))
return err;
nh = ip_hdr(skb);
- if (ipv4_key->ipv4_src != nh->saddr)
+ if (ipv4_key->ipv4_src != nh->saddr) {
set_ip_addr(skb, nh, &nh->saddr, ipv4_key->ipv4_src);
+ key->ipv4.addr.src = ipv4_key->ipv4_src;
+ }
- if (ipv4_key->ipv4_dst != nh->daddr)
+ if (ipv4_key->ipv4_dst != nh->daddr) {
set_ip_addr(skb, nh, &nh->daddr, ipv4_key->ipv4_dst);
+ key->ipv4.addr.dst = ipv4_key->ipv4_dst;
+ }
- if (ipv4_key->ipv4_tos != nh->tos)
+ if (ipv4_key->ipv4_tos != nh->tos) {
ipv4_change_dsfield(nh, 0, ipv4_key->ipv4_tos);
+ key->ip.tos = nh->tos;
+ }
- if (ipv4_key->ipv4_ttl != nh->ttl)
+ if (ipv4_key->ipv4_ttl != nh->ttl) {
set_ip_ttl(skb, nh, ipv4_key->ipv4_ttl);
+ key->ip.ttl = ipv4_key->ipv4_ttl;
+ }
return 0;
}
-static int set_ipv6(struct sk_buff *skb, const struct ovs_key_ipv6 *ipv6_key)
+static int set_ipv6(struct sk_buff *skb, struct sw_flow_key *key,
+ const struct ovs_key_ipv6 *ipv6_key)
{
struct ipv6hdr *nh;
int err;
__be32 *saddr;
__be32 *daddr;
- err = make_writable(skb, skb_network_offset(skb) +
- sizeof(struct ipv6hdr));
+ err = skb_ensure_writable(skb, skb_network_offset(skb) +
+ sizeof(struct ipv6hdr));
if (unlikely(err))
return err;
@@ -337,9 +386,12 @@ static int set_ipv6(struct sk_buff *skb, const struct ovs_key_ipv6 *ipv6_key)
saddr = (__be32 *)&nh->saddr;
daddr = (__be32 *)&nh->daddr;
- if (memcmp(ipv6_key->ipv6_src, saddr, sizeof(ipv6_key->ipv6_src)))
+ if (memcmp(ipv6_key->ipv6_src, saddr, sizeof(ipv6_key->ipv6_src))) {
set_ipv6_addr(skb, ipv6_key->ipv6_proto, saddr,
ipv6_key->ipv6_src, true);
+ memcpy(&key->ipv6.addr.src, ipv6_key->ipv6_src,
+ sizeof(ipv6_key->ipv6_src));
+ }
if (memcmp(ipv6_key->ipv6_dst, daddr, sizeof(ipv6_key->ipv6_dst))) {
unsigned int offset = 0;
@@ -353,16 +405,22 @@ static int set_ipv6(struct sk_buff *skb, const struct ovs_key_ipv6 *ipv6_key)
set_ipv6_addr(skb, ipv6_key->ipv6_proto, daddr,
ipv6_key->ipv6_dst, recalc_csum);
+ memcpy(&key->ipv6.addr.dst, ipv6_key->ipv6_dst,
+ sizeof(ipv6_key->ipv6_dst));
}
set_ipv6_tc(nh, ipv6_key->ipv6_tclass);
+ key->ip.tos = ipv6_get_dsfield(nh);
+
set_ipv6_fl(nh, ntohl(ipv6_key->ipv6_label));
- nh->hop_limit = ipv6_key->ipv6_hlimit;
+ key->ipv6.label = *(__be32 *)nh & htonl(IPV6_FLOWINFO_FLOWLABEL);
+ nh->hop_limit = ipv6_key->ipv6_hlimit;
+ key->ip.ttl = ipv6_key->ipv6_hlimit;
return 0;
}
-/* Must follow make_writable() since that can move the skb data. */
+/* Must follow skb_ensure_writable() since that can move the skb data. */
static void set_tp_port(struct sk_buff *skb, __be16 *port,
__be16 new_port, __sum16 *check)
{
@@ -386,54 +444,64 @@ static void set_udp_port(struct sk_buff *skb, __be16 *port, __be16 new_port)
}
}
-static int set_udp(struct sk_buff *skb, const struct ovs_key_udp *udp_port_key)
+static int set_udp(struct sk_buff *skb, struct sw_flow_key *key,
+ const struct ovs_key_udp *udp_port_key)
{
struct udphdr *uh;
int err;
- err = make_writable(skb, skb_transport_offset(skb) +
- sizeof(struct udphdr));
+ err = skb_ensure_writable(skb, skb_transport_offset(skb) +
+ sizeof(struct udphdr));
if (unlikely(err))
return err;
uh = udp_hdr(skb);
- if (udp_port_key->udp_src != uh->source)
+ if (udp_port_key->udp_src != uh->source) {
set_udp_port(skb, &uh->source, udp_port_key->udp_src);
+ key->tp.src = udp_port_key->udp_src;
+ }
- if (udp_port_key->udp_dst != uh->dest)
+ if (udp_port_key->udp_dst != uh->dest) {
set_udp_port(skb, &uh->dest, udp_port_key->udp_dst);
+ key->tp.dst = udp_port_key->udp_dst;
+ }
return 0;
}
-static int set_tcp(struct sk_buff *skb, const struct ovs_key_tcp *tcp_port_key)
+static int set_tcp(struct sk_buff *skb, struct sw_flow_key *key,
+ const struct ovs_key_tcp *tcp_port_key)
{
struct tcphdr *th;
int err;
- err = make_writable(skb, skb_transport_offset(skb) +
- sizeof(struct tcphdr));
+ err = skb_ensure_writable(skb, skb_transport_offset(skb) +
+ sizeof(struct tcphdr));
if (unlikely(err))
return err;
th = tcp_hdr(skb);
- if (tcp_port_key->tcp_src != th->source)
+ if (tcp_port_key->tcp_src != th->source) {
set_tp_port(skb, &th->source, tcp_port_key->tcp_src, &th->check);
+ key->tp.src = tcp_port_key->tcp_src;
+ }
- if (tcp_port_key->tcp_dst != th->dest)
+ if (tcp_port_key->tcp_dst != th->dest) {
set_tp_port(skb, &th->dest, tcp_port_key->tcp_dst, &th->check);
+ key->tp.dst = tcp_port_key->tcp_dst;
+ }
return 0;
}
-static int set_sctp(struct sk_buff *skb,
- const struct ovs_key_sctp *sctp_port_key)
+static int set_sctp(struct sk_buff *skb, struct sw_flow_key *key,
+ const struct ovs_key_sctp *sctp_port_key)
{
struct sctphdr *sh;
int err;
unsigned int sctphoff = skb_transport_offset(skb);
- err = make_writable(skb, sctphoff + sizeof(struct sctphdr));
+ err = skb_ensure_writable(skb, sctphoff + sizeof(struct sctphdr));
if (unlikely(err))
return err;
@@ -454,39 +522,35 @@ static int set_sctp(struct sk_buff *skb,
sh->checksum = old_csum ^ old_correct_csum ^ new_csum;
skb_clear_hash(skb);
+ key->tp.src = sctp_port_key->sctp_src;
+ key->tp.dst = sctp_port_key->sctp_dst;
}
return 0;
}
-static int do_output(struct datapath *dp, struct sk_buff *skb, int out_port)
+static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port)
{
- struct vport *vport;
+ struct vport *vport = ovs_vport_rcu(dp, out_port);
- if (unlikely(!skb))
- return -ENOMEM;
-
- vport = ovs_vport_rcu(dp, out_port);
- if (unlikely(!vport)) {
+ if (likely(vport))
+ ovs_vport_send(vport, skb);
+ else
kfree_skb(skb);
- return -ENODEV;
- }
-
- ovs_vport_send(vport, skb);
- return 0;
}
static int output_userspace(struct datapath *dp, struct sk_buff *skb,
struct sw_flow_key *key, const struct nlattr *attr)
{
+ struct ovs_tunnel_info info;
struct dp_upcall_info upcall;
const struct nlattr *a;
int rem;
upcall.cmd = OVS_PACKET_CMD_ACTION;
- upcall.key = key;
upcall.userdata = NULL;
upcall.portid = 0;
+ upcall.egress_tun_info = NULL;
for (a = nla_data(attr), rem = nla_len(attr); rem > 0;
a = nla_next(a, &rem)) {
@@ -498,15 +562,27 @@ static int output_userspace(struct datapath *dp, struct sk_buff *skb,
case OVS_USERSPACE_ATTR_PID:
upcall.portid = nla_get_u32(a);
break;
+
+ case OVS_USERSPACE_ATTR_EGRESS_TUN_PORT: {
+ /* Get out tunnel info. */
+ struct vport *vport;
+
+ vport = ovs_vport_rcu(dp, nla_get_u32(a));
+ if (vport) {
+ int err;
+
+ err = ovs_vport_get_egress_tun_info(vport, skb,
+ &info);
+ if (!err)
+ upcall.egress_tun_info = &info;
+ }
+ break;
}
- }
- return ovs_dp_upcall(dp, skb, &upcall);
-}
+ } /* End of switch. */
+ }
-static bool last_action(const struct nlattr *a, int rem)
-{
- return a->nla_len == rem;
+ return ovs_dp_upcall(dp, skb, key, &upcall);
}
static int sample(struct datapath *dp, struct sk_buff *skb,
@@ -543,7 +619,7 @@ static int sample(struct datapath *dp, struct sk_buff *skb,
* user space. This skb will be consumed by its caller.
*/
if (likely(nla_type(a) == OVS_ACTION_ATTR_USERSPACE &&
- last_action(a, rem)))
+ nla_is_last(a, rem)))
return output_userspace(dp, skb, key, a);
skb = skb_clone(skb, GFP_ATOMIC);
@@ -576,18 +652,20 @@ static void execute_hash(struct sk_buff *skb, struct sw_flow_key *key,
key->ovs_flow_hash = hash;
}
-static int execute_set_action(struct sk_buff *skb,
- const struct nlattr *nested_attr)
+static int execute_set_action(struct sk_buff *skb, struct sw_flow_key *key,
+ const struct nlattr *nested_attr)
{
int err = 0;
switch (nla_type(nested_attr)) {
case OVS_KEY_ATTR_PRIORITY:
skb->priority = nla_get_u32(nested_attr);
+ key->phy.priority = skb->priority;
break;
case OVS_KEY_ATTR_SKB_MARK:
skb->mark = nla_get_u32(nested_attr);
+ key->phy.skb_mark = skb->mark;
break;
case OVS_KEY_ATTR_TUNNEL_INFO:
@@ -595,27 +673,31 @@ static int execute_set_action(struct sk_buff *skb,
break;
case OVS_KEY_ATTR_ETHERNET:
- err = set_eth_addr(skb, nla_data(nested_attr));
+ err = set_eth_addr(skb, key, nla_data(nested_attr));
break;
case OVS_KEY_ATTR_IPV4:
- err = set_ipv4(skb, nla_data(nested_attr));
+ err = set_ipv4(skb, key, nla_data(nested_attr));
break;
case OVS_KEY_ATTR_IPV6:
- err = set_ipv6(skb, nla_data(nested_attr));
+ err = set_ipv6(skb, key, nla_data(nested_attr));
break;
case OVS_KEY_ATTR_TCP:
- err = set_tcp(skb, nla_data(nested_attr));
+ err = set_tcp(skb, key, nla_data(nested_attr));
break;
case OVS_KEY_ATTR_UDP:
- err = set_udp(skb, nla_data(nested_attr));
+ err = set_udp(skb, key, nla_data(nested_attr));
break;
case OVS_KEY_ATTR_SCTP:
- err = set_sctp(skb, nla_data(nested_attr));
+ err = set_sctp(skb, key, nla_data(nested_attr));
+ break;
+
+ case OVS_KEY_ATTR_MPLS:
+ err = set_mpls(skb, key, nla_data(nested_attr));
break;
}
@@ -627,13 +709,17 @@ static int execute_recirc(struct datapath *dp, struct sk_buff *skb,
const struct nlattr *a, int rem)
{
struct deferred_action *da;
- int err;
- err = ovs_flow_key_update(skb, key);
- if (err)
- return err;
+ if (!is_flow_key_valid(key)) {
+ int err;
+
+ err = ovs_flow_key_update(skb, key);
+ if (err)
+ return err;
+ }
+ BUG_ON(!is_flow_key_valid(key));
- if (!last_action(a, rem)) {
+ if (!nla_is_last(a, rem)) {
/* Recirc action is the not the last action
* of the action list, need to clone the skb.
*/
@@ -668,7 +754,8 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
/* Every output action needs a separate clone of 'skb', but the common
* case is just a single output action, so that doing a clone and
* then freeing the original skbuff is wasteful. So the following code
- * is slightly obscure just to avoid that. */
+ * is slightly obscure just to avoid that.
+ */
int prev_port = -1;
const struct nlattr *a;
int rem;
@@ -677,8 +764,12 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
a = nla_next(a, &rem)) {
int err = 0;
- if (prev_port != -1) {
- do_output(dp, skb_clone(skb, GFP_ATOMIC), prev_port);
+ if (unlikely(prev_port != -1)) {
+ struct sk_buff *out_skb = skb_clone(skb, GFP_ATOMIC);
+
+ if (out_skb)
+ do_output(dp, out_skb, prev_port);
+
prev_port = -1;
}
@@ -695,19 +786,25 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
execute_hash(skb, key, a);
break;
+ case OVS_ACTION_ATTR_PUSH_MPLS:
+ err = push_mpls(skb, key, nla_data(a));
+ break;
+
+ case OVS_ACTION_ATTR_POP_MPLS:
+ err = pop_mpls(skb, key, nla_get_be16(a));
+ break;
+
case OVS_ACTION_ATTR_PUSH_VLAN:
- err = push_vlan(skb, nla_data(a));
- if (unlikely(err)) /* skb already freed. */
- return err;
+ err = push_vlan(skb, key, nla_data(a));
break;
case OVS_ACTION_ATTR_POP_VLAN:
- err = pop_vlan(skb);
+ err = pop_vlan(skb, key);
break;
case OVS_ACTION_ATTR_RECIRC:
err = execute_recirc(dp, skb, key, a, rem);
- if (last_action(a, rem)) {
+ if (nla_is_last(a, rem)) {
/* If this is the last action, the skb has
* been consumed or freed.
* Return immediately.
@@ -717,13 +814,11 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
break;
case OVS_ACTION_ATTR_SET:
- err = execute_set_action(skb, nla_data(a));
+ err = execute_set_action(skb, key, nla_data(a));
break;
case OVS_ACTION_ATTR_SAMPLE:
err = sample(dp, skb, key, a);
- if (unlikely(err)) /* skb already freed. */
- return err;
break;
}
@@ -769,14 +864,12 @@ static void process_deferred_actions(struct datapath *dp)
/* Execute a list of actions against 'skb'. */
int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb,
+ const struct sw_flow_actions *acts,
struct sw_flow_key *key)
{
int level = this_cpu_read(exec_actions_level);
- struct sw_flow_actions *acts;
int err;
- acts = rcu_dereference(OVS_CB(skb)->flow->sf_acts);
-
this_cpu_inc(exec_actions_level);
OVS_CB(skb)->egress_tun_info = NULL;
err = do_execute_actions(dp, skb, key,
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
index e6d7255183eb..332b5a031739 100644
--- a/net/openvswitch/datapath.c
+++ b/net/openvswitch/datapath.c
@@ -59,6 +59,7 @@
#include "vport-netdev.h"
int ovs_net_id __read_mostly;
+EXPORT_SYMBOL_GPL(ovs_net_id);
static struct genl_family dp_packet_genl_family;
static struct genl_family dp_flow_genl_family;
@@ -130,27 +131,41 @@ int lockdep_ovsl_is_held(void)
else
return 1;
}
+EXPORT_SYMBOL_GPL(lockdep_ovsl_is_held);
#endif
static struct vport *new_vport(const struct vport_parms *);
static int queue_gso_packets(struct datapath *dp, struct sk_buff *,
+ const struct sw_flow_key *,
const struct dp_upcall_info *);
static int queue_userspace_packet(struct datapath *dp, struct sk_buff *,
+ const struct sw_flow_key *,
const struct dp_upcall_info *);
-/* Must be called with rcu_read_lock or ovs_mutex. */
-static struct datapath *get_dp(struct net *net, int dp_ifindex)
+/* Must be called with rcu_read_lock. */
+static struct datapath *get_dp_rcu(struct net *net, int dp_ifindex)
{
- struct datapath *dp = NULL;
- struct net_device *dev;
+ struct net_device *dev = dev_get_by_index_rcu(net, dp_ifindex);
- rcu_read_lock();
- dev = dev_get_by_index_rcu(net, dp_ifindex);
if (dev) {
struct vport *vport = ovs_internal_dev_get_vport(dev);
if (vport)
- dp = vport->dp;
+ return vport->dp;
}
+
+ return NULL;
+}
+
+/* The caller must hold either ovs_mutex or rcu_read_lock to keep the
+ * returned dp pointer valid.
+ */
+static inline struct datapath *get_dp(struct net *net, int dp_ifindex)
+{
+ struct datapath *dp;
+
+ WARN_ON_ONCE(!rcu_read_lock_held() && !lockdep_ovsl_is_held());
+ rcu_read_lock();
+ dp = get_dp_rcu(net, dp_ifindex);
rcu_read_unlock();
return dp;
@@ -163,7 +178,7 @@ const char *ovs_dp_name(const struct datapath *dp)
return vport->ops->get_name(vport);
}
-static int get_dpifindex(struct datapath *dp)
+static int get_dpifindex(const struct datapath *dp)
{
struct vport *local;
int ifindex;
@@ -185,6 +200,7 @@ static void destroy_dp_rcu(struct rcu_head *rcu)
{
struct datapath *dp = container_of(rcu, struct datapath, rcu);
+ ovs_flow_tbl_destroy(&dp->table);
free_percpu(dp->stats_percpu);
release_net(ovs_dp_get_net(dp));
kfree(dp->ports);
@@ -243,6 +259,7 @@ void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key)
const struct vport *p = OVS_CB(skb)->input_vport;
struct datapath *dp = p->dp;
struct sw_flow *flow;
+ struct sw_flow_actions *sf_acts;
struct dp_stats_percpu *stats;
u64 *stats_counter;
u32 n_mask_hit;
@@ -256,10 +273,10 @@ void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key)
int error;
upcall.cmd = OVS_PACKET_CMD_MISS;
- upcall.key = key;
upcall.userdata = NULL;
upcall.portid = ovs_vport_find_upcall_portid(p, skb);
- error = ovs_dp_upcall(dp, skb, &upcall);
+ upcall.egress_tun_info = NULL;
+ error = ovs_dp_upcall(dp, skb, key, &upcall);
if (unlikely(error))
kfree_skb(skb);
else
@@ -268,10 +285,10 @@ void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key)
goto out;
}
- OVS_CB(skb)->flow = flow;
+ ovs_flow_stats_update(flow, key->tp.flags, skb);
+ sf_acts = rcu_dereference(flow->sf_acts);
+ ovs_execute_actions(dp, skb, sf_acts, key);
- ovs_flow_stats_update(OVS_CB(skb)->flow, key->tp.flags, skb);
- ovs_execute_actions(dp, skb, key);
stats_counter = &stats->n_hit;
out:
@@ -283,6 +300,7 @@ out:
}
int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
+ const struct sw_flow_key *key,
const struct dp_upcall_info *upcall_info)
{
struct dp_stats_percpu *stats;
@@ -294,9 +312,9 @@ int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
}
if (!skb_is_gso(skb))
- err = queue_userspace_packet(dp, skb, upcall_info);
+ err = queue_userspace_packet(dp, skb, key, upcall_info);
else
- err = queue_gso_packets(dp, skb, upcall_info);
+ err = queue_gso_packets(dp, skb, key, upcall_info);
if (err)
goto err;
@@ -313,39 +331,43 @@ err:
}
static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb,
+ const struct sw_flow_key *key,
const struct dp_upcall_info *upcall_info)
{
unsigned short gso_type = skb_shinfo(skb)->gso_type;
- struct dp_upcall_info later_info;
struct sw_flow_key later_key;
struct sk_buff *segs, *nskb;
+ struct ovs_skb_cb ovs_cb;
int err;
+ ovs_cb = *OVS_CB(skb);
segs = __skb_gso_segment(skb, NETIF_F_SG, false);
+ *OVS_CB(skb) = ovs_cb;
if (IS_ERR(segs))
return PTR_ERR(segs);
if (segs == NULL)
return -EINVAL;
+ if (gso_type & SKB_GSO_UDP) {
+ /* The initial flow key extracted by ovs_flow_key_extract()
+ * in this case is for a first fragment, so we need to
+ * properly mark later fragments.
+ */
+ later_key = *key;
+ later_key.ip.frag = OVS_FRAG_TYPE_LATER;
+ }
+
/* Queue all of the segments. */
skb = segs;
do {
- err = queue_userspace_packet(dp, skb, upcall_info);
+ *OVS_CB(skb) = ovs_cb;
+ if (gso_type & SKB_GSO_UDP && skb != segs)
+ key = &later_key;
+
+ err = queue_userspace_packet(dp, skb, key, upcall_info);
if (err)
break;
- if (skb == segs && gso_type & SKB_GSO_UDP) {
- /* The initial flow key extracted by ovs_flow_extract()
- * in this case is for a first fragment, so we need to
- * properly mark later fragments.
- */
- later_key = *upcall_info->key;
- later_key.ip.frag = OVS_FRAG_TYPE_LATER;
-
- later_info = *upcall_info;
- later_info.key = &later_key;
- upcall_info = &later_info;
- }
} while ((skb = skb->next));
/* Free all of the segments. */
@@ -360,46 +382,26 @@ static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb,
return err;
}
-static size_t key_attr_size(void)
-{
- return nla_total_size(4) /* OVS_KEY_ATTR_PRIORITY */
- + nla_total_size(0) /* OVS_KEY_ATTR_TUNNEL */
- + nla_total_size(8) /* OVS_TUNNEL_KEY_ATTR_ID */
- + nla_total_size(4) /* OVS_TUNNEL_KEY_ATTR_IPV4_SRC */
- + nla_total_size(4) /* OVS_TUNNEL_KEY_ATTR_IPV4_DST */
- + nla_total_size(1) /* OVS_TUNNEL_KEY_ATTR_TOS */
- + nla_total_size(1) /* OVS_TUNNEL_KEY_ATTR_TTL */
- + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT */
- + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_CSUM */
- + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_OAM */
- + nla_total_size(256) /* OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS */
- + nla_total_size(4) /* OVS_KEY_ATTR_IN_PORT */
- + nla_total_size(4) /* OVS_KEY_ATTR_SKB_MARK */
- + nla_total_size(12) /* OVS_KEY_ATTR_ETHERNET */
- + nla_total_size(2) /* OVS_KEY_ATTR_ETHERTYPE */
- + nla_total_size(4) /* OVS_KEY_ATTR_8021Q */
- + nla_total_size(0) /* OVS_KEY_ATTR_ENCAP */
- + nla_total_size(2) /* OVS_KEY_ATTR_ETHERTYPE */
- + nla_total_size(40) /* OVS_KEY_ATTR_IPV6 */
- + nla_total_size(2) /* OVS_KEY_ATTR_ICMPV6 */
- + nla_total_size(28); /* OVS_KEY_ATTR_ND */
-}
-
-static size_t upcall_msg_size(const struct nlattr *userdata,
+static size_t upcall_msg_size(const struct dp_upcall_info *upcall_info,
unsigned int hdrlen)
{
size_t size = NLMSG_ALIGN(sizeof(struct ovs_header))
+ nla_total_size(hdrlen) /* OVS_PACKET_ATTR_PACKET */
- + nla_total_size(key_attr_size()); /* OVS_PACKET_ATTR_KEY */
+ + nla_total_size(ovs_key_attr_size()); /* OVS_PACKET_ATTR_KEY */
/* OVS_PACKET_ATTR_USERDATA */
- if (userdata)
- size += NLA_ALIGN(userdata->nla_len);
+ if (upcall_info->userdata)
+ size += NLA_ALIGN(upcall_info->userdata->nla_len);
+
+ /* OVS_PACKET_ATTR_EGRESS_TUN_KEY */
+ if (upcall_info->egress_tun_info)
+ size += nla_total_size(ovs_tun_key_attr_size());
return size;
}
static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
+ const struct sw_flow_key *key,
const struct dp_upcall_info *upcall_info)
{
struct ovs_header *upcall;
@@ -423,11 +425,10 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
if (!nskb)
return -ENOMEM;
- nskb = __vlan_put_tag(nskb, nskb->vlan_proto, vlan_tx_tag_get(nskb));
+ nskb = __vlan_hwaccel_push_inside(nskb);
if (!nskb)
return -ENOMEM;
- nskb->vlan_tci = 0;
skb = nskb;
}
@@ -450,7 +451,7 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
else
hlen = skb->len;
- len = upcall_msg_size(upcall_info->userdata, hlen);
+ len = upcall_msg_size(upcall_info, hlen);
user_skb = genlmsg_new_unicast(len, &info, GFP_ATOMIC);
if (!user_skb) {
err = -ENOMEM;
@@ -462,7 +463,7 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
upcall->dp_ifindex = dp_ifindex;
nla = nla_nest_start(user_skb, OVS_PACKET_ATTR_KEY);
- err = ovs_nla_put_flow(upcall_info->key, upcall_info->key, user_skb);
+ err = ovs_nla_put_flow(key, key, user_skb);
BUG_ON(err);
nla_nest_end(user_skb, nla);
@@ -471,6 +472,14 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
nla_len(upcall_info->userdata),
nla_data(upcall_info->userdata));
+ if (upcall_info->egress_tun_info) {
+ nla = nla_nest_start(user_skb, OVS_PACKET_ATTR_EGRESS_TUN_KEY);
+ err = ovs_nla_put_egress_tunnel_key(user_skb,
+ upcall_info->egress_tun_info);
+ BUG_ON(err);
+ nla_nest_end(user_skb, nla);
+ }
+
/* Only reserve room for attribute header, packet data is added
* in skb_zerocopy() */
if (!(nla = nla_reserve(user_skb, OVS_PACKET_ATTR_PACKET, 0))) {
@@ -510,11 +519,13 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
struct sw_flow_actions *acts;
struct sk_buff *packet;
struct sw_flow *flow;
+ struct sw_flow_actions *sf_acts;
struct datapath *dp;
struct ethhdr *eth;
struct vport *input_vport;
int len;
int err;
+ bool log = !a[OVS_FLOW_ATTR_PROBE];
err = -EINVAL;
if (!a[OVS_PACKET_ATTR_PACKET] || !a[OVS_PACKET_ATTR_KEY] ||
@@ -548,29 +559,22 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
goto err_kfree_skb;
err = ovs_flow_key_extract_userspace(a[OVS_PACKET_ATTR_KEY], packet,
- &flow->key);
+ &flow->key, log);
if (err)
goto err_flow_free;
- acts = ovs_nla_alloc_flow_actions(nla_len(a[OVS_PACKET_ATTR_ACTIONS]));
- err = PTR_ERR(acts);
- if (IS_ERR(acts))
- goto err_flow_free;
-
err = ovs_nla_copy_actions(a[OVS_PACKET_ATTR_ACTIONS],
- &flow->key, 0, &acts);
+ &flow->key, &acts, log);
if (err)
goto err_flow_free;
rcu_assign_pointer(flow->sf_acts, acts);
-
OVS_CB(packet)->egress_tun_info = NULL;
- OVS_CB(packet)->flow = flow;
packet->priority = flow->key.phy.priority;
packet->mark = flow->key.phy.skb_mark;
rcu_read_lock();
- dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
+ dp = get_dp_rcu(sock_net(skb->sk), ovs_header->dp_ifindex);
err = -ENODEV;
if (!dp)
goto err_unlock;
@@ -583,9 +587,10 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
goto err_unlock;
OVS_CB(packet)->input_vport = input_vport;
+ sf_acts = rcu_dereference(flow->sf_acts);
local_bh_disable();
- err = ovs_execute_actions(dp, packet, &flow->key);
+ err = ovs_execute_actions(dp, packet, sf_acts, &flow->key);
local_bh_enable();
rcu_read_unlock();
@@ -628,7 +633,7 @@ static struct genl_family dp_packet_genl_family = {
.n_ops = ARRAY_SIZE(dp_packet_genl_ops),
};
-static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats,
+static void get_dp_stats(const struct datapath *dp, struct ovs_dp_stats *stats,
struct ovs_dp_megaflow_stats *mega_stats)
{
int i;
@@ -662,8 +667,8 @@ static void get_dp_stats(struct datapath *dp, struct ovs_dp_stats *stats,
static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts)
{
return NLMSG_ALIGN(sizeof(struct ovs_header))
- + nla_total_size(key_attr_size()) /* OVS_FLOW_ATTR_KEY */
- + nla_total_size(key_attr_size()) /* OVS_FLOW_ATTR_MASK */
+ + nla_total_size(ovs_key_attr_size()) /* OVS_FLOW_ATTR_KEY */
+ + nla_total_size(ovs_key_attr_size()) /* OVS_FLOW_ATTR_MASK */
+ nla_total_size(sizeof(struct ovs_flow_stats)) /* OVS_FLOW_ATTR_STATS */
+ nla_total_size(1) /* OVS_FLOW_ATTR_TCP_FLAGS */
+ nla_total_size(8) /* OVS_FLOW_ATTR_USED */
@@ -671,58 +676,67 @@ static size_t ovs_flow_cmd_msg_size(const struct sw_flow_actions *acts)
}
/* Called with ovs_mutex or RCU read lock. */
-static int ovs_flow_cmd_fill_info(const struct sw_flow *flow, int dp_ifindex,
- struct sk_buff *skb, u32 portid,
- u32 seq, u32 flags, u8 cmd)
+static int ovs_flow_cmd_fill_match(const struct sw_flow *flow,
+ struct sk_buff *skb)
{
- const int skb_orig_len = skb->len;
- struct nlattr *start;
- struct ovs_flow_stats stats;
- __be16 tcp_flags;
- unsigned long used;
- struct ovs_header *ovs_header;
struct nlattr *nla;
int err;
- ovs_header = genlmsg_put(skb, portid, seq, &dp_flow_genl_family, flags, cmd);
- if (!ovs_header)
- return -EMSGSIZE;
-
- ovs_header->dp_ifindex = dp_ifindex;
-
/* Fill flow key. */
nla = nla_nest_start(skb, OVS_FLOW_ATTR_KEY);
if (!nla)
- goto nla_put_failure;
+ return -EMSGSIZE;
err = ovs_nla_put_flow(&flow->unmasked_key, &flow->unmasked_key, skb);
if (err)
- goto error;
+ return err;
+
nla_nest_end(skb, nla);
+ /* Fill flow mask. */
nla = nla_nest_start(skb, OVS_FLOW_ATTR_MASK);
if (!nla)
- goto nla_put_failure;
+ return -EMSGSIZE;
err = ovs_nla_put_flow(&flow->key, &flow->mask->key, skb);
if (err)
- goto error;
+ return err;
nla_nest_end(skb, nla);
+ return 0;
+}
+
+/* Called with ovs_mutex or RCU read lock. */
+static int ovs_flow_cmd_fill_stats(const struct sw_flow *flow,
+ struct sk_buff *skb)
+{
+ struct ovs_flow_stats stats;
+ __be16 tcp_flags;
+ unsigned long used;
ovs_flow_stats_get(flow, &stats, &used, &tcp_flags);
if (used &&
nla_put_u64(skb, OVS_FLOW_ATTR_USED, ovs_flow_used_time(used)))
- goto nla_put_failure;
+ return -EMSGSIZE;
if (stats.n_packets &&
nla_put(skb, OVS_FLOW_ATTR_STATS, sizeof(struct ovs_flow_stats), &stats))
- goto nla_put_failure;
+ return -EMSGSIZE;
if ((u8)ntohs(tcp_flags) &&
nla_put_u8(skb, OVS_FLOW_ATTR_TCP_FLAGS, (u8)ntohs(tcp_flags)))
- goto nla_put_failure;
+ return -EMSGSIZE;
+
+ return 0;
+}
+
+/* Called with ovs_mutex or RCU read lock. */
+static int ovs_flow_cmd_fill_actions(const struct sw_flow *flow,
+ struct sk_buff *skb, int skb_orig_len)
+{
+ struct nlattr *start;
+ int err;
/* If OVS_FLOW_ATTR_ACTIONS doesn't fit, skip dumping the actions if
* this is the first flow to be dumped into 'skb'. This is unusual for
@@ -746,17 +760,47 @@ static int ovs_flow_cmd_fill_info(const struct sw_flow *flow, int dp_ifindex,
nla_nest_end(skb, start);
else {
if (skb_orig_len)
- goto error;
+ return err;
nla_nest_cancel(skb, start);
}
- } else if (skb_orig_len)
- goto nla_put_failure;
+ } else if (skb_orig_len) {
+ return -EMSGSIZE;
+ }
+
+ return 0;
+}
+
+/* Called with ovs_mutex or RCU read lock. */
+static int ovs_flow_cmd_fill_info(const struct sw_flow *flow, int dp_ifindex,
+ struct sk_buff *skb, u32 portid,
+ u32 seq, u32 flags, u8 cmd)
+{
+ const int skb_orig_len = skb->len;
+ struct ovs_header *ovs_header;
+ int err;
+
+ ovs_header = genlmsg_put(skb, portid, seq, &dp_flow_genl_family,
+ flags, cmd);
+ if (!ovs_header)
+ return -EMSGSIZE;
+
+ ovs_header->dp_ifindex = dp_ifindex;
+
+ err = ovs_flow_cmd_fill_match(flow, skb);
+ if (err)
+ goto error;
+
+ err = ovs_flow_cmd_fill_stats(flow, skb);
+ if (err)
+ goto error;
+
+ err = ovs_flow_cmd_fill_actions(flow, skb, skb_orig_len);
+ if (err)
+ goto error;
return genlmsg_end(skb, ovs_header);
-nla_put_failure:
- err = -EMSGSIZE;
error:
genlmsg_cancel(skb, ovs_header);
return err;
@@ -811,13 +855,18 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
struct sw_flow_actions *acts;
struct sw_flow_match match;
int error;
+ bool log = !a[OVS_FLOW_ATTR_PROBE];
/* Must have key and actions. */
error = -EINVAL;
- if (!a[OVS_FLOW_ATTR_KEY])
+ if (!a[OVS_FLOW_ATTR_KEY]) {
+ OVS_NLERR(log, "Flow key attr not present in new flow.");
goto error;
- if (!a[OVS_FLOW_ATTR_ACTIONS])
+ }
+ if (!a[OVS_FLOW_ATTR_ACTIONS]) {
+ OVS_NLERR(log, "Flow actions attr not present in new flow.");
goto error;
+ }
/* Most of the time we need to allocate a new flow, do it before
* locking.
@@ -830,24 +879,19 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
/* Extract key. */
ovs_match_init(&match, &new_flow->unmasked_key, &mask);
- error = ovs_nla_get_match(&match,
- a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK]);
+ error = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY],
+ a[OVS_FLOW_ATTR_MASK], log);
if (error)
goto err_kfree_flow;
ovs_flow_mask_key(&new_flow->key, &new_flow->unmasked_key, &mask);
/* Validate actions. */
- acts = ovs_nla_alloc_flow_actions(nla_len(a[OVS_FLOW_ATTR_ACTIONS]));
- error = PTR_ERR(acts);
- if (IS_ERR(acts))
- goto err_kfree_flow;
-
error = ovs_nla_copy_actions(a[OVS_FLOW_ATTR_ACTIONS], &new_flow->key,
- 0, &acts);
+ &acts, log);
if (error) {
- OVS_NLERR("Flow actions may not be safe on all matching packets.\n");
- goto err_kfree_acts;
+ OVS_NLERR(log, "Flow actions may not be safe on all matching packets.");
+ goto err_kfree_flow;
}
reply = ovs_flow_cmd_alloc_info(acts, info, false);
@@ -899,6 +943,7 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
}
/* The unmasked key has to be the same for flow updates. */
if (unlikely(!ovs_flow_cmp_unmasked_key(flow, &match))) {
+ /* Look for any overlapping flow. */
flow = ovs_flow_tbl_lookup_exact(&dp->table, &match);
if (!flow) {
error = -ENOENT;
@@ -938,23 +983,21 @@ error:
return error;
}
+/* Factor out action copy to avoid "Wframe-larger-than=1024" warning. */
static struct sw_flow_actions *get_flow_actions(const struct nlattr *a,
const struct sw_flow_key *key,
- const struct sw_flow_mask *mask)
+ const struct sw_flow_mask *mask,
+ bool log)
{
struct sw_flow_actions *acts;
struct sw_flow_key masked_key;
int error;
- acts = ovs_nla_alloc_flow_actions(nla_len(a));
- if (IS_ERR(acts))
- return acts;
-
ovs_flow_mask_key(&masked_key, key, mask);
- error = ovs_nla_copy_actions(a, &masked_key, 0, &acts);
+ error = ovs_nla_copy_actions(a, &masked_key, &acts, log);
if (error) {
- OVS_NLERR("Flow actions may not be safe on all matching packets.\n");
- kfree(acts);
+ OVS_NLERR(log,
+ "Actions may not be safe on all matching packets");
return ERR_PTR(error);
}
@@ -973,29 +1016,31 @@ static int ovs_flow_cmd_set(struct sk_buff *skb, struct genl_info *info)
struct sw_flow_actions *old_acts = NULL, *acts = NULL;
struct sw_flow_match match;
int error;
+ bool log = !a[OVS_FLOW_ATTR_PROBE];
/* Extract key. */
error = -EINVAL;
- if (!a[OVS_FLOW_ATTR_KEY])
+ if (!a[OVS_FLOW_ATTR_KEY]) {
+ OVS_NLERR(log, "Flow key attribute not present in set flow.");
goto error;
+ }
ovs_match_init(&match, &key, &mask);
- error = ovs_nla_get_match(&match,
- a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK]);
+ error = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY],
+ a[OVS_FLOW_ATTR_MASK], log);
if (error)
goto error;
/* Validate actions. */
if (a[OVS_FLOW_ATTR_ACTIONS]) {
- acts = get_flow_actions(a[OVS_FLOW_ATTR_ACTIONS], &key, &mask);
+ acts = get_flow_actions(a[OVS_FLOW_ATTR_ACTIONS], &key, &mask,
+ log);
if (IS_ERR(acts)) {
error = PTR_ERR(acts);
goto error;
}
- }
- /* Can allocate before locking if have acts. */
- if (acts) {
+ /* Can allocate before locking if have acts. */
reply = ovs_flow_cmd_alloc_info(acts, info, false);
if (IS_ERR(reply)) {
error = PTR_ERR(reply);
@@ -1070,14 +1115,16 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
struct datapath *dp;
struct sw_flow_match match;
int err;
+ bool log = !a[OVS_FLOW_ATTR_PROBE];
if (!a[OVS_FLOW_ATTR_KEY]) {
- OVS_NLERR("Flow get message rejected, Key attribute missing.\n");
+ OVS_NLERR(log,
+ "Flow get message rejected, Key attribute missing.");
return -EINVAL;
}
ovs_match_init(&match, &key, NULL);
- err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL);
+ err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL, log);
if (err)
return err;
@@ -1118,10 +1165,12 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
struct datapath *dp;
struct sw_flow_match match;
int err;
+ bool log = !a[OVS_FLOW_ATTR_PROBE];
if (likely(a[OVS_FLOW_ATTR_KEY])) {
ovs_match_init(&match, &key, NULL);
- err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL);
+ err = ovs_nla_get_match(&match, a[OVS_FLOW_ATTR_KEY], NULL,
+ log);
if (unlikely(err))
return err;
}
@@ -1179,7 +1228,7 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
struct datapath *dp;
rcu_read_lock();
- dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
+ dp = get_dp_rcu(sock_net(skb->sk), ovs_header->dp_ifindex);
if (!dp) {
rcu_read_unlock();
return -ENODEV;
@@ -1211,8 +1260,10 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
static const struct nla_policy flow_policy[OVS_FLOW_ATTR_MAX + 1] = {
[OVS_FLOW_ATTR_KEY] = { .type = NLA_NESTED },
+ [OVS_FLOW_ATTR_MASK] = { .type = NLA_NESTED },
[OVS_FLOW_ATTR_ACTIONS] = { .type = NLA_NESTED },
[OVS_FLOW_ATTR_CLEAR] = { .type = NLA_FLAG },
+ [OVS_FLOW_ATTR_PROBE] = { .type = NLA_FLAG },
};
static const struct genl_ops dp_flow_genl_ops[] = {
@@ -1265,7 +1316,7 @@ static size_t ovs_dp_cmd_msg_size(void)
return msgsize;
}
-/* Called with ovs_mutex or RCU read lock. */
+/* Called with ovs_mutex. */
static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb,
u32 portid, u32 seq, u32 flags, u8 cmd)
{
@@ -1313,7 +1364,7 @@ static struct sk_buff *ovs_dp_cmd_alloc_info(struct genl_info *info)
/* Called with rcu_read_lock or ovs_mutex. */
static struct datapath *lookup_datapath(struct net *net,
- struct ovs_header *ovs_header,
+ const struct ovs_header *ovs_header,
struct nlattr *a[OVS_DP_ATTR_MAX + 1])
{
struct datapath *dp;
@@ -1341,7 +1392,7 @@ static void ovs_dp_reset_user_features(struct sk_buff *skb, struct genl_info *in
dp->user_features = 0;
}
-static void ovs_dp_change(struct datapath *dp, struct nlattr **a)
+static void ovs_dp_change(struct datapath *dp, struct nlattr *a[])
{
if (a[OVS_DP_ATTR_USER_FEATURES])
dp->user_features = nla_get_u32(a[OVS_DP_ATTR_USER_FEATURES]);
@@ -1442,7 +1493,7 @@ err_destroy_ports_array:
err_destroy_percpu:
free_percpu(dp->stats_percpu);
err_destroy_table:
- ovs_flow_tbl_destroy(&dp->table, false);
+ ovs_flow_tbl_destroy(&dp->table);
err_free_dp:
release_net(ovs_dp_get_net(dp));
kfree(dp);
@@ -1474,8 +1525,6 @@ static void __dp_destroy(struct datapath *dp)
ovs_dp_detach_port(ovs_vport_ovsl(dp, OVSP_LOCAL));
/* RCU destroy the flow table */
- ovs_flow_tbl_destroy(&dp->table, true);
-
call_rcu(&dp->rcu, destroy_dp_rcu);
}
@@ -1555,7 +1604,7 @@ static int ovs_dp_cmd_get(struct sk_buff *skb, struct genl_info *info)
if (!reply)
return -ENOMEM;
- rcu_read_lock();
+ ovs_lock();
dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs);
if (IS_ERR(dp)) {
err = PTR_ERR(dp);
@@ -1564,12 +1613,12 @@ static int ovs_dp_cmd_get(struct sk_buff *skb, struct genl_info *info)
err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid,
info->snd_seq, 0, OVS_DP_CMD_NEW);
BUG_ON(err < 0);
- rcu_read_unlock();
+ ovs_unlock();
return genlmsg_reply(reply, info);
err_unlock_free:
- rcu_read_unlock();
+ ovs_unlock();
kfree_skb(reply);
return err;
}
@@ -1581,8 +1630,8 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
int skip = cb->args[0];
int i = 0;
- rcu_read_lock();
- list_for_each_entry_rcu(dp, &ovs_net->dps, list_node) {
+ ovs_lock();
+ list_for_each_entry(dp, &ovs_net->dps, list_node) {
if (i >= skip &&
ovs_dp_cmd_fill_info(dp, skb, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
@@ -1590,7 +1639,7 @@ static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
break;
i++;
}
- rcu_read_unlock();
+ ovs_unlock();
cb->args[0] = i;
@@ -1707,7 +1756,7 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, u32 portid,
/* Called with ovs_mutex or RCU read lock. */
static struct vport *lookup_vport(struct net *net,
- struct ovs_header *ovs_header,
+ const struct ovs_header *ovs_header,
struct nlattr *a[OVS_VPORT_ATTR_MAX + 1])
{
struct datapath *dp;
@@ -1764,6 +1813,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
return -ENOMEM;
ovs_lock();
+restart:
dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
err = -ENODEV;
if (!dp)
@@ -1795,8 +1845,11 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
vport = new_vport(&parms);
err = PTR_ERR(vport);
- if (IS_ERR(vport))
+ if (IS_ERR(vport)) {
+ if (err == -EAGAIN)
+ goto restart;
goto exit_unlock_free;
+ }
err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid,
info->snd_seq, 0, OVS_VPORT_CMD_NEW);
@@ -1939,7 +1992,7 @@ static int ovs_vport_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
int i, j = 0;
rcu_read_lock();
- dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
+ dp = get_dp_rcu(sock_net(skb->sk), ovs_header->dp_ifindex);
if (!dp) {
rcu_read_unlock();
return -ENODEV;
@@ -2112,12 +2165,18 @@ static int __init dp_init(void)
if (err)
goto error_netns_exit;
+ err = ovs_netdev_init();
+ if (err)
+ goto error_unreg_notifier;
+
err = dp_register_genl();
if (err < 0)
- goto error_unreg_notifier;
+ goto error_unreg_netdev;
return 0;
+error_unreg_netdev:
+ ovs_netdev_exit();
error_unreg_notifier:
unregister_netdevice_notifier(&ovs_dp_device_notifier);
error_netns_exit:
@@ -2137,6 +2196,7 @@ error:
static void dp_cleanup(void)
{
dp_unregister_genl(ARRAY_SIZE(dp_genl_families));
+ ovs_netdev_exit();
unregister_netdevice_notifier(&ovs_dp_device_notifier);
unregister_pernet_device(&ovs_net_ops);
rcu_barrier();
diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h
index 974135439c5c..3ece94563079 100644
--- a/net/openvswitch/datapath.h
+++ b/net/openvswitch/datapath.h
@@ -94,14 +94,12 @@ struct datapath {
/**
* struct ovs_skb_cb - OVS data in skb CB
- * @flow: The flow associated with this packet. May be %NULL if no flow.
* @egress_tun_key: Tunnel information about this packet on egress path.
* NULL if the packet is not being tunneled.
* @input_vport: The original vport packet came in on. This value is cached
* when a packet is received by OVS.
*/
struct ovs_skb_cb {
- struct sw_flow *flow;
struct ovs_tunnel_info *egress_tun_info;
struct vport *input_vport;
};
@@ -110,18 +108,18 @@ struct ovs_skb_cb {
/**
* struct dp_upcall - metadata to include with a packet to send to userspace
* @cmd: One of %OVS_PACKET_CMD_*.
- * @key: Becomes %OVS_PACKET_ATTR_KEY. Must be nonnull.
* @userdata: If nonnull, its variable-length value is passed to userspace as
* %OVS_PACKET_ATTR_USERDATA.
- * @pid: Netlink PID to which packet should be sent. If @pid is 0 then no
- * packet is sent and the packet is accounted in the datapath's @n_lost
+ * @portid: Netlink portid to which packet should be sent. If @portid is 0
+ * then no packet is sent and the packet is accounted in the datapath's @n_lost
* counter.
+ * @egress_tun_info: If nonnull, becomes %OVS_PACKET_ATTR_EGRESS_TUN_KEY.
*/
struct dp_upcall_info {
- u8 cmd;
- const struct sw_flow_key *key;
+ const struct ovs_tunnel_info *egress_tun_info;
const struct nlattr *userdata;
u32 portid;
+ u8 cmd;
};
/**
@@ -151,7 +149,7 @@ int lockdep_ovsl_is_held(void);
#define rcu_dereference_ovsl(p) \
rcu_dereference_check(p, lockdep_ovsl_is_held())
-static inline struct net *ovs_dp_get_net(struct datapath *dp)
+static inline struct net *ovs_dp_get_net(const struct datapath *dp)
{
return read_pnet(&dp->net);
}
@@ -187,23 +185,23 @@ extern struct genl_family dp_vport_genl_family;
void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key);
void ovs_dp_detach_port(struct vport *);
int ovs_dp_upcall(struct datapath *, struct sk_buff *,
- const struct dp_upcall_info *);
+ const struct sw_flow_key *, const struct dp_upcall_info *);
const char *ovs_dp_name(const struct datapath *dp);
struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq,
u8 cmd);
int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb,
- struct sw_flow_key *);
+ const struct sw_flow_actions *, struct sw_flow_key *);
void ovs_dp_notify_wq(struct work_struct *work);
int action_fifos_init(void);
void action_fifos_exit(void);
-#define OVS_NLERR(fmt, ...) \
+#define OVS_NLERR(logging_allowed, fmt, ...) \
do { \
- if (net_ratelimit()) \
- pr_info("netlink: " fmt, ##__VA_ARGS__); \
+ if (logging_allowed && net_ratelimit()) \
+ pr_info("netlink: " fmt "\n", ##__VA_ARGS__); \
} while (0)
#endif /* datapath.h */
diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c
index 2b78789ea7c5..70bef2ab7f2b 100644
--- a/net/openvswitch/flow.c
+++ b/net/openvswitch/flow.c
@@ -32,6 +32,7 @@
#include <linux/if_arp.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
+#include <linux/mpls.h>
#include <linux/sctp.h>
#include <linux/smp.h>
#include <linux/tcp.h>
@@ -42,6 +43,7 @@
#include <net/ip.h>
#include <net/ip_tunnels.h>
#include <net/ipv6.h>
+#include <net/mpls.h>
#include <net/ndisc.h>
#include "datapath.h"
@@ -64,7 +66,7 @@ u64 ovs_flow_used_time(unsigned long flow_jiffies)
#define TCP_FLAGS_BE16(tp) (*(__be16 *)&tcp_flag_word(tp) & htons(0x0FFF))
void ovs_flow_stats_update(struct sw_flow *flow, __be16 tcp_flags,
- struct sk_buff *skb)
+ const struct sk_buff *skb)
{
struct flow_stats *stats;
int node = numa_node_id();
@@ -480,6 +482,7 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
return -ENOMEM;
skb_reset_network_header(skb);
+ skb_reset_mac_len(skb);
__skb_push(skb, skb->data - skb_mac_header(skb));
/* Network layer. */
@@ -584,6 +587,33 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
memset(&key->ip, 0, sizeof(key->ip));
memset(&key->ipv4, 0, sizeof(key->ipv4));
}
+ } else if (eth_p_mpls(key->eth.type)) {
+ size_t stack_len = MPLS_HLEN;
+
+ /* In the presence of an MPLS label stack the end of the L2
+ * header and the beginning of the L3 header differ.
+ *
+ * Advance network_header to the beginning of the L3
+ * header. mac_len corresponds to the end of the L2 header.
+ */
+ while (1) {
+ __be32 lse;
+
+ error = check_header(skb, skb->mac_len + stack_len);
+ if (unlikely(error))
+ return 0;
+
+ memcpy(&lse, skb_network_header(skb), MPLS_HLEN);
+
+ if (stack_len == MPLS_HLEN)
+ memcpy(&key->mpls.top_lse, &lse, MPLS_HLEN);
+
+ skb_set_network_header(skb, skb->mac_len + stack_len);
+ if (lse & htonl(MPLS_LS_S_MASK))
+ break;
+
+ stack_len += MPLS_HLEN;
+ }
} else if (key->eth.type == htons(ETH_P_IPV6)) {
int nh_len; /* IPv6 Header + Extensions */
@@ -649,7 +679,7 @@ int ovs_flow_key_update(struct sk_buff *skb, struct sw_flow_key *key)
return key_extract(skb, key);
}
-int ovs_flow_key_extract(struct ovs_tunnel_info *tun_info,
+int ovs_flow_key_extract(const struct ovs_tunnel_info *tun_info,
struct sk_buff *skb, struct sw_flow_key *key)
{
/* Extract metadata from packet. */
@@ -682,12 +712,12 @@ int ovs_flow_key_extract(struct ovs_tunnel_info *tun_info,
int ovs_flow_key_extract_userspace(const struct nlattr *attr,
struct sk_buff *skb,
- struct sw_flow_key *key)
+ struct sw_flow_key *key, bool log)
{
int err;
/* Extract metadata from netlink attributes. */
- err = ovs_nla_get_flow_metadata(attr, key);
+ err = ovs_nla_get_flow_metadata(attr, key, log);
if (err)
return err;
diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h
index 71813318c8c7..a8b30f334388 100644
--- a/net/openvswitch/flow.h
+++ b/net/openvswitch/flow.h
@@ -37,8 +37,8 @@ struct sk_buff;
/* Used to memset ovs_key_ipv4_tunnel padding. */
#define OVS_TUNNEL_KEY_SIZE \
- (offsetof(struct ovs_key_ipv4_tunnel, ipv4_ttl) + \
- FIELD_SIZEOF(struct ovs_key_ipv4_tunnel, ipv4_ttl))
+ (offsetof(struct ovs_key_ipv4_tunnel, tp_dst) + \
+ FIELD_SIZEOF(struct ovs_key_ipv4_tunnel, tp_dst))
struct ovs_key_ipv4_tunnel {
__be64 tun_id;
@@ -47,11 +47,13 @@ struct ovs_key_ipv4_tunnel {
__be16 tun_flags;
u8 ipv4_tos;
u8 ipv4_ttl;
+ __be16 tp_src;
+ __be16 tp_dst;
} __packed __aligned(4); /* Minimize padding. */
struct ovs_tunnel_info {
struct ovs_key_ipv4_tunnel tunnel;
- struct geneve_opt *options;
+ const struct geneve_opt *options;
u8 options_len;
};
@@ -64,27 +66,59 @@ struct ovs_tunnel_info {
FIELD_SIZEOF(struct sw_flow_key, tun_opts) - \
opt_len))
-static inline void ovs_flow_tun_info_init(struct ovs_tunnel_info *tun_info,
- const struct iphdr *iph,
- __be64 tun_id, __be16 tun_flags,
- struct geneve_opt *opts,
- u8 opts_len)
+static inline void __ovs_flow_tun_info_init(struct ovs_tunnel_info *tun_info,
+ __be32 saddr, __be32 daddr,
+ u8 tos, u8 ttl,
+ __be16 tp_src,
+ __be16 tp_dst,
+ __be64 tun_id,
+ __be16 tun_flags,
+ const struct geneve_opt *opts,
+ u8 opts_len)
{
tun_info->tunnel.tun_id = tun_id;
- tun_info->tunnel.ipv4_src = iph->saddr;
- tun_info->tunnel.ipv4_dst = iph->daddr;
- tun_info->tunnel.ipv4_tos = iph->tos;
- tun_info->tunnel.ipv4_ttl = iph->ttl;
+ tun_info->tunnel.ipv4_src = saddr;
+ tun_info->tunnel.ipv4_dst = daddr;
+ tun_info->tunnel.ipv4_tos = tos;
+ tun_info->tunnel.ipv4_ttl = ttl;
tun_info->tunnel.tun_flags = tun_flags;
- /* clear struct padding. */
- memset((unsigned char *)&tun_info->tunnel + OVS_TUNNEL_KEY_SIZE, 0,
- sizeof(tun_info->tunnel) - OVS_TUNNEL_KEY_SIZE);
+ /* For the tunnel types on the top of IPsec, the tp_src and tp_dst of
+ * the upper tunnel are used.
+ * E.g: GRE over IPSEC, the tp_src and tp_port are zero.
+ */
+ tun_info->tunnel.tp_src = tp_src;
+ tun_info->tunnel.tp_dst = tp_dst;
+
+ /* Clear struct padding. */
+ if (sizeof(tun_info->tunnel) != OVS_TUNNEL_KEY_SIZE)
+ memset((unsigned char *)&tun_info->tunnel + OVS_TUNNEL_KEY_SIZE,
+ 0, sizeof(tun_info->tunnel) - OVS_TUNNEL_KEY_SIZE);
tun_info->options = opts;
tun_info->options_len = opts_len;
}
+static inline void ovs_flow_tun_info_init(struct ovs_tunnel_info *tun_info,
+ const struct iphdr *iph,
+ __be16 tp_src,
+ __be16 tp_dst,
+ __be64 tun_id,
+ __be16 tun_flags,
+ const struct geneve_opt *opts,
+ u8 opts_len)
+{
+ __ovs_flow_tun_info_init(tun_info, iph->saddr, iph->daddr,
+ iph->tos, iph->ttl,
+ tp_src, tp_dst,
+ tun_id, tun_flags,
+ opts, opts_len);
+}
+
+#define OVS_SW_FLOW_KEY_METADATA_SIZE \
+ (offsetof(struct sw_flow_key, recirc_id) + \
+ FIELD_SIZEOF(struct sw_flow_key, recirc_id))
+
struct sw_flow_key {
u8 tun_opts[255];
u8 tun_opts_len;
@@ -102,12 +136,17 @@ struct sw_flow_key {
__be16 tci; /* 0 if no VLAN, VLAN_TAG_PRESENT set otherwise. */
__be16 type; /* Ethernet frame type. */
} eth;
- struct {
- u8 proto; /* IP protocol or lower 8 bits of ARP opcode. */
- u8 tos; /* IP ToS. */
- u8 ttl; /* IP TTL/hop limit. */
- u8 frag; /* One of OVS_FRAG_TYPE_*. */
- } ip;
+ union {
+ struct {
+ __be32 top_lse; /* top label stack entry */
+ } mpls;
+ struct {
+ u8 proto; /* IP protocol or lower 8 bits of ARP opcode. */
+ u8 tos; /* IP ToS. */
+ u8 ttl; /* IP TTL/hop limit. */
+ u8 frag; /* One of OVS_FRAG_TYPE_*. */
+ } ip;
+ };
struct {
__be16 src; /* TCP/UDP/SCTP source port. */
__be16 dst; /* TCP/UDP/SCTP destination port. */
@@ -205,18 +244,19 @@ struct arp_eth_header {
} __packed;
void ovs_flow_stats_update(struct sw_flow *, __be16 tcp_flags,
- struct sk_buff *);
+ const struct sk_buff *);
void ovs_flow_stats_get(const struct sw_flow *, struct ovs_flow_stats *,
unsigned long *used, __be16 *tcp_flags);
void ovs_flow_stats_clear(struct sw_flow *);
u64 ovs_flow_used_time(unsigned long flow_jiffies);
int ovs_flow_key_update(struct sk_buff *skb, struct sw_flow_key *key);
-int ovs_flow_key_extract(struct ovs_tunnel_info *tun_info, struct sk_buff *skb,
+int ovs_flow_key_extract(const struct ovs_tunnel_info *tun_info,
+ struct sk_buff *skb,
struct sw_flow_key *key);
/* Extract key from packet coming from userspace. */
int ovs_flow_key_extract_userspace(const struct nlattr *attr,
struct sk_buff *skb,
- struct sw_flow_key *key);
+ struct sw_flow_key *key, bool log);
#endif /* flow.h */
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index 939bcb32100f..9645a21d9eaa 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -46,24 +46,22 @@
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/ndisc.h>
+#include <net/mpls.h>
#include "flow_netlink.h"
-static void update_range__(struct sw_flow_match *match,
- size_t offset, size_t size, bool is_mask)
+static void update_range(struct sw_flow_match *match,
+ size_t offset, size_t size, bool is_mask)
{
- struct sw_flow_key_range *range = NULL;
+ struct sw_flow_key_range *range;
size_t start = rounddown(offset, sizeof(long));
size_t end = roundup(offset + size, sizeof(long));
if (!is_mask)
range = &match->range;
- else if (match->mask)
+ else
range = &match->mask->range;
- if (!range)
- return;
-
if (range->start == range->end) {
range->start = start;
range->end = end;
@@ -79,22 +77,20 @@ static void update_range__(struct sw_flow_match *match,
#define SW_FLOW_KEY_PUT(match, field, value, is_mask) \
do { \
- update_range__(match, offsetof(struct sw_flow_key, field), \
- sizeof((match)->key->field), is_mask); \
- if (is_mask) { \
- if ((match)->mask) \
- (match)->mask->key.field = value; \
- } else { \
+ update_range(match, offsetof(struct sw_flow_key, field), \
+ sizeof((match)->key->field), is_mask); \
+ if (is_mask) \
+ (match)->mask->key.field = value; \
+ else \
(match)->key->field = value; \
- } \
} while (0)
#define SW_FLOW_KEY_MEMCPY_OFFSET(match, offset, value_p, len, is_mask) \
do { \
- update_range__(match, offset, len, is_mask); \
+ update_range(match, offset, len, is_mask); \
if (is_mask) \
memcpy((u8 *)&(match)->mask->key + offset, value_p, \
- len); \
+ len); \
else \
memcpy((u8 *)(match)->key + offset, value_p, len); \
} while (0)
@@ -103,22 +99,20 @@ static void update_range__(struct sw_flow_match *match,
SW_FLOW_KEY_MEMCPY_OFFSET(match, offsetof(struct sw_flow_key, field), \
value_p, len, is_mask)
-#define SW_FLOW_KEY_MEMSET_FIELD(match, field, value, is_mask) \
- do { \
- update_range__(match, offsetof(struct sw_flow_key, field), \
- sizeof((match)->key->field), is_mask); \
- if (is_mask) { \
- if ((match)->mask) \
- memset((u8 *)&(match)->mask->key.field, value,\
- sizeof((match)->mask->key.field)); \
- } else { \
+#define SW_FLOW_KEY_MEMSET_FIELD(match, field, value, is_mask) \
+ do { \
+ update_range(match, offsetof(struct sw_flow_key, field), \
+ sizeof((match)->key->field), is_mask); \
+ if (is_mask) \
+ memset((u8 *)&(match)->mask->key.field, value, \
+ sizeof((match)->mask->key.field)); \
+ else \
memset((u8 *)&(match)->key->field, value, \
sizeof((match)->key->field)); \
- } \
} while (0)
static bool match_validate(const struct sw_flow_match *match,
- u64 key_attrs, u64 mask_attrs)
+ u64 key_attrs, u64 mask_attrs, bool log)
{
u64 key_expected = 1 << OVS_KEY_ATTR_ETHERNET;
u64 mask_allowed = key_attrs; /* At most allow all key attributes */
@@ -134,7 +128,8 @@ static bool match_validate(const struct sw_flow_match *match,
| (1 << OVS_KEY_ATTR_ICMP)
| (1 << OVS_KEY_ATTR_ICMPV6)
| (1 << OVS_KEY_ATTR_ARP)
- | (1 << OVS_KEY_ATTR_ND));
+ | (1 << OVS_KEY_ATTR_ND)
+ | (1 << OVS_KEY_ATTR_MPLS));
/* Always allowed mask fields. */
mask_allowed |= ((1 << OVS_KEY_ATTR_TUNNEL)
@@ -149,6 +144,12 @@ static bool match_validate(const struct sw_flow_match *match,
mask_allowed |= 1 << OVS_KEY_ATTR_ARP;
}
+ if (eth_p_mpls(match->key->eth.type)) {
+ key_expected |= 1 << OVS_KEY_ATTR_MPLS;
+ if (match->mask && (match->mask->key.eth.type == htons(0xffff)))
+ mask_allowed |= 1 << OVS_KEY_ATTR_MPLS;
+ }
+
if (match->key->eth.type == htons(ETH_P_IP)) {
key_expected |= 1 << OVS_KEY_ATTR_IPV4;
if (match->mask && (match->mask->key.eth.type == htons(0xffff)))
@@ -220,7 +221,7 @@ static bool match_validate(const struct sw_flow_match *match,
htons(NDISC_NEIGHBOUR_SOLICITATION) ||
match->key->tp.src == htons(NDISC_NEIGHBOUR_ADVERTISEMENT)) {
key_expected |= 1 << OVS_KEY_ATTR_ND;
- if (match->mask && (match->mask->key.tp.src == htons(0xffff)))
+ if (match->mask && (match->mask->key.tp.src == htons(0xff)))
mask_allowed |= 1 << OVS_KEY_ATTR_ND;
}
}
@@ -229,21 +230,65 @@ static bool match_validate(const struct sw_flow_match *match,
if ((key_attrs & key_expected) != key_expected) {
/* Key attributes check failed. */
- OVS_NLERR("Missing expected key attributes (key_attrs=%llx, expected=%llx).\n",
- (unsigned long long)key_attrs, (unsigned long long)key_expected);
+ OVS_NLERR(log, "Missing key (keys=%llx, expected=%llx)",
+ (unsigned long long)key_attrs,
+ (unsigned long long)key_expected);
return false;
}
if ((mask_attrs & mask_allowed) != mask_attrs) {
/* Mask attributes check failed. */
- OVS_NLERR("Contain more than allowed mask fields (mask_attrs=%llx, mask_allowed=%llx).\n",
- (unsigned long long)mask_attrs, (unsigned long long)mask_allowed);
+ OVS_NLERR(log, "Unexpected mask (mask=%llx, allowed=%llx)",
+ (unsigned long long)mask_attrs,
+ (unsigned long long)mask_allowed);
return false;
}
return true;
}
+size_t ovs_tun_key_attr_size(void)
+{
+ /* Whenever adding new OVS_TUNNEL_KEY_ FIELDS, we should consider
+ * updating this function.
+ */
+ return nla_total_size(8) /* OVS_TUNNEL_KEY_ATTR_ID */
+ + nla_total_size(4) /* OVS_TUNNEL_KEY_ATTR_IPV4_SRC */
+ + nla_total_size(4) /* OVS_TUNNEL_KEY_ATTR_IPV4_DST */
+ + nla_total_size(1) /* OVS_TUNNEL_KEY_ATTR_TOS */
+ + nla_total_size(1) /* OVS_TUNNEL_KEY_ATTR_TTL */
+ + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT */
+ + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_CSUM */
+ + nla_total_size(0) /* OVS_TUNNEL_KEY_ATTR_OAM */
+ + nla_total_size(256) /* OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS */
+ + nla_total_size(2) /* OVS_TUNNEL_KEY_ATTR_TP_SRC */
+ + nla_total_size(2); /* OVS_TUNNEL_KEY_ATTR_TP_DST */
+}
+
+size_t ovs_key_attr_size(void)
+{
+ /* Whenever adding new OVS_KEY_ FIELDS, we should consider
+ * updating this function.
+ */
+ BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 22);
+
+ return nla_total_size(4) /* OVS_KEY_ATTR_PRIORITY */
+ + nla_total_size(0) /* OVS_KEY_ATTR_TUNNEL */
+ + ovs_tun_key_attr_size()
+ + nla_total_size(4) /* OVS_KEY_ATTR_IN_PORT */
+ + nla_total_size(4) /* OVS_KEY_ATTR_SKB_MARK */
+ + nla_total_size(4) /* OVS_KEY_ATTR_DP_HASH */
+ + nla_total_size(4) /* OVS_KEY_ATTR_RECIRC_ID */
+ + nla_total_size(12) /* OVS_KEY_ATTR_ETHERNET */
+ + nla_total_size(2) /* OVS_KEY_ATTR_ETHERTYPE */
+ + nla_total_size(4) /* OVS_KEY_ATTR_VLAN */
+ + nla_total_size(0) /* OVS_KEY_ATTR_ENCAP */
+ + nla_total_size(2) /* OVS_KEY_ATTR_ETHERTYPE */
+ + nla_total_size(40) /* OVS_KEY_ATTR_IPV6 */
+ + nla_total_size(2) /* OVS_KEY_ATTR_ICMPV6 */
+ + nla_total_size(28); /* OVS_KEY_ATTR_ND */
+}
+
/* The size of the argument for each %OVS_KEY_ATTR_* Netlink attribute. */
static const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
[OVS_KEY_ATTR_ENCAP] = -1,
@@ -266,6 +311,7 @@ static const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
[OVS_KEY_ATTR_RECIRC_ID] = sizeof(u32),
[OVS_KEY_ATTR_DP_HASH] = sizeof(u32),
[OVS_KEY_ATTR_TUNNEL] = -1,
+ [OVS_KEY_ATTR_MPLS] = sizeof(struct ovs_key_mpls),
};
static bool is_all_zero(const u8 *fp, size_t size)
@@ -284,7 +330,7 @@ static bool is_all_zero(const u8 *fp, size_t size)
static int __parse_flow_nlattrs(const struct nlattr *attr,
const struct nlattr *a[],
- u64 *attrsp, bool nz)
+ u64 *attrsp, bool log, bool nz)
{
const struct nlattr *nla;
u64 attrs;
@@ -296,21 +342,20 @@ static int __parse_flow_nlattrs(const struct nlattr *attr,
int expected_len;
if (type > OVS_KEY_ATTR_MAX) {
- OVS_NLERR("Unknown key attribute (type=%d, max=%d).\n",
+ OVS_NLERR(log, "Key type %d is out of range max %d",
type, OVS_KEY_ATTR_MAX);
return -EINVAL;
}
if (attrs & (1 << type)) {
- OVS_NLERR("Duplicate key attribute (type %d).\n", type);
+ OVS_NLERR(log, "Duplicate key (type %d).", type);
return -EINVAL;
}
expected_len = ovs_key_lens[type];
if (nla_len(nla) != expected_len && expected_len != -1) {
- OVS_NLERR("Key attribute has unexpected length (type=%d"
- ", length=%d, expected=%d).\n", type,
- nla_len(nla), expected_len);
+ OVS_NLERR(log, "Key %d has unexpected len %d expected %d",
+ type, nla_len(nla), expected_len);
return -EINVAL;
}
@@ -320,7 +365,7 @@ static int __parse_flow_nlattrs(const struct nlattr *attr,
}
}
if (rem) {
- OVS_NLERR("Message has %d unknown bytes.\n", rem);
+ OVS_NLERR(log, "Message has %d unknown bytes.", rem);
return -EINVAL;
}
@@ -329,28 +374,84 @@ static int __parse_flow_nlattrs(const struct nlattr *attr,
}
static int parse_flow_mask_nlattrs(const struct nlattr *attr,
- const struct nlattr *a[], u64 *attrsp)
+ const struct nlattr *a[], u64 *attrsp,
+ bool log)
{
- return __parse_flow_nlattrs(attr, a, attrsp, true);
+ return __parse_flow_nlattrs(attr, a, attrsp, log, true);
}
static int parse_flow_nlattrs(const struct nlattr *attr,
- const struct nlattr *a[], u64 *attrsp)
+ const struct nlattr *a[], u64 *attrsp,
+ bool log)
{
- return __parse_flow_nlattrs(attr, a, attrsp, false);
+ return __parse_flow_nlattrs(attr, a, attrsp, log, false);
+}
+
+static int genev_tun_opt_from_nlattr(const struct nlattr *a,
+ struct sw_flow_match *match, bool is_mask,
+ bool log)
+{
+ unsigned long opt_key_offset;
+
+ if (nla_len(a) > sizeof(match->key->tun_opts)) {
+ OVS_NLERR(log, "Geneve option length err (len %d, max %zu).",
+ nla_len(a), sizeof(match->key->tun_opts));
+ return -EINVAL;
+ }
+
+ if (nla_len(a) % 4 != 0) {
+ OVS_NLERR(log, "Geneve opt len %d is not a multiple of 4.",
+ nla_len(a));
+ return -EINVAL;
+ }
+
+ /* We need to record the length of the options passed
+ * down, otherwise packets with the same format but
+ * additional options will be silently matched.
+ */
+ if (!is_mask) {
+ SW_FLOW_KEY_PUT(match, tun_opts_len, nla_len(a),
+ false);
+ } else {
+ /* This is somewhat unusual because it looks at
+ * both the key and mask while parsing the
+ * attributes (and by extension assumes the key
+ * is parsed first). Normally, we would verify
+ * that each is the correct length and that the
+ * attributes line up in the validate function.
+ * However, that is difficult because this is
+ * variable length and we won't have the
+ * information later.
+ */
+ if (match->key->tun_opts_len != nla_len(a)) {
+ OVS_NLERR(log, "Geneve option len %d != mask len %d",
+ match->key->tun_opts_len, nla_len(a));
+ return -EINVAL;
+ }
+
+ SW_FLOW_KEY_PUT(match, tun_opts_len, 0xff, true);
+ }
+
+ opt_key_offset = (unsigned long)GENEVE_OPTS((struct sw_flow_key *)0,
+ nla_len(a));
+ SW_FLOW_KEY_MEMCPY_OFFSET(match, opt_key_offset, nla_data(a),
+ nla_len(a), is_mask);
+ return 0;
}
static int ipv4_tun_from_nlattr(const struct nlattr *attr,
- struct sw_flow_match *match, bool is_mask)
+ struct sw_flow_match *match, bool is_mask,
+ bool log)
{
struct nlattr *a;
int rem;
bool ttl = false;
__be16 tun_flags = 0;
- unsigned long opt_key_offset;
nla_for_each_nested(a, attr, rem) {
int type = nla_type(a);
+ int err;
+
static const u32 ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1] = {
[OVS_TUNNEL_KEY_ATTR_ID] = sizeof(u64),
[OVS_TUNNEL_KEY_ATTR_IPV4_SRC] = sizeof(u32),
@@ -359,20 +460,21 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr,
[OVS_TUNNEL_KEY_ATTR_TTL] = 1,
[OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT] = 0,
[OVS_TUNNEL_KEY_ATTR_CSUM] = 0,
+ [OVS_TUNNEL_KEY_ATTR_TP_SRC] = sizeof(u16),
+ [OVS_TUNNEL_KEY_ATTR_TP_DST] = sizeof(u16),
[OVS_TUNNEL_KEY_ATTR_OAM] = 0,
[OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS] = -1,
};
if (type > OVS_TUNNEL_KEY_ATTR_MAX) {
- OVS_NLERR("Unknown IPv4 tunnel attribute (type=%d, max=%d).\n",
- type, OVS_TUNNEL_KEY_ATTR_MAX);
+ OVS_NLERR(log, "Tunnel attr %d out of range max %d",
+ type, OVS_TUNNEL_KEY_ATTR_MAX);
return -EINVAL;
}
if (ovs_tunnel_key_lens[type] != nla_len(a) &&
ovs_tunnel_key_lens[type] != -1) {
- OVS_NLERR("IPv4 tunnel attribute type has unexpected "
- " length (type=%d, length=%d, expected=%d).\n",
+ OVS_NLERR(log, "Tunnel attr %d has unexpected len %d expected %d",
type, nla_len(a), ovs_tunnel_key_lens[type]);
return -EINVAL;
}
@@ -406,62 +508,26 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr,
case OVS_TUNNEL_KEY_ATTR_CSUM:
tun_flags |= TUNNEL_CSUM;
break;
+ case OVS_TUNNEL_KEY_ATTR_TP_SRC:
+ SW_FLOW_KEY_PUT(match, tun_key.tp_src,
+ nla_get_be16(a), is_mask);
+ break;
+ case OVS_TUNNEL_KEY_ATTR_TP_DST:
+ SW_FLOW_KEY_PUT(match, tun_key.tp_dst,
+ nla_get_be16(a), is_mask);
+ break;
case OVS_TUNNEL_KEY_ATTR_OAM:
tun_flags |= TUNNEL_OAM;
break;
case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS:
- tun_flags |= TUNNEL_OPTIONS_PRESENT;
- if (nla_len(a) > sizeof(match->key->tun_opts)) {
- OVS_NLERR("Geneve option length exceeds maximum size (len %d, max %zu).\n",
- nla_len(a),
- sizeof(match->key->tun_opts));
- return -EINVAL;
- }
-
- if (nla_len(a) % 4 != 0) {
- OVS_NLERR("Geneve option length is not a multiple of 4 (len %d).\n",
- nla_len(a));
- return -EINVAL;
- }
-
- /* We need to record the length of the options passed
- * down, otherwise packets with the same format but
- * additional options will be silently matched.
- */
- if (!is_mask) {
- SW_FLOW_KEY_PUT(match, tun_opts_len, nla_len(a),
- false);
- } else {
- /* This is somewhat unusual because it looks at
- * both the key and mask while parsing the
- * attributes (and by extension assumes the key
- * is parsed first). Normally, we would verify
- * that each is the correct length and that the
- * attributes line up in the validate function.
- * However, that is difficult because this is
- * variable length and we won't have the
- * information later.
- */
- if (match->key->tun_opts_len != nla_len(a)) {
- OVS_NLERR("Geneve option key length (%d) is different from mask length (%d).",
- match->key->tun_opts_len,
- nla_len(a));
- return -EINVAL;
- }
-
- SW_FLOW_KEY_PUT(match, tun_opts_len, 0xff,
- true);
- }
+ err = genev_tun_opt_from_nlattr(a, match, is_mask, log);
+ if (err)
+ return err;
- opt_key_offset = (unsigned long)GENEVE_OPTS(
- (struct sw_flow_key *)0,
- nla_len(a));
- SW_FLOW_KEY_MEMCPY_OFFSET(match, opt_key_offset,
- nla_data(a), nla_len(a),
- is_mask);
+ tun_flags |= TUNNEL_OPTIONS_PRESENT;
break;
default:
- OVS_NLERR("Unknown IPv4 tunnel attribute (%d).\n",
+ OVS_NLERR(log, "Unknown IPv4 tunnel attribute %d",
type);
return -EINVAL;
}
@@ -470,18 +536,19 @@ static int ipv4_tun_from_nlattr(const struct nlattr *attr,
SW_FLOW_KEY_PUT(match, tun_key.tun_flags, tun_flags, is_mask);
if (rem > 0) {
- OVS_NLERR("IPv4 tunnel attribute has %d unknown bytes.\n", rem);
+ OVS_NLERR(log, "IPv4 tunnel attribute has %d unknown bytes.",
+ rem);
return -EINVAL;
}
if (!is_mask) {
if (!match->key->tun_key.ipv4_dst) {
- OVS_NLERR("IPv4 tunnel destination address is zero.\n");
+ OVS_NLERR(log, "IPv4 tunnel dst address is zero");
return -EINVAL;
}
if (!ttl) {
- OVS_NLERR("IPv4 tunnel TTL not specified.\n");
+ OVS_NLERR(log, "IPv4 tunnel TTL not specified.");
return -EINVAL;
}
}
@@ -514,6 +581,12 @@ static int __ipv4_tun_to_nlattr(struct sk_buff *skb,
if ((output->tun_flags & TUNNEL_CSUM) &&
nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_CSUM))
return -EMSGSIZE;
+ if (output->tp_src &&
+ nla_put_be16(skb, OVS_TUNNEL_KEY_ATTR_TP_SRC, output->tp_src))
+ return -EMSGSIZE;
+ if (output->tp_dst &&
+ nla_put_be16(skb, OVS_TUNNEL_KEY_ATTR_TP_DST, output->tp_dst))
+ return -EMSGSIZE;
if ((output->tun_flags & TUNNEL_OAM) &&
nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_OAM))
return -EMSGSIZE;
@@ -525,7 +598,6 @@ static int __ipv4_tun_to_nlattr(struct sk_buff *skb,
return 0;
}
-
static int ipv4_tun_to_nlattr(struct sk_buff *skb,
const struct ovs_key_ipv4_tunnel *output,
const struct geneve_opt *tun_opts,
@@ -546,8 +618,17 @@ static int ipv4_tun_to_nlattr(struct sk_buff *skb,
return 0;
}
+int ovs_nla_put_egress_tunnel_key(struct sk_buff *skb,
+ const struct ovs_tunnel_info *egress_tun_info)
+{
+ return __ipv4_tun_to_nlattr(skb, &egress_tun_info->tunnel,
+ egress_tun_info->options,
+ egress_tun_info->options_len);
+}
+
static int metadata_from_nlattrs(struct sw_flow_match *match, u64 *attrs,
- const struct nlattr **a, bool is_mask)
+ const struct nlattr **a, bool is_mask,
+ bool log)
{
if (*attrs & (1 << OVS_KEY_ATTR_DP_HASH)) {
u32 hash_val = nla_get_u32(a[OVS_KEY_ATTR_DP_HASH]);
@@ -572,10 +653,13 @@ static int metadata_from_nlattrs(struct sw_flow_match *match, u64 *attrs,
if (*attrs & (1 << OVS_KEY_ATTR_IN_PORT)) {
u32 in_port = nla_get_u32(a[OVS_KEY_ATTR_IN_PORT]);
- if (is_mask)
+ if (is_mask) {
in_port = 0xffffffff; /* Always exact match in_port. */
- else if (in_port >= DP_MAX_PORTS)
+ } else if (in_port >= DP_MAX_PORTS) {
+ OVS_NLERR(log, "Port %d exceeds max allowable %d",
+ in_port, DP_MAX_PORTS);
return -EINVAL;
+ }
SW_FLOW_KEY_PUT(match, phy.in_port, in_port, is_mask);
*attrs &= ~(1 << OVS_KEY_ATTR_IN_PORT);
@@ -591,7 +675,7 @@ static int metadata_from_nlattrs(struct sw_flow_match *match, u64 *attrs,
}
if (*attrs & (1 << OVS_KEY_ATTR_TUNNEL)) {
if (ipv4_tun_from_nlattr(a[OVS_KEY_ATTR_TUNNEL], match,
- is_mask))
+ is_mask, log))
return -EINVAL;
*attrs &= ~(1 << OVS_KEY_ATTR_TUNNEL);
}
@@ -599,12 +683,12 @@ static int metadata_from_nlattrs(struct sw_flow_match *match, u64 *attrs,
}
static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
- const struct nlattr **a, bool is_mask)
+ const struct nlattr **a, bool is_mask,
+ bool log)
{
int err;
- u64 orig_attrs = attrs;
- err = metadata_from_nlattrs(match, &attrs, a, is_mask);
+ err = metadata_from_nlattrs(match, &attrs, a, is_mask, log);
if (err)
return err;
@@ -625,17 +709,16 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
if (!(tci & htons(VLAN_TAG_PRESENT))) {
if (is_mask)
- OVS_NLERR("VLAN TCI mask does not have exact match for VLAN_TAG_PRESENT bit.\n");
+ OVS_NLERR(log, "VLAN TCI mask does not have exact match for VLAN_TAG_PRESENT bit.");
else
- OVS_NLERR("VLAN TCI does not have VLAN_TAG_PRESENT bit set.\n");
+ OVS_NLERR(log, "VLAN TCI does not have VLAN_TAG_PRESENT bit set.");
return -EINVAL;
}
SW_FLOW_KEY_PUT(match, eth.tci, tci, is_mask);
attrs &= ~(1 << OVS_KEY_ATTR_VLAN);
- } else if (!is_mask)
- SW_FLOW_KEY_PUT(match, eth.tci, htons(0xffff), true);
+ }
if (attrs & (1 << OVS_KEY_ATTR_ETHERTYPE)) {
__be16 eth_type;
@@ -645,8 +728,8 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
/* Always exact match EtherType. */
eth_type = htons(0xffff);
} else if (ntohs(eth_type) < ETH_P_802_3_MIN) {
- OVS_NLERR("EtherType is less than minimum (type=%x, min=%x).\n",
- ntohs(eth_type), ETH_P_802_3_MIN);
+ OVS_NLERR(log, "EtherType %x is less than min %x",
+ ntohs(eth_type), ETH_P_802_3_MIN);
return -EINVAL;
}
@@ -661,8 +744,8 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
ipv4_key = nla_data(a[OVS_KEY_ATTR_IPV4]);
if (!is_mask && ipv4_key->ipv4_frag > OVS_FRAG_TYPE_MAX) {
- OVS_NLERR("Unknown IPv4 fragment type (value=%d, max=%d).\n",
- ipv4_key->ipv4_frag, OVS_FRAG_TYPE_MAX);
+ OVS_NLERR(log, "IPv4 frag type %d is out of range max %d",
+ ipv4_key->ipv4_frag, OVS_FRAG_TYPE_MAX);
return -EINVAL;
}
SW_FLOW_KEY_PUT(match, ip.proto,
@@ -685,10 +768,17 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
ipv6_key = nla_data(a[OVS_KEY_ATTR_IPV6]);
if (!is_mask && ipv6_key->ipv6_frag > OVS_FRAG_TYPE_MAX) {
- OVS_NLERR("Unknown IPv6 fragment type (value=%d, max=%d).\n",
- ipv6_key->ipv6_frag, OVS_FRAG_TYPE_MAX);
+ OVS_NLERR(log, "IPv6 frag type %d is out of range max %d",
+ ipv6_key->ipv6_frag, OVS_FRAG_TYPE_MAX);
+ return -EINVAL;
+ }
+
+ if (!is_mask && ipv6_key->ipv6_label & htonl(0xFFF00000)) {
+ OVS_NLERR(log, "IPv6 flow label %x is out of range (max=%x).\n",
+ ntohl(ipv6_key->ipv6_label), (1 << 20) - 1);
return -EINVAL;
}
+
SW_FLOW_KEY_PUT(match, ipv6.label,
ipv6_key->ipv6_label, is_mask);
SW_FLOW_KEY_PUT(match, ip.proto,
@@ -716,7 +806,7 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
arp_key = nla_data(a[OVS_KEY_ATTR_ARP]);
if (!is_mask && (arp_key->arp_op & htons(0xff00))) {
- OVS_NLERR("Unknown ARP opcode (opcode=%d).\n",
+ OVS_NLERR(log, "Unknown ARP opcode (opcode=%d).",
arp_key->arp_op);
return -EINVAL;
}
@@ -735,6 +825,16 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
attrs &= ~(1 << OVS_KEY_ATTR_ARP);
}
+ if (attrs & (1 << OVS_KEY_ATTR_MPLS)) {
+ const struct ovs_key_mpls *mpls_key;
+
+ mpls_key = nla_data(a[OVS_KEY_ATTR_MPLS]);
+ SW_FLOW_KEY_PUT(match, mpls.top_lse,
+ mpls_key->mpls_lse, is_mask);
+
+ attrs &= ~(1 << OVS_KEY_ATTR_MPLS);
+ }
+
if (attrs & (1 << OVS_KEY_ATTR_TCP)) {
const struct ovs_key_tcp *tcp_key;
@@ -745,15 +845,9 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
}
if (attrs & (1 << OVS_KEY_ATTR_TCP_FLAGS)) {
- if (orig_attrs & (1 << OVS_KEY_ATTR_IPV4)) {
- SW_FLOW_KEY_PUT(match, tp.flags,
- nla_get_be16(a[OVS_KEY_ATTR_TCP_FLAGS]),
- is_mask);
- } else {
- SW_FLOW_KEY_PUT(match, tp.flags,
- nla_get_be16(a[OVS_KEY_ATTR_TCP_FLAGS]),
- is_mask);
- }
+ SW_FLOW_KEY_PUT(match, tp.flags,
+ nla_get_be16(a[OVS_KEY_ATTR_TCP_FLAGS]),
+ is_mask);
attrs &= ~(1 << OVS_KEY_ATTR_TCP_FLAGS);
}
@@ -812,8 +906,11 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
attrs &= ~(1 << OVS_KEY_ATTR_ND);
}
- if (attrs != 0)
+ if (attrs != 0) {
+ OVS_NLERR(log, "Unknown key attributes %llx",
+ (unsigned long long)attrs);
return -EINVAL;
+ }
return 0;
}
@@ -851,10 +948,14 @@ static void mask_set_nlattr(struct nlattr *attr, u8 val)
* of this flow.
* @mask: Optional. Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink
* attribute specifies the mask field of the wildcarded flow.
+ * @log: Boolean to allow kernel error logging. Normally true, but when
+ * probing for feature compatibility this should be passed in as false to
+ * suppress unnecessary error logging.
*/
int ovs_nla_get_match(struct sw_flow_match *match,
- const struct nlattr *key,
- const struct nlattr *mask)
+ const struct nlattr *nla_key,
+ const struct nlattr *nla_mask,
+ bool log)
{
const struct nlattr *a[OVS_KEY_ATTR_MAX + 1];
const struct nlattr *encap;
@@ -864,7 +965,7 @@ int ovs_nla_get_match(struct sw_flow_match *match,
bool encap_valid = false;
int err;
- err = parse_flow_nlattrs(key, a, &key_attrs);
+ err = parse_flow_nlattrs(nla_key, a, &key_attrs, log);
if (err)
return err;
@@ -875,7 +976,7 @@ int ovs_nla_get_match(struct sw_flow_match *match,
if (!((key_attrs & (1 << OVS_KEY_ATTR_VLAN)) &&
(key_attrs & (1 << OVS_KEY_ATTR_ENCAP)))) {
- OVS_NLERR("Invalid Vlan frame.\n");
+ OVS_NLERR(log, "Invalid Vlan frame.");
return -EINVAL;
}
@@ -886,61 +987,68 @@ int ovs_nla_get_match(struct sw_flow_match *match,
encap_valid = true;
if (tci & htons(VLAN_TAG_PRESENT)) {
- err = parse_flow_nlattrs(encap, a, &key_attrs);
+ err = parse_flow_nlattrs(encap, a, &key_attrs, log);
if (err)
return err;
} else if (!tci) {
/* Corner case for truncated 802.1Q header. */
if (nla_len(encap)) {
- OVS_NLERR("Truncated 802.1Q header has non-zero encap attribute.\n");
+ OVS_NLERR(log, "Truncated 802.1Q header has non-zero encap attribute.");
return -EINVAL;
}
} else {
- OVS_NLERR("Encap attribute is set for a non-VLAN frame.\n");
+ OVS_NLERR(log, "Encap attr is set for non-VLAN frame");
return -EINVAL;
}
}
- err = ovs_key_from_nlattrs(match, key_attrs, a, false);
+ err = ovs_key_from_nlattrs(match, key_attrs, a, false, log);
if (err)
return err;
- if (match->mask && !mask) {
- /* Create an exact match mask. We need to set to 0xff all the
- * 'match->mask' fields that have been touched in 'match->key'.
- * We cannot simply memset 'match->mask', because padding bytes
- * and fields not specified in 'match->key' should be left to 0.
- * Instead, we use a stream of netlink attributes, copied from
- * 'key' and set to 0xff: ovs_key_from_nlattrs() will take care
- * of filling 'match->mask' appropriately.
- */
- newmask = kmemdup(key, nla_total_size(nla_len(key)),
- GFP_KERNEL);
- if (!newmask)
- return -ENOMEM;
+ if (match->mask) {
+ if (!nla_mask) {
+ /* Create an exact match mask. We need to set to 0xff
+ * all the 'match->mask' fields that have been touched
+ * in 'match->key'. We cannot simply memset
+ * 'match->mask', because padding bytes and fields not
+ * specified in 'match->key' should be left to 0.
+ * Instead, we use a stream of netlink attributes,
+ * copied from 'key' and set to 0xff.
+ * ovs_key_from_nlattrs() will take care of filling
+ * 'match->mask' appropriately.
+ */
+ newmask = kmemdup(nla_key,
+ nla_total_size(nla_len(nla_key)),
+ GFP_KERNEL);
+ if (!newmask)
+ return -ENOMEM;
- mask_set_nlattr(newmask, 0xff);
+ mask_set_nlattr(newmask, 0xff);
- /* The userspace does not send tunnel attributes that are 0,
- * but we should not wildcard them nonetheless.
- */
- if (match->key->tun_key.ipv4_dst)
- SW_FLOW_KEY_MEMSET_FIELD(match, tun_key, 0xff, true);
+ /* The userspace does not send tunnel attributes that
+ * are 0, but we should not wildcard them nonetheless.
+ */
+ if (match->key->tun_key.ipv4_dst)
+ SW_FLOW_KEY_MEMSET_FIELD(match, tun_key,
+ 0xff, true);
- mask = newmask;
- }
+ nla_mask = newmask;
+ }
- if (mask) {
- err = parse_flow_mask_nlattrs(mask, a, &mask_attrs);
+ err = parse_flow_mask_nlattrs(nla_mask, a, &mask_attrs, log);
if (err)
goto free_newmask;
+ /* Always match on tci. */
+ SW_FLOW_KEY_PUT(match, eth.tci, htons(0xffff), true);
+
if (mask_attrs & 1 << OVS_KEY_ATTR_ENCAP) {
__be16 eth_type = 0;
__be16 tci = 0;
if (!encap_valid) {
- OVS_NLERR("Encap mask attribute is set for non-VLAN frame.\n");
+ OVS_NLERR(log, "Encap mask attribute is set for non-VLAN frame.");
err = -EINVAL;
goto free_newmask;
}
@@ -952,12 +1060,13 @@ int ovs_nla_get_match(struct sw_flow_match *match,
if (eth_type == htons(0xffff)) {
mask_attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE);
encap = a[OVS_KEY_ATTR_ENCAP];
- err = parse_flow_mask_nlattrs(encap, a, &mask_attrs);
+ err = parse_flow_mask_nlattrs(encap, a,
+ &mask_attrs, log);
if (err)
goto free_newmask;
} else {
- OVS_NLERR("VLAN frames must have an exact match on the TPID (mask=%x).\n",
- ntohs(eth_type));
+ OVS_NLERR(log, "VLAN frames must have an exact match on the TPID (mask=%x).",
+ ntohs(eth_type));
err = -EINVAL;
goto free_newmask;
}
@@ -966,18 +1075,19 @@ int ovs_nla_get_match(struct sw_flow_match *match,
tci = nla_get_be16(a[OVS_KEY_ATTR_VLAN]);
if (!(tci & htons(VLAN_TAG_PRESENT))) {
- OVS_NLERR("VLAN tag present bit must have an exact match (tci_mask=%x).\n", ntohs(tci));
+ OVS_NLERR(log, "VLAN tag present bit must have an exact match (tci_mask=%x).",
+ ntohs(tci));
err = -EINVAL;
goto free_newmask;
}
}
- err = ovs_key_from_nlattrs(match, mask_attrs, a, true);
+ err = ovs_key_from_nlattrs(match, mask_attrs, a, true, log);
if (err)
goto free_newmask;
}
- if (!match_validate(match, key_attrs, mask_attrs))
+ if (!match_validate(match, key_attrs, mask_attrs, log))
err = -EINVAL;
free_newmask:
@@ -990,6 +1100,9 @@ free_newmask:
* @key: Receives extracted in_port, priority, tun_key and skb_mark.
* @attr: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute
* sequence.
+ * @log: Boolean to allow kernel error logging. Normally true, but when
+ * probing for feature compatibility this should be passed in as false to
+ * suppress unnecessary error logging.
*
* This parses a series of Netlink attributes that form a flow key, which must
* take the same form accepted by flow_from_nlattrs(), but only enough of it to
@@ -998,14 +1111,15 @@ free_newmask:
*/
int ovs_nla_get_flow_metadata(const struct nlattr *attr,
- struct sw_flow_key *key)
+ struct sw_flow_key *key,
+ bool log)
{
const struct nlattr *a[OVS_KEY_ATTR_MAX + 1];
struct sw_flow_match match;
u64 attrs = 0;
int err;
- err = parse_flow_nlattrs(attr, a, &attrs);
+ err = parse_flow_nlattrs(attr, a, &attrs, log);
if (err)
return -EINVAL;
@@ -1014,7 +1128,7 @@ int ovs_nla_get_flow_metadata(const struct nlattr *attr,
key->phy.in_port = DP_MAX_PORTS;
- return metadata_from_nlattrs(&match, &attrs, a, false);
+ return metadata_from_nlattrs(&match, &attrs, a, false, log);
}
int ovs_nla_put_flow(const struct sw_flow_key *swkey,
@@ -1140,6 +1254,14 @@ int ovs_nla_put_flow(const struct sw_flow_key *swkey,
arp_key->arp_op = htons(output->ip.proto);
ether_addr_copy(arp_key->arp_sha, output->ipv4.arp.sha);
ether_addr_copy(arp_key->arp_tha, output->ipv4.arp.tha);
+ } else if (eth_p_mpls(swkey->eth.type)) {
+ struct ovs_key_mpls *mpls_key;
+
+ nla = nla_reserve(skb, OVS_KEY_ATTR_MPLS, sizeof(*mpls_key));
+ if (!nla)
+ goto nla_put_failure;
+ mpls_key = nla_data(nla);
+ mpls_key->mpls_lse = output->mpls.top_lse;
}
if ((swkey->eth.type == htons(ETH_P_IP) ||
@@ -1226,12 +1348,14 @@ nla_put_failure:
#define MAX_ACTIONS_BUFSIZE (32 * 1024)
-struct sw_flow_actions *ovs_nla_alloc_flow_actions(int size)
+static struct sw_flow_actions *nla_alloc_flow_actions(int size, bool log)
{
struct sw_flow_actions *sfa;
- if (size > MAX_ACTIONS_BUFSIZE)
+ if (size > MAX_ACTIONS_BUFSIZE) {
+ OVS_NLERR(log, "Flow action size %u bytes exceeds max", size);
return ERR_PTR(-EINVAL);
+ }
sfa = kmalloc(sizeof(*sfa) + size, GFP_KERNEL);
if (!sfa)
@@ -1249,7 +1373,7 @@ void ovs_nla_free_flow_actions(struct sw_flow_actions *sf_acts)
}
static struct nlattr *reserve_sfa_size(struct sw_flow_actions **sfa,
- int attr_len)
+ int attr_len, bool log)
{
struct sw_flow_actions *acts;
@@ -1269,7 +1393,7 @@ static struct nlattr *reserve_sfa_size(struct sw_flow_actions **sfa,
new_acts_size = MAX_ACTIONS_BUFSIZE;
}
- acts = ovs_nla_alloc_flow_actions(new_acts_size);
+ acts = nla_alloc_flow_actions(new_acts_size, log);
if (IS_ERR(acts))
return (void *)acts;
@@ -1284,11 +1408,11 @@ out:
}
static struct nlattr *__add_action(struct sw_flow_actions **sfa,
- int attrtype, void *data, int len)
+ int attrtype, void *data, int len, bool log)
{
struct nlattr *a;
- a = reserve_sfa_size(sfa, nla_attr_size(len));
+ a = reserve_sfa_size(sfa, nla_attr_size(len), log);
if (IS_ERR(a))
return a;
@@ -1303,24 +1427,22 @@ static struct nlattr *__add_action(struct sw_flow_actions **sfa,
}
static int add_action(struct sw_flow_actions **sfa, int attrtype,
- void *data, int len)
+ void *data, int len, bool log)
{
struct nlattr *a;
- a = __add_action(sfa, attrtype, data, len);
- if (IS_ERR(a))
- return PTR_ERR(a);
+ a = __add_action(sfa, attrtype, data, len, log);
- return 0;
+ return PTR_ERR_OR_ZERO(a);
}
static inline int add_nested_action_start(struct sw_flow_actions **sfa,
- int attrtype)
+ int attrtype, bool log)
{
int used = (*sfa)->actions_len;
int err;
- err = add_action(sfa, attrtype, NULL, 0);
+ err = add_action(sfa, attrtype, NULL, 0, log);
if (err)
return err;
@@ -1336,9 +1458,15 @@ static inline void add_nested_action_end(struct sw_flow_actions *sfa,
a->nla_len = sfa->actions_len - st_offset;
}
+static int __ovs_nla_copy_actions(const struct nlattr *attr,
+ const struct sw_flow_key *key,
+ int depth, struct sw_flow_actions **sfa,
+ __be16 eth_type, __be16 vlan_tci, bool log);
+
static int validate_and_copy_sample(const struct nlattr *attr,
const struct sw_flow_key *key, int depth,
- struct sw_flow_actions **sfa)
+ struct sw_flow_actions **sfa,
+ __be16 eth_type, __be16 vlan_tci, bool log)
{
const struct nlattr *attrs[OVS_SAMPLE_ATTR_MAX + 1];
const struct nlattr *probability, *actions;
@@ -1364,18 +1492,19 @@ static int validate_and_copy_sample(const struct nlattr *attr,
return -EINVAL;
/* validation done, copy sample action. */
- start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SAMPLE);
+ start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SAMPLE, log);
if (start < 0)
return start;
err = add_action(sfa, OVS_SAMPLE_ATTR_PROBABILITY,
- nla_data(probability), sizeof(u32));
+ nla_data(probability), sizeof(u32), log);
if (err)
return err;
- st_acts = add_nested_action_start(sfa, OVS_SAMPLE_ATTR_ACTIONS);
+ st_acts = add_nested_action_start(sfa, OVS_SAMPLE_ATTR_ACTIONS, log);
if (st_acts < 0)
return st_acts;
- err = ovs_nla_copy_actions(actions, key, depth + 1, sfa);
+ err = __ovs_nla_copy_actions(actions, key, depth + 1, sfa,
+ eth_type, vlan_tci, log);
if (err)
return err;
@@ -1385,10 +1514,10 @@ static int validate_and_copy_sample(const struct nlattr *attr,
return 0;
}
-static int validate_tp_port(const struct sw_flow_key *flow_key)
+static int validate_tp_port(const struct sw_flow_key *flow_key,
+ __be16 eth_type)
{
- if ((flow_key->eth.type == htons(ETH_P_IP) ||
- flow_key->eth.type == htons(ETH_P_IPV6)) &&
+ if ((eth_type == htons(ETH_P_IP) || eth_type == htons(ETH_P_IPV6)) &&
(flow_key->tp.src || flow_key->tp.dst))
return 0;
@@ -1412,7 +1541,7 @@ void ovs_match_init(struct sw_flow_match *match,
}
static int validate_and_copy_set_tun(const struct nlattr *attr,
- struct sw_flow_actions **sfa)
+ struct sw_flow_actions **sfa, bool log)
{
struct sw_flow_match match;
struct sw_flow_key key;
@@ -1421,7 +1550,7 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
int err, start;
ovs_match_init(&match, &key, NULL);
- err = ipv4_tun_from_nlattr(nla_data(attr), &match, false);
+ err = ipv4_tun_from_nlattr(nla_data(attr), &match, false, log);
if (err)
return err;
@@ -1450,12 +1579,12 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
key.tun_key.tun_flags |= crit_opt ? TUNNEL_CRIT_OPT : 0;
};
- start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SET);
+ start = add_nested_action_start(sfa, OVS_ACTION_ATTR_SET, log);
if (start < 0)
return start;
a = __add_action(sfa, OVS_KEY_ATTR_TUNNEL_INFO, NULL,
- sizeof(*tun_info) + key.tun_opts_len);
+ sizeof(*tun_info) + key.tun_opts_len, log);
if (IS_ERR(a))
return PTR_ERR(a);
@@ -1483,7 +1612,7 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
static int validate_set(const struct nlattr *a,
const struct sw_flow_key *flow_key,
struct sw_flow_actions **sfa,
- bool *set_tun)
+ bool *set_tun, __be16 eth_type, bool log)
{
const struct nlattr *ovs_key = nla_data(a);
int key_type = nla_type(ovs_key);
@@ -1508,14 +1637,17 @@ static int validate_set(const struct nlattr *a,
break;
case OVS_KEY_ATTR_TUNNEL:
+ if (eth_p_mpls(eth_type))
+ return -EINVAL;
+
*set_tun = true;
- err = validate_and_copy_set_tun(a, sfa);
+ err = validate_and_copy_set_tun(a, sfa, log);
if (err)
return err;
break;
case OVS_KEY_ATTR_IPV4:
- if (flow_key->eth.type != htons(ETH_P_IP))
+ if (eth_type != htons(ETH_P_IP))
return -EINVAL;
if (!flow_key->ip.proto)
@@ -1531,7 +1663,7 @@ static int validate_set(const struct nlattr *a,
break;
case OVS_KEY_ATTR_IPV6:
- if (flow_key->eth.type != htons(ETH_P_IPV6))
+ if (eth_type != htons(ETH_P_IPV6))
return -EINVAL;
if (!flow_key->ip.proto)
@@ -1553,19 +1685,24 @@ static int validate_set(const struct nlattr *a,
if (flow_key->ip.proto != IPPROTO_TCP)
return -EINVAL;
- return validate_tp_port(flow_key);
+ return validate_tp_port(flow_key, eth_type);
case OVS_KEY_ATTR_UDP:
if (flow_key->ip.proto != IPPROTO_UDP)
return -EINVAL;
- return validate_tp_port(flow_key);
+ return validate_tp_port(flow_key, eth_type);
+
+ case OVS_KEY_ATTR_MPLS:
+ if (!eth_p_mpls(eth_type))
+ return -EINVAL;
+ break;
case OVS_KEY_ATTR_SCTP:
if (flow_key->ip.proto != IPPROTO_SCTP)
return -EINVAL;
- return validate_tp_port(flow_key);
+ return validate_tp_port(flow_key, eth_type);
default:
return -EINVAL;
@@ -1579,6 +1716,7 @@ static int validate_userspace(const struct nlattr *attr)
static const struct nla_policy userspace_policy[OVS_USERSPACE_ATTR_MAX + 1] = {
[OVS_USERSPACE_ATTR_PID] = {.type = NLA_U32 },
[OVS_USERSPACE_ATTR_USERDATA] = {.type = NLA_UNSPEC },
+ [OVS_USERSPACE_ATTR_EGRESS_TUN_PORT] = {.type = NLA_U32 },
};
struct nlattr *a[OVS_USERSPACE_ATTR_MAX + 1];
int error;
@@ -1596,12 +1734,12 @@ static int validate_userspace(const struct nlattr *attr)
}
static int copy_action(const struct nlattr *from,
- struct sw_flow_actions **sfa)
+ struct sw_flow_actions **sfa, bool log)
{
int totlen = NLA_ALIGN(from->nla_len);
struct nlattr *to;
- to = reserve_sfa_size(sfa, from->nla_len);
+ to = reserve_sfa_size(sfa, from->nla_len, log);
if (IS_ERR(to))
return PTR_ERR(to);
@@ -1609,12 +1747,13 @@ static int copy_action(const struct nlattr *from,
return 0;
}
-int ovs_nla_copy_actions(const struct nlattr *attr,
- const struct sw_flow_key *key,
- int depth,
- struct sw_flow_actions **sfa)
+static int __ovs_nla_copy_actions(const struct nlattr *attr,
+ const struct sw_flow_key *key,
+ int depth, struct sw_flow_actions **sfa,
+ __be16 eth_type, __be16 vlan_tci, bool log)
{
const struct nlattr *a;
+ bool out_tnl_port = false;
int rem, err;
if (depth >= SAMPLE_ACTION_DEPTH)
@@ -1626,6 +1765,8 @@ int ovs_nla_copy_actions(const struct nlattr *attr,
[OVS_ACTION_ATTR_OUTPUT] = sizeof(u32),
[OVS_ACTION_ATTR_RECIRC] = sizeof(u32),
[OVS_ACTION_ATTR_USERSPACE] = (u32)-1,
+ [OVS_ACTION_ATTR_PUSH_MPLS] = sizeof(struct ovs_action_push_mpls),
+ [OVS_ACTION_ATTR_POP_MPLS] = sizeof(__be16),
[OVS_ACTION_ATTR_PUSH_VLAN] = sizeof(struct ovs_action_push_vlan),
[OVS_ACTION_ATTR_POP_VLAN] = 0,
[OVS_ACTION_ATTR_SET] = (u32)-1,
@@ -1655,6 +1796,8 @@ int ovs_nla_copy_actions(const struct nlattr *attr,
case OVS_ACTION_ATTR_OUTPUT:
if (nla_get_u32(a) >= DP_MAX_PORTS)
return -EINVAL;
+ out_tnl_port = false;
+
break;
case OVS_ACTION_ATTR_HASH: {
@@ -1671,6 +1814,7 @@ int ovs_nla_copy_actions(const struct nlattr *attr,
}
case OVS_ACTION_ATTR_POP_VLAN:
+ vlan_tci = htons(0);
break;
case OVS_ACTION_ATTR_PUSH_VLAN:
@@ -1679,29 +1823,77 @@ int ovs_nla_copy_actions(const struct nlattr *attr,
return -EINVAL;
if (!(vlan->vlan_tci & htons(VLAN_TAG_PRESENT)))
return -EINVAL;
+ vlan_tci = vlan->vlan_tci;
break;
case OVS_ACTION_ATTR_RECIRC:
break;
+ case OVS_ACTION_ATTR_PUSH_MPLS: {
+ const struct ovs_action_push_mpls *mpls = nla_data(a);
+
+ /* Networking stack do not allow simultaneous Tunnel
+ * and MPLS GSO.
+ */
+ if (out_tnl_port)
+ return -EINVAL;
+
+ if (!eth_p_mpls(mpls->mpls_ethertype))
+ return -EINVAL;
+ /* Prohibit push MPLS other than to a white list
+ * for packets that have a known tag order.
+ */
+ if (vlan_tci & htons(VLAN_TAG_PRESENT) ||
+ (eth_type != htons(ETH_P_IP) &&
+ eth_type != htons(ETH_P_IPV6) &&
+ eth_type != htons(ETH_P_ARP) &&
+ eth_type != htons(ETH_P_RARP) &&
+ !eth_p_mpls(eth_type)))
+ return -EINVAL;
+ eth_type = mpls->mpls_ethertype;
+ break;
+ }
+
+ case OVS_ACTION_ATTR_POP_MPLS:
+ if (vlan_tci & htons(VLAN_TAG_PRESENT) ||
+ !eth_p_mpls(eth_type))
+ return -EINVAL;
+
+ /* Disallow subsequent L2.5+ set and mpls_pop actions
+ * as there is no check here to ensure that the new
+ * eth_type is valid and thus set actions could
+ * write off the end of the packet or otherwise
+ * corrupt it.
+ *
+ * Support for these actions is planned using packet
+ * recirculation.
+ */
+ eth_type = htons(0);
+ break;
+
case OVS_ACTION_ATTR_SET:
- err = validate_set(a, key, sfa, &skip_copy);
+ err = validate_set(a, key, sfa,
+ &out_tnl_port, eth_type, log);
if (err)
return err;
+
+ skip_copy = out_tnl_port;
break;
case OVS_ACTION_ATTR_SAMPLE:
- err = validate_and_copy_sample(a, key, depth, sfa);
+ err = validate_and_copy_sample(a, key, depth, sfa,
+ eth_type, vlan_tci, log);
if (err)
return err;
skip_copy = true;
break;
default:
+ OVS_NLERR(log, "Unknown Action type %d", type);
return -EINVAL;
}
if (!skip_copy) {
- err = copy_action(a, sfa);
+ err = copy_action(a, sfa, log);
if (err)
return err;
}
@@ -1713,6 +1905,24 @@ int ovs_nla_copy_actions(const struct nlattr *attr,
return 0;
}
+int ovs_nla_copy_actions(const struct nlattr *attr,
+ const struct sw_flow_key *key,
+ struct sw_flow_actions **sfa, bool log)
+{
+ int err;
+
+ *sfa = nla_alloc_flow_actions(nla_len(attr), log);
+ if (IS_ERR(*sfa))
+ return PTR_ERR(*sfa);
+
+ err = __ovs_nla_copy_actions(attr, key, 0, sfa, key->eth.type,
+ key->eth.tci, log);
+ if (err)
+ kfree(*sfa);
+
+ return err;
+}
+
static int sample_action_to_attr(const struct nlattr *attr, struct sk_buff *skb)
{
const struct nlattr *a;
diff --git a/net/openvswitch/flow_netlink.h b/net/openvswitch/flow_netlink.h
index 206e45add888..577f12be3459 100644
--- a/net/openvswitch/flow_netlink.h
+++ b/net/openvswitch/flow_netlink.h
@@ -37,24 +37,28 @@
#include "flow.h"
+size_t ovs_tun_key_attr_size(void);
+size_t ovs_key_attr_size(void);
+
void ovs_match_init(struct sw_flow_match *match,
struct sw_flow_key *key, struct sw_flow_mask *mask);
int ovs_nla_put_flow(const struct sw_flow_key *,
const struct sw_flow_key *, struct sk_buff *);
-int ovs_nla_get_flow_metadata(const struct nlattr *, struct sw_flow_key *);
+int ovs_nla_get_flow_metadata(const struct nlattr *, struct sw_flow_key *,
+ bool log);
-int ovs_nla_get_match(struct sw_flow_match *match,
- const struct nlattr *,
- const struct nlattr *);
+int ovs_nla_get_match(struct sw_flow_match *, const struct nlattr *key,
+ const struct nlattr *mask, bool log);
+int ovs_nla_put_egress_tunnel_key(struct sk_buff *,
+ const struct ovs_tunnel_info *);
int ovs_nla_copy_actions(const struct nlattr *attr,
- const struct sw_flow_key *key, int depth,
- struct sw_flow_actions **sfa);
+ const struct sw_flow_key *key,
+ struct sw_flow_actions **sfa, bool log);
int ovs_nla_put_actions(const struct nlattr *attr,
int len, struct sk_buff *skb);
-struct sw_flow_actions *ovs_nla_alloc_flow_actions(int actions_len);
void ovs_nla_free_flow_actions(struct sw_flow_actions *);
#endif /* flow_netlink.h */
diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c
index cf2d853646f0..5899bf161c61 100644
--- a/net/openvswitch/flow_table.c
+++ b/net/openvswitch/flow_table.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007-2013 Nicira, Inc.
+ * Copyright (c) 2007-2014 Nicira, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
@@ -25,7 +25,7 @@
#include <linux/if_vlan.h>
#include <net/llc_pdu.h>
#include <linux/kernel.h>
-#include <linux/hash.h>
+#include <linux/jhash.h>
#include <linux/jiffies.h>
#include <linux/llc.h>
#include <linux/module.h>
@@ -107,7 +107,7 @@ err:
return ERR_PTR(-ENOMEM);
}
-int ovs_flow_tbl_count(struct flow_table *table)
+int ovs_flow_tbl_count(const struct flow_table *table)
{
return table->count;
}
@@ -250,11 +250,14 @@ skip_flows:
__table_instance_destroy(ti);
}
-void ovs_flow_tbl_destroy(struct flow_table *table, bool deferred)
+/* No need for locking this function is called from RCU callback or
+ * error path.
+ */
+void ovs_flow_tbl_destroy(struct flow_table *table)
{
- struct table_instance *ti = ovsl_dereference(table->ti);
+ struct table_instance *ti = rcu_dereference_raw(table->ti);
- table_instance_destroy(ti, deferred);
+ table_instance_destroy(ti, false);
}
struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *ti,
@@ -363,7 +366,7 @@ static u32 flow_hash(const struct sw_flow_key *key, int key_start,
/* Make sure number of hash bytes are multiple of u32. */
BUILD_BUG_ON(sizeof(long) % sizeof(u32));
- return arch_fast_hash2(hash_key, hash_u32s, 0);
+ return jhash2(hash_key, hash_u32s, 0);
}
static int flow_key_start(const struct sw_flow_key *key)
@@ -398,7 +401,7 @@ static bool flow_cmp_masked_key(const struct sw_flow *flow,
}
bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow,
- struct sw_flow_match *match)
+ const struct sw_flow_match *match)
{
struct sw_flow_key *key = match->key;
int key_start = flow_key_start(key);
@@ -409,7 +412,7 @@ bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow,
static struct sw_flow *masked_flow_lookup(struct table_instance *ti,
const struct sw_flow_key *unmasked,
- struct sw_flow_mask *mask)
+ const struct sw_flow_mask *mask)
{
struct sw_flow *flow;
struct hlist_head *head;
@@ -457,7 +460,7 @@ struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *tbl,
}
struct sw_flow *ovs_flow_tbl_lookup_exact(struct flow_table *tbl,
- struct sw_flow_match *match)
+ const struct sw_flow_match *match)
{
struct table_instance *ti = rcu_dereference_ovsl(tbl->ti);
struct sw_flow_mask *mask;
@@ -560,7 +563,7 @@ static struct sw_flow_mask *flow_mask_find(const struct flow_table *tbl,
/* Add 'mask' into the mask list, if it is not already there. */
static int flow_mask_insert(struct flow_table *tbl, struct sw_flow *flow,
- struct sw_flow_mask *new)
+ const struct sw_flow_mask *new)
{
struct sw_flow_mask *mask;
mask = flow_mask_find(tbl, new);
@@ -583,7 +586,7 @@ static int flow_mask_insert(struct flow_table *tbl, struct sw_flow *flow,
/* Must be called with OVS mutex held. */
int ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow,
- struct sw_flow_mask *mask)
+ const struct sw_flow_mask *mask)
{
struct table_instance *new_ti = NULL;
struct table_instance *ti;
diff --git a/net/openvswitch/flow_table.h b/net/openvswitch/flow_table.h
index 5918bff7f3f6..309fa6415689 100644
--- a/net/openvswitch/flow_table.h
+++ b/net/openvswitch/flow_table.h
@@ -61,12 +61,12 @@ struct sw_flow *ovs_flow_alloc(void);
void ovs_flow_free(struct sw_flow *, bool deferred);
int ovs_flow_tbl_init(struct flow_table *);
-int ovs_flow_tbl_count(struct flow_table *table);
-void ovs_flow_tbl_destroy(struct flow_table *table, bool deferred);
+int ovs_flow_tbl_count(const struct flow_table *table);
+void ovs_flow_tbl_destroy(struct flow_table *table);
int ovs_flow_tbl_flush(struct flow_table *flow_table);
int ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow,
- struct sw_flow_mask *mask);
+ const struct sw_flow_mask *mask);
void ovs_flow_tbl_remove(struct flow_table *table, struct sw_flow *flow);
int ovs_flow_tbl_num_masks(const struct flow_table *table);
struct sw_flow *ovs_flow_tbl_dump_next(struct table_instance *table,
@@ -77,9 +77,9 @@ struct sw_flow *ovs_flow_tbl_lookup_stats(struct flow_table *,
struct sw_flow *ovs_flow_tbl_lookup(struct flow_table *,
const struct sw_flow_key *);
struct sw_flow *ovs_flow_tbl_lookup_exact(struct flow_table *tbl,
- struct sw_flow_match *match);
+ const struct sw_flow_match *match);
bool ovs_flow_cmp_unmasked_key(const struct sw_flow *flow,
- struct sw_flow_match *match);
+ const struct sw_flow_match *match);
void ovs_flow_mask_key(struct sw_flow_key *dst, const struct sw_flow_key *src,
const struct sw_flow_mask *mask);
diff --git a/net/openvswitch/vport-geneve.c b/net/openvswitch/vport-geneve.c
index 106a9d80b663..347fa2325b22 100644
--- a/net/openvswitch/vport-geneve.c
+++ b/net/openvswitch/vport-geneve.c
@@ -17,6 +17,7 @@
#include <linux/rculist.h>
#include <linux/udp.h>
#include <linux/if_vlan.h>
+#include <linux/module.h>
#include <net/geneve.h>
#include <net/icmp.h>
@@ -28,6 +29,8 @@
#include "datapath.h"
#include "vport.h"
+static struct vport_ops ovs_geneve_vport_ops;
+
/**
* struct geneve_port - Keeps track of open UDP ports
* @gs: The socket created for this port number.
@@ -65,7 +68,7 @@ static void tunnel_id_to_vni(__be64 tun_id, __u8 *vni)
}
/* Convert 24 bit VNI to 64 bit tunnel ID. */
-static __be64 vni_to_tunnel_id(__u8 *vni)
+static __be64 vni_to_tunnel_id(const __u8 *vni)
{
#ifdef __BIG_ENDIAN
return (vni[0] << 16) | (vni[1] << 8) | vni[2];
@@ -94,7 +97,9 @@ static void geneve_rcv(struct geneve_sock *gs, struct sk_buff *skb)
key = vni_to_tunnel_id(geneveh->vni);
- ovs_flow_tun_info_init(&tun_info, ip_hdr(skb), key, flags,
+ ovs_flow_tun_info_init(&tun_info, ip_hdr(skb),
+ udp_hdr(skb)->source, udp_hdr(skb)->dest,
+ key, flags,
geneveh->options, opts_len);
ovs_vport_receive(vport, skb, &tun_info);
@@ -225,11 +230,46 @@ static const char *geneve_get_name(const struct vport *vport)
return geneve_port->name;
}
-const struct vport_ops ovs_geneve_vport_ops = {
+static int geneve_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
+ struct ovs_tunnel_info *egress_tun_info)
+{
+ struct geneve_port *geneve_port = geneve_vport(vport);
+ struct net *net = ovs_dp_get_net(vport->dp);
+ __be16 dport = inet_sk(geneve_port->gs->sock->sk)->inet_sport;
+ __be16 sport = udp_flow_src_port(net, skb, 1, USHRT_MAX, true);
+
+ /* Get tp_src and tp_dst, refert to geneve_build_header().
+ */
+ return ovs_tunnel_get_egress_info(egress_tun_info,
+ ovs_dp_get_net(vport->dp),
+ OVS_CB(skb)->egress_tun_info,
+ IPPROTO_UDP, skb->mark, sport, dport);
+}
+
+static struct vport_ops ovs_geneve_vport_ops = {
.type = OVS_VPORT_TYPE_GENEVE,
.create = geneve_tnl_create,
.destroy = geneve_tnl_destroy,
.get_name = geneve_get_name,
.get_options = geneve_get_options,
.send = geneve_tnl_send,
+ .owner = THIS_MODULE,
+ .get_egress_tun_info = geneve_get_egress_tun_info,
};
+
+static int __init ovs_geneve_tnl_init(void)
+{
+ return ovs_vport_ops_register(&ovs_geneve_vport_ops);
+}
+
+static void __exit ovs_geneve_tnl_exit(void)
+{
+ ovs_vport_ops_unregister(&ovs_geneve_vport_ops);
+}
+
+module_init(ovs_geneve_tnl_init);
+module_exit(ovs_geneve_tnl_exit);
+
+MODULE_DESCRIPTION("OVS: Geneve swiching port");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("vport-type-5");
diff --git a/net/openvswitch/vport-gre.c b/net/openvswitch/vport-gre.c
index 108b82da2fd9..6b69df545b1d 100644
--- a/net/openvswitch/vport-gre.c
+++ b/net/openvswitch/vport-gre.c
@@ -29,6 +29,7 @@
#include <linux/jhash.h>
#include <linux/list.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/workqueue.h>
#include <linux/rculist.h>
#include <net/route.h>
@@ -45,6 +46,8 @@
#include "datapath.h"
#include "vport.h"
+static struct vport_ops ovs_gre_vport_ops;
+
/* Returns the least-significant 32 bits of a __be64. */
static __be32 be64_get_low32(__be64 x)
{
@@ -105,7 +108,7 @@ static int gre_rcv(struct sk_buff *skb,
return PACKET_REJECT;
key = key_to_tunnel_id(tpi->key, tpi->seq);
- ovs_flow_tun_info_init(&tun_info, ip_hdr(skb), key,
+ ovs_flow_tun_info_init(&tun_info, ip_hdr(skb), 0, 0, key,
filter_tnl_flags(tpi->flags), NULL, 0);
ovs_vport_receive(vport, skb, &tun_info);
@@ -172,14 +175,10 @@ static int gre_tnl_send(struct vport *vport, struct sk_buff *skb)
goto err_free_rt;
}
- if (vlan_tx_tag_present(skb)) {
- if (unlikely(!__vlan_put_tag(skb,
- skb->vlan_proto,
- vlan_tx_tag_get(skb)))) {
- err = -ENOMEM;
- goto err_free_rt;
- }
- skb->vlan_tci = 0;
+ skb = vlan_hwaccel_push_inside(skb);
+ if (unlikely(!skb)) {
+ err = -ENOMEM;
+ goto err_free_rt;
}
/* Push Tunnel header. */
@@ -281,10 +280,38 @@ static void gre_tnl_destroy(struct vport *vport)
gre_exit();
}
-const struct vport_ops ovs_gre_vport_ops = {
+static int gre_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
+ struct ovs_tunnel_info *egress_tun_info)
+{
+ return ovs_tunnel_get_egress_info(egress_tun_info,
+ ovs_dp_get_net(vport->dp),
+ OVS_CB(skb)->egress_tun_info,
+ IPPROTO_GRE, skb->mark, 0, 0);
+}
+
+static struct vport_ops ovs_gre_vport_ops = {
.type = OVS_VPORT_TYPE_GRE,
.create = gre_create,
.destroy = gre_tnl_destroy,
.get_name = gre_get_name,
.send = gre_tnl_send,
+ .get_egress_tun_info = gre_get_egress_tun_info,
+ .owner = THIS_MODULE,
};
+
+static int __init ovs_gre_tnl_init(void)
+{
+ return ovs_vport_ops_register(&ovs_gre_vport_ops);
+}
+
+static void __exit ovs_gre_tnl_exit(void)
+{
+ ovs_vport_ops_unregister(&ovs_gre_vport_ops);
+}
+
+module_init(ovs_gre_tnl_init);
+module_exit(ovs_gre_tnl_exit);
+
+MODULE_DESCRIPTION("OVS: GRE switching port");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("vport-type-3");
diff --git a/net/openvswitch/vport-internal_dev.c b/net/openvswitch/vport-internal_dev.c
index 84516126e5f3..6a55f7105505 100644
--- a/net/openvswitch/vport-internal_dev.c
+++ b/net/openvswitch/vport-internal_dev.c
@@ -36,6 +36,8 @@ struct internal_dev {
struct vport *vport;
};
+static struct vport_ops ovs_internal_vport_ops;
+
static struct internal_dev *internal_dev_priv(struct net_device *netdev)
{
return netdev_priv(netdev);
@@ -222,6 +224,11 @@ static int internal_dev_recv(struct vport *vport, struct sk_buff *skb)
struct net_device *netdev = netdev_vport_priv(vport)->dev;
int len;
+ if (unlikely(!(netdev->flags & IFF_UP))) {
+ kfree_skb(skb);
+ return 0;
+ }
+
len = skb->len;
skb_dst_drop(skb);
@@ -238,7 +245,7 @@ static int internal_dev_recv(struct vport *vport, struct sk_buff *skb)
return len;
}
-const struct vport_ops ovs_internal_vport_ops = {
+static struct vport_ops ovs_internal_vport_ops = {
.type = OVS_VPORT_TYPE_INTERNAL,
.create = internal_dev_create,
.destroy = internal_dev_destroy,
@@ -261,10 +268,21 @@ struct vport *ovs_internal_dev_get_vport(struct net_device *netdev)
int ovs_internal_dev_rtnl_link_register(void)
{
- return rtnl_link_register(&internal_dev_link_ops);
+ int err;
+
+ err = rtnl_link_register(&internal_dev_link_ops);
+ if (err < 0)
+ return err;
+
+ err = ovs_vport_ops_register(&ovs_internal_vport_ops);
+ if (err < 0)
+ rtnl_link_unregister(&internal_dev_link_ops);
+
+ return err;
}
void ovs_internal_dev_rtnl_link_unregister(void)
{
+ ovs_vport_ops_unregister(&ovs_internal_vport_ops);
rtnl_link_unregister(&internal_dev_link_ops);
}
diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c
index d21f77d875ba..4776282c6417 100644
--- a/net/openvswitch/vport-netdev.c
+++ b/net/openvswitch/vport-netdev.c
@@ -33,6 +33,8 @@
#include "vport-internal_dev.h"
#include "vport-netdev.h"
+static struct vport_ops ovs_netdev_vport_ops;
+
/* Must be called with rcu_read_lock. */
static void netdev_port_receive(struct vport *vport, struct sk_buff *skb)
{
@@ -75,7 +77,7 @@ static rx_handler_result_t netdev_frame_hook(struct sk_buff **pskb)
return RX_HANDLER_CONSUMED;
}
-static struct net_device *get_dpdev(struct datapath *dp)
+static struct net_device *get_dpdev(const struct datapath *dp)
{
struct vport *local;
@@ -224,10 +226,20 @@ struct vport *ovs_netdev_get_vport(struct net_device *dev)
return NULL;
}
-const struct vport_ops ovs_netdev_vport_ops = {
+static struct vport_ops ovs_netdev_vport_ops = {
.type = OVS_VPORT_TYPE_NETDEV,
.create = netdev_create,
.destroy = netdev_destroy,
.get_name = ovs_netdev_get_name,
.send = netdev_send,
};
+
+int __init ovs_netdev_init(void)
+{
+ return ovs_vport_ops_register(&ovs_netdev_vport_ops);
+}
+
+void ovs_netdev_exit(void)
+{
+ ovs_vport_ops_unregister(&ovs_netdev_vport_ops);
+}
diff --git a/net/openvswitch/vport-netdev.h b/net/openvswitch/vport-netdev.h
index 8df01c1127e5..6f7038e79c52 100644
--- a/net/openvswitch/vport-netdev.h
+++ b/net/openvswitch/vport-netdev.h
@@ -41,4 +41,7 @@ netdev_vport_priv(const struct vport *vport)
const char *ovs_netdev_get_name(const struct vport *);
void ovs_netdev_detach_dev(struct vport *);
+int __init ovs_netdev_init(void);
+void ovs_netdev_exit(void);
+
#endif /* vport_netdev.h */
diff --git a/net/openvswitch/vport-vxlan.c b/net/openvswitch/vport-vxlan.c
index 2735e01dca73..38f95a52241b 100644
--- a/net/openvswitch/vport-vxlan.c
+++ b/net/openvswitch/vport-vxlan.c
@@ -24,6 +24,7 @@
#include <linux/net.h>
#include <linux/rculist.h>
#include <linux/udp.h>
+#include <linux/module.h>
#include <net/icmp.h>
#include <net/ip.h>
@@ -50,6 +51,8 @@ struct vxlan_port {
char name[IFNAMSIZ];
};
+static struct vport_ops ovs_vxlan_vport_ops;
+
static inline struct vxlan_port *vxlan_vport(const struct vport *vport)
{
return vport_priv(vport);
@@ -66,7 +69,9 @@ static void vxlan_rcv(struct vxlan_sock *vs, struct sk_buff *skb, __be32 vx_vni)
/* Save outer tunnel values */
iph = ip_hdr(skb);
key = cpu_to_be64(ntohl(vx_vni) >> 8);
- ovs_flow_tun_info_init(&tun_info, iph, key, TUNNEL_KEY, NULL, 0);
+ ovs_flow_tun_info_init(&tun_info, iph,
+ udp_hdr(skb)->source, udp_hdr(skb)->dest,
+ key, TUNNEL_KEY, NULL, 0);
ovs_vport_receive(vport, skb, &tun_info);
}
@@ -186,17 +191,55 @@ error:
return err;
}
+static int vxlan_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
+ struct ovs_tunnel_info *egress_tun_info)
+{
+ struct net *net = ovs_dp_get_net(vport->dp);
+ struct vxlan_port *vxlan_port = vxlan_vport(vport);
+ __be16 dst_port = inet_sk(vxlan_port->vs->sock->sk)->inet_sport;
+ __be16 src_port;
+ int port_min;
+ int port_max;
+
+ inet_get_local_port_range(net, &port_min, &port_max);
+ src_port = udp_flow_src_port(net, skb, 0, 0, true);
+
+ return ovs_tunnel_get_egress_info(egress_tun_info, net,
+ OVS_CB(skb)->egress_tun_info,
+ IPPROTO_UDP, skb->mark,
+ src_port, dst_port);
+}
+
static const char *vxlan_get_name(const struct vport *vport)
{
struct vxlan_port *vxlan_port = vxlan_vport(vport);
return vxlan_port->name;
}
-const struct vport_ops ovs_vxlan_vport_ops = {
+static struct vport_ops ovs_vxlan_vport_ops = {
.type = OVS_VPORT_TYPE_VXLAN,
.create = vxlan_tnl_create,
.destroy = vxlan_tnl_destroy,
.get_name = vxlan_get_name,
.get_options = vxlan_get_options,
.send = vxlan_tnl_send,
+ .get_egress_tun_info = vxlan_get_egress_tun_info,
+ .owner = THIS_MODULE,
};
+
+static int __init ovs_vxlan_tnl_init(void)
+{
+ return ovs_vport_ops_register(&ovs_vxlan_vport_ops);
+}
+
+static void __exit ovs_vxlan_tnl_exit(void)
+{
+ ovs_vport_ops_unregister(&ovs_vxlan_vport_ops);
+}
+
+module_init(ovs_vxlan_tnl_init);
+module_exit(ovs_vxlan_tnl_exit);
+
+MODULE_DESCRIPTION("OVS: VXLAN switching port");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("vport-type-4");
diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c
index 6015802ebe6f..9584526c0778 100644
--- a/net/openvswitch/vport.c
+++ b/net/openvswitch/vport.c
@@ -28,6 +28,7 @@
#include <linux/rtnetlink.h>
#include <linux/compat.h>
#include <net/net_namespace.h>
+#include <linux/module.h>
#include "datapath.h"
#include "vport.h"
@@ -36,22 +37,7 @@
static void ovs_vport_record_error(struct vport *,
enum vport_err_type err_type);
-/* List of statically compiled vport implementations. Don't forget to also
- * add yours to the list at the bottom of vport.h. */
-static const struct vport_ops *vport_ops_list[] = {
- &ovs_netdev_vport_ops,
- &ovs_internal_vport_ops,
-
-#ifdef CONFIG_OPENVSWITCH_GRE
- &ovs_gre_vport_ops,
-#endif
-#ifdef CONFIG_OPENVSWITCH_VXLAN
- &ovs_vxlan_vport_ops,
-#endif
-#ifdef CONFIG_OPENVSWITCH_GENEVE
- &ovs_geneve_vport_ops,
-#endif
-};
+static LIST_HEAD(vport_ops_list);
/* Protected by RCU read lock for reading, ovs_mutex for writing. */
static struct hlist_head *dev_table;
@@ -82,12 +68,38 @@ void ovs_vport_exit(void)
kfree(dev_table);
}
-static struct hlist_head *hash_bucket(struct net *net, const char *name)
+static struct hlist_head *hash_bucket(const struct net *net, const char *name)
{
unsigned int hash = jhash(name, strlen(name), (unsigned long) net);
return &dev_table[hash & (VPORT_HASH_BUCKETS - 1)];
}
+int ovs_vport_ops_register(struct vport_ops *ops)
+{
+ int err = -EEXIST;
+ struct vport_ops *o;
+
+ ovs_lock();
+ list_for_each_entry(o, &vport_ops_list, list)
+ if (ops->type == o->type)
+ goto errout;
+
+ list_add_tail(&ops->list, &vport_ops_list);
+ err = 0;
+errout:
+ ovs_unlock();
+ return err;
+}
+EXPORT_SYMBOL_GPL(ovs_vport_ops_register);
+
+void ovs_vport_ops_unregister(struct vport_ops *ops)
+{
+ ovs_lock();
+ list_del(&ops->list);
+ ovs_unlock();
+}
+EXPORT_SYMBOL_GPL(ovs_vport_ops_unregister);
+
/**
* ovs_vport_locate - find a port that has already been created
*
@@ -95,7 +107,7 @@ static struct hlist_head *hash_bucket(struct net *net, const char *name)
*
* Must be called with ovs or RCU read lock.
*/
-struct vport *ovs_vport_locate(struct net *net, const char *name)
+struct vport *ovs_vport_locate(const struct net *net, const char *name)
{
struct hlist_head *bucket = hash_bucket(net, name);
struct vport *vport;
@@ -153,6 +165,7 @@ struct vport *ovs_vport_alloc(int priv_size, const struct vport_ops *ops,
return vport;
}
+EXPORT_SYMBOL_GPL(ovs_vport_alloc);
/**
* ovs_vport_free - uninitialize and free vport
@@ -173,6 +186,18 @@ void ovs_vport_free(struct vport *vport)
free_percpu(vport->percpu_stats);
kfree(vport);
}
+EXPORT_SYMBOL_GPL(ovs_vport_free);
+
+static struct vport_ops *ovs_vport_lookup(const struct vport_parms *parms)
+{
+ struct vport_ops *ops;
+
+ list_for_each_entry(ops, &vport_ops_list, list)
+ if (ops->type == parms->type)
+ return ops;
+
+ return NULL;
+}
/**
* ovs_vport_add - add vport device (for kernel callers)
@@ -184,31 +209,40 @@ void ovs_vport_free(struct vport *vport)
*/
struct vport *ovs_vport_add(const struct vport_parms *parms)
{
+ struct vport_ops *ops;
struct vport *vport;
- int err = 0;
- int i;
- for (i = 0; i < ARRAY_SIZE(vport_ops_list); i++) {
- if (vport_ops_list[i]->type == parms->type) {
- struct hlist_head *bucket;
+ ops = ovs_vport_lookup(parms);
+ if (ops) {
+ struct hlist_head *bucket;
- vport = vport_ops_list[i]->create(parms);
- if (IS_ERR(vport)) {
- err = PTR_ERR(vport);
- goto out;
- }
+ if (!try_module_get(ops->owner))
+ return ERR_PTR(-EAFNOSUPPORT);
- bucket = hash_bucket(ovs_dp_get_net(vport->dp),
- vport->ops->get_name(vport));
- hlist_add_head_rcu(&vport->hash_node, bucket);
+ vport = ops->create(parms);
+ if (IS_ERR(vport)) {
+ module_put(ops->owner);
return vport;
}
+
+ bucket = hash_bucket(ovs_dp_get_net(vport->dp),
+ vport->ops->get_name(vport));
+ hlist_add_head_rcu(&vport->hash_node, bucket);
+ return vport;
}
- err = -EAFNOSUPPORT;
+ /* Unlock to attempt module load and return -EAGAIN if load
+ * was successful as we need to restart the port addition
+ * workflow.
+ */
+ ovs_unlock();
+ request_module("vport-type-%d", parms->type);
+ ovs_lock();
-out:
- return ERR_PTR(err);
+ if (!ovs_vport_lookup(parms))
+ return ERR_PTR(-EAFNOSUPPORT);
+ else
+ return ERR_PTR(-EAGAIN);
}
/**
@@ -242,6 +276,8 @@ void ovs_vport_del(struct vport *vport)
hlist_del_rcu(&vport->hash_node);
vport->ops->destroy(vport);
+
+ module_put(vport->ops->owner);
}
/**
@@ -344,7 +380,7 @@ int ovs_vport_get_options(const struct vport *vport, struct sk_buff *skb)
*
* Must be called with ovs_mutex.
*/
-int ovs_vport_set_upcall_portids(struct vport *vport, struct nlattr *ids)
+int ovs_vport_set_upcall_portids(struct vport *vport, const struct nlattr *ids)
{
struct vport_portids *old, *vport_portids;
@@ -435,7 +471,7 @@ u32 ovs_vport_find_upcall_portid(const struct vport *vport, struct sk_buff *skb)
* skb->data should point to the Ethernet header.
*/
void ovs_vport_receive(struct vport *vport, struct sk_buff *skb,
- struct ovs_tunnel_info *tun_info)
+ const struct ovs_tunnel_info *tun_info)
{
struct pcpu_sw_netstats *stats;
struct sw_flow_key key;
@@ -457,6 +493,7 @@ void ovs_vport_receive(struct vport *vport, struct sk_buff *skb,
}
ovs_dp_process_packet(skb, &key);
}
+EXPORT_SYMBOL_GPL(ovs_vport_receive);
/**
* ovs_vport_send - send a packet on a device
@@ -535,3 +572,65 @@ void ovs_vport_deferred_free(struct vport *vport)
call_rcu(&vport->rcu, free_vport_rcu);
}
+EXPORT_SYMBOL_GPL(ovs_vport_deferred_free);
+
+int ovs_tunnel_get_egress_info(struct ovs_tunnel_info *egress_tun_info,
+ struct net *net,
+ const struct ovs_tunnel_info *tun_info,
+ u8 ipproto,
+ u32 skb_mark,
+ __be16 tp_src,
+ __be16 tp_dst)
+{
+ const struct ovs_key_ipv4_tunnel *tun_key;
+ struct rtable *rt;
+ struct flowi4 fl;
+
+ if (unlikely(!tun_info))
+ return -EINVAL;
+
+ tun_key = &tun_info->tunnel;
+
+ /* Route lookup to get srouce IP address.
+ * The process may need to be changed if the corresponding process
+ * in vports ops changed.
+ */
+ memset(&fl, 0, sizeof(fl));
+ fl.daddr = tun_key->ipv4_dst;
+ fl.saddr = tun_key->ipv4_src;
+ fl.flowi4_tos = RT_TOS(tun_key->ipv4_tos);
+ fl.flowi4_mark = skb_mark;
+ fl.flowi4_proto = ipproto;
+
+ rt = ip_route_output_key(net, &fl);
+ if (IS_ERR(rt))
+ return PTR_ERR(rt);
+
+ ip_rt_put(rt);
+
+ /* Generate egress_tun_info based on tun_info,
+ * saddr, tp_src and tp_dst
+ */
+ __ovs_flow_tun_info_init(egress_tun_info,
+ fl.saddr, tun_key->ipv4_dst,
+ tun_key->ipv4_tos,
+ tun_key->ipv4_ttl,
+ tp_src, tp_dst,
+ tun_key->tun_id,
+ tun_key->tun_flags,
+ tun_info->options,
+ tun_info->options_len);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ovs_tunnel_get_egress_info);
+
+int ovs_vport_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
+ struct ovs_tunnel_info *info)
+{
+ /* get_egress_tun_info() is only implemented on tunnel ports. */
+ if (unlikely(!vport->ops->get_egress_tun_info))
+ return -EINVAL;
+
+ return vport->ops->get_egress_tun_info(vport, skb, info);
+}
diff --git a/net/openvswitch/vport.h b/net/openvswitch/vport.h
index 8942125de3a6..99c8e71d9e6c 100644
--- a/net/openvswitch/vport.h
+++ b/net/openvswitch/vport.h
@@ -45,19 +45,29 @@ void ovs_vport_exit(void);
struct vport *ovs_vport_add(const struct vport_parms *);
void ovs_vport_del(struct vport *);
-struct vport *ovs_vport_locate(struct net *net, const char *name);
+struct vport *ovs_vport_locate(const struct net *net, const char *name);
void ovs_vport_get_stats(struct vport *, struct ovs_vport_stats *);
int ovs_vport_set_options(struct vport *, struct nlattr *options);
int ovs_vport_get_options(const struct vport *, struct sk_buff *);
-int ovs_vport_set_upcall_portids(struct vport *, struct nlattr *pids);
+int ovs_vport_set_upcall_portids(struct vport *, const struct nlattr *pids);
int ovs_vport_get_upcall_portids(const struct vport *, struct sk_buff *);
u32 ovs_vport_find_upcall_portid(const struct vport *, struct sk_buff *);
int ovs_vport_send(struct vport *, struct sk_buff *);
+int ovs_tunnel_get_egress_info(struct ovs_tunnel_info *egress_tun_info,
+ struct net *net,
+ const struct ovs_tunnel_info *tun_info,
+ u8 ipproto,
+ u32 skb_mark,
+ __be16 tp_src,
+ __be16 tp_dst);
+int ovs_vport_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
+ struct ovs_tunnel_info *info);
+
/* The following definitions are for implementers of vport devices: */
struct vport_err_stats {
@@ -146,6 +156,8 @@ struct vport_parms {
* @get_name: Get the device's name.
* @send: Send a packet on the device. Returns the length of the packet sent,
* zero for dropped packets or negative for error.
+ * @get_egress_tun_info: Get the egress tunnel 5-tuple and other info for
+ * a packet.
*/
struct vport_ops {
enum ovs_vport_type type;
@@ -161,6 +173,11 @@ struct vport_ops {
const char *(*get_name)(const struct vport *);
int (*send)(struct vport *, struct sk_buff *);
+ int (*get_egress_tun_info)(struct vport *, struct sk_buff *,
+ struct ovs_tunnel_info *);
+
+ struct module *owner;
+ struct list_head list;
};
enum vport_err_type {
@@ -207,15 +224,7 @@ static inline struct vport *vport_from_priv(void *priv)
}
void ovs_vport_receive(struct vport *, struct sk_buff *,
- struct ovs_tunnel_info *);
-
-/* List of statically compiled vport implementations. Don't forget to also
- * add yours to the list at the top of vport.c. */
-extern const struct vport_ops ovs_netdev_vport_ops;
-extern const struct vport_ops ovs_internal_vport_ops;
-extern const struct vport_ops ovs_gre_vport_ops;
-extern const struct vport_ops ovs_vxlan_vport_ops;
-extern const struct vport_ops ovs_geneve_vport_ops;
+ const struct ovs_tunnel_info *);
static inline void ovs_skb_postpush_rcsum(struct sk_buff *skb,
const void *start, unsigned int len)
@@ -224,4 +233,7 @@ static inline void ovs_skb_postpush_rcsum(struct sk_buff *skb,
skb->csum = csum_add(skb->csum, csum_partial(start, len, 0));
}
+int ovs_vport_ops_register(struct vport_ops *ops);
+void ovs_vport_ops_unregister(struct vport_ops *ops);
+
#endif /* vport.h */
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 87d20f48ff06..e52a44785681 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -378,7 +378,7 @@ static void unregister_prot_hook(struct sock *sk, bool sync)
__unregister_prot_hook(sk, sync);
}
-static inline __pure struct page *pgv_to_page(void *addr)
+static inline struct page * __pure pgv_to_page(void *addr)
{
if (is_vmalloc_addr(addr))
return vmalloc_to_page(addr);
@@ -1676,7 +1676,7 @@ retry:
if (len < hhlen)
skb_reset_network_header(skb);
}
- err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
+ err = memcpy_from_msg(skb_put(skb, len), msg, len);
if (err)
goto out_free;
goto retry;
@@ -2095,6 +2095,18 @@ static void tpacket_destruct_skb(struct sk_buff *skb)
sock_wfree(skb);
}
+static bool ll_header_truncated(const struct net_device *dev, int len)
+{
+ /* net device doesn't like empty head */
+ if (unlikely(len <= dev->hard_header_len)) {
+ net_warn_ratelimited("%s: packet size is too short (%d < %d)\n",
+ current->comm, len, dev->hard_header_len);
+ return true;
+ }
+
+ return false;
+}
+
static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
void *frame, struct net_device *dev, int size_max,
__be16 proto, unsigned char *addr, int hlen)
@@ -2170,12 +2182,8 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
if (unlikely(err < 0))
return -EINVAL;
} else if (dev->hard_header_len) {
- /* net device doesn't like empty head */
- if (unlikely(tp_len <= dev->hard_header_len)) {
- pr_err("packet size is too short (%d < %d)\n",
- tp_len, dev->hard_header_len);
+ if (ll_header_truncated(dev, tp_len))
return -EINVAL;
- }
skb_push(skb, dev->hard_header_len);
err = skb_store_bits(skb, 0, data,
@@ -2400,6 +2408,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
unsigned short gso_type = 0;
int hlen, tlen;
int extra_len = 0;
+ ssize_t n;
/*
* Get and verify the address.
@@ -2438,19 +2447,21 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
len -= vnet_hdr_len;
- err = memcpy_fromiovec((void *)&vnet_hdr, msg->msg_iov,
- vnet_hdr_len);
- if (err < 0)
+ err = -EFAULT;
+ n = copy_from_iter(&vnet_hdr, vnet_hdr_len, &msg->msg_iter);
+ if (n != vnet_hdr_len)
goto out_unlock;
if ((vnet_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) &&
- (vnet_hdr.csum_start + vnet_hdr.csum_offset + 2 >
- vnet_hdr.hdr_len))
- vnet_hdr.hdr_len = vnet_hdr.csum_start +
- vnet_hdr.csum_offset + 2;
+ (__virtio16_to_cpu(false, vnet_hdr.csum_start) +
+ __virtio16_to_cpu(false, vnet_hdr.csum_offset) + 2 >
+ __virtio16_to_cpu(false, vnet_hdr.hdr_len)))
+ vnet_hdr.hdr_len = __cpu_to_virtio16(false,
+ __virtio16_to_cpu(false, vnet_hdr.csum_start) +
+ __virtio16_to_cpu(false, vnet_hdr.csum_offset) + 2);
err = -EINVAL;
- if (vnet_hdr.hdr_len > len)
+ if (__virtio16_to_cpu(false, vnet_hdr.hdr_len) > len)
goto out_unlock;
if (vnet_hdr.gso_type != VIRTIO_NET_HDR_GSO_NONE) {
@@ -2492,7 +2503,8 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
err = -ENOBUFS;
hlen = LL_RESERVED_SPACE(dev);
tlen = dev->needed_tailroom;
- skb = packet_alloc_skb(sk, hlen + tlen, hlen, len, vnet_hdr.hdr_len,
+ skb = packet_alloc_skb(sk, hlen + tlen, hlen, len,
+ __virtio16_to_cpu(false, vnet_hdr.hdr_len),
msg->msg_flags & MSG_DONTWAIT, &err);
if (skb == NULL)
goto out_unlock;
@@ -2500,12 +2512,17 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
skb_set_network_header(skb, reserve);
err = -EINVAL;
- if (sock->type == SOCK_DGRAM &&
- (offset = dev_hard_header(skb, dev, ntohs(proto), addr, NULL, len)) < 0)
- goto out_free;
+ if (sock->type == SOCK_DGRAM) {
+ offset = dev_hard_header(skb, dev, ntohs(proto), addr, NULL, len);
+ if (unlikely(offset) < 0)
+ goto out_free;
+ } else {
+ if (ll_header_truncated(dev, len))
+ goto out_free;
+ }
/* Returns -EFAULT on error */
- err = skb_copy_datagram_from_iovec(skb, offset, msg->msg_iov, 0, len);
+ err = skb_copy_datagram_from_iter(skb, offset, &msg->msg_iter, len);
if (err)
goto out_free;
@@ -2534,14 +2551,16 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
if (po->has_vnet_hdr) {
if (vnet_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
- if (!skb_partial_csum_set(skb, vnet_hdr.csum_start,
- vnet_hdr.csum_offset)) {
+ u16 s = __virtio16_to_cpu(false, vnet_hdr.csum_start);
+ u16 o = __virtio16_to_cpu(false, vnet_hdr.csum_offset);
+ if (!skb_partial_csum_set(skb, s, o)) {
err = -EINVAL;
goto out_free;
}
}
- skb_shinfo(skb)->gso_size = vnet_hdr.gso_size;
+ skb_shinfo(skb)->gso_size =
+ __virtio16_to_cpu(false, vnet_hdr.gso_size);
skb_shinfo(skb)->gso_type = gso_type;
/* Header must be checked, and gso_segs computed. */
@@ -2912,8 +2931,10 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,
struct skb_shared_info *sinfo = skb_shinfo(skb);
/* This is a hint as to how much should be linear. */
- vnet_hdr.hdr_len = skb_headlen(skb);
- vnet_hdr.gso_size = sinfo->gso_size;
+ vnet_hdr.hdr_len =
+ __cpu_to_virtio16(false, skb_headlen(skb));
+ vnet_hdr.gso_size =
+ __cpu_to_virtio16(false, sinfo->gso_size);
if (sinfo->gso_type & SKB_GSO_TCPV4)
vnet_hdr.gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
else if (sinfo->gso_type & SKB_GSO_TCPV6)
@@ -2931,14 +2952,15 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,
if (skb->ip_summed == CHECKSUM_PARTIAL) {
vnet_hdr.flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
- vnet_hdr.csum_start = skb_checksum_start_offset(skb);
- vnet_hdr.csum_offset = skb->csum_offset;
+ vnet_hdr.csum_start = __cpu_to_virtio16(false,
+ skb_checksum_start_offset(skb));
+ vnet_hdr.csum_offset = __cpu_to_virtio16(false,
+ skb->csum_offset);
} else if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
vnet_hdr.flags = VIRTIO_NET_HDR_F_DATA_VALID;
} /* else everything is zero */
- err = memcpy_toiovec(msg->msg_iov, (void *)&vnet_hdr,
- vnet_hdr_len);
+ err = memcpy_to_msg(msg, (void *)&vnet_hdr, vnet_hdr_len);
if (err < 0)
goto out_free;
}
@@ -2953,7 +2975,7 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,
msg->msg_flags |= MSG_TRUNC;
}
- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
if (err)
goto out_free;
diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c
index 5a940dbd74a3..32ab87d34828 100644
--- a/net/phonet/af_phonet.c
+++ b/net/phonet/af_phonet.c
@@ -426,16 +426,17 @@ static int phonet_rcv(struct sk_buff *skb, struct net_device *dev,
out_dev = phonet_route_output(net, pn_sockaddr_get_addr(&sa));
if (!out_dev) {
- LIMIT_NETDEBUG(KERN_WARNING"No Phonet route to %02X\n",
- pn_sockaddr_get_addr(&sa));
+ net_dbg_ratelimited("No Phonet route to %02X\n",
+ pn_sockaddr_get_addr(&sa));
goto out;
}
__skb_push(skb, sizeof(struct phonethdr));
skb->dev = out_dev;
if (out_dev == dev) {
- LIMIT_NETDEBUG(KERN_ERR"Phonet loop to %02X on %s\n",
- pn_sockaddr_get_addr(&sa), dev->name);
+ net_dbg_ratelimited("Phonet loop to %02X on %s\n",
+ pn_sockaddr_get_addr(&sa),
+ dev->name);
goto out_dev;
}
/* Some drivers (e.g. TUN) do not allocate HW header space */
diff --git a/net/phonet/datagram.c b/net/phonet/datagram.c
index 290352c0e6b4..26054b4b467c 100644
--- a/net/phonet/datagram.c
+++ b/net/phonet/datagram.c
@@ -109,7 +109,7 @@ static int pn_sendmsg(struct kiocb *iocb, struct sock *sk,
return err;
skb_reserve(skb, MAX_PHONET_HEADER);
- err = memcpy_fromiovec((void *)skb_put(skb, len), msg->msg_iov, len);
+ err = memcpy_from_msg((void *)skb_put(skb, len), msg, len);
if (err < 0) {
kfree_skb(skb);
return err;
@@ -150,7 +150,7 @@ static int pn_recvmsg(struct kiocb *iocb, struct sock *sk,
copylen = len;
}
- rval = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copylen);
+ rval = skb_copy_datagram_msg(skb, 0, msg, copylen);
if (rval) {
rval = -EFAULT;
goto out;
diff --git a/net/phonet/pep-gprs.c b/net/phonet/pep-gprs.c
index e9a83a637185..fa8237fdc57b 100644
--- a/net/phonet/pep-gprs.c
+++ b/net/phonet/pep-gprs.c
@@ -203,8 +203,7 @@ static netdev_tx_t gprs_xmit(struct sk_buff *skb, struct net_device *dev)
len = skb->len;
err = pep_write(sk, skb);
if (err) {
- LIMIT_NETDEBUG(KERN_WARNING"%s: TX error (%d)\n",
- dev->name, err);
+ net_dbg_ratelimited("%s: TX error (%d)\n", dev->name, err);
dev->stats.tx_aborted_errors++;
dev->stats.tx_errors++;
} else {
diff --git a/net/phonet/pep.c b/net/phonet/pep.c
index 70a547ea5177..5d3f2b7507d4 100644
--- a/net/phonet/pep.c
+++ b/net/phonet/pep.c
@@ -272,8 +272,8 @@ static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb)
hdr = pnp_hdr(skb);
if (hdr->data[0] != PN_PEP_TYPE_COMMON) {
- LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP type: %u\n",
- (unsigned int)hdr->data[0]);
+ net_dbg_ratelimited("Phonet unknown PEP type: %u\n",
+ (unsigned int)hdr->data[0]);
return -EOPNOTSUPP;
}
@@ -304,8 +304,8 @@ static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb)
break;
default:
- LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP indication: %u\n",
- (unsigned int)hdr->data[1]);
+ net_dbg_ratelimited("Phonet unknown PEP indication: %u\n",
+ (unsigned int)hdr->data[1]);
return -EOPNOTSUPP;
}
if (wake)
@@ -451,8 +451,8 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb)
break;
default:
- LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP message: %u\n",
- hdr->message_id);
+ net_dbg_ratelimited("Phonet unknown PEP message: %u\n",
+ hdr->message_id);
err = -EINVAL;
}
out:
@@ -1141,7 +1141,7 @@ static int pep_sendmsg(struct kiocb *iocb, struct sock *sk,
return err;
skb_reserve(skb, MAX_PHONET_HEADER + 3 + pn->aligned);
- err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
+ err = memcpy_from_msg(skb_put(skb, len), msg, len);
if (err < 0)
goto outfree;
@@ -1296,7 +1296,7 @@ copy:
else
len = skb->len;
- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len);
+ err = skb_copy_datagram_msg(skb, 0, msg, len);
if (!err)
err = (flags & MSG_TRUNC) ? skb->len : len;
diff --git a/net/rds/ib.h b/net/rds/ib.h
index 7280ab8810c2..c36d713229e0 100644
--- a/net/rds/ib.h
+++ b/net/rds/ib.h
@@ -316,8 +316,7 @@ int rds_ib_recv_alloc_caches(struct rds_ib_connection *ic);
void rds_ib_recv_free_caches(struct rds_ib_connection *ic);
void rds_ib_recv_refill(struct rds_connection *conn, int prefill);
void rds_ib_inc_free(struct rds_incoming *inc);
-int rds_ib_inc_copy_to_user(struct rds_incoming *inc, struct iovec *iov,
- size_t size);
+int rds_ib_inc_copy_to_user(struct rds_incoming *inc, struct iov_iter *to);
void rds_ib_recv_cq_comp_handler(struct ib_cq *cq, void *context);
void rds_ib_recv_tasklet_fn(unsigned long data);
void rds_ib_recv_init_ring(struct rds_ib_connection *ic);
diff --git a/net/rds/ib_recv.c b/net/rds/ib_recv.c
index d67de453c35a..1b981a4e42c2 100644
--- a/net/rds/ib_recv.c
+++ b/net/rds/ib_recv.c
@@ -472,15 +472,12 @@ static struct list_head *rds_ib_recv_cache_get(struct rds_ib_refill_cache *cache
return head;
}
-int rds_ib_inc_copy_to_user(struct rds_incoming *inc, struct iovec *first_iov,
- size_t size)
+int rds_ib_inc_copy_to_user(struct rds_incoming *inc, struct iov_iter *to)
{
struct rds_ib_incoming *ibinc;
struct rds_page_frag *frag;
- struct iovec *iov = first_iov;
unsigned long to_copy;
unsigned long frag_off = 0;
- unsigned long iov_off = 0;
int copied = 0;
int ret;
u32 len;
@@ -489,37 +486,25 @@ int rds_ib_inc_copy_to_user(struct rds_incoming *inc, struct iovec *first_iov,
frag = list_entry(ibinc->ii_frags.next, struct rds_page_frag, f_item);
len = be32_to_cpu(inc->i_hdr.h_len);
- while (copied < size && copied < len) {
+ while (iov_iter_count(to) && copied < len) {
if (frag_off == RDS_FRAG_SIZE) {
frag = list_entry(frag->f_item.next,
struct rds_page_frag, f_item);
frag_off = 0;
}
- while (iov_off == iov->iov_len) {
- iov_off = 0;
- iov++;
- }
-
- to_copy = min(iov->iov_len - iov_off, RDS_FRAG_SIZE - frag_off);
- to_copy = min_t(size_t, to_copy, size - copied);
+ to_copy = min_t(unsigned long, iov_iter_count(to),
+ RDS_FRAG_SIZE - frag_off);
to_copy = min_t(unsigned long, to_copy, len - copied);
- rdsdebug("%lu bytes to user [%p, %zu] + %lu from frag "
- "[%p, %u] + %lu\n",
- to_copy, iov->iov_base, iov->iov_len, iov_off,
- sg_page(&frag->f_sg), frag->f_sg.offset, frag_off);
-
/* XXX needs + offset for multiple recvs per page */
- ret = rds_page_copy_to_user(sg_page(&frag->f_sg),
- frag->f_sg.offset + frag_off,
- iov->iov_base + iov_off,
- to_copy);
- if (ret) {
- copied = ret;
- break;
- }
+ rds_stats_add(s_copy_to_user, to_copy);
+ ret = copy_page_to_iter(sg_page(&frag->f_sg),
+ frag->f_sg.offset + frag_off,
+ to_copy,
+ to);
+ if (ret != to_copy)
+ return -EFAULT;
- iov_off += to_copy;
frag_off += to_copy;
copied += to_copy;
}
diff --git a/net/rds/iw.h b/net/rds/iw.h
index 04ce3b193f79..cbe6674e31ee 100644
--- a/net/rds/iw.h
+++ b/net/rds/iw.h
@@ -325,8 +325,7 @@ int rds_iw_recv(struct rds_connection *conn);
int rds_iw_recv_refill(struct rds_connection *conn, gfp_t kptr_gfp,
gfp_t page_gfp, int prefill);
void rds_iw_inc_free(struct rds_incoming *inc);
-int rds_iw_inc_copy_to_user(struct rds_incoming *inc, struct iovec *iov,
- size_t size);
+int rds_iw_inc_copy_to_user(struct rds_incoming *inc, struct iov_iter *to);
void rds_iw_recv_cq_comp_handler(struct ib_cq *cq, void *context);
void rds_iw_recv_tasklet_fn(unsigned long data);
void rds_iw_recv_init_ring(struct rds_iw_connection *ic);
diff --git a/net/rds/iw_recv.c b/net/rds/iw_recv.c
index aa8bf6786008..a66d1794b2d0 100644
--- a/net/rds/iw_recv.c
+++ b/net/rds/iw_recv.c
@@ -303,15 +303,12 @@ void rds_iw_inc_free(struct rds_incoming *inc)
BUG_ON(atomic_read(&rds_iw_allocation) < 0);
}
-int rds_iw_inc_copy_to_user(struct rds_incoming *inc, struct iovec *first_iov,
- size_t size)
+int rds_iw_inc_copy_to_user(struct rds_incoming *inc, struct iov_iter *to)
{
struct rds_iw_incoming *iwinc;
struct rds_page_frag *frag;
- struct iovec *iov = first_iov;
unsigned long to_copy;
unsigned long frag_off = 0;
- unsigned long iov_off = 0;
int copied = 0;
int ret;
u32 len;
@@ -320,37 +317,25 @@ int rds_iw_inc_copy_to_user(struct rds_incoming *inc, struct iovec *first_iov,
frag = list_entry(iwinc->ii_frags.next, struct rds_page_frag, f_item);
len = be32_to_cpu(inc->i_hdr.h_len);
- while (copied < size && copied < len) {
+ while (iov_iter_count(to) && copied < len) {
if (frag_off == RDS_FRAG_SIZE) {
frag = list_entry(frag->f_item.next,
struct rds_page_frag, f_item);
frag_off = 0;
}
- while (iov_off == iov->iov_len) {
- iov_off = 0;
- iov++;
- }
-
- to_copy = min(iov->iov_len - iov_off, RDS_FRAG_SIZE - frag_off);
- to_copy = min_t(size_t, to_copy, size - copied);
+ to_copy = min_t(unsigned long, iov_iter_count(to),
+ RDS_FRAG_SIZE - frag_off);
to_copy = min_t(unsigned long, to_copy, len - copied);
- rdsdebug("%lu bytes to user [%p, %zu] + %lu from frag "
- "[%p, %lu] + %lu\n",
- to_copy, iov->iov_base, iov->iov_len, iov_off,
- frag->f_page, frag->f_offset, frag_off);
-
/* XXX needs + offset for multiple recvs per page */
- ret = rds_page_copy_to_user(frag->f_page,
- frag->f_offset + frag_off,
- iov->iov_base + iov_off,
- to_copy);
- if (ret) {
- copied = ret;
- break;
- }
+ rds_stats_add(s_copy_to_user, to_copy);
+ ret = copy_page_to_iter(frag->f_page,
+ frag->f_offset + frag_off,
+ to_copy,
+ to);
+ if (ret != to_copy)
+ return -EFAULT;
- iov_off += to_copy;
frag_off += to_copy;
copied += to_copy;
}
diff --git a/net/rds/message.c b/net/rds/message.c
index aba232f9f308..ff2202218187 100644
--- a/net/rds/message.c
+++ b/net/rds/message.c
@@ -264,75 +264,54 @@ struct rds_message *rds_message_map_pages(unsigned long *page_addrs, unsigned in
return rm;
}
-int rds_message_copy_from_user(struct rds_message *rm, struct iovec *first_iov,
- size_t total_len)
+int rds_message_copy_from_user(struct rds_message *rm, struct iov_iter *from)
{
unsigned long to_copy;
- unsigned long iov_off;
unsigned long sg_off;
- struct iovec *iov;
struct scatterlist *sg;
int ret = 0;
- rm->m_inc.i_hdr.h_len = cpu_to_be32(total_len);
+ rm->m_inc.i_hdr.h_len = cpu_to_be32(iov_iter_count(from));
/*
* now allocate and copy in the data payload.
*/
sg = rm->data.op_sg;
- iov = first_iov;
- iov_off = 0;
sg_off = 0; /* Dear gcc, sg->page will be null from kzalloc. */
- while (total_len) {
+ while (iov_iter_count(from)) {
if (!sg_page(sg)) {
- ret = rds_page_remainder_alloc(sg, total_len,
+ ret = rds_page_remainder_alloc(sg, iov_iter_count(from),
GFP_HIGHUSER);
if (ret)
- goto out;
+ return ret;
rm->data.op_nents++;
sg_off = 0;
}
- while (iov_off == iov->iov_len) {
- iov_off = 0;
- iov++;
- }
-
- to_copy = min(iov->iov_len - iov_off, sg->length - sg_off);
- to_copy = min_t(size_t, to_copy, total_len);
-
- rdsdebug("copying %lu bytes from user iov [%p, %zu] + %lu to "
- "sg [%p, %u, %u] + %lu\n",
- to_copy, iov->iov_base, iov->iov_len, iov_off,
- (void *)sg_page(sg), sg->offset, sg->length, sg_off);
+ to_copy = min_t(unsigned long, iov_iter_count(from),
+ sg->length - sg_off);
- ret = rds_page_copy_from_user(sg_page(sg), sg->offset + sg_off,
- iov->iov_base + iov_off,
- to_copy);
- if (ret)
- goto out;
+ rds_stats_add(s_copy_from_user, to_copy);
+ ret = copy_page_from_iter(sg_page(sg), sg->offset + sg_off,
+ to_copy, from);
+ if (ret != to_copy)
+ return -EFAULT;
- iov_off += to_copy;
- total_len -= to_copy;
sg_off += to_copy;
if (sg_off == sg->length)
sg++;
}
-out:
return ret;
}
-int rds_message_inc_copy_to_user(struct rds_incoming *inc,
- struct iovec *first_iov, size_t size)
+int rds_message_inc_copy_to_user(struct rds_incoming *inc, struct iov_iter *to)
{
struct rds_message *rm;
- struct iovec *iov;
struct scatterlist *sg;
unsigned long to_copy;
- unsigned long iov_off;
unsigned long vec_off;
int copied;
int ret;
@@ -341,36 +320,20 @@ int rds_message_inc_copy_to_user(struct rds_incoming *inc,
rm = container_of(inc, struct rds_message, m_inc);
len = be32_to_cpu(rm->m_inc.i_hdr.h_len);
- iov = first_iov;
- iov_off = 0;
sg = rm->data.op_sg;
vec_off = 0;
copied = 0;
- while (copied < size && copied < len) {
- while (iov_off == iov->iov_len) {
- iov_off = 0;
- iov++;
- }
-
- to_copy = min(iov->iov_len - iov_off, sg->length - vec_off);
- to_copy = min_t(size_t, to_copy, size - copied);
+ while (iov_iter_count(to) && copied < len) {
+ to_copy = min(iov_iter_count(to), sg->length - vec_off);
to_copy = min_t(unsigned long, to_copy, len - copied);
- rdsdebug("copying %lu bytes to user iov [%p, %zu] + %lu to "
- "sg [%p, %u, %u] + %lu\n",
- to_copy, iov->iov_base, iov->iov_len, iov_off,
- sg_page(sg), sg->offset, sg->length, vec_off);
-
- ret = rds_page_copy_to_user(sg_page(sg), sg->offset + vec_off,
- iov->iov_base + iov_off,
- to_copy);
- if (ret) {
- copied = ret;
- break;
- }
+ rds_stats_add(s_copy_to_user, to_copy);
+ ret = copy_page_to_iter(sg_page(sg), sg->offset + vec_off,
+ to_copy, to);
+ if (ret != to_copy)
+ return -EFAULT;
- iov_off += to_copy;
vec_off += to_copy;
copied += to_copy;
diff --git a/net/rds/rds.h b/net/rds/rds.h
index 48f8ffc60f8f..c2a5eef41343 100644
--- a/net/rds/rds.h
+++ b/net/rds/rds.h
@@ -431,8 +431,7 @@ struct rds_transport {
int (*xmit_rdma)(struct rds_connection *conn, struct rm_rdma_op *op);
int (*xmit_atomic)(struct rds_connection *conn, struct rm_atomic_op *op);
int (*recv)(struct rds_connection *conn);
- int (*inc_copy_to_user)(struct rds_incoming *inc, struct iovec *iov,
- size_t size);
+ int (*inc_copy_to_user)(struct rds_incoming *inc, struct iov_iter *to);
void (*inc_free)(struct rds_incoming *inc);
int (*cm_handle_connect)(struct rdma_cm_id *cm_id,
@@ -657,8 +656,7 @@ rds_conn_connecting(struct rds_connection *conn)
/* message.c */
struct rds_message *rds_message_alloc(unsigned int nents, gfp_t gfp);
struct scatterlist *rds_message_alloc_sgs(struct rds_message *rm, int nents);
-int rds_message_copy_from_user(struct rds_message *rm, struct iovec *first_iov,
- size_t total_len);
+int rds_message_copy_from_user(struct rds_message *rm, struct iov_iter *from);
struct rds_message *rds_message_map_pages(unsigned long *page_addrs, unsigned int total_len);
void rds_message_populate_header(struct rds_header *hdr, __be16 sport,
__be16 dport, u64 seq);
@@ -667,8 +665,7 @@ int rds_message_add_extension(struct rds_header *hdr,
int rds_message_next_extension(struct rds_header *hdr,
unsigned int *pos, void *buf, unsigned int *buflen);
int rds_message_add_rdma_dest_extension(struct rds_header *hdr, u32 r_key, u32 offset);
-int rds_message_inc_copy_to_user(struct rds_incoming *inc,
- struct iovec *first_iov, size_t size);
+int rds_message_inc_copy_to_user(struct rds_incoming *inc, struct iov_iter *to);
void rds_message_inc_free(struct rds_incoming *inc);
void rds_message_addref(struct rds_message *rm);
void rds_message_put(struct rds_message *rm);
diff --git a/net/rds/recv.c b/net/rds/recv.c
index bd82522534fc..f9ec1acd801c 100644
--- a/net/rds/recv.c
+++ b/net/rds/recv.c
@@ -414,6 +414,7 @@ int rds_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
goto out;
while (1) {
+ struct iov_iter save;
/* If there are pending notifications, do those - and nothing else */
if (!list_empty(&rs->rs_notify_queue)) {
ret = rds_notify_queue_get(rs, msg);
@@ -449,8 +450,8 @@ int rds_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
rdsdebug("copying inc %p from %pI4:%u to user\n", inc,
&inc->i_conn->c_faddr,
ntohs(inc->i_hdr.h_sport));
- ret = inc->i_conn->c_trans->inc_copy_to_user(inc, msg->msg_iov,
- size);
+ save = msg->msg_iter;
+ ret = inc->i_conn->c_trans->inc_copy_to_user(inc, &msg->msg_iter);
if (ret < 0)
break;
@@ -463,6 +464,7 @@ int rds_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
rds_inc_put(inc);
inc = NULL;
rds_stats_inc(s_recv_deliver_raced);
+ msg->msg_iter = save;
continue;
}
diff --git a/net/rds/send.c b/net/rds/send.c
index 0a64541020b0..42f65d4305c8 100644
--- a/net/rds/send.c
+++ b/net/rds/send.c
@@ -826,7 +826,7 @@ static int rds_rm_size(struct msghdr *msg, int data_len)
int cmsg_groups = 0;
int retval;
- for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ for_each_cmsghdr(cmsg, msg) {
if (!CMSG_OK(msg, cmsg))
return -EINVAL;
@@ -878,7 +878,7 @@ static int rds_cmsg_send(struct rds_sock *rs, struct rds_message *rm,
struct cmsghdr *cmsg;
int ret = 0;
- for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ for_each_cmsghdr(cmsg, msg) {
if (!CMSG_OK(msg, cmsg))
return -EINVAL;
@@ -982,7 +982,7 @@ int rds_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
ret = -ENOMEM;
goto out;
}
- ret = rds_message_copy_from_user(rm, msg->msg_iov, payload_len);
+ ret = rds_message_copy_from_user(rm, &msg->msg_iter);
if (ret)
goto out;
}
diff --git a/net/rds/tcp.h b/net/rds/tcp.h
index 65637491f728..0dbdd37162da 100644
--- a/net/rds/tcp.h
+++ b/net/rds/tcp.h
@@ -69,8 +69,7 @@ void rds_tcp_recv_exit(void);
void rds_tcp_data_ready(struct sock *sk);
int rds_tcp_recv(struct rds_connection *conn);
void rds_tcp_inc_free(struct rds_incoming *inc);
-int rds_tcp_inc_copy_to_user(struct rds_incoming *inc, struct iovec *iov,
- size_t size);
+int rds_tcp_inc_copy_to_user(struct rds_incoming *inc, struct iov_iter *to);
/* tcp_send.c */
void rds_tcp_xmit_prepare(struct rds_connection *conn);
diff --git a/net/rds/tcp_recv.c b/net/rds/tcp_recv.c
index 9ae6e0a264ec..fbc5ef88bc0e 100644
--- a/net/rds/tcp_recv.c
+++ b/net/rds/tcp_recv.c
@@ -59,50 +59,30 @@ void rds_tcp_inc_free(struct rds_incoming *inc)
/*
* this is pretty lame, but, whatever.
*/
-int rds_tcp_inc_copy_to_user(struct rds_incoming *inc, struct iovec *first_iov,
- size_t size)
+int rds_tcp_inc_copy_to_user(struct rds_incoming *inc, struct iov_iter *to)
{
struct rds_tcp_incoming *tinc;
- struct iovec *iov, tmp;
struct sk_buff *skb;
- unsigned long to_copy, skb_off;
int ret = 0;
- if (size == 0)
+ if (!iov_iter_count(to))
goto out;
tinc = container_of(inc, struct rds_tcp_incoming, ti_inc);
- iov = first_iov;
- tmp = *iov;
skb_queue_walk(&tinc->ti_skb_list, skb) {
- skb_off = 0;
- while (skb_off < skb->len) {
- while (tmp.iov_len == 0) {
- iov++;
- tmp = *iov;
- }
-
- to_copy = min(tmp.iov_len, size);
+ unsigned long to_copy, skb_off;
+ for (skb_off = 0; skb_off < skb->len; skb_off += to_copy) {
+ to_copy = iov_iter_count(to);
to_copy = min(to_copy, skb->len - skb_off);
- rdsdebug("ret %d size %zu skb %p skb_off %lu "
- "skblen %d iov_base %p iov_len %zu cpy %lu\n",
- ret, size, skb, skb_off, skb->len,
- tmp.iov_base, tmp.iov_len, to_copy);
-
- /* modifies tmp as it copies */
- if (skb_copy_datagram_iovec(skb, skb_off, &tmp,
- to_copy)) {
- ret = -EFAULT;
- goto out;
- }
+ if (skb_copy_datagram_iter(skb, skb_off, to, to_copy))
+ return -EFAULT;
rds_stats_add(s_copy_to_user, to_copy);
- size -= to_copy;
ret += to_copy;
- skb_off += to_copy;
- if (size == 0)
+
+ if (!iov_iter_count(to))
goto out;
}
}
diff --git a/net/rfkill/rfkill-gpio.c b/net/rfkill/rfkill-gpio.c
index 0f62326c0f5e..2a4717967502 100644
--- a/net/rfkill/rfkill-gpio.c
+++ b/net/rfkill/rfkill-gpio.c
@@ -63,6 +63,15 @@ static const struct rfkill_ops rfkill_gpio_ops = {
.set_block = rfkill_gpio_set_power,
};
+static const struct acpi_gpio_params reset_gpios = { 0, 0, false };
+static const struct acpi_gpio_params shutdown_gpios = { 1, 0, false };
+
+static const struct acpi_gpio_mapping acpi_rfkill_default_gpios[] = {
+ { "reset-gpios", &reset_gpios, 1 },
+ { "shutdown-gpios", &shutdown_gpios, 1 },
+ { },
+};
+
static int rfkill_gpio_acpi_probe(struct device *dev,
struct rfkill_gpio_data *rfkill)
{
@@ -75,7 +84,8 @@ static int rfkill_gpio_acpi_probe(struct device *dev,
rfkill->name = dev_name(dev);
rfkill->type = (unsigned)id->driver_data;
- return 0;
+ return acpi_dev_add_driver_gpios(ACPI_COMPANION(dev),
+ acpi_rfkill_default_gpios);
}
static int rfkill_gpio_probe(struct platform_device *pdev)
@@ -102,7 +112,7 @@ static int rfkill_gpio_probe(struct platform_device *pdev)
rfkill->clk = devm_clk_get(&pdev->dev, NULL);
- gpio = devm_gpiod_get_index(&pdev->dev, "reset", 0);
+ gpio = devm_gpiod_get(&pdev->dev, "reset");
if (!IS_ERR(gpio)) {
ret = gpiod_direction_output(gpio, 0);
if (ret)
@@ -110,7 +120,7 @@ static int rfkill_gpio_probe(struct platform_device *pdev)
rfkill->reset_gpio = gpio;
}
- gpio = devm_gpiod_get_index(&pdev->dev, "shutdown", 1);
+ gpio = devm_gpiod_get(&pdev->dev, "shutdown");
if (!IS_ERR(gpio)) {
ret = gpiod_direction_output(gpio, 0);
if (ret)
@@ -150,6 +160,8 @@ static int rfkill_gpio_remove(struct platform_device *pdev)
rfkill_unregister(rfkill->rfkill_dev);
rfkill_destroy(rfkill->rfkill_dev);
+ acpi_dev_remove_driver_gpios(ACPI_COMPANION(&pdev->dev));
+
return 0;
}
diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c
index a85c1a086ae4..43bac7c4dd9e 100644
--- a/net/rose/af_rose.c
+++ b/net/rose/af_rose.c
@@ -1121,7 +1121,7 @@ static int rose_sendmsg(struct kiocb *iocb, struct socket *sock,
skb_reset_transport_header(skb);
skb_put(skb, len);
- err = memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len);
+ err = memcpy_from_msg(skb_transport_header(skb), msg, len);
if (err) {
kfree_skb(skb);
return err;
@@ -1249,7 +1249,7 @@ static int rose_recvmsg(struct kiocb *iocb, struct socket *sock,
msg->msg_flags |= MSG_TRUNC;
}
- skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ skb_copy_datagram_msg(skb, 0, msg, copied);
if (msg->msg_name) {
struct sockaddr_rose *srose;
diff --git a/net/rxrpc/ar-output.c b/net/rxrpc/ar-output.c
index 0b4b9a79f5ab..e1a9373e5979 100644
--- a/net/rxrpc/ar-output.c
+++ b/net/rxrpc/ar-output.c
@@ -45,7 +45,7 @@ static int rxrpc_sendmsg_cmsg(struct rxrpc_sock *rx, struct msghdr *msg,
if (msg->msg_controllen == 0)
return -EINVAL;
- for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
+ for_each_cmsghdr(cmsg, msg) {
if (!CMSG_OK(msg, cmsg))
return -EINVAL;
@@ -531,14 +531,12 @@ static int rxrpc_send_data(struct kiocb *iocb,
struct rxrpc_skb_priv *sp;
unsigned char __user *from;
struct sk_buff *skb;
- struct iovec *iov;
+ const struct iovec *iov;
struct sock *sk = &rx->sk;
long timeo;
bool more;
int ret, ioc, segment, copied;
- _enter(",,,{%zu},%zu", msg->msg_iovlen, len);
-
timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
/* this should be in poll */
@@ -547,8 +545,8 @@ static int rxrpc_send_data(struct kiocb *iocb,
if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN))
return -EPIPE;
- iov = msg->msg_iov;
- ioc = msg->msg_iovlen - 1;
+ iov = msg->msg_iter.iov;
+ ioc = msg->msg_iter.nr_segs - 1;
from = iov->iov_base;
segment = iov->iov_len;
iov++;
diff --git a/net/rxrpc/ar-recvmsg.c b/net/rxrpc/ar-recvmsg.c
index e9aaa65c0778..4575485ad1b4 100644
--- a/net/rxrpc/ar-recvmsg.c
+++ b/net/rxrpc/ar-recvmsg.c
@@ -180,7 +180,7 @@ int rxrpc_recvmsg(struct kiocb *iocb, struct socket *sock,
if (copy > len - copied)
copy = len - copied;
- ret = skb_copy_datagram_iovec(skb, offset, msg->msg_iov, copy);
+ ret = skb_copy_datagram_msg(skb, offset, msg, copy);
if (ret < 0)
goto copy_error;
diff --git a/net/sched/Kconfig b/net/sched/Kconfig
index a1a8e29e5fc9..c54c9d9d1ffb 100644
--- a/net/sched/Kconfig
+++ b/net/sched/Kconfig
@@ -22,8 +22,9 @@ menuconfig NET_SCHED
This code is considered to be experimental.
To administer these schedulers, you'll need the user-level utilities
- from the package iproute2+tc at <ftp://ftp.tux.org/pub/net/ip-routing/>.
- That package also contains some documentation; for more, check out
+ from the package iproute2+tc at
+ <https://www.kernel.org/pub/linux/utils/net/iproute2/>. That package
+ also contains some documentation; for more, check out
<http://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2>.
This Quality of Service (QoS) support will enable you to use
@@ -336,7 +337,7 @@ config NET_SCH_PLUG
of virtual machines by allowing the generated network output to be rolled
back if needed.
- For more information, please refer to http://wiki.xensource.com/xenwiki/Remus
+ For more information, please refer to <http://wiki.xenproject.org/wiki/Remus>
Say Y here if you are using this kernel for Xen dom0 and
want to protect Xen guests with Remus.
@@ -686,6 +687,17 @@ config NET_ACT_CSUM
To compile this code as a module, choose M here: the
module will be called act_csum.
+config NET_ACT_VLAN
+ tristate "Vlan manipulation"
+ depends on NET_CLS_ACT
+ ---help---
+ Say Y here to push or pop vlan headers.
+
+ If unsure, say N.
+
+ To compile this code as a module, choose M here: the
+ module will be called act_vlan.
+
config NET_CLS_IND
bool "Incoming device classification"
depends on NET_CLS_U32 || NET_CLS_FW
diff --git a/net/sched/Makefile b/net/sched/Makefile
index 0a869a11f3e6..679f24ae7f93 100644
--- a/net/sched/Makefile
+++ b/net/sched/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_NET_ACT_PEDIT) += act_pedit.o
obj-$(CONFIG_NET_ACT_SIMP) += act_simple.o
obj-$(CONFIG_NET_ACT_SKBEDIT) += act_skbedit.o
obj-$(CONFIG_NET_ACT_CSUM) += act_csum.o
+obj-$(CONFIG_NET_ACT_VLAN) += act_vlan.o
obj-$(CONFIG_NET_SCH_FIFO) += sch_fifo.o
obj-$(CONFIG_NET_SCH_CBQ) += sch_cbq.o
obj-$(CONFIG_NET_SCH_HTB) += sch_htb.o
diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c
index d6bcbd9f7791..7fffc2272701 100644
--- a/net/sched/act_gact.c
+++ b/net/sched/act_gact.c
@@ -1,5 +1,5 @@
/*
- * net/sched/gact.c Generic actions
+ * net/sched/act_gact.c Generic actions
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c
index 8a64a0734aee..cbc8dd7dd48a 100644
--- a/net/sched/act_ipt.c
+++ b/net/sched/act_ipt.c
@@ -1,5 +1,5 @@
/*
- * net/sched/ipt.c iptables target interface
+ * net/sched/act_ipt.c iptables target interface
*
*TODO: Add other tables. For now we only support the ipv4 table targets
*
diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
index eb48306033d9..5953517ec059 100644
--- a/net/sched/act_mirred.c
+++ b/net/sched/act_mirred.c
@@ -1,5 +1,5 @@
/*
- * net/sched/mirred.c packet mirroring and redirect actions
+ * net/sched/act_mirred.c packet mirroring and redirect actions
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
index 5f9bcb2e080b..59649d588d79 100644
--- a/net/sched/act_pedit.c
+++ b/net/sched/act_pedit.c
@@ -1,5 +1,5 @@
/*
- * net/sched/pedit.c Generic packet editor
+ * net/sched/act_pedit.c Generic packet editor
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/net/sched/act_police.c b/net/sched/act_police.c
index 69791ca77a05..9a1c42a43f92 100644
--- a/net/sched/act_police.c
+++ b/net/sched/act_police.c
@@ -1,5 +1,5 @@
/*
- * net/sched/police.c Input police filter.
+ * net/sched/act_police.c Input police filter
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c
index 992c2317ce88..6a8d9488613a 100644
--- a/net/sched/act_simple.c
+++ b/net/sched/act_simple.c
@@ -1,5 +1,5 @@
/*
- * net/sched/simp.c Simple example of an action
+ * net/sched/act_simple.c Simple example of an action
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
diff --git a/net/sched/act_vlan.c b/net/sched/act_vlan.c
new file mode 100644
index 000000000000..d735ecf0b1a7
--- /dev/null
+++ b/net/sched/act_vlan.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_vlan.h>
+#include <net/netlink.h>
+#include <net/pkt_sched.h>
+
+#include <linux/tc_act/tc_vlan.h>
+#include <net/tc_act/tc_vlan.h>
+
+#define VLAN_TAB_MASK 15
+
+static int tcf_vlan(struct sk_buff *skb, const struct tc_action *a,
+ struct tcf_result *res)
+{
+ struct tcf_vlan *v = a->priv;
+ int action;
+ int err;
+
+ spin_lock(&v->tcf_lock);
+ v->tcf_tm.lastuse = jiffies;
+ bstats_update(&v->tcf_bstats, skb);
+ action = v->tcf_action;
+
+ switch (v->tcfv_action) {
+ case TCA_VLAN_ACT_POP:
+ err = skb_vlan_pop(skb);
+ if (err)
+ goto drop;
+ break;
+ case TCA_VLAN_ACT_PUSH:
+ err = skb_vlan_push(skb, v->tcfv_push_proto, v->tcfv_push_vid);
+ if (err)
+ goto drop;
+ break;
+ default:
+ BUG();
+ }
+
+ goto unlock;
+
+drop:
+ action = TC_ACT_SHOT;
+ v->tcf_qstats.drops++;
+unlock:
+ spin_unlock(&v->tcf_lock);
+ return action;
+}
+
+static const struct nla_policy vlan_policy[TCA_VLAN_MAX + 1] = {
+ [TCA_VLAN_PARMS] = { .len = sizeof(struct tc_vlan) },
+ [TCA_VLAN_PUSH_VLAN_ID] = { .type = NLA_U16 },
+ [TCA_VLAN_PUSH_VLAN_PROTOCOL] = { .type = NLA_U16 },
+};
+
+static int tcf_vlan_init(struct net *net, struct nlattr *nla,
+ struct nlattr *est, struct tc_action *a,
+ int ovr, int bind)
+{
+ struct nlattr *tb[TCA_VLAN_MAX + 1];
+ struct tc_vlan *parm;
+ struct tcf_vlan *v;
+ int action;
+ __be16 push_vid = 0;
+ __be16 push_proto = 0;
+ int ret = 0;
+ int err;
+
+ if (!nla)
+ return -EINVAL;
+
+ err = nla_parse_nested(tb, TCA_VLAN_MAX, nla, vlan_policy);
+ if (err < 0)
+ return err;
+
+ if (!tb[TCA_VLAN_PARMS])
+ return -EINVAL;
+ parm = nla_data(tb[TCA_VLAN_PARMS]);
+ switch (parm->v_action) {
+ case TCA_VLAN_ACT_POP:
+ break;
+ case TCA_VLAN_ACT_PUSH:
+ if (!tb[TCA_VLAN_PUSH_VLAN_ID])
+ return -EINVAL;
+ push_vid = nla_get_u16(tb[TCA_VLAN_PUSH_VLAN_ID]);
+ if (push_vid >= VLAN_VID_MASK)
+ return -ERANGE;
+
+ if (tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]) {
+ push_proto = nla_get_be16(tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]);
+ switch (push_proto) {
+ case htons(ETH_P_8021Q):
+ case htons(ETH_P_8021AD):
+ break;
+ default:
+ return -EPROTONOSUPPORT;
+ }
+ } else {
+ push_proto = htons(ETH_P_8021Q);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ action = parm->v_action;
+
+ if (!tcf_hash_check(parm->index, a, bind)) {
+ ret = tcf_hash_create(parm->index, est, a, sizeof(*v), bind);
+ if (ret)
+ return ret;
+
+ ret = ACT_P_CREATED;
+ } else {
+ if (bind)
+ return 0;
+ tcf_hash_release(a, bind);
+ if (!ovr)
+ return -EEXIST;
+ }
+
+ v = to_vlan(a);
+
+ spin_lock_bh(&v->tcf_lock);
+
+ v->tcfv_action = action;
+ v->tcfv_push_vid = push_vid;
+ v->tcfv_push_proto = push_proto;
+
+ v->tcf_action = parm->action;
+
+ spin_unlock_bh(&v->tcf_lock);
+
+ if (ret == ACT_P_CREATED)
+ tcf_hash_insert(a);
+ return ret;
+}
+
+static int tcf_vlan_dump(struct sk_buff *skb, struct tc_action *a,
+ int bind, int ref)
+{
+ unsigned char *b = skb_tail_pointer(skb);
+ struct tcf_vlan *v = a->priv;
+ struct tc_vlan opt = {
+ .index = v->tcf_index,
+ .refcnt = v->tcf_refcnt - ref,
+ .bindcnt = v->tcf_bindcnt - bind,
+ .action = v->tcf_action,
+ .v_action = v->tcfv_action,
+ };
+ struct tcf_t t;
+
+ if (nla_put(skb, TCA_VLAN_PARMS, sizeof(opt), &opt))
+ goto nla_put_failure;
+
+ if (v->tcfv_action == TCA_VLAN_ACT_PUSH &&
+ (nla_put_u16(skb, TCA_VLAN_PUSH_VLAN_ID, v->tcfv_push_vid) ||
+ nla_put_be16(skb, TCA_VLAN_PUSH_VLAN_PROTOCOL, v->tcfv_push_proto)))
+ goto nla_put_failure;
+
+ t.install = jiffies_to_clock_t(jiffies - v->tcf_tm.install);
+ t.lastuse = jiffies_to_clock_t(jiffies - v->tcf_tm.lastuse);
+ t.expires = jiffies_to_clock_t(v->tcf_tm.expires);
+ if (nla_put(skb, TCA_VLAN_TM, sizeof(t), &t))
+ goto nla_put_failure;
+ return skb->len;
+
+nla_put_failure:
+ nlmsg_trim(skb, b);
+ return -1;
+}
+
+static struct tc_action_ops act_vlan_ops = {
+ .kind = "vlan",
+ .type = TCA_ACT_VLAN,
+ .owner = THIS_MODULE,
+ .act = tcf_vlan,
+ .dump = tcf_vlan_dump,
+ .init = tcf_vlan_init,
+};
+
+static int __init vlan_init_module(void)
+{
+ return tcf_register_action(&act_vlan_ops, VLAN_TAB_MASK);
+}
+
+static void __exit vlan_cleanup_module(void)
+{
+ tcf_unregister_action(&act_vlan_ops);
+}
+
+module_init(vlan_init_module);
+module_exit(vlan_cleanup_module);
+
+MODULE_AUTHOR("Jiri Pirko <jiri@resnulli.us>");
+MODULE_DESCRIPTION("vlan manipulation actions");
+MODULE_LICENSE("GPL v2");
diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c
index cd61280941e5..5aed341406c2 100644
--- a/net/sched/cls_basic.c
+++ b/net/sched/cls_basic.c
@@ -72,10 +72,6 @@ static unsigned long basic_get(struct tcf_proto *tp, u32 handle)
return l;
}
-static void basic_put(struct tcf_proto *tp, unsigned long f)
-{
-}
-
static int basic_init(struct tcf_proto *tp)
{
struct basic_head *head;
@@ -113,18 +109,12 @@ static void basic_destroy(struct tcf_proto *tp)
static int basic_delete(struct tcf_proto *tp, unsigned long arg)
{
- struct basic_head *head = rtnl_dereference(tp->root);
- struct basic_filter *t, *f = (struct basic_filter *) arg;
-
- list_for_each_entry(t, &head->flist, link)
- if (t == f) {
- list_del_rcu(&t->link);
- tcf_unbind_filter(tp, &t->res);
- call_rcu(&t->rcu, basic_delete_filter);
- return 0;
- }
+ struct basic_filter *f = (struct basic_filter *) arg;
- return -ENOENT;
+ list_del_rcu(&f->link);
+ tcf_unbind_filter(tp, &f->res);
+ call_rcu(&f->rcu, basic_delete_filter);
+ return 0;
}
static const struct nla_policy basic_policy[TCA_BASIC_MAX + 1] = {
@@ -188,10 +178,9 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,
return -EINVAL;
}
- err = -ENOBUFS;
fnew = kzalloc(sizeof(*fnew), GFP_KERNEL);
- if (fnew == NULL)
- goto errout;
+ if (!fnew)
+ return -ENOBUFS;
tcf_exts_init(&fnew->exts, TCA_BASIC_ACT, TCA_BASIC_POLICE);
err = -EINVAL;
@@ -293,7 +282,6 @@ static struct tcf_proto_ops cls_basic_ops __read_mostly = {
.init = basic_init,
.destroy = basic_destroy,
.get = basic_get,
- .put = basic_put,
.change = basic_change,
.delete = basic_delete,
.walk = basic_walk,
diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c
index eed49d1d0878..84c8219c3e1c 100644
--- a/net/sched/cls_bpf.c
+++ b/net/sched/cls_bpf.c
@@ -109,19 +109,12 @@ static void __cls_bpf_delete_prog(struct rcu_head *rcu)
static int cls_bpf_delete(struct tcf_proto *tp, unsigned long arg)
{
- struct cls_bpf_head *head = rtnl_dereference(tp->root);
- struct cls_bpf_prog *prog, *todel = (struct cls_bpf_prog *) arg;
+ struct cls_bpf_prog *prog = (struct cls_bpf_prog *) arg;
- list_for_each_entry(prog, &head->plist, link) {
- if (prog == todel) {
- list_del_rcu(&prog->link);
- tcf_unbind_filter(tp, &prog->res);
- call_rcu(&prog->rcu, __cls_bpf_delete_prog);
- return 0;
- }
- }
-
- return -ENOENT;
+ list_del_rcu(&prog->link);
+ tcf_unbind_filter(tp, &prog->res);
+ call_rcu(&prog->rcu, __cls_bpf_delete_prog);
+ return 0;
}
static void cls_bpf_destroy(struct tcf_proto *tp)
@@ -148,7 +141,7 @@ static unsigned long cls_bpf_get(struct tcf_proto *tp, u32 handle)
if (head == NULL)
return 0UL;
- list_for_each_entry_rcu(prog, &head->plist, link) {
+ list_for_each_entry(prog, &head->plist, link) {
if (prog->handle == handle) {
ret = (unsigned long) prog;
break;
@@ -158,10 +151,6 @@ static unsigned long cls_bpf_get(struct tcf_proto *tp, u32 handle)
return ret;
}
-static void cls_bpf_put(struct tcf_proto *tp, unsigned long f)
-{
-}
-
static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp,
struct cls_bpf_prog *prog,
unsigned long base, struct nlattr **tb,
@@ -344,7 +333,7 @@ static void cls_bpf_walk(struct tcf_proto *tp, struct tcf_walker *arg)
struct cls_bpf_head *head = rtnl_dereference(tp->root);
struct cls_bpf_prog *prog;
- list_for_each_entry_rcu(prog, &head->plist, link) {
+ list_for_each_entry(prog, &head->plist, link) {
if (arg->count < arg->skip)
goto skip;
if (arg->fn(tp, (unsigned long) prog, arg) < 0) {
@@ -363,7 +352,6 @@ static struct tcf_proto_ops cls_bpf_ops __read_mostly = {
.init = cls_bpf_init,
.destroy = cls_bpf_destroy,
.get = cls_bpf_get,
- .put = cls_bpf_put,
.change = cls_bpf_change,
.delete = cls_bpf_delete,
.walk = cls_bpf_walk,
diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c
index d61a801222c1..221697ab0247 100644
--- a/net/sched/cls_cgroup.c
+++ b/net/sched/cls_cgroup.c
@@ -67,10 +67,6 @@ static unsigned long cls_cgroup_get(struct tcf_proto *tp, u32 handle)
return 0UL;
}
-static void cls_cgroup_put(struct tcf_proto *tp, unsigned long f)
-{
-}
-
static int cls_cgroup_init(struct tcf_proto *tp)
{
return 0;
@@ -117,11 +113,7 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb,
return -ENOBUFS;
tcf_exts_init(&new->exts, TCA_CGROUP_ACT, TCA_CGROUP_POLICE);
- if (head)
- new->handle = head->handle;
- else
- new->handle = handle;
-
+ new->handle = handle;
new->tp = tp;
err = nla_parse_nested(tb, TCA_CGROUP_MAX, tca[TCA_OPTIONS],
cgroup_policy);
@@ -185,7 +177,6 @@ static int cls_cgroup_dump(struct net *net, struct tcf_proto *tp, unsigned long
struct sk_buff *skb, struct tcmsg *t)
{
struct cls_cgroup_head *head = rtnl_dereference(tp->root);
- unsigned char *b = skb_tail_pointer(skb);
struct nlattr *nest;
t->tcm_handle = head->handle;
@@ -206,7 +197,7 @@ static int cls_cgroup_dump(struct net *net, struct tcf_proto *tp, unsigned long
return skb->len;
nla_put_failure:
- nlmsg_trim(skb, b);
+ nla_nest_cancel(skb, nest);
return -1;
}
@@ -217,7 +208,6 @@ static struct tcf_proto_ops cls_cgroup_ops __read_mostly = {
.classify = cls_cgroup_classify,
.destroy = cls_cgroup_destroy,
.get = cls_cgroup_get,
- .put = cls_cgroup_put,
.delete = cls_cgroup_delete,
.walk = cls_cgroup_walk,
.dump = cls_cgroup_dump,
diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c
index 4ac515f2a6ce..15d68f24a521 100644
--- a/net/sched/cls_flow.c
+++ b/net/sched/cls_flow.c
@@ -426,10 +426,7 @@ static int flow_change(struct net *net, struct sk_buff *in_skb,
goto err2;
/* Copy fold into fnew */
- fnew->handle = fold->handle;
- fnew->keymask = fold->keymask;
fnew->tp = fold->tp;
-
fnew->handle = fold->handle;
fnew->nkeys = fold->nkeys;
fnew->keymask = fold->keymask;
@@ -578,16 +575,12 @@ static unsigned long flow_get(struct tcf_proto *tp, u32 handle)
struct flow_head *head = rtnl_dereference(tp->root);
struct flow_filter *f;
- list_for_each_entry_rcu(f, &head->filters, list)
+ list_for_each_entry(f, &head->filters, list)
if (f->handle == handle)
return (unsigned long)f;
return 0;
}
-static void flow_put(struct tcf_proto *tp, unsigned long f)
-{
-}
-
static int flow_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
struct sk_buff *skb, struct tcmsg *t)
{
@@ -645,7 +638,7 @@ static int flow_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
return skb->len;
nla_put_failure:
- nlmsg_trim(skb, nest);
+ nla_nest_cancel(skb, nest);
return -1;
}
@@ -654,7 +647,7 @@ static void flow_walk(struct tcf_proto *tp, struct tcf_walker *arg)
struct flow_head *head = rtnl_dereference(tp->root);
struct flow_filter *f;
- list_for_each_entry_rcu(f, &head->filters, list) {
+ list_for_each_entry(f, &head->filters, list) {
if (arg->count < arg->skip)
goto skip;
if (arg->fn(tp, (unsigned long)f, arg) < 0) {
@@ -674,7 +667,6 @@ static struct tcf_proto_ops cls_flow_ops __read_mostly = {
.change = flow_change,
.delete = flow_delete,
.get = flow_get,
- .put = flow_put,
.dump = flow_dump,
.walk = flow_walk,
.owner = THIS_MODULE,
diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c
index dbfdfd1f1a9f..a5269f76004c 100644
--- a/net/sched/cls_fw.c
+++ b/net/sched/cls_fw.c
@@ -111,10 +111,6 @@ static unsigned long fw_get(struct tcf_proto *tp, u32 handle)
return 0;
}
-static void fw_put(struct tcf_proto *tp, unsigned long f)
-{
-}
-
static int fw_init(struct tcf_proto *tp)
{
return 0;
@@ -360,7 +356,6 @@ static int fw_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
{
struct fw_head *head = rtnl_dereference(tp->root);
struct fw_filter *f = (struct fw_filter *)fh;
- unsigned char *b = skb_tail_pointer(skb);
struct nlattr *nest;
if (f == NULL)
@@ -401,7 +396,7 @@ static int fw_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
return skb->len;
nla_put_failure:
- nlmsg_trim(skb, b);
+ nla_nest_cancel(skb, nest);
return -1;
}
@@ -411,7 +406,6 @@ static struct tcf_proto_ops cls_fw_ops __read_mostly = {
.init = fw_init,
.destroy = fw_destroy,
.get = fw_get,
- .put = fw_put,
.change = fw_change,
.delete = fw_delete,
.walk = fw_walk,
diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c
index 109a329b7198..2ecd24688554 100644
--- a/net/sched/cls_route.c
+++ b/net/sched/cls_route.c
@@ -256,10 +256,6 @@ static unsigned long route4_get(struct tcf_proto *tp, u32 handle)
return 0;
}
-static void route4_put(struct tcf_proto *tp, unsigned long f)
-{
-}
-
static int route4_init(struct tcf_proto *tp)
{
return 0;
@@ -597,7 +593,6 @@ static int route4_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
struct sk_buff *skb, struct tcmsg *t)
{
struct route4_filter *f = (struct route4_filter *)fh;
- unsigned char *b = skb_tail_pointer(skb);
struct nlattr *nest;
u32 id;
@@ -639,7 +634,7 @@ static int route4_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
return skb->len;
nla_put_failure:
- nlmsg_trim(skb, b);
+ nla_nest_cancel(skb, nest);
return -1;
}
@@ -649,7 +644,6 @@ static struct tcf_proto_ops cls_route4_ops __read_mostly = {
.init = route4_init,
.destroy = route4_destroy,
.get = route4_get,
- .put = route4_put,
.change = route4_change,
.delete = route4_delete,
.walk = route4_walk,
diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h
index 6bb55f277a5a..edd8ade3fbc1 100644
--- a/net/sched/cls_rsvp.h
+++ b/net/sched/cls_rsvp.h
@@ -271,10 +271,6 @@ static unsigned long rsvp_get(struct tcf_proto *tp, u32 handle)
return 0;
}
-static void rsvp_put(struct tcf_proto *tp, unsigned long f)
-{
-}
-
static int rsvp_init(struct tcf_proto *tp)
{
struct rsvp_head *data;
@@ -657,7 +653,6 @@ static int rsvp_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
{
struct rsvp_filter *f = (struct rsvp_filter *)fh;
struct rsvp_session *s;
- unsigned char *b = skb_tail_pointer(skb);
struct nlattr *nest;
struct tc_rsvp_pinfo pinfo;
@@ -698,7 +693,7 @@ static int rsvp_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
return skb->len;
nla_put_failure:
- nlmsg_trim(skb, b);
+ nla_nest_cancel(skb, nest);
return -1;
}
@@ -708,7 +703,6 @@ static struct tcf_proto_ops RSVP_OPS __read_mostly = {
.init = rsvp_init,
.destroy = rsvp_destroy,
.get = rsvp_get,
- .put = rsvp_put,
.change = rsvp_change,
.delete = rsvp_delete,
.walk = rsvp_walk,
diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c
index 30f10fb07f4a..bd49bf547a47 100644
--- a/net/sched/cls_tcindex.c
+++ b/net/sched/cls_tcindex.c
@@ -116,13 +116,6 @@ static unsigned long tcindex_get(struct tcf_proto *tp, u32 handle)
return r && tcindex_filter_is_set(r) ? (unsigned long) r : 0UL;
}
-
-static void tcindex_put(struct tcf_proto *tp, unsigned long f)
-{
- pr_debug("tcindex_put(tp %p,f 0x%lx)\n", tp, f);
-}
-
-
static int tcindex_init(struct tcf_proto *tp)
{
struct tcindex_data *p;
@@ -496,11 +489,10 @@ static int tcindex_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
{
struct tcindex_data *p = rtnl_dereference(tp->root);
struct tcindex_filter_result *r = (struct tcindex_filter_result *) fh;
- unsigned char *b = skb_tail_pointer(skb);
struct nlattr *nest;
- pr_debug("tcindex_dump(tp %p,fh 0x%lx,skb %p,t %p),p %p,r %p,b %p\n",
- tp, fh, skb, t, p, r, b);
+ pr_debug("tcindex_dump(tp %p,fh 0x%lx,skb %p,t %p),p %p,r %p\n",
+ tp, fh, skb, t, p, r);
pr_debug("p->perfect %p p->h %p\n", p->perfect, p->h);
nest = nla_nest_start(skb, TCA_OPTIONS);
@@ -550,7 +542,7 @@ static int tcindex_dump(struct net *net, struct tcf_proto *tp, unsigned long fh,
return skb->len;
nla_put_failure:
- nlmsg_trim(skb, b);
+ nla_nest_cancel(skb, nest);
return -1;
}
@@ -560,7 +552,6 @@ static struct tcf_proto_ops cls_tcindex_ops __read_mostly = {
.init = tcindex_init,
.destroy = tcindex_destroy,
.get = tcindex_get,
- .put = tcindex_put,
.change = tcindex_change,
.delete = tcindex_delete,
.walk = tcindex_walk,
diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c
index 0472909bb014..09487afbfd51 100644
--- a/net/sched/cls_u32.c
+++ b/net/sched/cls_u32.c
@@ -299,10 +299,6 @@ static unsigned long u32_get(struct tcf_proto *tp, u32 handle)
return (unsigned long)u32_lookup_key(ht, handle);
}
-static void u32_put(struct tcf_proto *tp, unsigned long f)
-{
-}
-
static u32 gen_new_htid(struct tc_u_common *tp_c)
{
int i = 0x800;
@@ -1021,7 +1017,6 @@ static struct tcf_proto_ops cls_u32_ops __read_mostly = {
.init = u32_init,
.destroy = u32_destroy,
.get = u32_get,
- .put = u32_put,
.change = u32_change,
.delete = u32_delete,
.walk = u32_walk,
diff --git a/net/sched/sch_fq.c b/net/sched/sch_fq.c
index cbd7e1fd23b4..9b05924cc386 100644
--- a/net/sched/sch_fq.c
+++ b/net/sched/sch_fq.c
@@ -481,12 +481,11 @@ begin:
if (likely(rate))
do_div(len, rate);
/* Since socket rate can change later,
- * clamp the delay to 125 ms.
- * TODO: maybe segment the too big skb, as in commit
- * e43ac79a4bc ("sch_tbf: segment too big GSO packets")
+ * clamp the delay to 1 second.
+ * Really, providers of too big packets should be fixed !
*/
- if (unlikely(len > 125 * NSEC_PER_MSEC)) {
- len = 125 * NSEC_PER_MSEC;
+ if (unlikely(len > NSEC_PER_SEC)) {
+ len = NSEC_PER_SEC;
q->stat_pkts_too_long++;
}
diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c
index b9ca32ebc1de..1e52decb7b59 100644
--- a/net/sched/sch_fq_codel.c
+++ b/net/sched/sch_fq_codel.c
@@ -94,7 +94,7 @@ static unsigned int fq_codel_classify(struct sk_buff *skb, struct Qdisc *sch,
TC_H_MIN(skb->priority) <= q->flows_cnt)
return TC_H_MIN(skb->priority);
- filter = rcu_dereference(q->filter_list);
+ filter = rcu_dereference_bh(q->filter_list);
if (!filter)
return fq_codel_hash(q, skb) + 1;
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index b34331967e02..179f1c8c0d8b 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -139,33 +139,20 @@ struct netem_sched_data {
/* Time stamp put into socket buffer control block
* Only valid when skbs are in our internal t(ime)fifo queue.
+ *
+ * As skb->rbnode uses same storage than skb->next, skb->prev and skb->tstamp,
+ * and skb->next & skb->prev are scratch space for a qdisc,
+ * we save skb->tstamp value in skb->cb[] before destroying it.
*/
struct netem_skb_cb {
psched_time_t time_to_send;
ktime_t tstamp_save;
};
-/* Because space in skb->cb[] is tight, netem overloads skb->next/prev/tstamp
- * to hold a rb_node structure.
- *
- * If struct sk_buff layout is changed, the following checks will complain.
- */
-static struct rb_node *netem_rb_node(struct sk_buff *skb)
-{
- BUILD_BUG_ON(offsetof(struct sk_buff, next) != 0);
- BUILD_BUG_ON(offsetof(struct sk_buff, prev) !=
- offsetof(struct sk_buff, next) + sizeof(skb->next));
- BUILD_BUG_ON(offsetof(struct sk_buff, tstamp) !=
- offsetof(struct sk_buff, prev) + sizeof(skb->prev));
- BUILD_BUG_ON(sizeof(struct rb_node) > sizeof(skb->next) +
- sizeof(skb->prev) +
- sizeof(skb->tstamp));
- return (struct rb_node *)&skb->next;
-}
static struct sk_buff *netem_rb_to_skb(struct rb_node *rb)
{
- return (struct sk_buff *)rb;
+ return container_of(rb, struct sk_buff, rbnode);
}
static inline struct netem_skb_cb *netem_skb_cb(struct sk_buff *skb)
@@ -403,8 +390,8 @@ static void tfifo_enqueue(struct sk_buff *nskb, struct Qdisc *sch)
else
p = &parent->rb_left;
}
- rb_link_node(netem_rb_node(nskb), parent, p);
- rb_insert_color(netem_rb_node(nskb), &q->t_root);
+ rb_link_node(&nskb->rbnode, parent, p);
+ rb_insert_color(&nskb->rbnode, &q->t_root);
sch->q.qlen++;
}
diff --git a/net/sctp/auth.c b/net/sctp/auth.c
index 0e8529113dc5..fb7976aee61c 100644
--- a/net/sctp/auth.c
+++ b/net/sctp/auth.c
@@ -862,8 +862,6 @@ int sctp_auth_set_key(struct sctp_endpoint *ep,
list_add(&cur_key->key_list, sh_keys);
cur_key->key = key;
- sctp_auth_key_hold(key);
-
return 0;
nomem:
if (!replace)
diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c
index 158701da2d31..a3380917f197 100644
--- a/net/sctp/chunk.c
+++ b/net/sctp/chunk.c
@@ -164,7 +164,7 @@ static void sctp_datamsg_assign(struct sctp_datamsg *msg, struct sctp_chunk *chu
*/
struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
struct sctp_sndrcvinfo *sinfo,
- struct msghdr *msgh, int msg_len)
+ struct iov_iter *from)
{
int max, whole, i, offset, over, err;
int len, first_len;
@@ -172,6 +172,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
struct sctp_chunk *chunk;
struct sctp_datamsg *msg;
struct list_head *pos, *temp;
+ size_t msg_len = iov_iter_count(from);
__u8 frag;
msg = sctp_datamsg_new(GFP_KERNEL);
@@ -279,12 +280,10 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
goto errout;
}
- err = sctp_user_addto_chunk(chunk, offset, len, msgh->msg_iov);
+ err = sctp_user_addto_chunk(chunk, len, from);
if (err < 0)
goto errout_chunk_free;
- offset += len;
-
/* Put the chunk->skb back into the form expected by send. */
__skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr
- (__u8 *)chunk->skb->data);
@@ -317,7 +316,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
goto errout;
}
- err = sctp_user_addto_chunk(chunk, offset, over, msgh->msg_iov);
+ err = sctp_user_addto_chunk(chunk, over, from);
/* Put the chunk->skb back into the form expected by send. */
__skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr
diff --git a/net/sctp/output.c b/net/sctp/output.c
index 42dffd428389..fc5e45b8a832 100644
--- a/net/sctp/output.c
+++ b/net/sctp/output.c
@@ -401,12 +401,12 @@ int sctp_packet_transmit(struct sctp_packet *packet)
sk = chunk->skb->sk;
/* Allocate the new skb. */
- nskb = alloc_skb(packet->size + LL_MAX_HEADER, GFP_ATOMIC);
+ nskb = alloc_skb(packet->size + MAX_HEADER, GFP_ATOMIC);
if (!nskb)
goto nomem;
/* Make sure the outbound skb has enough header room reserved. */
- skb_reserve(nskb, packet->overhead + LL_MAX_HEADER);
+ skb_reserve(nskb, packet->overhead + MAX_HEADER);
/* Set the owning socket so that we know where to get the
* destination IP address.
diff --git a/net/sctp/proc.c b/net/sctp/proc.c
index 34229ee7f379..0697eda5aed8 100644
--- a/net/sctp/proc.c
+++ b/net/sctp/proc.c
@@ -417,7 +417,7 @@ static void *sctp_remaddr_seq_start(struct seq_file *seq, loff_t *pos)
if (*pos == 0)
seq_printf(seq, "ADDR ASSOC_ID HB_ACT RTO MAX_PATH_RTX "
- "REM_ADDR_RTX START\n");
+ "REM_ADDR_RTX START STATE\n");
return (void *)pos;
}
@@ -490,14 +490,20 @@ static int sctp_remaddr_seq_show(struct seq_file *seq, void *v)
* Note: We don't have a way to tally this at the moment
* so lets just leave it as zero for the moment
*/
- seq_printf(seq, "0 ");
+ seq_puts(seq, "0 ");
/*
* remote address start time (START). This is also not
* currently implemented, but we can record it with a
* jiffies marker in a subsequent patch
*/
- seq_printf(seq, "0");
+ seq_puts(seq, "0 ");
+
+ /*
+ * The current state of this destination. I.e.
+ * SCTP_ACTIVE, SCTP_INACTIVE, ...
+ */
+ seq_printf(seq, "%d", tsp->state);
seq_printf(seq, "\n");
}
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index ab734be8cb20..e49e231cef52 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -1001,7 +1001,7 @@ no_mem:
/* Helper to create ABORT with a SCTP_ERROR_USER_ABORT error. */
struct sctp_chunk *sctp_make_abort_user(const struct sctp_association *asoc,
- const struct msghdr *msg,
+ struct msghdr *msg,
size_t paylen)
{
struct sctp_chunk *retval;
@@ -1018,7 +1018,7 @@ struct sctp_chunk *sctp_make_abort_user(const struct sctp_association *asoc,
if (!payload)
goto err_payload;
- err = memcpy_fromiovec(payload, msg->msg_iov, paylen);
+ err = memcpy_from_msg(payload, msg, paylen);
if (err < 0)
goto err_copy;
}
@@ -1491,26 +1491,26 @@ static void *sctp_addto_chunk_fixed(struct sctp_chunk *chunk,
* chunk is not big enough.
* Returns a kernel err value.
*/
-int sctp_user_addto_chunk(struct sctp_chunk *chunk, int off, int len,
- struct iovec *data)
+int sctp_user_addto_chunk(struct sctp_chunk *chunk, int len,
+ struct iov_iter *from)
{
- __u8 *target;
- int err = 0;
+ void *target;
+ ssize_t copied;
/* Make room in chunk for data. */
target = skb_put(chunk->skb, len);
/* Copy data (whole iovec) into chunk */
- if ((err = memcpy_fromiovecend(target, data, off, len)))
- goto out;
+ copied = copy_from_iter(target, len, from);
+ if (copied != len)
+ return -EFAULT;
/* Adjust the chunk length field. */
chunk->chunk_hdr->length =
htons(ntohs(chunk->chunk_hdr->length) + len);
chunk->chunk_end = skb_tail_pointer(chunk->skb);
-out:
- return err;
+ return 0;
}
/* Helper function to assign a TSN if needed. This assumes that both
@@ -2609,6 +2609,9 @@ do_addr_param:
addr_param = param.v + sizeof(sctp_addip_param_t);
af = sctp_get_af_specific(param_type2af(param.p->type));
+ if (af == NULL)
+ break;
+
af->from_addr_param(&addr, addr_param,
htons(asoc->peer.port), 0);
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 634a2abb5f3a..2625eccb77d5 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -162,7 +162,7 @@ static inline void sctp_set_owner_w(struct sctp_chunk *chunk)
chunk->skb->destructor = sctp_wfree;
/* Save the chunk pointer in skb for sctp_wfree to use later. */
- *((struct sctp_chunk **)(chunk->skb->cb)) = chunk;
+ skb_shinfo(chunk->skb)->destructor_arg = chunk;
asoc->sndbuf_used += SCTP_DATA_SNDSIZE(chunk) +
sizeof(struct sk_buff) +
@@ -1947,7 +1947,7 @@ static int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
}
/* Break the message into multiple chunks of maximum size. */
- datamsg = sctp_datamsg_from_user(asoc, sinfo, msg, msg_len);
+ datamsg = sctp_datamsg_from_user(asoc, sinfo, &msg->msg_iter);
if (IS_ERR(datamsg)) {
err = PTR_ERR(datamsg);
goto out_free;
@@ -2095,7 +2095,7 @@ static int sctp_recvmsg(struct kiocb *iocb, struct sock *sk,
if (copied > len)
copied = len;
- err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ err = skb_copy_datagram_msg(skb, 0, msg, copied);
event = sctp_skb2event(skb);
@@ -6592,8 +6592,7 @@ static int sctp_msghdr_parse(const struct msghdr *msg, sctp_cmsgs_t *cmsgs)
struct cmsghdr *cmsg;
struct msghdr *my_msg = (struct msghdr *)msg;
- for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
- cmsg = CMSG_NXTHDR(my_msg, cmsg)) {
+ for_each_cmsghdr(cmsg, my_msg) {
if (!CMSG_OK(my_msg, cmsg))
return -EINVAL;
@@ -6870,14 +6869,10 @@ static void sctp_wake_up_waiters(struct sock *sk,
*/
static void sctp_wfree(struct sk_buff *skb)
{
- struct sctp_association *asoc;
- struct sctp_chunk *chunk;
- struct sock *sk;
+ struct sctp_chunk *chunk = skb_shinfo(skb)->destructor_arg;
+ struct sctp_association *asoc = chunk->asoc;
+ struct sock *sk = asoc->base.sk;
- /* Get the saved chunk pointer. */
- chunk = *((struct sctp_chunk **)(skb->cb));
- asoc = chunk->asoc;
- sk = asoc->base.sk;
asoc->sndbuf_used -= SCTP_DATA_SNDSIZE(chunk) +
sizeof(struct sk_buff) +
sizeof(struct sctp_chunk);
diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c
index d49dc2ed30ad..ce469d648ffb 100644
--- a/net/sctp/ulpqueue.c
+++ b/net/sctp/ulpqueue.c
@@ -205,9 +205,10 @@ int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event)
if (sock_flag(sk, SOCK_DEAD) || (sk->sk_shutdown & RCV_SHUTDOWN))
goto out_free;
- if (!sctp_ulpevent_is_notification(event))
+ if (!sctp_ulpevent_is_notification(event)) {
sk_mark_napi_id(sk, skb);
-
+ sk_incoming_cpu_update(sk);
+ }
/* Check if the user wishes to receive this event. */
if (!sctp_ulpevent_is_enabled(event, &sctp_sk(sk)->subscribe))
goto out_free;
diff --git a/net/socket.c b/net/socket.c
index fe20c319a0bb..8809afccf7fa 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -651,7 +651,8 @@ static inline int __sock_sendmsg(struct kiocb *iocb, struct socket *sock,
return err ?: __sock_sendmsg_nosec(iocb, sock, msg, size);
}
-int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+static int do_sock_sendmsg(struct socket *sock, struct msghdr *msg,
+ size_t size, bool nosec)
{
struct kiocb iocb;
struct sock_iocb siocb;
@@ -659,25 +660,22 @@ int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
init_sync_kiocb(&iocb, NULL);
iocb.private = &siocb;
- ret = __sock_sendmsg(&iocb, sock, msg, size);
+ ret = nosec ? __sock_sendmsg_nosec(&iocb, sock, msg, size) :
+ __sock_sendmsg(&iocb, sock, msg, size);
if (-EIOCBQUEUED == ret)
ret = wait_on_sync_kiocb(&iocb);
return ret;
}
+
+int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
+{
+ return do_sock_sendmsg(sock, msg, size, false);
+}
EXPORT_SYMBOL(sock_sendmsg);
static int sock_sendmsg_nosec(struct socket *sock, struct msghdr *msg, size_t size)
{
- struct kiocb iocb;
- struct sock_iocb siocb;
- int ret;
-
- init_sync_kiocb(&iocb, NULL);
- iocb.private = &siocb;
- ret = __sock_sendmsg_nosec(&iocb, sock, msg, size);
- if (-EIOCBQUEUED == ret)
- ret = wait_on_sync_kiocb(&iocb);
- return ret;
+ return do_sock_sendmsg(sock, msg, size, true);
}
int kernel_sendmsg(struct socket *sock, struct msghdr *msg,
@@ -691,8 +689,7 @@ int kernel_sendmsg(struct socket *sock, struct msghdr *msg,
* the following is safe, since for compiler definitions of kvec and
* iovec are identical, yielding the same in-core layout and alignment
*/
- msg->msg_iov = (struct iovec *)vec;
- msg->msg_iovlen = num;
+ iov_iter_init(&msg->msg_iter, WRITE, (struct iovec *)vec, num, size);
result = sock_sendmsg(sock, msg, size);
set_fs(oldfs);
return result;
@@ -855,7 +852,7 @@ int kernel_recvmsg(struct socket *sock, struct msghdr *msg,
* the following is safe, since for compiler definitions of kvec and
* iovec are identical, yielding the same in-core layout and alignment
*/
- msg->msg_iov = (struct iovec *)vec, msg->msg_iovlen = num;
+ iov_iter_init(&msg->msg_iter, READ, (struct iovec *)vec, num, size);
result = sock_recvmsg(sock, msg, size, flags);
set_fs(oldfs);
return result;
@@ -915,8 +912,7 @@ static ssize_t do_sock_read(struct msghdr *msg, struct kiocb *iocb,
msg->msg_namelen = 0;
msg->msg_control = NULL;
msg->msg_controllen = 0;
- msg->msg_iov = (struct iovec *)iov;
- msg->msg_iovlen = nr_segs;
+ iov_iter_init(&msg->msg_iter, READ, iov, nr_segs, size);
msg->msg_flags = (file->f_flags & O_NONBLOCK) ? MSG_DONTWAIT : 0;
return __sock_recvmsg(iocb, sock, msg, size, msg->msg_flags);
@@ -955,8 +951,7 @@ static ssize_t do_sock_write(struct msghdr *msg, struct kiocb *iocb,
msg->msg_namelen = 0;
msg->msg_control = NULL;
msg->msg_controllen = 0;
- msg->msg_iov = (struct iovec *)iov;
- msg->msg_iovlen = nr_segs;
+ iov_iter_init(&msg->msg_iter, WRITE, iov, nr_segs, size);
msg->msg_flags = (file->f_flags & O_NONBLOCK) ? MSG_DONTWAIT : 0;
if (sock->type == SOCK_SEQPACKET)
msg->msg_flags |= MSG_EOR;
@@ -1800,8 +1795,7 @@ SYSCALL_DEFINE6(sendto, int, fd, void __user *, buff, size_t, len,
iov.iov_base = buff;
iov.iov_len = len;
msg.msg_name = NULL;
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
+ iov_iter_init(&msg.msg_iter, WRITE, &iov, 1, len);
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_namelen = 0;
@@ -1858,10 +1852,9 @@ SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, ubuf, size_t, size,
msg.msg_control = NULL;
msg.msg_controllen = 0;
- msg.msg_iovlen = 1;
- msg.msg_iov = &iov;
iov.iov_len = size;
iov.iov_base = ubuf;
+ iov_iter_init(&msg.msg_iter, READ, &iov, 1, size);
/* Save some cycles and don't copy the address if not needed */
msg.msg_name = addr ? (struct sockaddr *)&address : NULL;
/* We assume all kernel code knows the size of sockaddr_storage */
@@ -1988,13 +1981,27 @@ struct used_address {
unsigned int name_len;
};
-static int copy_msghdr_from_user(struct msghdr *kmsg,
- struct msghdr __user *umsg)
+static ssize_t copy_msghdr_from_user(struct msghdr *kmsg,
+ struct user_msghdr __user *umsg,
+ struct sockaddr __user **save_addr,
+ struct iovec **iov)
{
- if (copy_from_user(kmsg, umsg, sizeof(struct msghdr)))
+ struct sockaddr __user *uaddr;
+ struct iovec __user *uiov;
+ size_t nr_segs;
+ ssize_t err;
+
+ if (!access_ok(VERIFY_READ, umsg, sizeof(*umsg)) ||
+ __get_user(uaddr, &umsg->msg_name) ||
+ __get_user(kmsg->msg_namelen, &umsg->msg_namelen) ||
+ __get_user(uiov, &umsg->msg_iov) ||
+ __get_user(nr_segs, &umsg->msg_iovlen) ||
+ __get_user(kmsg->msg_control, &umsg->msg_control) ||
+ __get_user(kmsg->msg_controllen, &umsg->msg_controllen) ||
+ __get_user(kmsg->msg_flags, &umsg->msg_flags))
return -EFAULT;
- if (kmsg->msg_name == NULL)
+ if (!uaddr)
kmsg->msg_namelen = 0;
if (kmsg->msg_namelen < 0)
@@ -2002,10 +2009,35 @@ static int copy_msghdr_from_user(struct msghdr *kmsg,
if (kmsg->msg_namelen > sizeof(struct sockaddr_storage))
kmsg->msg_namelen = sizeof(struct sockaddr_storage);
- return 0;
+
+ if (save_addr)
+ *save_addr = uaddr;
+
+ if (uaddr && kmsg->msg_namelen) {
+ if (!save_addr) {
+ err = move_addr_to_kernel(uaddr, kmsg->msg_namelen,
+ kmsg->msg_name);
+ if (err < 0)
+ return err;
+ }
+ } else {
+ kmsg->msg_name = NULL;
+ kmsg->msg_namelen = 0;
+ }
+
+ if (nr_segs > UIO_MAXIOV)
+ return -EMSGSIZE;
+
+ err = rw_copy_check_uvector(save_addr ? READ : WRITE,
+ uiov, nr_segs,
+ UIO_FASTIOV, *iov, iov);
+ if (err >= 0)
+ iov_iter_init(&kmsg->msg_iter, save_addr ? READ : WRITE,
+ *iov, nr_segs, err);
+ return err;
}
-static int ___sys_sendmsg(struct socket *sock, struct msghdr __user *msg,
+static int ___sys_sendmsg(struct socket *sock, struct user_msghdr __user *msg,
struct msghdr *msg_sys, unsigned int flags,
struct used_address *used_address)
{
@@ -2017,34 +2049,15 @@ static int ___sys_sendmsg(struct socket *sock, struct msghdr __user *msg,
__attribute__ ((aligned(sizeof(__kernel_size_t))));
/* 20 is size of ipv6_pktinfo */
unsigned char *ctl_buf = ctl;
- int err, ctl_len, total_len;
-
- err = -EFAULT;
- if (MSG_CMSG_COMPAT & flags) {
- if (get_compat_msghdr(msg_sys, msg_compat))
- return -EFAULT;
- } else {
- err = copy_msghdr_from_user(msg_sys, msg);
- if (err)
- return err;
- }
+ int ctl_len, total_len;
+ ssize_t err;
- if (msg_sys->msg_iovlen > UIO_FASTIOV) {
- err = -EMSGSIZE;
- if (msg_sys->msg_iovlen > UIO_MAXIOV)
- goto out;
- err = -ENOMEM;
- iov = kmalloc(msg_sys->msg_iovlen * sizeof(struct iovec),
- GFP_KERNEL);
- if (!iov)
- goto out;
- }
+ msg_sys->msg_name = &address;
- /* This will also move the address data into kernel space */
- if (MSG_CMSG_COMPAT & flags) {
- err = verify_compat_iovec(msg_sys, iov, &address, VERIFY_READ);
- } else
- err = verify_iovec(msg_sys, iov, &address, VERIFY_READ);
+ if (MSG_CMSG_COMPAT & flags)
+ err = get_compat_msghdr(msg_sys, msg_compat, NULL, &iov);
+ else
+ err = copy_msghdr_from_user(msg_sys, msg, NULL, &iov);
if (err < 0)
goto out_freeiov;
total_len = err;
@@ -2115,7 +2128,6 @@ out_freectl:
out_freeiov:
if (iov != iovstack)
kfree(iov);
-out:
return err;
}
@@ -2123,7 +2135,7 @@ out:
* BSD sendmsg interface
*/
-long __sys_sendmsg(int fd, struct msghdr __user *msg, unsigned flags)
+long __sys_sendmsg(int fd, struct user_msghdr __user *msg, unsigned flags)
{
int fput_needed, err;
struct msghdr msg_sys;
@@ -2140,7 +2152,7 @@ out:
return err;
}
-SYSCALL_DEFINE3(sendmsg, int, fd, struct msghdr __user *, msg, unsigned int, flags)
+SYSCALL_DEFINE3(sendmsg, int, fd, struct user_msghdr __user *, msg, unsigned int, flags)
{
if (flags & MSG_CMSG_COMPAT)
return -EINVAL;
@@ -2177,7 +2189,7 @@ int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,
while (datagrams < vlen) {
if (MSG_CMSG_COMPAT & flags) {
- err = ___sys_sendmsg(sock, (struct msghdr __user *)compat_entry,
+ err = ___sys_sendmsg(sock, (struct user_msghdr __user *)compat_entry,
&msg_sys, flags, &used_address);
if (err < 0)
break;
@@ -2185,7 +2197,7 @@ int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,
++compat_entry;
} else {
err = ___sys_sendmsg(sock,
- (struct msghdr __user *)entry,
+ (struct user_msghdr __user *)entry,
&msg_sys, flags, &used_address);
if (err < 0)
break;
@@ -2215,7 +2227,7 @@ SYSCALL_DEFINE4(sendmmsg, int, fd, struct mmsghdr __user *, mmsg,
return __sys_sendmmsg(fd, mmsg, vlen, flags);
}
-static int ___sys_recvmsg(struct socket *sock, struct msghdr __user *msg,
+static int ___sys_recvmsg(struct socket *sock, struct user_msghdr __user *msg,
struct msghdr *msg_sys, unsigned int flags, int nosec)
{
struct compat_msghdr __user *msg_compat =
@@ -2223,44 +2235,22 @@ static int ___sys_recvmsg(struct socket *sock, struct msghdr __user *msg,
struct iovec iovstack[UIO_FASTIOV];
struct iovec *iov = iovstack;
unsigned long cmsg_ptr;
- int err, total_len, len;
+ int total_len, len;
+ ssize_t err;
/* kernel mode address */
struct sockaddr_storage addr;
/* user mode address pointers */
struct sockaddr __user *uaddr;
- int __user *uaddr_len;
-
- if (MSG_CMSG_COMPAT & flags) {
- if (get_compat_msghdr(msg_sys, msg_compat))
- return -EFAULT;
- } else {
- err = copy_msghdr_from_user(msg_sys, msg);
- if (err)
- return err;
- }
+ int __user *uaddr_len = COMPAT_NAMELEN(msg);
- if (msg_sys->msg_iovlen > UIO_FASTIOV) {
- err = -EMSGSIZE;
- if (msg_sys->msg_iovlen > UIO_MAXIOV)
- goto out;
- err = -ENOMEM;
- iov = kmalloc(msg_sys->msg_iovlen * sizeof(struct iovec),
- GFP_KERNEL);
- if (!iov)
- goto out;
- }
+ msg_sys->msg_name = &addr;
- /* Save the user-mode address (verify_iovec will change the
- * kernel msghdr to use the kernel address space)
- */
- uaddr = (__force void __user *)msg_sys->msg_name;
- uaddr_len = COMPAT_NAMELEN(msg);
if (MSG_CMSG_COMPAT & flags)
- err = verify_compat_iovec(msg_sys, iov, &addr, VERIFY_WRITE);
+ err = get_compat_msghdr(msg_sys, msg_compat, &uaddr, &iov);
else
- err = verify_iovec(msg_sys, iov, &addr, VERIFY_WRITE);
+ err = copy_msghdr_from_user(msg_sys, msg, &uaddr, &iov);
if (err < 0)
goto out_freeiov;
total_len = err;
@@ -2303,7 +2293,6 @@ static int ___sys_recvmsg(struct socket *sock, struct msghdr __user *msg,
out_freeiov:
if (iov != iovstack)
kfree(iov);
-out:
return err;
}
@@ -2311,7 +2300,7 @@ out:
* BSD recvmsg interface
*/
-long __sys_recvmsg(int fd, struct msghdr __user *msg, unsigned flags)
+long __sys_recvmsg(int fd, struct user_msghdr __user *msg, unsigned flags)
{
int fput_needed, err;
struct msghdr msg_sys;
@@ -2328,7 +2317,7 @@ out:
return err;
}
-SYSCALL_DEFINE3(recvmsg, int, fd, struct msghdr __user *, msg,
+SYSCALL_DEFINE3(recvmsg, int, fd, struct user_msghdr __user *, msg,
unsigned int, flags)
{
if (flags & MSG_CMSG_COMPAT)
@@ -2373,7 +2362,7 @@ int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,
* No need to ask LSM for more than the first datagram.
*/
if (MSG_CMSG_COMPAT & flags) {
- err = ___sys_recvmsg(sock, (struct msghdr __user *)compat_entry,
+ err = ___sys_recvmsg(sock, (struct user_msghdr __user *)compat_entry,
&msg_sys, flags & ~MSG_WAITFORONE,
datagrams);
if (err < 0)
@@ -2382,7 +2371,7 @@ int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen,
++compat_entry;
} else {
err = ___sys_recvmsg(sock,
- (struct msghdr __user *)entry,
+ (struct user_msghdr __user *)entry,
&msg_sys, flags & ~MSG_WAITFORONE,
datagrams);
if (err < 0)
@@ -2571,13 +2560,13 @@ SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)
(int __user *)a[4]);
break;
case SYS_SENDMSG:
- err = sys_sendmsg(a0, (struct msghdr __user *)a1, a[2]);
+ err = sys_sendmsg(a0, (struct user_msghdr __user *)a1, a[2]);
break;
case SYS_SENDMMSG:
err = sys_sendmmsg(a0, (struct mmsghdr __user *)a1, a[2], a[3]);
break;
case SYS_RECVMSG:
- err = sys_recvmsg(a0, (struct msghdr __user *)a1, a[2]);
+ err = sys_recvmsg(a0, (struct user_msghdr __user *)a1, a[2]);
break;
case SYS_RECVMMSG:
err = sys_recvmmsg(a0, (struct mmsghdr __user *)a1, a[2], a[3],
diff --git a/net/sunrpc/Kconfig b/net/sunrpc/Kconfig
index 0754d0f466d2..fb78117b896c 100644
--- a/net/sunrpc/Kconfig
+++ b/net/sunrpc/Kconfig
@@ -35,6 +35,7 @@ config RPCSEC_GSS_KRB5
config SUNRPC_DEBUG
bool "RPC: Enable dprintk debugging"
depends on SUNRPC && SYSCTL
+ select DEBUG_FS
help
This option enables a sysctl-based debugging interface
that is be used by the 'rpcdebug' utility to turn on or off
diff --git a/net/sunrpc/Makefile b/net/sunrpc/Makefile
index e5a7a1cac8f3..15e6f6c23c5d 100644
--- a/net/sunrpc/Makefile
+++ b/net/sunrpc/Makefile
@@ -14,6 +14,7 @@ sunrpc-y := clnt.o xprt.o socklib.o xprtsock.o sched.o \
addr.o rpcb_clnt.o timer.o xdr.o \
sunrpc_syms.o cache.o rpc_pipe.o \
svc_xprt.o
+sunrpc-$(CONFIG_SUNRPC_DEBUG) += debugfs.o
sunrpc-$(CONFIG_SUNRPC_BACKCHANNEL) += backchannel_rqst.o bc_svc.o
sunrpc-$(CONFIG_PROC_FS) += stats.o
sunrpc-$(CONFIG_SYSCTL) += sysctl.o
diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c
index 383eb919ac0b..47f38be4155f 100644
--- a/net/sunrpc/auth.c
+++ b/net/sunrpc/auth.c
@@ -16,7 +16,7 @@
#include <linux/sunrpc/gss_api.h>
#include <linux/spinlock.h>
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
@@ -646,7 +646,7 @@ rpcauth_init_cred(struct rpc_cred *cred, const struct auth_cred *acred,
cred->cr_auth = auth;
cred->cr_ops = ops;
cred->cr_expire = jiffies;
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
cred->cr_magic = RPCAUTH_CRED_MAGIC;
#endif
cred->cr_uid = acred->uid;
diff --git a/net/sunrpc/auth_generic.c b/net/sunrpc/auth_generic.c
index 6f6b829c9e8e..41248b1820c7 100644
--- a/net/sunrpc/auth_generic.c
+++ b/net/sunrpc/auth_generic.c
@@ -14,7 +14,7 @@
#include <linux/sunrpc/debug.h>
#include <linux/sunrpc/sched.h>
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index afb292cd797d..dace13d7638e 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -66,7 +66,7 @@ static unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED;
#define GSS_KEY_EXPIRE_TIMEO 240
static unsigned int gss_key_expire_timeo = GSS_KEY_EXPIRE_TIMEO;
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
@@ -1353,6 +1353,7 @@ gss_stringify_acceptor(struct rpc_cred *cred)
char *string = NULL;
struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
struct gss_cl_ctx *ctx;
+ unsigned int len;
struct xdr_netobj *acceptor;
rcu_read_lock();
@@ -1360,15 +1361,39 @@ gss_stringify_acceptor(struct rpc_cred *cred)
if (!ctx)
goto out;
- acceptor = &ctx->gc_acceptor;
+ len = ctx->gc_acceptor.len;
+ rcu_read_unlock();
/* no point if there's no string */
- if (!acceptor->len)
- goto out;
-
- string = kmalloc(acceptor->len + 1, GFP_KERNEL);
+ if (!len)
+ return NULL;
+realloc:
+ string = kmalloc(len + 1, GFP_KERNEL);
if (!string)
+ return NULL;
+
+ rcu_read_lock();
+ ctx = rcu_dereference(gss_cred->gc_ctx);
+
+ /* did the ctx disappear or was it replaced by one with no acceptor? */
+ if (!ctx || !ctx->gc_acceptor.len) {
+ kfree(string);
+ string = NULL;
goto out;
+ }
+
+ acceptor = &ctx->gc_acceptor;
+
+ /*
+ * Did we find a new acceptor that's longer than the original? Allocate
+ * a longer buffer and try again.
+ */
+ if (len < acceptor->len) {
+ len = acceptor->len;
+ rcu_read_unlock();
+ kfree(string);
+ goto realloc;
+ }
memcpy(string, acceptor->data, acceptor->len);
string[acceptor->len] = '\0';
diff --git a/net/sunrpc/auth_gss/gss_generic_token.c b/net/sunrpc/auth_gss/gss_generic_token.c
index c586e92bcf76..254defe446a7 100644
--- a/net/sunrpc/auth_gss/gss_generic_token.c
+++ b/net/sunrpc/auth_gss/gss_generic_token.c
@@ -38,7 +38,7 @@
#include <linux/sunrpc/gss_asn1.h>
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
diff --git a/net/sunrpc/auth_gss/gss_krb5_crypto.c b/net/sunrpc/auth_gss/gss_krb5_crypto.c
index f5ed9f6ece06..b5408e8a37f2 100644
--- a/net/sunrpc/auth_gss/gss_krb5_crypto.c
+++ b/net/sunrpc/auth_gss/gss_krb5_crypto.c
@@ -45,7 +45,7 @@
#include <linux/sunrpc/gss_krb5.h>
#include <linux/sunrpc/xdr.h>
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
diff --git a/net/sunrpc/auth_gss/gss_krb5_keys.c b/net/sunrpc/auth_gss/gss_krb5_keys.c
index 24589bd2a4b6..234fa8d0fd9b 100644
--- a/net/sunrpc/auth_gss/gss_krb5_keys.c
+++ b/net/sunrpc/auth_gss/gss_krb5_keys.c
@@ -61,7 +61,7 @@
#include <linux/sunrpc/xdr.h>
#include <linux/lcm.h>
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c
index 0d3c158ef8fa..28db442a0034 100644
--- a/net/sunrpc/auth_gss/gss_krb5_mech.c
+++ b/net/sunrpc/auth_gss/gss_krb5_mech.c
@@ -45,7 +45,7 @@
#include <linux/crypto.h>
#include <linux/sunrpc/gss_krb5_enctypes.h>
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
diff --git a/net/sunrpc/auth_gss/gss_krb5_seal.c b/net/sunrpc/auth_gss/gss_krb5_seal.c
index 42768e5c3994..1d74d653e6c0 100644
--- a/net/sunrpc/auth_gss/gss_krb5_seal.c
+++ b/net/sunrpc/auth_gss/gss_krb5_seal.c
@@ -64,7 +64,7 @@
#include <linux/random.h>
#include <linux/crypto.h>
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
diff --git a/net/sunrpc/auth_gss/gss_krb5_seqnum.c b/net/sunrpc/auth_gss/gss_krb5_seqnum.c
index 62ac90c62cb1..20d55c793eb6 100644
--- a/net/sunrpc/auth_gss/gss_krb5_seqnum.c
+++ b/net/sunrpc/auth_gss/gss_krb5_seqnum.c
@@ -35,7 +35,7 @@
#include <linux/sunrpc/gss_krb5.h>
#include <linux/crypto.h>
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
diff --git a/net/sunrpc/auth_gss/gss_krb5_unseal.c b/net/sunrpc/auth_gss/gss_krb5_unseal.c
index 6c981ddc19f8..dcf9515d9aef 100644
--- a/net/sunrpc/auth_gss/gss_krb5_unseal.c
+++ b/net/sunrpc/auth_gss/gss_krb5_unseal.c
@@ -62,7 +62,7 @@
#include <linux/sunrpc/gss_krb5.h>
#include <linux/crypto.h>
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
diff --git a/net/sunrpc/auth_gss/gss_krb5_wrap.c b/net/sunrpc/auth_gss/gss_krb5_wrap.c
index 4b614c604fe0..ca7e92a32f84 100644
--- a/net/sunrpc/auth_gss/gss_krb5_wrap.c
+++ b/net/sunrpc/auth_gss/gss_krb5_wrap.c
@@ -35,7 +35,7 @@
#include <linux/pagemap.h>
#include <linux/crypto.h>
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
diff --git a/net/sunrpc/auth_gss/gss_mech_switch.c b/net/sunrpc/auth_gss/gss_mech_switch.c
index 92d5ab99fbf3..7063d856a598 100644
--- a/net/sunrpc/auth_gss/gss_mech_switch.c
+++ b/net/sunrpc/auth_gss/gss_mech_switch.c
@@ -46,7 +46,7 @@
#include <linux/sunrpc/gss_api.h>
#include <linux/sunrpc/clnt.h>
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.h b/net/sunrpc/auth_gss/gss_rpc_xdr.h
index 685a688f3d8a..9d88c6239f01 100644
--- a/net/sunrpc/auth_gss/gss_rpc_xdr.h
+++ b/net/sunrpc/auth_gss/gss_rpc_xdr.h
@@ -25,7 +25,7 @@
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/xprtsock.h>
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index c548ab213f76..de856ddf5fed 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -51,7 +51,7 @@
#include "gss_rpc_upcall.h"
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
diff --git a/net/sunrpc/auth_null.c b/net/sunrpc/auth_null.c
index 712c123e04e9..c2a2b584a056 100644
--- a/net/sunrpc/auth_null.c
+++ b/net/sunrpc/auth_null.c
@@ -10,7 +10,7 @@
#include <linux/module.h>
#include <linux/sunrpc/clnt.h>
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
@@ -138,7 +138,7 @@ struct rpc_cred null_cred = {
.cr_ops = &null_credops,
.cr_count = ATOMIC_INIT(1),
.cr_flags = 1UL << RPCAUTH_CRED_UPTODATE,
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
.cr_magic = RPCAUTH_CRED_MAGIC,
#endif
};
diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c
index d5d692366294..4feda2d0a833 100644
--- a/net/sunrpc/auth_unix.c
+++ b/net/sunrpc/auth_unix.c
@@ -25,7 +25,7 @@ struct unx_cred {
#define UNX_WRITESLACK (21 + (UNX_MAXNODENAME >> 2))
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
diff --git a/net/sunrpc/backchannel_rqst.c b/net/sunrpc/backchannel_rqst.c
index 9761a0da964d..651f49ab601f 100644
--- a/net/sunrpc/backchannel_rqst.c
+++ b/net/sunrpc/backchannel_rqst.c
@@ -27,7 +27,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <linux/export.h>
#include <linux/sunrpc/bc_xprt.h>
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
#define RPCDBG_FACILITY RPCDBG_TRANS
#endif
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index 9acd6ce88db7..05da12a33945 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -42,7 +42,7 @@
#include "sunrpc.h"
#include "netns.h"
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_CALL
#endif
@@ -305,6 +305,10 @@ static int rpc_client_register(struct rpc_clnt *clnt,
struct super_block *pipefs_sb;
int err;
+ err = rpc_clnt_debugfs_register(clnt);
+ if (err)
+ return err;
+
pipefs_sb = rpc_get_sb_net(net);
if (pipefs_sb) {
err = rpc_setup_pipedir(pipefs_sb, clnt);
@@ -331,6 +335,7 @@ err_auth:
out:
if (pipefs_sb)
rpc_put_sb_net(net);
+ rpc_clnt_debugfs_unregister(clnt);
return err;
}
@@ -670,6 +675,7 @@ int rpc_switch_client_transport(struct rpc_clnt *clnt,
rpc_unregister_client(clnt);
__rpc_clnt_remove_pipedir(clnt);
+ rpc_clnt_debugfs_unregister(clnt);
/*
* A new transport was created. "clnt" therefore
@@ -771,6 +777,7 @@ rpc_free_client(struct rpc_clnt *clnt)
rcu_dereference(clnt->cl_xprt)->servername);
if (clnt->cl_parent != clnt)
parent = clnt->cl_parent;
+ rpc_clnt_debugfs_unregister(clnt);
rpc_clnt_remove_pipedir(clnt);
rpc_unregister_client(clnt);
rpc_free_iostats(clnt->cl_metrics);
@@ -1396,8 +1403,9 @@ rpc_restart_call(struct rpc_task *task)
}
EXPORT_SYMBOL_GPL(rpc_restart_call);
-#ifdef RPC_DEBUG
-static const char *rpc_proc_name(const struct rpc_task *task)
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
+const char
+*rpc_proc_name(const struct rpc_task *task)
{
const struct rpc_procinfo *proc = task->tk_msg.rpc_proc;
@@ -2421,7 +2429,7 @@ struct rpc_task *rpc_call_null(struct rpc_clnt *clnt, struct rpc_cred *cred, int
}
EXPORT_SYMBOL_GPL(rpc_call_null);
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
static void rpc_show_header(void)
{
printk(KERN_INFO "-pid- flgs status -client- --rqstp- "
diff --git a/net/sunrpc/debugfs.c b/net/sunrpc/debugfs.c
new file mode 100644
index 000000000000..e811f390f9f6
--- /dev/null
+++ b/net/sunrpc/debugfs.c
@@ -0,0 +1,292 @@
+/**
+ * debugfs interface for sunrpc
+ *
+ * (c) 2014 Jeff Layton <jlayton@primarydata.com>
+ */
+
+#include <linux/debugfs.h>
+#include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/clnt.h>
+#include "netns.h"
+
+static struct dentry *topdir;
+static struct dentry *rpc_clnt_dir;
+static struct dentry *rpc_xprt_dir;
+
+struct rpc_clnt_iter {
+ struct rpc_clnt *clnt;
+ loff_t pos;
+};
+
+static int
+tasks_show(struct seq_file *f, void *v)
+{
+ u32 xid = 0;
+ struct rpc_task *task = v;
+ struct rpc_clnt *clnt = task->tk_client;
+ const char *rpc_waitq = "none";
+
+ if (RPC_IS_QUEUED(task))
+ rpc_waitq = rpc_qname(task->tk_waitqueue);
+
+ if (task->tk_rqstp)
+ xid = be32_to_cpu(task->tk_rqstp->rq_xid);
+
+ seq_printf(f, "%5u %04x %6d 0x%x 0x%x %8ld %ps %sv%u %s a:%ps q:%s\n",
+ task->tk_pid, task->tk_flags, task->tk_status,
+ clnt->cl_clid, xid, task->tk_timeout, task->tk_ops,
+ clnt->cl_program->name, clnt->cl_vers, rpc_proc_name(task),
+ task->tk_action, rpc_waitq);
+ return 0;
+}
+
+static void *
+tasks_start(struct seq_file *f, loff_t *ppos)
+ __acquires(&clnt->cl_lock)
+{
+ struct rpc_clnt_iter *iter = f->private;
+ loff_t pos = *ppos;
+ struct rpc_clnt *clnt = iter->clnt;
+ struct rpc_task *task;
+
+ iter->pos = pos + 1;
+ spin_lock(&clnt->cl_lock);
+ list_for_each_entry(task, &clnt->cl_tasks, tk_task)
+ if (pos-- == 0)
+ return task;
+ return NULL;
+}
+
+static void *
+tasks_next(struct seq_file *f, void *v, loff_t *pos)
+{
+ struct rpc_clnt_iter *iter = f->private;
+ struct rpc_clnt *clnt = iter->clnt;
+ struct rpc_task *task = v;
+ struct list_head *next = task->tk_task.next;
+
+ ++iter->pos;
+ ++*pos;
+
+ /* If there's another task on list, return it */
+ if (next == &clnt->cl_tasks)
+ return NULL;
+ return list_entry(next, struct rpc_task, tk_task);
+}
+
+static void
+tasks_stop(struct seq_file *f, void *v)
+ __releases(&clnt->cl_lock)
+{
+ struct rpc_clnt_iter *iter = f->private;
+ struct rpc_clnt *clnt = iter->clnt;
+
+ spin_unlock(&clnt->cl_lock);
+}
+
+static const struct seq_operations tasks_seq_operations = {
+ .start = tasks_start,
+ .next = tasks_next,
+ .stop = tasks_stop,
+ .show = tasks_show,
+};
+
+static int tasks_open(struct inode *inode, struct file *filp)
+{
+ int ret = seq_open_private(filp, &tasks_seq_operations,
+ sizeof(struct rpc_clnt_iter));
+
+ if (!ret) {
+ struct seq_file *seq = filp->private_data;
+ struct rpc_clnt_iter *iter = seq->private;
+
+ iter->clnt = inode->i_private;
+
+ if (!atomic_inc_not_zero(&iter->clnt->cl_count)) {
+ seq_release_private(inode, filp);
+ ret = -EINVAL;
+ }
+ }
+
+ return ret;
+}
+
+static int
+tasks_release(struct inode *inode, struct file *filp)
+{
+ struct seq_file *seq = filp->private_data;
+ struct rpc_clnt_iter *iter = seq->private;
+
+ rpc_release_client(iter->clnt);
+ return seq_release_private(inode, filp);
+}
+
+static const struct file_operations tasks_fops = {
+ .owner = THIS_MODULE,
+ .open = tasks_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = tasks_release,
+};
+
+int
+rpc_clnt_debugfs_register(struct rpc_clnt *clnt)
+{
+ int len, err;
+ char name[24]; /* enough for "../../rpc_xprt/ + 8 hex digits + NULL */
+
+ /* Already registered? */
+ if (clnt->cl_debugfs)
+ return 0;
+
+ len = snprintf(name, sizeof(name), "%x", clnt->cl_clid);
+ if (len >= sizeof(name))
+ return -EINVAL;
+
+ /* make the per-client dir */
+ clnt->cl_debugfs = debugfs_create_dir(name, rpc_clnt_dir);
+ if (!clnt->cl_debugfs)
+ return -ENOMEM;
+
+ /* make tasks file */
+ err = -ENOMEM;
+ if (!debugfs_create_file("tasks", S_IFREG | S_IRUSR, clnt->cl_debugfs,
+ clnt, &tasks_fops))
+ goto out_err;
+
+ err = -EINVAL;
+ rcu_read_lock();
+ len = snprintf(name, sizeof(name), "../../rpc_xprt/%s",
+ rcu_dereference(clnt->cl_xprt)->debugfs->d_name.name);
+ rcu_read_unlock();
+ if (len >= sizeof(name))
+ goto out_err;
+
+ err = -ENOMEM;
+ if (!debugfs_create_symlink("xprt", clnt->cl_debugfs, name))
+ goto out_err;
+
+ return 0;
+out_err:
+ debugfs_remove_recursive(clnt->cl_debugfs);
+ clnt->cl_debugfs = NULL;
+ return err;
+}
+
+void
+rpc_clnt_debugfs_unregister(struct rpc_clnt *clnt)
+{
+ debugfs_remove_recursive(clnt->cl_debugfs);
+ clnt->cl_debugfs = NULL;
+}
+
+static int
+xprt_info_show(struct seq_file *f, void *v)
+{
+ struct rpc_xprt *xprt = f->private;
+
+ seq_printf(f, "netid: %s\n", xprt->address_strings[RPC_DISPLAY_NETID]);
+ seq_printf(f, "addr: %s\n", xprt->address_strings[RPC_DISPLAY_ADDR]);
+ seq_printf(f, "port: %s\n", xprt->address_strings[RPC_DISPLAY_PORT]);
+ seq_printf(f, "state: 0x%lx\n", xprt->state);
+ return 0;
+}
+
+static int
+xprt_info_open(struct inode *inode, struct file *filp)
+{
+ int ret;
+ struct rpc_xprt *xprt = inode->i_private;
+
+ ret = single_open(filp, xprt_info_show, xprt);
+
+ if (!ret) {
+ if (!xprt_get(xprt)) {
+ single_release(inode, filp);
+ ret = -EINVAL;
+ }
+ }
+ return ret;
+}
+
+static int
+xprt_info_release(struct inode *inode, struct file *filp)
+{
+ struct rpc_xprt *xprt = inode->i_private;
+
+ xprt_put(xprt);
+ return single_release(inode, filp);
+}
+
+static const struct file_operations xprt_info_fops = {
+ .owner = THIS_MODULE,
+ .open = xprt_info_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = xprt_info_release,
+};
+
+int
+rpc_xprt_debugfs_register(struct rpc_xprt *xprt)
+{
+ int len, id;
+ static atomic_t cur_id;
+ char name[9]; /* 8 hex digits + NULL term */
+
+ id = (unsigned int)atomic_inc_return(&cur_id);
+
+ len = snprintf(name, sizeof(name), "%x", id);
+ if (len >= sizeof(name))
+ return -EINVAL;
+
+ /* make the per-client dir */
+ xprt->debugfs = debugfs_create_dir(name, rpc_xprt_dir);
+ if (!xprt->debugfs)
+ return -ENOMEM;
+
+ /* make tasks file */
+ if (!debugfs_create_file("info", S_IFREG | S_IRUSR, xprt->debugfs,
+ xprt, &xprt_info_fops)) {
+ debugfs_remove_recursive(xprt->debugfs);
+ xprt->debugfs = NULL;
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+void
+rpc_xprt_debugfs_unregister(struct rpc_xprt *xprt)
+{
+ debugfs_remove_recursive(xprt->debugfs);
+ xprt->debugfs = NULL;
+}
+
+void __exit
+sunrpc_debugfs_exit(void)
+{
+ debugfs_remove_recursive(topdir);
+}
+
+int __init
+sunrpc_debugfs_init(void)
+{
+ topdir = debugfs_create_dir("sunrpc", NULL);
+ if (!topdir)
+ goto out;
+
+ rpc_clnt_dir = debugfs_create_dir("rpc_clnt", topdir);
+ if (!rpc_clnt_dir)
+ goto out_remove;
+
+ rpc_xprt_dir = debugfs_create_dir("rpc_xprt", topdir);
+ if (!rpc_xprt_dir)
+ goto out_remove;
+
+ return 0;
+out_remove:
+ debugfs_remove_recursive(topdir);
+ topdir = NULL;
+out:
+ return -ENOMEM;
+}
diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c
index 1891a1022c17..05202012bcfc 100644
--- a/net/sunrpc/rpcb_clnt.c
+++ b/net/sunrpc/rpcb_clnt.c
@@ -32,7 +32,7 @@
#include "netns.h"
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_BIND
#endif
diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c
index fe3441abdbe5..d20f2329eea3 100644
--- a/net/sunrpc/sched.c
+++ b/net/sunrpc/sched.c
@@ -24,7 +24,7 @@
#include "sunrpc.h"
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
#define RPCDBG_FACILITY RPCDBG_SCHED
#endif
@@ -258,7 +258,7 @@ static int rpc_wait_bit_killable(struct wait_bit_key *key)
return 0;
}
-#if defined(RPC_DEBUG) || defined(RPC_TRACEPOINTS)
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG) || IS_ENABLED(CONFIG_TRACEPOINTS)
static void rpc_task_set_debuginfo(struct rpc_task *task)
{
static atomic_t rpc_pid;
diff --git a/net/sunrpc/stats.c b/net/sunrpc/stats.c
index 54530490944e..9711a155bc50 100644
--- a/net/sunrpc/stats.c
+++ b/net/sunrpc/stats.c
@@ -116,7 +116,15 @@ EXPORT_SYMBOL_GPL(svc_seq_show);
*/
struct rpc_iostats *rpc_alloc_iostats(struct rpc_clnt *clnt)
{
- return kcalloc(clnt->cl_maxproc, sizeof(struct rpc_iostats), GFP_KERNEL);
+ struct rpc_iostats *stats;
+ int i;
+
+ stats = kcalloc(clnt->cl_maxproc, sizeof(*stats), GFP_KERNEL);
+ if (stats) {
+ for (i = 0; i < clnt->cl_maxproc; i++)
+ spin_lock_init(&stats[i].om_lock);
+ }
+ return stats;
}
EXPORT_SYMBOL_GPL(rpc_alloc_iostats);
@@ -135,20 +143,21 @@ EXPORT_SYMBOL_GPL(rpc_free_iostats);
* rpc_count_iostats - tally up per-task stats
* @task: completed rpc_task
* @stats: array of stat structures
- *
- * Relies on the caller for serialization.
*/
void rpc_count_iostats(const struct rpc_task *task, struct rpc_iostats *stats)
{
struct rpc_rqst *req = task->tk_rqstp;
struct rpc_iostats *op_metrics;
- ktime_t delta;
+ ktime_t delta, now;
if (!stats || !req)
return;
+ now = ktime_get();
op_metrics = &stats[task->tk_msg.rpc_proc->p_statidx];
+ spin_lock(&op_metrics->om_lock);
+
op_metrics->om_ops++;
op_metrics->om_ntrans += req->rq_ntrans;
op_metrics->om_timeouts += task->tk_timeouts;
@@ -161,8 +170,10 @@ void rpc_count_iostats(const struct rpc_task *task, struct rpc_iostats *stats)
op_metrics->om_rtt = ktime_add(op_metrics->om_rtt, req->rq_rtt);
- delta = ktime_sub(ktime_get(), task->tk_start);
+ delta = ktime_sub(now, task->tk_start);
op_metrics->om_execute = ktime_add(op_metrics->om_execute, delta);
+
+ spin_unlock(&op_metrics->om_lock);
}
EXPORT_SYMBOL_GPL(rpc_count_iostats);
diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c
index cd30120de9e4..e37fbed87956 100644
--- a/net/sunrpc/sunrpc_syms.c
+++ b/net/sunrpc/sunrpc_syms.c
@@ -97,13 +97,20 @@ init_sunrpc(void)
err = register_rpc_pipefs();
if (err)
goto out4;
-#ifdef RPC_DEBUG
+
+ err = sunrpc_debugfs_init();
+ if (err)
+ goto out5;
+
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
rpc_register_sysctl();
#endif
svc_init_xprt_sock(); /* svc sock transport */
init_socket_xprt(); /* clnt sock transport */
return 0;
+out5:
+ unregister_rpc_pipefs();
out4:
unregister_pernet_subsys(&sunrpc_net_ops);
out3:
@@ -120,10 +127,11 @@ cleanup_sunrpc(void)
rpcauth_remove_module();
cleanup_socket_xprt();
svc_cleanup_xprt_sock();
+ sunrpc_debugfs_exit();
unregister_rpc_pipefs();
rpc_destroy_mempool();
unregister_pernet_subsys(&sunrpc_net_ops);
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
rpc_unregister_sysctl();
#endif
rcu_barrier(); /* Wait for completion of call_rcu()'s */
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index ca8a7958f4e6..2783fd80c229 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -28,6 +28,8 @@
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/bc_xprt.h>
+#include <trace/events/sunrpc.h>
+
#define RPCDBG_FACILITY RPCDBG_SVCDSP
static void svc_unregister(const struct svc_serv *serv, struct net *net);
@@ -1040,7 +1042,7 @@ static void svc_unregister(const struct svc_serv *serv, struct net *net)
/*
* dprintk the given error with the address of the client that caused it.
*/
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
static __printf(2, 3)
void svc_printk(struct svc_rqst *rqstp, const char *fmt, ...)
{
@@ -1314,24 +1316,25 @@ svc_process(struct svc_rqst *rqstp)
rqstp->rq_res.tail[0].iov_base = NULL;
rqstp->rq_res.tail[0].iov_len = 0;
- rqstp->rq_xid = svc_getu32(argv);
-
dir = svc_getnl(argv);
if (dir != 0) {
/* direction != CALL */
svc_printk(rqstp, "bad direction %d, dropping request\n", dir);
serv->sv_stats->rpcbadfmt++;
- svc_drop(rqstp);
- return 0;
+ goto out_drop;
}
/* Returns 1 for send, 0 for drop */
- if (svc_process_common(rqstp, argv, resv))
- return svc_send(rqstp);
- else {
- svc_drop(rqstp);
- return 0;
+ if (likely(svc_process_common(rqstp, argv, resv))) {
+ int ret = svc_send(rqstp);
+
+ trace_svc_process(rqstp, ret);
+ return ret;
}
+out_drop:
+ trace_svc_process(rqstp, 0);
+ svc_drop(rqstp);
+ return 0;
}
#if defined(CONFIG_SUNRPC_BACKCHANNEL)
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c
index c179ca2a5aa4..bbb3b044b877 100644
--- a/net/sunrpc/svc_xprt.c
+++ b/net/sunrpc/svc_xprt.c
@@ -15,6 +15,7 @@
#include <linux/sunrpc/svcsock.h>
#include <linux/sunrpc/xprt.h>
#include <linux/module.h>
+#include <trace/events/sunrpc.h>
#define RPCDBG_FACILITY RPCDBG_SVCXPRT
@@ -773,35 +774,43 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)
err = svc_alloc_arg(rqstp);
if (err)
- return err;
+ goto out;
try_to_freeze();
cond_resched();
+ err = -EINTR;
if (signalled() || kthread_should_stop())
- return -EINTR;
+ goto out;
xprt = svc_get_next_xprt(rqstp, timeout);
- if (IS_ERR(xprt))
- return PTR_ERR(xprt);
+ if (IS_ERR(xprt)) {
+ err = PTR_ERR(xprt);
+ goto out;
+ }
len = svc_handle_xprt(rqstp, xprt);
/* No data, incomplete (TCP) read, or accept() */
+ err = -EAGAIN;
if (len <= 0)
- goto out;
+ goto out_release;
clear_bit(XPT_OLD, &xprt->xpt_flags);
rqstp->rq_secure = xprt->xpt_ops->xpo_secure_port(rqstp);
rqstp->rq_chandle.defer = svc_defer;
+ rqstp->rq_xid = svc_getu32(&rqstp->rq_arg.head[0]);
if (serv->sv_stats)
serv->sv_stats->netcnt++;
+ trace_svc_recv(rqstp, len);
return len;
-out:
+out_release:
rqstp->rq_res.len = 0;
svc_xprt_release(rqstp);
- return -EAGAIN;
+out:
+ trace_svc_recv(rqstp, err);
+ return err;
}
EXPORT_SYMBOL_GPL(svc_recv);
@@ -821,12 +830,12 @@ EXPORT_SYMBOL_GPL(svc_drop);
int svc_send(struct svc_rqst *rqstp)
{
struct svc_xprt *xprt;
- int len;
+ int len = -EFAULT;
struct xdr_buf *xb;
xprt = rqstp->rq_xprt;
if (!xprt)
- return -EFAULT;
+ goto out;
/* release the receive skb before sending the reply */
rqstp->rq_xprt->xpt_ops->xpo_release_rqst(rqstp);
@@ -849,7 +858,9 @@ int svc_send(struct svc_rqst *rqstp)
svc_xprt_release(rqstp);
if (len == -ECONNREFUSED || len == -ENOTCONN || len == -EAGAIN)
- return 0;
+ len = 0;
+out:
+ trace_svc_send(rqstp, len);
return len;
}
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index 3f959c681885..f9c052d508f0 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -1019,17 +1019,12 @@ static int receive_cb_reply(struct svc_sock *svsk, struct svc_rqst *rqstp)
xid = *p++;
calldir = *p;
- if (bc_xprt)
- req = xprt_lookup_rqst(bc_xprt, xid);
-
- if (!req) {
- printk(KERN_NOTICE
- "%s: Got unrecognized reply: "
- "calldir 0x%x xpt_bc_xprt %p xid %08x\n",
- __func__, ntohl(calldir),
- bc_xprt, ntohl(xid));
+ if (!bc_xprt)
return -EAGAIN;
- }
+ spin_lock_bh(&bc_xprt->transport_lock);
+ req = xprt_lookup_rqst(bc_xprt, xid);
+ if (!req)
+ goto unlock_notfound;
memcpy(&req->rq_private_buf, &req->rq_rcv_buf, sizeof(struct xdr_buf));
/*
@@ -1040,11 +1035,21 @@ static int receive_cb_reply(struct svc_sock *svsk, struct svc_rqst *rqstp)
dst = &req->rq_private_buf.head[0];
src = &rqstp->rq_arg.head[0];
if (dst->iov_len < src->iov_len)
- return -EAGAIN; /* whatever; just giving up. */
+ goto unlock_eagain; /* whatever; just giving up. */
memcpy(dst->iov_base, src->iov_base, src->iov_len);
xprt_complete_rqst(req->rq_task, rqstp->rq_arg.len);
rqstp->rq_arg.len = 0;
+ spin_unlock_bh(&bc_xprt->transport_lock);
return 0;
+unlock_notfound:
+ printk(KERN_NOTICE
+ "%s: Got unrecognized reply: "
+ "calldir 0x%x xpt_bc_xprt %p xid %08x\n",
+ __func__, ntohl(calldir),
+ bc_xprt, ntohl(xid));
+unlock_eagain:
+ spin_unlock_bh(&bc_xprt->transport_lock);
+ return -EAGAIN;
}
static int copy_pages_to_kvecs(struct kvec *vec, struct page **pages, int len)
diff --git a/net/sunrpc/sysctl.c b/net/sunrpc/sysctl.c
index c99c58e2ee66..887f0183b4c6 100644
--- a/net/sunrpc/sysctl.c
+++ b/net/sunrpc/sysctl.c
@@ -37,7 +37,7 @@ EXPORT_SYMBOL_GPL(nfsd_debug);
unsigned int nlm_debug;
EXPORT_SYMBOL_GPL(nlm_debug);
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
static struct ctl_table_header *sunrpc_table_header;
static struct ctl_table sunrpc_table[];
diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c
index 56e4e150e80e..ebbefad21a37 100644
--- a/net/sunrpc/xprt.c
+++ b/net/sunrpc/xprt.c
@@ -49,13 +49,15 @@
#include <linux/sunrpc/metrics.h>
#include <linux/sunrpc/bc_xprt.h>
+#include <trace/events/sunrpc.h>
+
#include "sunrpc.h"
/*
* Local variables
*/
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_XPRT
#endif
@@ -772,11 +774,14 @@ struct rpc_rqst *xprt_lookup_rqst(struct rpc_xprt *xprt, __be32 xid)
struct rpc_rqst *entry;
list_for_each_entry(entry, &xprt->recv, rq_list)
- if (entry->rq_xid == xid)
+ if (entry->rq_xid == xid) {
+ trace_xprt_lookup_rqst(xprt, xid, 0);
return entry;
+ }
dprintk("RPC: xprt_lookup_rqst did not find xid %08x\n",
ntohl(xid));
+ trace_xprt_lookup_rqst(xprt, xid, -ENOENT);
xprt->stat.bad_xids++;
return NULL;
}
@@ -810,6 +815,7 @@ void xprt_complete_rqst(struct rpc_task *task, int copied)
dprintk("RPC: %5u xid %08x complete (%d bytes received)\n",
task->tk_pid, ntohl(req->rq_xid), copied);
+ trace_xprt_complete_rqst(xprt, req->rq_xid, copied);
xprt->stat.recvs++;
req->rq_rtt = ktime_sub(ktime_get(), req->rq_xtime);
@@ -926,6 +932,7 @@ void xprt_transmit(struct rpc_task *task)
req->rq_xtime = ktime_get();
status = xprt->ops->send_request(task);
+ trace_xprt_transmit(xprt, req->rq_xid, status);
if (status != 0) {
task->tk_status = status;
return;
@@ -1296,6 +1303,7 @@ static void xprt_init(struct rpc_xprt *xprt, struct net *net)
*/
struct rpc_xprt *xprt_create_transport(struct xprt_create *args)
{
+ int err;
struct rpc_xprt *xprt;
struct xprt_class *t;
@@ -1336,6 +1344,12 @@ found:
return ERR_PTR(-ENOMEM);
}
+ err = rpc_xprt_debugfs_register(xprt);
+ if (err) {
+ xprt_destroy(xprt);
+ return ERR_PTR(err);
+ }
+
dprintk("RPC: created transport %p with %u slots\n", xprt,
xprt->max_reqs);
out:
@@ -1352,6 +1366,7 @@ static void xprt_destroy(struct rpc_xprt *xprt)
dprintk("RPC: destroying transport %p\n", xprt);
del_timer_sync(&xprt->timer);
+ rpc_xprt_debugfs_unregister(xprt);
rpc_destroy_wait_queue(&xprt->binding);
rpc_destroy_wait_queue(&xprt->pending);
rpc_destroy_wait_queue(&xprt->sending);
diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c
index 6166c985fe24..df01d124936c 100644
--- a/net/sunrpc/xprtrdma/rpc_rdma.c
+++ b/net/sunrpc/xprtrdma/rpc_rdma.c
@@ -49,11 +49,11 @@
#include <linux/highmem.h>
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_TRANS
#endif
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
static const char transfertypes[][12] = {
"pure inline", /* no chunks */
" read chunk", /* some argument via rdma read */
diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c
index 6a4615dd0261..bbd6155d3e34 100644
--- a/net/sunrpc/xprtrdma/transport.c
+++ b/net/sunrpc/xprtrdma/transport.c
@@ -55,7 +55,7 @@
#include "xprt_rdma.h"
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_TRANS
#endif
@@ -73,9 +73,9 @@ static unsigned int xprt_rdma_max_inline_read = RPCRDMA_DEF_INLINE;
static unsigned int xprt_rdma_max_inline_write = RPCRDMA_DEF_INLINE;
static unsigned int xprt_rdma_inline_write_padding;
static unsigned int xprt_rdma_memreg_strategy = RPCRDMA_FRMR;
- int xprt_rdma_pad_optimize = 0;
+ int xprt_rdma_pad_optimize = 1;
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
static unsigned int min_slot_table_size = RPCRDMA_MIN_SLOT_TABLE;
static unsigned int max_slot_table_size = RPCRDMA_MAX_SLOT_TABLE;
@@ -599,7 +599,7 @@ xprt_rdma_send_request(struct rpc_task *task)
if (req->rl_niovs == 0)
rc = rpcrdma_marshal_req(rqst);
- else if (r_xprt->rx_ia.ri_memreg_strategy == RPCRDMA_FRMR)
+ else if (r_xprt->rx_ia.ri_memreg_strategy != RPCRDMA_ALLPHYSICAL)
rc = rpcrdma_marshal_chunks(rqst, 0);
if (rc < 0)
goto failed_marshal;
@@ -705,7 +705,7 @@ static void __exit xprt_rdma_cleanup(void)
int rc;
dprintk("RPCRDMA Module Removed, deregister RPC RDMA transport\n");
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
if (sunrpc_table_header) {
unregister_sysctl_table(sunrpc_table_header);
sunrpc_table_header = NULL;
@@ -736,7 +736,7 @@ static int __init xprt_rdma_init(void)
dprintk("\tPadding %d\n\tMemreg %d\n",
xprt_rdma_inline_write_padding, xprt_rdma_memreg_strategy);
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
if (!sunrpc_table_header)
sunrpc_table_header = register_sysctl_table(sunrpc_table);
#endif
diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c
index 61c41298b4ea..c98e40643910 100644
--- a/net/sunrpc/xprtrdma/verbs.c
+++ b/net/sunrpc/xprtrdma/verbs.c
@@ -57,11 +57,12 @@
* Globals/Macros
*/
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_TRANS
#endif
static void rpcrdma_reset_frmrs(struct rpcrdma_ia *);
+static void rpcrdma_reset_fmrs(struct rpcrdma_ia *);
/*
* internal functions
@@ -105,13 +106,51 @@ rpcrdma_run_tasklet(unsigned long data)
static DECLARE_TASKLET(rpcrdma_tasklet_g, rpcrdma_run_tasklet, 0UL);
+static const char * const async_event[] = {
+ "CQ error",
+ "QP fatal error",
+ "QP request error",
+ "QP access error",
+ "communication established",
+ "send queue drained",
+ "path migration successful",
+ "path mig error",
+ "device fatal error",
+ "port active",
+ "port error",
+ "LID change",
+ "P_key change",
+ "SM change",
+ "SRQ error",
+ "SRQ limit reached",
+ "last WQE reached",
+ "client reregister",
+ "GID change",
+};
+
+#define ASYNC_MSG(status) \
+ ((status) < ARRAY_SIZE(async_event) ? \
+ async_event[(status)] : "unknown async error")
+
+static void
+rpcrdma_schedule_tasklet(struct list_head *sched_list)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&rpcrdma_tk_lock_g, flags);
+ list_splice_tail(sched_list, &rpcrdma_tasklets_g);
+ spin_unlock_irqrestore(&rpcrdma_tk_lock_g, flags);
+ tasklet_schedule(&rpcrdma_tasklet_g);
+}
+
static void
rpcrdma_qp_async_error_upcall(struct ib_event *event, void *context)
{
struct rpcrdma_ep *ep = context;
- dprintk("RPC: %s: QP error %X on device %s ep %p\n",
- __func__, event->event, event->device->name, context);
+ pr_err("RPC: %s: %s on device %s ep %p\n",
+ __func__, ASYNC_MSG(event->event),
+ event->device->name, context);
if (ep->rep_connected == 1) {
ep->rep_connected = -EIO;
ep->rep_func(ep);
@@ -124,8 +163,9 @@ rpcrdma_cq_async_error_upcall(struct ib_event *event, void *context)
{
struct rpcrdma_ep *ep = context;
- dprintk("RPC: %s: CQ error %X on device %s ep %p\n",
- __func__, event->event, event->device->name, context);
+ pr_err("RPC: %s: %s on device %s ep %p\n",
+ __func__, ASYNC_MSG(event->event),
+ event->device->name, context);
if (ep->rep_connected == 1) {
ep->rep_connected = -EIO;
ep->rep_func(ep);
@@ -243,7 +283,6 @@ rpcrdma_recvcq_poll(struct ib_cq *cq, struct rpcrdma_ep *ep)
struct list_head sched_list;
struct ib_wc *wcs;
int budget, count, rc;
- unsigned long flags;
INIT_LIST_HEAD(&sched_list);
budget = RPCRDMA_WC_BUDGET / RPCRDMA_POLLSIZE;
@@ -261,10 +300,7 @@ rpcrdma_recvcq_poll(struct ib_cq *cq, struct rpcrdma_ep *ep)
rc = 0;
out_schedule:
- spin_lock_irqsave(&rpcrdma_tk_lock_g, flags);
- list_splice_tail(&sched_list, &rpcrdma_tasklets_g);
- spin_unlock_irqrestore(&rpcrdma_tk_lock_g, flags);
- tasklet_schedule(&rpcrdma_tasklet_g);
+ rpcrdma_schedule_tasklet(&sched_list);
return rc;
}
@@ -309,11 +345,18 @@ rpcrdma_recvcq_upcall(struct ib_cq *cq, void *cq_context)
static void
rpcrdma_flush_cqs(struct rpcrdma_ep *ep)
{
- rpcrdma_recvcq_upcall(ep->rep_attr.recv_cq, ep);
- rpcrdma_sendcq_upcall(ep->rep_attr.send_cq, ep);
+ struct ib_wc wc;
+ LIST_HEAD(sched_list);
+
+ while (ib_poll_cq(ep->rep_attr.recv_cq, 1, &wc) > 0)
+ rpcrdma_recvcq_process_wc(&wc, &sched_list);
+ if (!list_empty(&sched_list))
+ rpcrdma_schedule_tasklet(&sched_list);
+ while (ib_poll_cq(ep->rep_attr.send_cq, 1, &wc) > 0)
+ rpcrdma_sendcq_process_wc(&wc);
}
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
static const char * const conn[] = {
"address resolved",
"address error",
@@ -344,7 +387,7 @@ rpcrdma_conn_upcall(struct rdma_cm_id *id, struct rdma_cm_event *event)
struct rpcrdma_xprt *xprt = id->context;
struct rpcrdma_ia *ia = &xprt->rx_ia;
struct rpcrdma_ep *ep = &xprt->rx_ep;
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
struct sockaddr_in *addr = (struct sockaddr_in *) &ep->rep_remote_addr;
#endif
struct ib_qp_attr attr;
@@ -408,7 +451,7 @@ connected:
break;
}
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
if (connstate == 1) {
int ird = attr.max_dest_rd_atomic;
int tird = ep->rep_remote_cma.responder_resources;
@@ -733,7 +776,9 @@ rpcrdma_ep_create(struct rpcrdma_ep *ep, struct rpcrdma_ia *ia,
/* set trigger for requesting send completion */
ep->rep_cqinit = ep->rep_attr.cap.max_send_wr/2 - 1;
- if (ep->rep_cqinit <= 2)
+ if (ep->rep_cqinit > RPCRDMA_MAX_UNSIGNALED_SENDS)
+ ep->rep_cqinit = RPCRDMA_MAX_UNSIGNALED_SENDS;
+ else if (ep->rep_cqinit <= 2)
ep->rep_cqinit = 0;
INIT_CQCOUNT(ep);
ep->rep_ia = ia;
@@ -866,8 +911,19 @@ retry:
rpcrdma_ep_disconnect(ep, ia);
rpcrdma_flush_cqs(ep);
- if (ia->ri_memreg_strategy == RPCRDMA_FRMR)
+ switch (ia->ri_memreg_strategy) {
+ case RPCRDMA_FRMR:
rpcrdma_reset_frmrs(ia);
+ break;
+ case RPCRDMA_MTHCAFMR:
+ rpcrdma_reset_fmrs(ia);
+ break;
+ case RPCRDMA_ALLPHYSICAL:
+ break;
+ default:
+ rc = -EIO;
+ goto out;
+ }
xprt = container_of(ia, struct rpcrdma_xprt, rx_ia);
id = rpcrdma_create_id(xprt, ia,
@@ -1287,6 +1343,34 @@ rpcrdma_buffer_destroy(struct rpcrdma_buffer *buf)
kfree(buf->rb_pool);
}
+/* After a disconnect, unmap all FMRs.
+ *
+ * This is invoked only in the transport connect worker in order
+ * to serialize with rpcrdma_register_fmr_external().
+ */
+static void
+rpcrdma_reset_fmrs(struct rpcrdma_ia *ia)
+{
+ struct rpcrdma_xprt *r_xprt =
+ container_of(ia, struct rpcrdma_xprt, rx_ia);
+ struct rpcrdma_buffer *buf = &r_xprt->rx_buf;
+ struct list_head *pos;
+ struct rpcrdma_mw *r;
+ LIST_HEAD(l);
+ int rc;
+
+ list_for_each(pos, &buf->rb_all) {
+ r = list_entry(pos, struct rpcrdma_mw, mw_all);
+
+ INIT_LIST_HEAD(&l);
+ list_add(&r->r.fmr->list, &l);
+ rc = ib_unmap_fmr(&l);
+ if (rc)
+ dprintk("RPC: %s: ib_unmap_fmr failed %i\n",
+ __func__, rc);
+ }
+}
+
/* After a disconnect, a flushed FAST_REG_MR can leave an FRMR in
* an unusable state. Find FRMRs in this state and dereg / reg
* each. FRMRs that are VALID and attached to an rpcrdma_req are
@@ -1918,10 +2002,10 @@ rpcrdma_register_external(struct rpcrdma_mr_seg *seg,
break;
default:
- return -1;
+ return -EIO;
}
if (rc)
- return -1;
+ return rc;
return nsegs;
}
diff --git a/net/sunrpc/xprtrdma/xprt_rdma.h b/net/sunrpc/xprtrdma/xprt_rdma.h
index ac7fc9a31342..b799041b75bf 100644
--- a/net/sunrpc/xprtrdma/xprt_rdma.h
+++ b/net/sunrpc/xprtrdma/xprt_rdma.h
@@ -97,6 +97,12 @@ struct rpcrdma_ep {
struct ib_wc rep_recv_wcs[RPCRDMA_POLLSIZE];
};
+/*
+ * Force a signaled SEND Work Request every so often,
+ * in case the provider needs to do some housekeeping.
+ */
+#define RPCRDMA_MAX_UNSIGNALED_SENDS (32)
+
#define INIT_CQCOUNT(ep) atomic_set(&(ep)->rep_cqcount, (ep)->rep_cqinit)
#define DECR_CQCOUNT(ep) atomic_sub_return(1, &(ep)->rep_cqcount)
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index 3b305ab17afe..87ce7e8bb8dc 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -75,7 +75,7 @@ static unsigned int xs_tcp_fin_timeout __read_mostly = XS_TCP_LINGER_TO;
* someone else's file names!
*/
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
static unsigned int min_slot_table_size = RPC_MIN_SLOT_TABLE;
static unsigned int max_slot_table_size = RPC_MAX_SLOT_TABLE;
@@ -186,7 +186,7 @@ static struct ctl_table sunrpc_table[] = {
*/
#define XS_IDLE_DISC_TO (5U * 60 * HZ)
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# undef RPC_DEBUG_DATA
# define RPCDBG_FACILITY RPCDBG_TRANS
#endif
@@ -216,65 +216,6 @@ static inline void xs_pktdump(char *msg, u32 *packet, unsigned int count)
}
#endif
-struct sock_xprt {
- struct rpc_xprt xprt;
-
- /*
- * Network layer
- */
- struct socket * sock;
- struct sock * inet;
-
- /*
- * State of TCP reply receive
- */
- __be32 tcp_fraghdr,
- tcp_xid,
- tcp_calldir;
-
- u32 tcp_offset,
- tcp_reclen;
-
- unsigned long tcp_copied,
- tcp_flags;
-
- /*
- * Connection of transports
- */
- struct delayed_work connect_worker;
- struct sockaddr_storage srcaddr;
- unsigned short srcport;
-
- /*
- * UDP socket buffer size parameters
- */
- size_t rcvsize,
- sndsize;
-
- /*
- * Saved socket callback addresses
- */
- void (*old_data_ready)(struct sock *);
- void (*old_state_change)(struct sock *);
- void (*old_write_space)(struct sock *);
- void (*old_error_report)(struct sock *);
-};
-
-/*
- * TCP receive state flags
- */
-#define TCP_RCV_LAST_FRAG (1UL << 0)
-#define TCP_RCV_COPY_FRAGHDR (1UL << 1)
-#define TCP_RCV_COPY_XID (1UL << 2)
-#define TCP_RCV_COPY_DATA (1UL << 3)
-#define TCP_RCV_READ_CALLDIR (1UL << 4)
-#define TCP_RCV_COPY_CALLDIR (1UL << 5)
-
-/*
- * TCP RPC flags
- */
-#define TCP_RPC_REPLY (1UL << 6)
-
static inline struct rpc_xprt *xprt_from_sock(struct sock *sk)
{
return (struct rpc_xprt *) sk->sk_user_data;
@@ -1415,6 +1356,7 @@ static int xs_tcp_data_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, uns
dprintk("RPC: xs_tcp_data_recv started\n");
do {
+ trace_xs_tcp_data_recv(transport);
/* Read in a new fragment marker if necessary */
/* Can we ever really expect to get completely empty fragments? */
if (transport->tcp_flags & TCP_RCV_COPY_FRAGHDR) {
@@ -1439,6 +1381,7 @@ static int xs_tcp_data_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, uns
/* Skip over any trailing bytes on short reads */
xs_tcp_read_discard(transport, &desc);
} while (desc.count);
+ trace_xs_tcp_data_recv(transport);
dprintk("RPC: xs_tcp_data_recv done\n");
return len - desc.count;
}
@@ -1454,12 +1397,15 @@ static void xs_tcp_data_ready(struct sock *sk)
struct rpc_xprt *xprt;
read_descriptor_t rd_desc;
int read;
+ unsigned long total = 0;
dprintk("RPC: xs_tcp_data_ready...\n");
read_lock_bh(&sk->sk_callback_lock);
- if (!(xprt = xprt_from_sock(sk)))
+ if (!(xprt = xprt_from_sock(sk))) {
+ read = 0;
goto out;
+ }
/* Any data means we had a useful conversation, so
* the we don't need to delay the next reconnect
*/
@@ -1471,8 +1417,11 @@ static void xs_tcp_data_ready(struct sock *sk)
do {
rd_desc.count = 65536;
read = tcp_read_sock(sk, &rd_desc, xs_tcp_data_recv);
+ if (read > 0)
+ total += read;
} while (read > 0);
out:
+ trace_xs_tcp_data_ready(xprt, read, total);
read_unlock_bh(&sk->sk_callback_lock);
}
@@ -3042,7 +2991,7 @@ static struct xprt_class xs_bc_tcp_transport = {
*/
int init_socket_xprt(void)
{
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
if (!sunrpc_table_header)
sunrpc_table_header = register_sysctl_table(sunrpc_table);
#endif
@@ -3061,7 +3010,7 @@ int init_socket_xprt(void)
*/
void cleanup_socket_xprt(void)
{
-#ifdef RPC_DEBUG
+#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
if (sunrpc_table_header) {
unregister_sysctl_table(sunrpc_table_header);
sunrpc_table_header = NULL;
diff --git a/net/switchdev/Kconfig b/net/switchdev/Kconfig
new file mode 100644
index 000000000000..155754588fd6
--- /dev/null
+++ b/net/switchdev/Kconfig
@@ -0,0 +1,13 @@
+#
+# Configuration for Switch device support
+#
+
+config NET_SWITCHDEV
+ boolean "Switch (and switch-ish) device support (EXPERIMENTAL)"
+ depends on INET
+ ---help---
+ This module provides glue between core networking code and device
+ drivers in order to support hardware switch chips in very generic
+ meaning of the word "switch". This include devices supporting L2/L3 but
+ also various flow offloading chips, including switches embedded into
+ SR-IOV NICs.
diff --git a/net/switchdev/Makefile b/net/switchdev/Makefile
new file mode 100644
index 000000000000..5ed63ed324d0
--- /dev/null
+++ b/net/switchdev/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the Switch device API
+#
+
+obj-$(CONFIG_NET_SWITCHDEV) += switchdev.o
diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c
new file mode 100644
index 000000000000..d162b21b14bd
--- /dev/null
+++ b/net/switchdev/switchdev.c
@@ -0,0 +1,52 @@
+/*
+ * net/switchdev/switchdev.c - Switch device API
+ * Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <net/switchdev.h>
+
+/**
+ * netdev_switch_parent_id_get - Get ID of a switch
+ * @dev: port device
+ * @psid: switch ID
+ *
+ * Get ID of a switch this port is part of.
+ */
+int netdev_switch_parent_id_get(struct net_device *dev,
+ struct netdev_phys_item_id *psid)
+{
+ const struct net_device_ops *ops = dev->netdev_ops;
+
+ if (!ops->ndo_switch_parent_id_get)
+ return -EOPNOTSUPP;
+ return ops->ndo_switch_parent_id_get(dev, psid);
+}
+EXPORT_SYMBOL(netdev_switch_parent_id_get);
+
+/**
+ * netdev_switch_port_stp_update - Notify switch device port of STP
+ * state change
+ * @dev: port device
+ * @state: port STP state
+ *
+ * Notify switch device port of bridge port STP state change.
+ */
+int netdev_switch_port_stp_update(struct net_device *dev, u8 state)
+{
+ const struct net_device_ops *ops = dev->netdev_ops;
+
+ if (!ops->ndo_switch_port_stp_update)
+ return -EOPNOTSUPP;
+ WARN_ON(!ops->ndo_switch_parent_id_get);
+ return ops->ndo_switch_port_stp_update(dev, state);
+}
+EXPORT_SYMBOL(netdev_switch_port_stp_update);
diff --git a/net/tipc/Makefile b/net/tipc/Makefile
index b8a13caad59a..333e4592772c 100644
--- a/net/tipc/Makefile
+++ b/net/tipc/Makefile
@@ -7,8 +7,8 @@ obj-$(CONFIG_TIPC) := tipc.o
tipc-y += addr.o bcast.o bearer.o config.o \
core.o link.o discover.o msg.o \
name_distr.o subscr.o name_table.o net.o \
- netlink.o node.o node_subscr.o \
- socket.o log.o eth_media.o server.o
+ netlink.o node.o socket.o log.o eth_media.o \
+ server.o
tipc-$(CONFIG_TIPC_MEDIA_IB) += ib_media.o
tipc-$(CONFIG_SYSCTL) += sysctl.o
diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c
index b8670bf262e2..96ceefeb9daf 100644
--- a/net/tipc/bcast.c
+++ b/net/tipc/bcast.c
@@ -217,12 +217,13 @@ struct tipc_node *tipc_bclink_retransmit_to(void)
*/
static void bclink_retransmit_pkt(u32 after, u32 to)
{
- struct sk_buff *buf;
+ struct sk_buff *skb;
- buf = bcl->first_out;
- while (buf && less_eq(buf_seqno(buf), after))
- buf = buf->next;
- tipc_link_retransmit(bcl, buf, mod(to - after));
+ skb_queue_walk(&bcl->outqueue, skb) {
+ if (more(buf_seqno(skb), after))
+ break;
+ }
+ tipc_link_retransmit(bcl, skb, mod(to - after));
}
/**
@@ -232,8 +233,11 @@ static void bclink_retransmit_pkt(u32 after, u32 to)
*/
void tipc_bclink_wakeup_users(void)
{
- while (skb_queue_len(&bclink->link.waiting_sks))
- tipc_sk_rcv(skb_dequeue(&bclink->link.waiting_sks));
+ struct sk_buff *skb;
+
+ while ((skb = skb_dequeue(&bclink->link.waiting_sks)))
+ tipc_sk_rcv(skb);
+
}
/**
@@ -245,14 +249,14 @@ void tipc_bclink_wakeup_users(void)
*/
void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked)
{
- struct sk_buff *crs;
+ struct sk_buff *skb, *tmp;
struct sk_buff *next;
unsigned int released = 0;
tipc_bclink_lock();
/* Bail out if tx queue is empty (no clean up is required) */
- crs = bcl->first_out;
- if (!crs)
+ skb = skb_peek(&bcl->outqueue);
+ if (!skb)
goto exit;
/* Determine which messages need to be acknowledged */
@@ -271,43 +275,43 @@ void tipc_bclink_acknowledge(struct tipc_node *n_ptr, u32 acked)
* Bail out if specified sequence number does not correspond
* to a message that has been sent and not yet acknowledged
*/
- if (less(acked, buf_seqno(crs)) ||
+ if (less(acked, buf_seqno(skb)) ||
less(bcl->fsm_msg_cnt, acked) ||
less_eq(acked, n_ptr->bclink.acked))
goto exit;
}
/* Skip over packets that node has previously acknowledged */
- while (crs && less_eq(buf_seqno(crs), n_ptr->bclink.acked))
- crs = crs->next;
+ skb_queue_walk(&bcl->outqueue, skb) {
+ if (more(buf_seqno(skb), n_ptr->bclink.acked))
+ break;
+ }
/* Update packets that node is now acknowledging */
+ skb_queue_walk_from_safe(&bcl->outqueue, skb, tmp) {
+ if (more(buf_seqno(skb), acked))
+ break;
- while (crs && less_eq(buf_seqno(crs), acked)) {
- next = crs->next;
-
- if (crs != bcl->next_out)
- bcbuf_decr_acks(crs);
- else {
- bcbuf_set_acks(crs, 0);
+ next = tipc_skb_queue_next(&bcl->outqueue, skb);
+ if (skb != bcl->next_out) {
+ bcbuf_decr_acks(skb);
+ } else {
+ bcbuf_set_acks(skb, 0);
bcl->next_out = next;
bclink_set_last_sent();
}
- if (bcbuf_acks(crs) == 0) {
- bcl->first_out = next;
- bcl->out_queue_size--;
- kfree_skb(crs);
+ if (bcbuf_acks(skb) == 0) {
+ __skb_unlink(skb, &bcl->outqueue);
+ kfree_skb(skb);
released = 1;
}
- crs = next;
}
n_ptr->bclink.acked = acked;
/* Try resolving broadcast link congestion, if necessary */
-
if (unlikely(bcl->next_out)) {
- tipc_link_push_queue(bcl);
+ tipc_link_push_packets(bcl);
bclink_set_last_sent();
}
if (unlikely(released && !skb_queue_empty(&bcl->waiting_sks)))
@@ -327,19 +331,16 @@ void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent)
struct sk_buff *buf;
/* Ignore "stale" link state info */
-
if (less_eq(last_sent, n_ptr->bclink.last_in))
return;
/* Update link synchronization state; quit if in sync */
-
bclink_update_last_sent(n_ptr, last_sent);
if (n_ptr->bclink.last_sent == n_ptr->bclink.last_in)
return;
/* Update out-of-sync state; quit if loss is still unconfirmed */
-
if ((++n_ptr->bclink.oos_state) == 1) {
if (n_ptr->bclink.deferred_size < (TIPC_MIN_LINK_WIN / 2))
return;
@@ -347,15 +348,15 @@ void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent)
}
/* Don't NACK if one has been recently sent (or seen) */
-
if (n_ptr->bclink.oos_state & 0x1)
return;
/* Send NACK */
-
buf = tipc_buf_acquire(INT_H_SIZE);
if (buf) {
struct tipc_msg *msg = buf_msg(buf);
+ struct sk_buff *skb = skb_peek(&n_ptr->bclink.deferred_queue);
+ u32 to = skb ? buf_seqno(skb) - 1 : n_ptr->bclink.last_sent;
tipc_msg_init(msg, BCAST_PROTOCOL, STATE_MSG,
INT_H_SIZE, n_ptr->addr);
@@ -363,9 +364,7 @@ void tipc_bclink_update_link_state(struct tipc_node *n_ptr, u32 last_sent)
msg_set_mc_netid(msg, tipc_net_id);
msg_set_bcast_ack(msg, n_ptr->bclink.last_in);
msg_set_bcgap_after(msg, n_ptr->bclink.last_in);
- msg_set_bcgap_to(msg, n_ptr->bclink.deferred_head
- ? buf_seqno(n_ptr->bclink.deferred_head) - 1
- : n_ptr->bclink.last_sent);
+ msg_set_bcgap_to(msg, to);
tipc_bclink_lock();
tipc_bearer_send(MAX_BEARERS, buf, NULL);
@@ -402,20 +401,20 @@ static void bclink_peek_nack(struct tipc_msg *msg)
/* tipc_bclink_xmit - broadcast buffer chain to all nodes in cluster
* and to identified node local sockets
- * @buf: chain of buffers containing message
+ * @list: chain of buffers containing message
* Consumes the buffer chain, except when returning -ELINKCONG
* Returns 0 if success, otherwise errno: -ELINKCONG,-EHOSTUNREACH,-EMSGSIZE
*/
-int tipc_bclink_xmit(struct sk_buff *buf)
+int tipc_bclink_xmit(struct sk_buff_head *list)
{
int rc = 0;
int bc = 0;
- struct sk_buff *clbuf;
+ struct sk_buff *skb;
/* Prepare clone of message for local node */
- clbuf = tipc_msg_reassemble(buf);
- if (unlikely(!clbuf)) {
- kfree_skb_list(buf);
+ skb = tipc_msg_reassemble(list);
+ if (unlikely(!skb)) {
+ __skb_queue_purge(list);
return -EHOSTUNREACH;
}
@@ -423,11 +422,13 @@ int tipc_bclink_xmit(struct sk_buff *buf)
if (likely(bclink)) {
tipc_bclink_lock();
if (likely(bclink->bcast_nodes.count)) {
- rc = __tipc_link_xmit(bcl, buf);
+ rc = __tipc_link_xmit(bcl, list);
if (likely(!rc)) {
+ u32 len = skb_queue_len(&bcl->outqueue);
+
bclink_set_last_sent();
bcl->stats.queue_sz_counts++;
- bcl->stats.accu_queue_sz += bcl->out_queue_size;
+ bcl->stats.accu_queue_sz += len;
}
bc = 1;
}
@@ -435,13 +436,13 @@ int tipc_bclink_xmit(struct sk_buff *buf)
}
if (unlikely(!bc))
- kfree_skb_list(buf);
+ __skb_queue_purge(list);
/* Deliver message clone */
if (likely(!rc))
- tipc_sk_mcast_rcv(clbuf);
+ tipc_sk_mcast_rcv(skb);
else
- kfree_skb(clbuf);
+ kfree_skb(skb);
return rc;
}
@@ -462,7 +463,6 @@ static void bclink_accept_pkt(struct tipc_node *node, u32 seqno)
* Unicast an ACK periodically, ensuring that
* all nodes in the cluster don't ACK at the same time
*/
-
if (((seqno - tipc_own_addr) % TIPC_MIN_LINK_WIN) == 0) {
tipc_link_proto_xmit(node->active_links[node->addr & 1],
STATE_MSG, 0, 0, 0, 0, 0);
@@ -484,7 +484,6 @@ void tipc_bclink_rcv(struct sk_buff *buf)
int deferred = 0;
/* Screen out unwanted broadcast messages */
-
if (msg_mc_netid(msg) != tipc_net_id)
goto exit;
@@ -497,7 +496,6 @@ void tipc_bclink_rcv(struct sk_buff *buf)
goto unlock;
/* Handle broadcast protocol message */
-
if (unlikely(msg_user(msg) == BCAST_PROTOCOL)) {
if (msg_type(msg) != STATE_MSG)
goto unlock;
@@ -518,14 +516,12 @@ void tipc_bclink_rcv(struct sk_buff *buf)
}
/* Handle in-sequence broadcast message */
-
seqno = msg_seqno(msg);
next_in = mod(node->bclink.last_in + 1);
if (likely(seqno == next_in)) {
receive:
/* Deliver message to destination */
-
if (likely(msg_isdata(msg))) {
tipc_bclink_lock();
bclink_accept_pkt(node, seqno);
@@ -574,7 +570,6 @@ receive:
buf = NULL;
/* Determine new synchronization state */
-
tipc_node_lock(node);
if (unlikely(!tipc_node_is_up(node)))
goto unlock;
@@ -582,33 +577,26 @@ receive:
if (node->bclink.last_in == node->bclink.last_sent)
goto unlock;
- if (!node->bclink.deferred_head) {
+ if (skb_queue_empty(&node->bclink.deferred_queue)) {
node->bclink.oos_state = 1;
goto unlock;
}
- msg = buf_msg(node->bclink.deferred_head);
+ msg = buf_msg(skb_peek(&node->bclink.deferred_queue));
seqno = msg_seqno(msg);
next_in = mod(next_in + 1);
if (seqno != next_in)
goto unlock;
/* Take in-sequence message from deferred queue & deliver it */
-
- buf = node->bclink.deferred_head;
- node->bclink.deferred_head = buf->next;
- buf->next = NULL;
- node->bclink.deferred_size--;
+ buf = __skb_dequeue(&node->bclink.deferred_queue);
goto receive;
}
/* Handle out-of-sequence broadcast message */
-
if (less(next_in, seqno)) {
- deferred = tipc_link_defer_pkt(&node->bclink.deferred_head,
- &node->bclink.deferred_tail,
+ deferred = tipc_link_defer_pkt(&node->bclink.deferred_queue,
buf);
- node->bclink.deferred_size += deferred;
bclink_update_last_sent(node, seqno);
buf = NULL;
}
@@ -767,6 +755,118 @@ void tipc_bcbearer_sort(struct tipc_node_map *nm_ptr, u32 node, bool action)
tipc_bclink_unlock();
}
+static int __tipc_nl_add_bc_link_stat(struct sk_buff *skb,
+ struct tipc_stats *stats)
+{
+ int i;
+ struct nlattr *nest;
+
+ struct nla_map {
+ __u32 key;
+ __u32 val;
+ };
+
+ struct nla_map map[] = {
+ {TIPC_NLA_STATS_RX_INFO, stats->recv_info},
+ {TIPC_NLA_STATS_RX_FRAGMENTS, stats->recv_fragments},
+ {TIPC_NLA_STATS_RX_FRAGMENTED, stats->recv_fragmented},
+ {TIPC_NLA_STATS_RX_BUNDLES, stats->recv_bundles},
+ {TIPC_NLA_STATS_RX_BUNDLED, stats->recv_bundled},
+ {TIPC_NLA_STATS_TX_INFO, stats->sent_info},
+ {TIPC_NLA_STATS_TX_FRAGMENTS, stats->sent_fragments},
+ {TIPC_NLA_STATS_TX_FRAGMENTED, stats->sent_fragmented},
+ {TIPC_NLA_STATS_TX_BUNDLES, stats->sent_bundles},
+ {TIPC_NLA_STATS_TX_BUNDLED, stats->sent_bundled},
+ {TIPC_NLA_STATS_RX_NACKS, stats->recv_nacks},
+ {TIPC_NLA_STATS_RX_DEFERRED, stats->deferred_recv},
+ {TIPC_NLA_STATS_TX_NACKS, stats->sent_nacks},
+ {TIPC_NLA_STATS_TX_ACKS, stats->sent_acks},
+ {TIPC_NLA_STATS_RETRANSMITTED, stats->retransmitted},
+ {TIPC_NLA_STATS_DUPLICATES, stats->duplicates},
+ {TIPC_NLA_STATS_LINK_CONGS, stats->link_congs},
+ {TIPC_NLA_STATS_MAX_QUEUE, stats->max_queue_sz},
+ {TIPC_NLA_STATS_AVG_QUEUE, stats->queue_sz_counts ?
+ (stats->accu_queue_sz / stats->queue_sz_counts) : 0}
+ };
+
+ nest = nla_nest_start(skb, TIPC_NLA_LINK_STATS);
+ if (!nest)
+ return -EMSGSIZE;
+
+ for (i = 0; i < ARRAY_SIZE(map); i++)
+ if (nla_put_u32(skb, map[i].key, map[i].val))
+ goto msg_full;
+
+ nla_nest_end(skb, nest);
+
+ return 0;
+msg_full:
+ nla_nest_cancel(skb, nest);
+
+ return -EMSGSIZE;
+}
+
+int tipc_nl_add_bc_link(struct tipc_nl_msg *msg)
+{
+ int err;
+ void *hdr;
+ struct nlattr *attrs;
+ struct nlattr *prop;
+
+ if (!bcl)
+ return 0;
+
+ tipc_bclink_lock();
+
+ hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_v2_family,
+ NLM_F_MULTI, TIPC_NL_LINK_GET);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ attrs = nla_nest_start(msg->skb, TIPC_NLA_LINK);
+ if (!attrs)
+ goto msg_full;
+
+ /* The broadcast link is always up */
+ if (nla_put_flag(msg->skb, TIPC_NLA_LINK_UP))
+ goto attr_msg_full;
+
+ if (nla_put_flag(msg->skb, TIPC_NLA_LINK_BROADCAST))
+ goto attr_msg_full;
+ if (nla_put_string(msg->skb, TIPC_NLA_LINK_NAME, bcl->name))
+ goto attr_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_LINK_RX, bcl->next_in_no))
+ goto attr_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_LINK_TX, bcl->next_out_no))
+ goto attr_msg_full;
+
+ prop = nla_nest_start(msg->skb, TIPC_NLA_LINK_PROP);
+ if (!prop)
+ goto attr_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_PROP_WIN, bcl->queue_limit[0]))
+ goto prop_msg_full;
+ nla_nest_end(msg->skb, prop);
+
+ err = __tipc_nl_add_bc_link_stat(msg->skb, &bcl->stats);
+ if (err)
+ goto attr_msg_full;
+
+ tipc_bclink_unlock();
+ nla_nest_end(msg->skb, attrs);
+ genlmsg_end(msg->skb, hdr);
+
+ return 0;
+
+prop_msg_full:
+ nla_nest_cancel(msg->skb, prop);
+attr_msg_full:
+ nla_nest_cancel(msg->skb, attrs);
+msg_full:
+ tipc_bclink_unlock();
+ genlmsg_cancel(msg->skb, hdr);
+
+ return -EMSGSIZE;
+}
int tipc_bclink_stats(char *buf, const u32 buf_size)
{
@@ -851,7 +951,9 @@ int tipc_bclink_init(void)
sprintf(bcbearer->media.name, "tipc-broadcast");
spin_lock_init(&bclink->lock);
- __skb_queue_head_init(&bcl->waiting_sks);
+ __skb_queue_head_init(&bcl->outqueue);
+ __skb_queue_head_init(&bcl->deferred_queue);
+ skb_queue_head_init(&bcl->waiting_sks);
bcl->next_out_no = 1;
spin_lock_init(&bclink->node.lock);
__skb_queue_head_init(&bclink->node.waiting_sks);
diff --git a/net/tipc/bcast.h b/net/tipc/bcast.h
index e7b0f85a82bc..644d79129fba 100644
--- a/net/tipc/bcast.h
+++ b/net/tipc/bcast.h
@@ -37,6 +37,8 @@
#ifndef _TIPC_BCAST_H
#define _TIPC_BCAST_H
+#include "netlink.h"
+
#define MAX_NODES 4096
#define WSIZE 32
#define TIPC_BCLINK_RESET 1
@@ -98,6 +100,8 @@ int tipc_bclink_reset_stats(void);
int tipc_bclink_set_queue_limits(u32 limit);
void tipc_bcbearer_sort(struct tipc_node_map *nm_ptr, u32 node, bool action);
uint tipc_bclink_get_mtu(void);
-int tipc_bclink_xmit(struct sk_buff *buf);
+int tipc_bclink_xmit(struct sk_buff_head *list);
void tipc_bclink_wakeup_users(void);
+int tipc_nl_add_bc_link(struct tipc_nl_msg *msg);
+
#endif
diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c
index 264474394f9f..463db5b15b8b 100644
--- a/net/tipc/bearer.c
+++ b/net/tipc/bearer.c
@@ -1,7 +1,7 @@
/*
* net/tipc/bearer.c: TIPC bearer code
*
- * Copyright (c) 1996-2006, 2013, Ericsson AB
+ * Copyright (c) 1996-2006, 2013-2014, Ericsson AB
* Copyright (c) 2004-2006, 2010-2013, Wind River Systems
* All rights reserved.
*
@@ -37,6 +37,7 @@
#include "core.h"
#include "config.h"
#include "bearer.h"
+#include "link.h"
#include "discover.h"
#define MAX_ADDR_STR 60
@@ -49,6 +50,23 @@ static struct tipc_media * const media_info_array[] = {
NULL
};
+static const struct nla_policy
+tipc_nl_bearer_policy[TIPC_NLA_BEARER_MAX + 1] = {
+ [TIPC_NLA_BEARER_UNSPEC] = { .type = NLA_UNSPEC },
+ [TIPC_NLA_BEARER_NAME] = {
+ .type = NLA_STRING,
+ .len = TIPC_MAX_BEARER_NAME
+ },
+ [TIPC_NLA_BEARER_PROP] = { .type = NLA_NESTED },
+ [TIPC_NLA_BEARER_DOMAIN] = { .type = NLA_U32 }
+};
+
+static const struct nla_policy tipc_nl_media_policy[TIPC_NLA_MEDIA_MAX + 1] = {
+ [TIPC_NLA_MEDIA_UNSPEC] = { .type = NLA_UNSPEC },
+ [TIPC_NLA_MEDIA_NAME] = { .type = NLA_STRING },
+ [TIPC_NLA_MEDIA_PROP] = { .type = NLA_NESTED }
+};
+
struct tipc_bearer __rcu *bearer_list[MAX_BEARERS + 1];
static void bearer_disable(struct tipc_bearer *b_ptr, bool shutting_down);
@@ -627,3 +645,430 @@ void tipc_bearer_stop(void)
}
}
}
+
+/* Caller should hold rtnl_lock to protect the bearer */
+static int __tipc_nl_add_bearer(struct tipc_nl_msg *msg,
+ struct tipc_bearer *bearer)
+{
+ void *hdr;
+ struct nlattr *attrs;
+ struct nlattr *prop;
+
+ hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_v2_family,
+ NLM_F_MULTI, TIPC_NL_BEARER_GET);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ attrs = nla_nest_start(msg->skb, TIPC_NLA_BEARER);
+ if (!attrs)
+ goto msg_full;
+
+ if (nla_put_string(msg->skb, TIPC_NLA_BEARER_NAME, bearer->name))
+ goto attr_msg_full;
+
+ prop = nla_nest_start(msg->skb, TIPC_NLA_BEARER_PROP);
+ if (!prop)
+ goto prop_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_PROP_PRIO, bearer->priority))
+ goto prop_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_PROP_TOL, bearer->tolerance))
+ goto prop_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_PROP_WIN, bearer->window))
+ goto prop_msg_full;
+
+ nla_nest_end(msg->skb, prop);
+ nla_nest_end(msg->skb, attrs);
+ genlmsg_end(msg->skb, hdr);
+
+ return 0;
+
+prop_msg_full:
+ nla_nest_cancel(msg->skb, prop);
+attr_msg_full:
+ nla_nest_cancel(msg->skb, attrs);
+msg_full:
+ genlmsg_cancel(msg->skb, hdr);
+
+ return -EMSGSIZE;
+}
+
+int tipc_nl_bearer_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int err;
+ int i = cb->args[0];
+ struct tipc_bearer *bearer;
+ struct tipc_nl_msg msg;
+
+ if (i == MAX_BEARERS)
+ return 0;
+
+ msg.skb = skb;
+ msg.portid = NETLINK_CB(cb->skb).portid;
+ msg.seq = cb->nlh->nlmsg_seq;
+
+ rtnl_lock();
+ for (i = 0; i < MAX_BEARERS; i++) {
+ bearer = rtnl_dereference(bearer_list[i]);
+ if (!bearer)
+ continue;
+
+ err = __tipc_nl_add_bearer(&msg, bearer);
+ if (err)
+ break;
+ }
+ rtnl_unlock();
+
+ cb->args[0] = i;
+ return skb->len;
+}
+
+int tipc_nl_bearer_get(struct sk_buff *skb, struct genl_info *info)
+{
+ int err;
+ char *name;
+ struct sk_buff *rep;
+ struct tipc_bearer *bearer;
+ struct tipc_nl_msg msg;
+ struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1];
+
+ if (!info->attrs[TIPC_NLA_BEARER])
+ return -EINVAL;
+
+ err = nla_parse_nested(attrs, TIPC_NLA_BEARER_MAX,
+ info->attrs[TIPC_NLA_BEARER],
+ tipc_nl_bearer_policy);
+ if (err)
+ return err;
+
+ if (!attrs[TIPC_NLA_BEARER_NAME])
+ return -EINVAL;
+ name = nla_data(attrs[TIPC_NLA_BEARER_NAME]);
+
+ rep = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!rep)
+ return -ENOMEM;
+
+ msg.skb = rep;
+ msg.portid = info->snd_portid;
+ msg.seq = info->snd_seq;
+
+ rtnl_lock();
+ bearer = tipc_bearer_find(name);
+ if (!bearer) {
+ err = -EINVAL;
+ goto err_out;
+ }
+
+ err = __tipc_nl_add_bearer(&msg, bearer);
+ if (err)
+ goto err_out;
+ rtnl_unlock();
+
+ return genlmsg_reply(rep, info);
+err_out:
+ rtnl_unlock();
+ nlmsg_free(rep);
+
+ return err;
+}
+
+int tipc_nl_bearer_disable(struct sk_buff *skb, struct genl_info *info)
+{
+ int err;
+ char *name;
+ struct tipc_bearer *bearer;
+ struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1];
+
+ if (!info->attrs[TIPC_NLA_BEARER])
+ return -EINVAL;
+
+ err = nla_parse_nested(attrs, TIPC_NLA_BEARER_MAX,
+ info->attrs[TIPC_NLA_BEARER],
+ tipc_nl_bearer_policy);
+ if (err)
+ return err;
+
+ if (!attrs[TIPC_NLA_BEARER_NAME])
+ return -EINVAL;
+
+ name = nla_data(attrs[TIPC_NLA_BEARER_NAME]);
+
+ rtnl_lock();
+ bearer = tipc_bearer_find(name);
+ if (!bearer) {
+ rtnl_unlock();
+ return -EINVAL;
+ }
+
+ bearer_disable(bearer, false);
+ rtnl_unlock();
+
+ return 0;
+}
+
+int tipc_nl_bearer_enable(struct sk_buff *skb, struct genl_info *info)
+{
+ int err;
+ char *bearer;
+ struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1];
+ u32 domain;
+ u32 prio;
+
+ prio = TIPC_MEDIA_LINK_PRI;
+ domain = tipc_own_addr & TIPC_CLUSTER_MASK;
+
+ if (!info->attrs[TIPC_NLA_BEARER])
+ return -EINVAL;
+
+ err = nla_parse_nested(attrs, TIPC_NLA_BEARER_MAX,
+ info->attrs[TIPC_NLA_BEARER],
+ tipc_nl_bearer_policy);
+ if (err)
+ return err;
+
+ if (!attrs[TIPC_NLA_BEARER_NAME])
+ return -EINVAL;
+
+ bearer = nla_data(attrs[TIPC_NLA_BEARER_NAME]);
+
+ if (attrs[TIPC_NLA_BEARER_DOMAIN])
+ domain = nla_get_u32(attrs[TIPC_NLA_BEARER_DOMAIN]);
+
+ if (attrs[TIPC_NLA_BEARER_PROP]) {
+ struct nlattr *props[TIPC_NLA_PROP_MAX + 1];
+
+ err = tipc_nl_parse_link_prop(attrs[TIPC_NLA_BEARER_PROP],
+ props);
+ if (err)
+ return err;
+
+ if (props[TIPC_NLA_PROP_PRIO])
+ prio = nla_get_u32(props[TIPC_NLA_PROP_PRIO]);
+ }
+
+ rtnl_lock();
+ err = tipc_enable_bearer(bearer, domain, prio);
+ if (err) {
+ rtnl_unlock();
+ return err;
+ }
+ rtnl_unlock();
+
+ return 0;
+}
+
+int tipc_nl_bearer_set(struct sk_buff *skb, struct genl_info *info)
+{
+ int err;
+ char *name;
+ struct tipc_bearer *b;
+ struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1];
+
+ if (!info->attrs[TIPC_NLA_BEARER])
+ return -EINVAL;
+
+ err = nla_parse_nested(attrs, TIPC_NLA_BEARER_MAX,
+ info->attrs[TIPC_NLA_BEARER],
+ tipc_nl_bearer_policy);
+ if (err)
+ return err;
+
+ if (!attrs[TIPC_NLA_BEARER_NAME])
+ return -EINVAL;
+ name = nla_data(attrs[TIPC_NLA_BEARER_NAME]);
+
+ rtnl_lock();
+ b = tipc_bearer_find(name);
+ if (!b) {
+ rtnl_unlock();
+ return -EINVAL;
+ }
+
+ if (attrs[TIPC_NLA_BEARER_PROP]) {
+ struct nlattr *props[TIPC_NLA_PROP_MAX + 1];
+
+ err = tipc_nl_parse_link_prop(attrs[TIPC_NLA_BEARER_PROP],
+ props);
+ if (err) {
+ rtnl_unlock();
+ return err;
+ }
+
+ if (props[TIPC_NLA_PROP_TOL])
+ b->tolerance = nla_get_u32(props[TIPC_NLA_PROP_TOL]);
+ if (props[TIPC_NLA_PROP_PRIO])
+ b->priority = nla_get_u32(props[TIPC_NLA_PROP_PRIO]);
+ if (props[TIPC_NLA_PROP_WIN])
+ b->window = nla_get_u32(props[TIPC_NLA_PROP_WIN]);
+ }
+ rtnl_unlock();
+
+ return 0;
+}
+
+static int __tipc_nl_add_media(struct tipc_nl_msg *msg,
+ struct tipc_media *media)
+{
+ void *hdr;
+ struct nlattr *attrs;
+ struct nlattr *prop;
+
+ hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_v2_family,
+ NLM_F_MULTI, TIPC_NL_MEDIA_GET);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ attrs = nla_nest_start(msg->skb, TIPC_NLA_MEDIA);
+ if (!attrs)
+ goto msg_full;
+
+ if (nla_put_string(msg->skb, TIPC_NLA_MEDIA_NAME, media->name))
+ goto attr_msg_full;
+
+ prop = nla_nest_start(msg->skb, TIPC_NLA_MEDIA_PROP);
+ if (!prop)
+ goto prop_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_PROP_PRIO, media->priority))
+ goto prop_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_PROP_TOL, media->tolerance))
+ goto prop_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_PROP_WIN, media->window))
+ goto prop_msg_full;
+
+ nla_nest_end(msg->skb, prop);
+ nla_nest_end(msg->skb, attrs);
+ genlmsg_end(msg->skb, hdr);
+
+ return 0;
+
+prop_msg_full:
+ nla_nest_cancel(msg->skb, prop);
+attr_msg_full:
+ nla_nest_cancel(msg->skb, attrs);
+msg_full:
+ genlmsg_cancel(msg->skb, hdr);
+
+ return -EMSGSIZE;
+}
+
+int tipc_nl_media_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int err;
+ int i = cb->args[0];
+ struct tipc_nl_msg msg;
+
+ if (i == MAX_MEDIA)
+ return 0;
+
+ msg.skb = skb;
+ msg.portid = NETLINK_CB(cb->skb).portid;
+ msg.seq = cb->nlh->nlmsg_seq;
+
+ rtnl_lock();
+ for (; media_info_array[i] != NULL; i++) {
+ err = __tipc_nl_add_media(&msg, media_info_array[i]);
+ if (err)
+ break;
+ }
+ rtnl_unlock();
+
+ cb->args[0] = i;
+ return skb->len;
+}
+
+int tipc_nl_media_get(struct sk_buff *skb, struct genl_info *info)
+{
+ int err;
+ char *name;
+ struct tipc_nl_msg msg;
+ struct tipc_media *media;
+ struct sk_buff *rep;
+ struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1];
+
+ if (!info->attrs[TIPC_NLA_MEDIA])
+ return -EINVAL;
+
+ err = nla_parse_nested(attrs, TIPC_NLA_MEDIA_MAX,
+ info->attrs[TIPC_NLA_MEDIA],
+ tipc_nl_media_policy);
+ if (err)
+ return err;
+
+ if (!attrs[TIPC_NLA_MEDIA_NAME])
+ return -EINVAL;
+ name = nla_data(attrs[TIPC_NLA_MEDIA_NAME]);
+
+ rep = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!rep)
+ return -ENOMEM;
+
+ msg.skb = rep;
+ msg.portid = info->snd_portid;
+ msg.seq = info->snd_seq;
+
+ rtnl_lock();
+ media = tipc_media_find(name);
+ if (!media) {
+ err = -EINVAL;
+ goto err_out;
+ }
+
+ err = __tipc_nl_add_media(&msg, media);
+ if (err)
+ goto err_out;
+ rtnl_unlock();
+
+ return genlmsg_reply(rep, info);
+err_out:
+ rtnl_unlock();
+ nlmsg_free(rep);
+
+ return err;
+}
+
+int tipc_nl_media_set(struct sk_buff *skb, struct genl_info *info)
+{
+ int err;
+ char *name;
+ struct tipc_media *m;
+ struct nlattr *attrs[TIPC_NLA_BEARER_MAX + 1];
+
+ if (!info->attrs[TIPC_NLA_MEDIA])
+ return -EINVAL;
+
+ err = nla_parse_nested(attrs, TIPC_NLA_MEDIA_MAX,
+ info->attrs[TIPC_NLA_MEDIA],
+ tipc_nl_media_policy);
+
+ if (!attrs[TIPC_NLA_MEDIA_NAME])
+ return -EINVAL;
+ name = nla_data(attrs[TIPC_NLA_MEDIA_NAME]);
+
+ rtnl_lock();
+ m = tipc_media_find(name);
+ if (!m) {
+ rtnl_unlock();
+ return -EINVAL;
+ }
+
+ if (attrs[TIPC_NLA_MEDIA_PROP]) {
+ struct nlattr *props[TIPC_NLA_PROP_MAX + 1];
+
+ err = tipc_nl_parse_link_prop(attrs[TIPC_NLA_MEDIA_PROP],
+ props);
+ if (err) {
+ rtnl_unlock();
+ return err;
+ }
+
+ if (props[TIPC_NLA_PROP_TOL])
+ m->tolerance = nla_get_u32(props[TIPC_NLA_PROP_TOL]);
+ if (props[TIPC_NLA_PROP_PRIO])
+ m->priority = nla_get_u32(props[TIPC_NLA_PROP_PRIO]);
+ if (props[TIPC_NLA_PROP_WIN])
+ m->window = nla_get_u32(props[TIPC_NLA_PROP_WIN]);
+ }
+ rtnl_unlock();
+
+ return 0;
+}
diff --git a/net/tipc/bearer.h b/net/tipc/bearer.h
index 78fccc49de23..2c1230ac5dfe 100644
--- a/net/tipc/bearer.h
+++ b/net/tipc/bearer.h
@@ -1,7 +1,7 @@
/*
* net/tipc/bearer.h: Include file for TIPC bearer code
*
- * Copyright (c) 1996-2006, 2013, Ericsson AB
+ * Copyright (c) 1996-2006, 2013-2014, Ericsson AB
* Copyright (c) 2005, 2010-2011, Wind River Systems
* All rights reserved.
*
@@ -38,6 +38,8 @@
#define _TIPC_BEARER_H
#include "bcast.h"
+#include "netlink.h"
+#include <net/genetlink.h>
#define MAX_BEARERS 2
#define MAX_MEDIA 2
@@ -163,7 +165,7 @@ extern struct tipc_bearer __rcu *bearer_list[];
* TIPC routines available to supported media types
*/
-void tipc_rcv(struct sk_buff *buf, struct tipc_bearer *tb_ptr);
+void tipc_rcv(struct sk_buff *skb, struct tipc_bearer *tb_ptr);
int tipc_enable_bearer(const char *bearer_name, u32 disc_domain, u32 priority);
int tipc_disable_bearer(const char *name);
@@ -176,6 +178,16 @@ extern struct tipc_media eth_media_info;
extern struct tipc_media ib_media_info;
#endif
+int tipc_nl_bearer_disable(struct sk_buff *skb, struct genl_info *info);
+int tipc_nl_bearer_enable(struct sk_buff *skb, struct genl_info *info);
+int tipc_nl_bearer_dump(struct sk_buff *skb, struct netlink_callback *cb);
+int tipc_nl_bearer_get(struct sk_buff *skb, struct genl_info *info);
+int tipc_nl_bearer_set(struct sk_buff *skb, struct genl_info *info);
+
+int tipc_nl_media_dump(struct sk_buff *skb, struct netlink_callback *cb);
+int tipc_nl_media_get(struct sk_buff *skb, struct genl_info *info);
+int tipc_nl_media_set(struct sk_buff *skb, struct genl_info *info);
+
int tipc_media_set_priority(const char *name, u32 new_value);
int tipc_media_set_window(const char *name, u32 new_value);
void tipc_media_addr_printf(char *buf, int len, struct tipc_media_addr *a);
diff --git a/net/tipc/core.h b/net/tipc/core.h
index f773b148722f..84602137ce20 100644
--- a/net/tipc/core.h
+++ b/net/tipc/core.h
@@ -41,6 +41,7 @@
#include <linux/tipc.h>
#include <linux/tipc_config.h>
+#include <linux/tipc_netlink.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/errno.h>
@@ -191,6 +192,7 @@ struct tipc_skb_cb {
struct sk_buff *tail;
bool deferred;
bool wakeup_pending;
+ bool bundling;
u16 chain_sz;
u16 chain_imp;
};
diff --git a/net/tipc/link.c b/net/tipc/link.c
index 1db162aa64a5..23bcc1132365 100644
--- a/net/tipc/link.c
+++ b/net/tipc/link.c
@@ -36,10 +36,12 @@
#include "core.h"
#include "link.h"
+#include "bcast.h"
#include "socket.h"
#include "name_distr.h"
#include "discover.h"
#include "config.h"
+#include "netlink.h"
#include <linux/pkt_sched.h>
@@ -50,6 +52,30 @@ static const char *link_co_err = "Link changeover error, ";
static const char *link_rst_msg = "Resetting link ";
static const char *link_unk_evt = "Unknown link event ";
+static const struct nla_policy tipc_nl_link_policy[TIPC_NLA_LINK_MAX + 1] = {
+ [TIPC_NLA_LINK_UNSPEC] = { .type = NLA_UNSPEC },
+ [TIPC_NLA_LINK_NAME] = {
+ .type = NLA_STRING,
+ .len = TIPC_MAX_LINK_NAME
+ },
+ [TIPC_NLA_LINK_MTU] = { .type = NLA_U32 },
+ [TIPC_NLA_LINK_BROADCAST] = { .type = NLA_FLAG },
+ [TIPC_NLA_LINK_UP] = { .type = NLA_FLAG },
+ [TIPC_NLA_LINK_ACTIVE] = { .type = NLA_FLAG },
+ [TIPC_NLA_LINK_PROP] = { .type = NLA_NESTED },
+ [TIPC_NLA_LINK_STATS] = { .type = NLA_NESTED },
+ [TIPC_NLA_LINK_RX] = { .type = NLA_U32 },
+ [TIPC_NLA_LINK_TX] = { .type = NLA_U32 }
+};
+
+/* Properties valid for media, bearar and link */
+static const struct nla_policy tipc_nl_prop_policy[TIPC_NLA_PROP_MAX + 1] = {
+ [TIPC_NLA_PROP_UNSPEC] = { .type = NLA_UNSPEC },
+ [TIPC_NLA_PROP_PRIO] = { .type = NLA_U32 },
+ [TIPC_NLA_PROP_TOL] = { .type = NLA_U32 },
+ [TIPC_NLA_PROP_WIN] = { .type = NLA_U32 }
+};
+
/*
* Out-of-range value for link session numbers
*/
@@ -123,18 +149,6 @@ static void link_init_max_pkt(struct tipc_link *l_ptr)
l_ptr->max_pkt_probes = 0;
}
-static u32 link_next_sent(struct tipc_link *l_ptr)
-{
- if (l_ptr->next_out)
- return buf_seqno(l_ptr->next_out);
- return mod(l_ptr->next_out_no);
-}
-
-static u32 link_last_sent(struct tipc_link *l_ptr)
-{
- return mod(link_next_sent(l_ptr) - 1);
-}
-
/*
* Simple non-static link routines (i.e. referenced outside this file)
*/
@@ -157,14 +171,17 @@ int tipc_link_is_active(struct tipc_link *l_ptr)
*/
static void link_timeout(struct tipc_link *l_ptr)
{
+ struct sk_buff *skb;
+
tipc_node_lock(l_ptr->owner);
/* update counters used in statistical profiling of send traffic */
- l_ptr->stats.accu_queue_sz += l_ptr->out_queue_size;
+ l_ptr->stats.accu_queue_sz += skb_queue_len(&l_ptr->outqueue);
l_ptr->stats.queue_sz_counts++;
- if (l_ptr->first_out) {
- struct tipc_msg *msg = buf_msg(l_ptr->first_out);
+ skb = skb_peek(&l_ptr->outqueue);
+ if (skb) {
+ struct tipc_msg *msg = buf_msg(skb);
u32 length = msg_size(msg);
if ((msg_user(msg) == MSG_FRAGMENTER) &&
@@ -192,11 +209,10 @@ static void link_timeout(struct tipc_link *l_ptr)
}
/* do all other link processing performed on a periodic basis */
-
link_state_event(l_ptr, TIMEOUT_EVT);
if (l_ptr->next_out)
- tipc_link_push_queue(l_ptr);
+ tipc_link_push_packets(l_ptr);
tipc_node_unlock(l_ptr->owner);
}
@@ -224,9 +240,10 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr,
char addr_string[16];
u32 peer = n_ptr->addr;
- if (n_ptr->link_cnt >= 2) {
+ if (n_ptr->link_cnt >= MAX_BEARERS) {
tipc_addr_string_fill(addr_string, n_ptr->addr);
- pr_err("Attempt to establish third link to %s\n", addr_string);
+ pr_err("Attempt to establish %uth link to %s. Max %u allowed.\n",
+ n_ptr->link_cnt, addr_string, MAX_BEARERS);
return NULL;
}
@@ -274,7 +291,9 @@ struct tipc_link *tipc_link_create(struct tipc_node *n_ptr,
link_init_max_pkt(l_ptr);
l_ptr->next_out_no = 1;
- __skb_queue_head_init(&l_ptr->waiting_sks);
+ __skb_queue_head_init(&l_ptr->outqueue);
+ __skb_queue_head_init(&l_ptr->deferred_queue);
+ skb_queue_head_init(&l_ptr->waiting_sks);
link_reset_statistics(l_ptr);
@@ -339,7 +358,7 @@ static bool link_schedule_user(struct tipc_link *link, u32 oport,
return false;
TIPC_SKB_CB(buf)->chain_sz = chain_sz;
TIPC_SKB_CB(buf)->chain_imp = imp;
- __skb_queue_tail(&link->waiting_sks, buf);
+ skb_queue_tail(&link->waiting_sks, buf);
link->stats.link_congs++;
return true;
}
@@ -352,30 +371,19 @@ static bool link_schedule_user(struct tipc_link *link, u32 oport,
*/
static void link_prepare_wakeup(struct tipc_link *link)
{
- struct sk_buff_head *wq = &link->waiting_sks;
- struct sk_buff *buf;
- uint pend_qsz = link->out_queue_size;
+ uint pend_qsz = skb_queue_len(&link->outqueue);
+ struct sk_buff *skb, *tmp;
- for (buf = skb_peek(wq); buf; buf = skb_peek(wq)) {
- if (pend_qsz >= link->queue_limit[TIPC_SKB_CB(buf)->chain_imp])
+ skb_queue_walk_safe(&link->waiting_sks, skb, tmp) {
+ if (pend_qsz >= link->queue_limit[TIPC_SKB_CB(skb)->chain_imp])
break;
- pend_qsz += TIPC_SKB_CB(buf)->chain_sz;
- __skb_queue_tail(&link->owner->waiting_sks, __skb_dequeue(wq));
+ pend_qsz += TIPC_SKB_CB(skb)->chain_sz;
+ skb_unlink(skb, &link->waiting_sks);
+ skb_queue_tail(&link->owner->waiting_sks, skb);
}
}
/**
- * link_release_outqueue - purge link's outbound message queue
- * @l_ptr: pointer to link
- */
-static void link_release_outqueue(struct tipc_link *l_ptr)
-{
- kfree_skb_list(l_ptr->first_out);
- l_ptr->first_out = NULL;
- l_ptr->out_queue_size = 0;
-}
-
-/**
* tipc_link_reset_fragments - purge link's inbound message fragments queue
* @l_ptr: pointer to link
*/
@@ -391,11 +399,9 @@ void tipc_link_reset_fragments(struct tipc_link *l_ptr)
*/
void tipc_link_purge_queues(struct tipc_link *l_ptr)
{
- kfree_skb_list(l_ptr->oldest_deferred_in);
- kfree_skb_list(l_ptr->first_out);
+ __skb_queue_purge(&l_ptr->deferred_queue);
+ __skb_queue_purge(&l_ptr->outqueue);
tipc_link_reset_fragments(l_ptr);
- kfree_skb(l_ptr->proto_msg_queue);
- l_ptr->proto_msg_queue = NULL;
}
void tipc_link_reset(struct tipc_link *l_ptr)
@@ -427,25 +433,16 @@ void tipc_link_reset(struct tipc_link *l_ptr)
}
/* Clean up all queues: */
- link_release_outqueue(l_ptr);
- kfree_skb(l_ptr->proto_msg_queue);
- l_ptr->proto_msg_queue = NULL;
- kfree_skb_list(l_ptr->oldest_deferred_in);
+ __skb_queue_purge(&l_ptr->outqueue);
+ __skb_queue_purge(&l_ptr->deferred_queue);
if (!skb_queue_empty(&l_ptr->waiting_sks)) {
skb_queue_splice_init(&l_ptr->waiting_sks, &owner->waiting_sks);
owner->action_flags |= TIPC_WAKEUP_USERS;
}
- l_ptr->retransm_queue_head = 0;
- l_ptr->retransm_queue_size = 0;
- l_ptr->last_out = NULL;
- l_ptr->first_out = NULL;
l_ptr->next_out = NULL;
l_ptr->unacked_window = 0;
l_ptr->checkpoint = 1;
l_ptr->next_out_no = 1;
- l_ptr->deferred_inqueue_sz = 0;
- l_ptr->oldest_deferred_in = NULL;
- l_ptr->newest_deferred_in = NULL;
l_ptr->fsm_msg_cnt = 0;
l_ptr->stale_count = 0;
link_reset_statistics(l_ptr);
@@ -667,9 +664,10 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event)
* - For all other messages we discard the buffer and return -EHOSTUNREACH
* - For TIPC internal messages we also reset the link
*/
-static int tipc_link_cong(struct tipc_link *link, struct sk_buff *buf)
+static int tipc_link_cong(struct tipc_link *link, struct sk_buff_head *list)
{
- struct tipc_msg *msg = buf_msg(buf);
+ struct sk_buff *skb = skb_peek(list);
+ struct tipc_msg *msg = buf_msg(skb);
uint imp = tipc_msg_tot_importance(msg);
u32 oport = msg_tot_origport(msg);
@@ -682,30 +680,30 @@ static int tipc_link_cong(struct tipc_link *link, struct sk_buff *buf)
goto drop;
if (unlikely(msg_reroute_cnt(msg)))
goto drop;
- if (TIPC_SKB_CB(buf)->wakeup_pending)
+ if (TIPC_SKB_CB(skb)->wakeup_pending)
return -ELINKCONG;
- if (link_schedule_user(link, oport, TIPC_SKB_CB(buf)->chain_sz, imp))
+ if (link_schedule_user(link, oport, skb_queue_len(list), imp))
return -ELINKCONG;
drop:
- kfree_skb_list(buf);
+ __skb_queue_purge(list);
return -EHOSTUNREACH;
}
/**
* __tipc_link_xmit(): same as tipc_link_xmit, but destlink is known & locked
* @link: link to use
- * @buf: chain of buffers containing message
+ * @list: chain of buffers containing message
+ *
* Consumes the buffer chain, except when returning -ELINKCONG
* Returns 0 if success, otherwise errno: -ELINKCONG, -EMSGSIZE (plain socket
* user data messages) or -EHOSTUNREACH (all other messages/senders)
* Only the socket functions tipc_send_stream() and tipc_send_packet() need
* to act on the return value, since they may need to do more send attempts.
*/
-int __tipc_link_xmit(struct tipc_link *link, struct sk_buff *buf)
+int __tipc_link_xmit(struct tipc_link *link, struct sk_buff_head *list)
{
- struct tipc_msg *msg = buf_msg(buf);
+ struct tipc_msg *msg = buf_msg(skb_peek(list));
uint psz = msg_size(msg);
- uint qsz = link->out_queue_size;
uint sndlim = link->queue_limit[0];
uint imp = tipc_msg_tot_importance(msg);
uint mtu = link->max_pkt;
@@ -713,71 +711,83 @@ int __tipc_link_xmit(struct tipc_link *link, struct sk_buff *buf)
uint seqno = link->next_out_no;
uint bc_last_in = link->owner->bclink.last_in;
struct tipc_media_addr *addr = &link->media_addr;
- struct sk_buff *next = buf->next;
+ struct sk_buff_head *outqueue = &link->outqueue;
+ struct sk_buff *skb, *tmp;
/* Match queue limits against msg importance: */
- if (unlikely(qsz >= link->queue_limit[imp]))
- return tipc_link_cong(link, buf);
+ if (unlikely(skb_queue_len(outqueue) >= link->queue_limit[imp]))
+ return tipc_link_cong(link, list);
/* Has valid packet limit been used ? */
if (unlikely(psz > mtu)) {
- kfree_skb_list(buf);
+ __skb_queue_purge(list);
return -EMSGSIZE;
}
/* Prepare each packet for sending, and add to outqueue: */
- while (buf) {
- next = buf->next;
- msg = buf_msg(buf);
+ skb_queue_walk_safe(list, skb, tmp) {
+ __skb_unlink(skb, list);
+ msg = buf_msg(skb);
msg_set_word(msg, 2, ((ack << 16) | mod(seqno)));
msg_set_bcast_ack(msg, bc_last_in);
- if (!link->first_out) {
- link->first_out = buf;
- } else if (qsz < sndlim) {
- link->last_out->next = buf;
- } else if (tipc_msg_bundle(link->last_out, buf, mtu)) {
+ if (skb_queue_len(outqueue) < sndlim) {
+ __skb_queue_tail(outqueue, skb);
+ tipc_bearer_send(link->bearer_id, skb, addr);
+ link->next_out = NULL;
+ link->unacked_window = 0;
+ } else if (tipc_msg_bundle(outqueue, skb, mtu)) {
link->stats.sent_bundled++;
- buf = next;
- next = buf->next;
continue;
- } else if (tipc_msg_make_bundle(&buf, mtu, link->addr)) {
+ } else if (tipc_msg_make_bundle(outqueue, skb, mtu,
+ link->addr)) {
link->stats.sent_bundled++;
link->stats.sent_bundles++;
- link->last_out->next = buf;
if (!link->next_out)
- link->next_out = buf;
+ link->next_out = skb_peek_tail(outqueue);
} else {
- link->last_out->next = buf;
+ __skb_queue_tail(outqueue, skb);
if (!link->next_out)
- link->next_out = buf;
- }
-
- /* Send packet if possible: */
- if (likely(++qsz <= sndlim)) {
- tipc_bearer_send(link->bearer_id, buf, addr);
- link->next_out = next;
- link->unacked_window = 0;
+ link->next_out = skb;
}
seqno++;
- link->last_out = buf;
- buf = next;
}
link->next_out_no = seqno;
- link->out_queue_size = qsz;
return 0;
}
+static void skb2list(struct sk_buff *skb, struct sk_buff_head *list)
+{
+ __skb_queue_head_init(list);
+ __skb_queue_tail(list, skb);
+}
+
+static int __tipc_link_xmit_skb(struct tipc_link *link, struct sk_buff *skb)
+{
+ struct sk_buff_head head;
+
+ skb2list(skb, &head);
+ return __tipc_link_xmit(link, &head);
+}
+
+int tipc_link_xmit_skb(struct sk_buff *skb, u32 dnode, u32 selector)
+{
+ struct sk_buff_head head;
+
+ skb2list(skb, &head);
+ return tipc_link_xmit(&head, dnode, selector);
+}
+
/**
* tipc_link_xmit() is the general link level function for message sending
- * @buf: chain of buffers containing message
+ * @list: chain of buffers containing message
* @dsz: amount of user data to be sent
* @dnode: address of destination node
* @selector: a number used for deterministic link selection
* Consumes the buffer chain, except when returning -ELINKCONG
* Returns 0 if success, otherwise errno: -ELINKCONG,-EHOSTUNREACH,-EMSGSIZE
*/
-int tipc_link_xmit(struct sk_buff *buf, u32 dnode, u32 selector)
+int tipc_link_xmit(struct sk_buff_head *list, u32 dnode, u32 selector)
{
struct tipc_link *link = NULL;
struct tipc_node *node;
@@ -788,17 +798,22 @@ int tipc_link_xmit(struct sk_buff *buf, u32 dnode, u32 selector)
tipc_node_lock(node);
link = node->active_links[selector & 1];
if (link)
- rc = __tipc_link_xmit(link, buf);
+ rc = __tipc_link_xmit(link, list);
tipc_node_unlock(node);
}
if (link)
return rc;
- if (likely(in_own_node(dnode)))
- return tipc_sk_rcv(buf);
+ if (likely(in_own_node(dnode))) {
+ /* As a node local message chain never contains more than one
+ * buffer, we just need to dequeue one SKB buffer from the
+ * head list.
+ */
+ return tipc_sk_rcv(__skb_dequeue(list));
+ }
+ __skb_queue_purge(list);
- kfree_skb_list(buf);
return rc;
}
@@ -812,17 +827,17 @@ int tipc_link_xmit(struct sk_buff *buf, u32 dnode, u32 selector)
*/
static void tipc_link_sync_xmit(struct tipc_link *link)
{
- struct sk_buff *buf;
+ struct sk_buff *skb;
struct tipc_msg *msg;
- buf = tipc_buf_acquire(INT_H_SIZE);
- if (!buf)
+ skb = tipc_buf_acquire(INT_H_SIZE);
+ if (!skb)
return;
- msg = buf_msg(buf);
+ msg = buf_msg(skb);
tipc_msg_init(msg, BCAST_PROTOCOL, STATE_MSG, INT_H_SIZE, link->addr);
msg_set_last_bcast(msg, link->owner->bclink.acked);
- __tipc_link_xmit(link, buf);
+ __tipc_link_xmit_skb(link, skb);
}
/*
@@ -842,85 +857,46 @@ static void tipc_link_sync_rcv(struct tipc_node *n, struct sk_buff *buf)
kfree_skb(buf);
}
+struct sk_buff *tipc_skb_queue_next(const struct sk_buff_head *list,
+ const struct sk_buff *skb)
+{
+ if (skb_queue_is_last(list, skb))
+ return NULL;
+ return skb->next;
+}
+
/*
- * tipc_link_push_packet: Push one unsent packet to the media
+ * tipc_link_push_packets - push unsent packets to bearer
+ *
+ * Push out the unsent messages of a link where congestion
+ * has abated. Node is locked.
+ *
+ * Called with node locked
*/
-static u32 tipc_link_push_packet(struct tipc_link *l_ptr)
-{
- struct sk_buff *buf = l_ptr->first_out;
- u32 r_q_size = l_ptr->retransm_queue_size;
- u32 r_q_head = l_ptr->retransm_queue_head;
-
- /* Step to position where retransmission failed, if any, */
- /* consider that buffers may have been released in meantime */
- if (r_q_size && buf) {
- u32 last = lesser(mod(r_q_head + r_q_size),
- link_last_sent(l_ptr));
- u32 first = buf_seqno(buf);
-
- while (buf && less(first, r_q_head)) {
- first = mod(first + 1);
- buf = buf->next;
- }
- l_ptr->retransm_queue_head = r_q_head = first;
- l_ptr->retransm_queue_size = r_q_size = mod(last - first);
- }
-
- /* Continue retransmission now, if there is anything: */
- if (r_q_size && buf) {
- msg_set_ack(buf_msg(buf), mod(l_ptr->next_in_no - 1));
- msg_set_bcast_ack(buf_msg(buf), l_ptr->owner->bclink.last_in);
- tipc_bearer_send(l_ptr->bearer_id, buf, &l_ptr->media_addr);
- l_ptr->retransm_queue_head = mod(++r_q_head);
- l_ptr->retransm_queue_size = --r_q_size;
- l_ptr->stats.retransmitted++;
- return 0;
- }
-
- /* Send deferred protocol message, if any: */
- buf = l_ptr->proto_msg_queue;
- if (buf) {
- msg_set_ack(buf_msg(buf), mod(l_ptr->next_in_no - 1));
- msg_set_bcast_ack(buf_msg(buf), l_ptr->owner->bclink.last_in);
- tipc_bearer_send(l_ptr->bearer_id, buf, &l_ptr->media_addr);
- l_ptr->unacked_window = 0;
- kfree_skb(buf);
- l_ptr->proto_msg_queue = NULL;
- return 0;
- }
+void tipc_link_push_packets(struct tipc_link *l_ptr)
+{
+ struct sk_buff_head *outqueue = &l_ptr->outqueue;
+ struct sk_buff *skb = l_ptr->next_out;
+ struct tipc_msg *msg;
+ u32 next, first;
- /* Send one deferred data message, if send window not full: */
- buf = l_ptr->next_out;
- if (buf) {
- struct tipc_msg *msg = buf_msg(buf);
- u32 next = msg_seqno(msg);
- u32 first = buf_seqno(l_ptr->first_out);
+ skb_queue_walk_from(outqueue, skb) {
+ msg = buf_msg(skb);
+ next = msg_seqno(msg);
+ first = buf_seqno(skb_peek(outqueue));
if (mod(next - first) < l_ptr->queue_limit[0]) {
msg_set_ack(msg, mod(l_ptr->next_in_no - 1));
msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in);
- tipc_bearer_send(l_ptr->bearer_id, buf,
- &l_ptr->media_addr);
if (msg_user(msg) == MSG_BUNDLER)
- msg_set_type(msg, BUNDLE_CLOSED);
- l_ptr->next_out = buf->next;
- return 0;
+ TIPC_SKB_CB(skb)->bundling = false;
+ tipc_bearer_send(l_ptr->bearer_id, skb,
+ &l_ptr->media_addr);
+ l_ptr->next_out = tipc_skb_queue_next(outqueue, skb);
+ } else {
+ break;
}
}
- return 1;
-}
-
-/*
- * push_queue(): push out the unsent messages of a link where
- * congestion has abated. Node is locked
- */
-void tipc_link_push_queue(struct tipc_link *l_ptr)
-{
- u32 res;
-
- do {
- res = tipc_link_push_packet(l_ptr);
- } while (!res);
}
void tipc_link_reset_all(struct tipc_node *node)
@@ -984,20 +960,20 @@ static void link_retransmit_failure(struct tipc_link *l_ptr,
}
}
-void tipc_link_retransmit(struct tipc_link *l_ptr, struct sk_buff *buf,
+void tipc_link_retransmit(struct tipc_link *l_ptr, struct sk_buff *skb,
u32 retransmits)
{
struct tipc_msg *msg;
- if (!buf)
+ if (!skb)
return;
- msg = buf_msg(buf);
+ msg = buf_msg(skb);
/* Detect repeated retransmit failures */
if (l_ptr->last_retransmitted == msg_seqno(msg)) {
if (++l_ptr->stale_count > 100) {
- link_retransmit_failure(l_ptr, buf);
+ link_retransmit_failure(l_ptr, skb);
return;
}
} else {
@@ -1005,38 +981,29 @@ void tipc_link_retransmit(struct tipc_link *l_ptr, struct sk_buff *buf,
l_ptr->stale_count = 1;
}
- while (retransmits && (buf != l_ptr->next_out) && buf) {
- msg = buf_msg(buf);
+ skb_queue_walk_from(&l_ptr->outqueue, skb) {
+ if (!retransmits || skb == l_ptr->next_out)
+ break;
+ msg = buf_msg(skb);
msg_set_ack(msg, mod(l_ptr->next_in_no - 1));
msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in);
- tipc_bearer_send(l_ptr->bearer_id, buf, &l_ptr->media_addr);
- buf = buf->next;
+ tipc_bearer_send(l_ptr->bearer_id, skb, &l_ptr->media_addr);
retransmits--;
l_ptr->stats.retransmitted++;
}
-
- l_ptr->retransm_queue_head = l_ptr->retransm_queue_size = 0;
}
-/**
- * link_insert_deferred_queue - insert deferred messages back into receive chain
- */
-static struct sk_buff *link_insert_deferred_queue(struct tipc_link *l_ptr,
- struct sk_buff *buf)
+static void link_retrieve_defq(struct tipc_link *link,
+ struct sk_buff_head *list)
{
u32 seq_no;
- if (l_ptr->oldest_deferred_in == NULL)
- return buf;
+ if (skb_queue_empty(&link->deferred_queue))
+ return;
- seq_no = buf_seqno(l_ptr->oldest_deferred_in);
- if (seq_no == mod(l_ptr->next_in_no)) {
- l_ptr->newest_deferred_in->next = buf;
- buf = l_ptr->oldest_deferred_in;
- l_ptr->oldest_deferred_in = NULL;
- l_ptr->deferred_inqueue_sz = 0;
- }
- return buf;
+ seq_no = buf_seqno(skb_peek(&link->deferred_queue));
+ if (seq_no == mod(link->next_in_no))
+ skb_queue_splice_tail_init(&link->deferred_queue, list);
}
/**
@@ -1096,43 +1063,42 @@ static int link_recv_buf_validate(struct sk_buff *buf)
/**
* tipc_rcv - process TIPC packets/messages arriving from off-node
- * @head: pointer to message buffer chain
+ * @skb: TIPC packet
* @b_ptr: pointer to bearer message arrived on
*
* Invoked with no locks held. Bearer pointer must point to a valid bearer
* structure (i.e. cannot be NULL), but bearer can be inactive.
*/
-void tipc_rcv(struct sk_buff *head, struct tipc_bearer *b_ptr)
+void tipc_rcv(struct sk_buff *skb, struct tipc_bearer *b_ptr)
{
- while (head) {
- struct tipc_node *n_ptr;
- struct tipc_link *l_ptr;
- struct sk_buff *crs;
- struct sk_buff *buf = head;
- struct tipc_msg *msg;
- u32 seq_no;
- u32 ackd;
- u32 released = 0;
+ struct sk_buff_head head;
+ struct tipc_node *n_ptr;
+ struct tipc_link *l_ptr;
+ struct sk_buff *skb1, *tmp;
+ struct tipc_msg *msg;
+ u32 seq_no;
+ u32 ackd;
+ u32 released;
- head = head->next;
- buf->next = NULL;
+ skb2list(skb, &head);
+ while ((skb = __skb_dequeue(&head))) {
/* Ensure message is well-formed */
- if (unlikely(!link_recv_buf_validate(buf)))
+ if (unlikely(!link_recv_buf_validate(skb)))
goto discard;
/* Ensure message data is a single contiguous unit */
- if (unlikely(skb_linearize(buf)))
+ if (unlikely(skb_linearize(skb)))
goto discard;
/* Handle arrival of a non-unicast link message */
- msg = buf_msg(buf);
+ msg = buf_msg(skb);
if (unlikely(msg_non_seq(msg))) {
if (msg_user(msg) == LINK_CONFIG)
- tipc_disc_rcv(buf, b_ptr);
+ tipc_disc_rcv(skb, b_ptr);
else
- tipc_bclink_rcv(buf);
+ tipc_bclink_rcv(skb);
continue;
}
@@ -1171,22 +1137,19 @@ void tipc_rcv(struct sk_buff *head, struct tipc_bearer *b_ptr)
if (n_ptr->bclink.recv_permitted)
tipc_bclink_acknowledge(n_ptr, msg_bcast_ack(msg));
- crs = l_ptr->first_out;
- while ((crs != l_ptr->next_out) &&
- less_eq(buf_seqno(crs), ackd)) {
- struct sk_buff *next = crs->next;
- kfree_skb(crs);
- crs = next;
- released++;
- }
- if (released) {
- l_ptr->first_out = crs;
- l_ptr->out_queue_size -= released;
+ released = 0;
+ skb_queue_walk_safe(&l_ptr->outqueue, skb1, tmp) {
+ if (skb1 == l_ptr->next_out ||
+ more(buf_seqno(skb1), ackd))
+ break;
+ __skb_unlink(skb1, &l_ptr->outqueue);
+ kfree_skb(skb1);
+ released = 1;
}
/* Try sending any messages link endpoint has pending */
if (unlikely(l_ptr->next_out))
- tipc_link_push_queue(l_ptr);
+ tipc_link_push_packets(l_ptr);
if (released && !skb_queue_empty(&l_ptr->waiting_sks)) {
link_prepare_wakeup(l_ptr);
@@ -1196,8 +1159,8 @@ void tipc_rcv(struct sk_buff *head, struct tipc_bearer *b_ptr)
/* Process the incoming packet */
if (unlikely(!link_working_working(l_ptr))) {
if (msg_user(msg) == LINK_PROTOCOL) {
- tipc_link_proto_rcv(l_ptr, buf);
- head = link_insert_deferred_queue(l_ptr, head);
+ tipc_link_proto_rcv(l_ptr, skb);
+ link_retrieve_defq(l_ptr, &head);
tipc_node_unlock(n_ptr);
continue;
}
@@ -1207,8 +1170,7 @@ void tipc_rcv(struct sk_buff *head, struct tipc_bearer *b_ptr)
if (link_working_working(l_ptr)) {
/* Re-insert buffer in front of queue */
- buf->next = head;
- head = buf;
+ __skb_queue_head(&head, skb);
tipc_node_unlock(n_ptr);
continue;
}
@@ -1217,33 +1179,33 @@ void tipc_rcv(struct sk_buff *head, struct tipc_bearer *b_ptr)
/* Link is now in state WORKING_WORKING */
if (unlikely(seq_no != mod(l_ptr->next_in_no))) {
- link_handle_out_of_seq_msg(l_ptr, buf);
- head = link_insert_deferred_queue(l_ptr, head);
+ link_handle_out_of_seq_msg(l_ptr, skb);
+ link_retrieve_defq(l_ptr, &head);
tipc_node_unlock(n_ptr);
continue;
}
l_ptr->next_in_no++;
- if (unlikely(l_ptr->oldest_deferred_in))
- head = link_insert_deferred_queue(l_ptr, head);
+ if (unlikely(!skb_queue_empty(&l_ptr->deferred_queue)))
+ link_retrieve_defq(l_ptr, &head);
if (unlikely(++l_ptr->unacked_window >= TIPC_MIN_LINK_WIN)) {
l_ptr->stats.sent_acks++;
tipc_link_proto_xmit(l_ptr, STATE_MSG, 0, 0, 0, 0, 0);
}
- if (tipc_link_prepare_input(l_ptr, &buf)) {
+ if (tipc_link_prepare_input(l_ptr, &skb)) {
tipc_node_unlock(n_ptr);
continue;
}
tipc_node_unlock(n_ptr);
- msg = buf_msg(buf);
- if (tipc_link_input(l_ptr, buf) != 0)
+
+ if (tipc_link_input(l_ptr, skb) != 0)
goto discard;
continue;
unlock_discard:
tipc_node_unlock(n_ptr);
discard:
- kfree_skb(buf);
+ kfree_skb(skb);
}
}
@@ -1326,48 +1288,37 @@ static int tipc_link_input(struct tipc_link *l, struct sk_buff *buf)
*
* Returns increase in queue length (i.e. 0 or 1)
*/
-u32 tipc_link_defer_pkt(struct sk_buff **head, struct sk_buff **tail,
- struct sk_buff *buf)
+u32 tipc_link_defer_pkt(struct sk_buff_head *list, struct sk_buff *skb)
{
- struct sk_buff *queue_buf;
- struct sk_buff **prev;
- u32 seq_no = buf_seqno(buf);
-
- buf->next = NULL;
+ struct sk_buff *skb1;
+ u32 seq_no = buf_seqno(skb);
/* Empty queue ? */
- if (*head == NULL) {
- *head = *tail = buf;
+ if (skb_queue_empty(list)) {
+ __skb_queue_tail(list, skb);
return 1;
}
/* Last ? */
- if (less(buf_seqno(*tail), seq_no)) {
- (*tail)->next = buf;
- *tail = buf;
+ if (less(buf_seqno(skb_peek_tail(list)), seq_no)) {
+ __skb_queue_tail(list, skb);
return 1;
}
/* Locate insertion point in queue, then insert; discard if duplicate */
- prev = head;
- queue_buf = *head;
- for (;;) {
- u32 curr_seqno = buf_seqno(queue_buf);
+ skb_queue_walk(list, skb1) {
+ u32 curr_seqno = buf_seqno(skb1);
if (seq_no == curr_seqno) {
- kfree_skb(buf);
+ kfree_skb(skb);
return 0;
}
if (less(seq_no, curr_seqno))
break;
-
- prev = &queue_buf->next;
- queue_buf = queue_buf->next;
}
- buf->next = queue_buf;
- *prev = buf;
+ __skb_queue_before(list, skb1, skb);
return 1;
}
@@ -1397,15 +1348,14 @@ static void link_handle_out_of_seq_msg(struct tipc_link *l_ptr,
return;
}
- if (tipc_link_defer_pkt(&l_ptr->oldest_deferred_in,
- &l_ptr->newest_deferred_in, buf)) {
- l_ptr->deferred_inqueue_sz++;
+ if (tipc_link_defer_pkt(&l_ptr->deferred_queue, buf)) {
l_ptr->stats.deferred_recv++;
TIPC_SKB_CB(buf)->deferred = true;
- if ((l_ptr->deferred_inqueue_sz % 16) == 1)
+ if ((skb_queue_len(&l_ptr->deferred_queue) % 16) == 1)
tipc_link_proto_xmit(l_ptr, STATE_MSG, 0, 0, 0, 0, 0);
- } else
+ } else {
l_ptr->stats.duplicates++;
+ }
}
/*
@@ -1419,12 +1369,6 @@ void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int probe_msg,
u32 msg_size = sizeof(l_ptr->proto_msg);
int r_flag;
- /* Discard any previous message that was deferred due to congestion */
- if (l_ptr->proto_msg_queue) {
- kfree_skb(l_ptr->proto_msg_queue);
- l_ptr->proto_msg_queue = NULL;
- }
-
/* Don't send protocol message during link changeover */
if (l_ptr->exp_msg_count)
return;
@@ -1447,8 +1391,8 @@ void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int probe_msg,
if (l_ptr->next_out)
next_sent = buf_seqno(l_ptr->next_out);
msg_set_next_sent(msg, next_sent);
- if (l_ptr->oldest_deferred_in) {
- u32 rec = buf_seqno(l_ptr->oldest_deferred_in);
+ if (!skb_queue_empty(&l_ptr->deferred_queue)) {
+ u32 rec = buf_seqno(skb_peek(&l_ptr->deferred_queue));
gap = mod(rec - mod(l_ptr->next_in_no));
}
msg_set_seq_gap(msg, gap);
@@ -1636,7 +1580,7 @@ static void tipc_link_proto_rcv(struct tipc_link *l_ptr, struct sk_buff *buf)
}
if (msg_seq_gap(msg)) {
l_ptr->stats.recv_nacks++;
- tipc_link_retransmit(l_ptr, l_ptr->first_out,
+ tipc_link_retransmit(l_ptr, skb_peek(&l_ptr->outqueue),
msg_seq_gap(msg));
}
break;
@@ -1655,7 +1599,7 @@ static void tipc_link_tunnel_xmit(struct tipc_link *l_ptr,
u32 selector)
{
struct tipc_link *tunnel;
- struct sk_buff *buf;
+ struct sk_buff *skb;
u32 length = msg_size(msg);
tunnel = l_ptr->owner->active_links[selector & 1];
@@ -1664,14 +1608,14 @@ static void tipc_link_tunnel_xmit(struct tipc_link *l_ptr,
return;
}
msg_set_size(tunnel_hdr, length + INT_H_SIZE);
- buf = tipc_buf_acquire(length + INT_H_SIZE);
- if (!buf) {
+ skb = tipc_buf_acquire(length + INT_H_SIZE);
+ if (!skb) {
pr_warn("%sunable to send tunnel msg\n", link_co_err);
return;
}
- skb_copy_to_linear_data(buf, tunnel_hdr, INT_H_SIZE);
- skb_copy_to_linear_data_offset(buf, INT_H_SIZE, msg, length);
- __tipc_link_xmit(tunnel, buf);
+ skb_copy_to_linear_data(skb, tunnel_hdr, INT_H_SIZE);
+ skb_copy_to_linear_data_offset(skb, INT_H_SIZE, msg, length);
+ __tipc_link_xmit_skb(tunnel, skb);
}
@@ -1683,10 +1627,10 @@ static void tipc_link_tunnel_xmit(struct tipc_link *l_ptr,
*/
void tipc_link_failover_send_queue(struct tipc_link *l_ptr)
{
- u32 msgcount = l_ptr->out_queue_size;
- struct sk_buff *crs = l_ptr->first_out;
+ u32 msgcount = skb_queue_len(&l_ptr->outqueue);
struct tipc_link *tunnel = l_ptr->owner->active_links[0];
struct tipc_msg tunnel_hdr;
+ struct sk_buff *skb;
int split_bundles;
if (!tunnel)
@@ -1697,14 +1641,12 @@ void tipc_link_failover_send_queue(struct tipc_link *l_ptr)
msg_set_bearer_id(&tunnel_hdr, l_ptr->peer_bearer_id);
msg_set_msgcnt(&tunnel_hdr, msgcount);
- if (!l_ptr->first_out) {
- struct sk_buff *buf;
-
- buf = tipc_buf_acquire(INT_H_SIZE);
- if (buf) {
- skb_copy_to_linear_data(buf, &tunnel_hdr, INT_H_SIZE);
+ if (skb_queue_empty(&l_ptr->outqueue)) {
+ skb = tipc_buf_acquire(INT_H_SIZE);
+ if (skb) {
+ skb_copy_to_linear_data(skb, &tunnel_hdr, INT_H_SIZE);
msg_set_size(&tunnel_hdr, INT_H_SIZE);
- __tipc_link_xmit(tunnel, buf);
+ __tipc_link_xmit_skb(tunnel, skb);
} else {
pr_warn("%sunable to send changeover msg\n",
link_co_err);
@@ -1715,8 +1657,8 @@ void tipc_link_failover_send_queue(struct tipc_link *l_ptr)
split_bundles = (l_ptr->owner->active_links[0] !=
l_ptr->owner->active_links[1]);
- while (crs) {
- struct tipc_msg *msg = buf_msg(crs);
+ skb_queue_walk(&l_ptr->outqueue, skb) {
+ struct tipc_msg *msg = buf_msg(skb);
if ((msg_user(msg) == MSG_BUNDLER) && split_bundles) {
struct tipc_msg *m = msg_get_wrapped(msg);
@@ -1734,7 +1676,6 @@ void tipc_link_failover_send_queue(struct tipc_link *l_ptr)
tipc_link_tunnel_xmit(l_ptr, &tunnel_hdr, msg,
msg_link_selector(msg));
}
- crs = crs->next;
}
}
@@ -1750,17 +1691,16 @@ void tipc_link_failover_send_queue(struct tipc_link *l_ptr)
void tipc_link_dup_queue_xmit(struct tipc_link *l_ptr,
struct tipc_link *tunnel)
{
- struct sk_buff *iter;
+ struct sk_buff *skb;
struct tipc_msg tunnel_hdr;
tipc_msg_init(&tunnel_hdr, CHANGEOVER_PROTOCOL,
DUPLICATE_MSG, INT_H_SIZE, l_ptr->addr);
- msg_set_msgcnt(&tunnel_hdr, l_ptr->out_queue_size);
+ msg_set_msgcnt(&tunnel_hdr, skb_queue_len(&l_ptr->outqueue));
msg_set_bearer_id(&tunnel_hdr, l_ptr->peer_bearer_id);
- iter = l_ptr->first_out;
- while (iter) {
- struct sk_buff *outbuf;
- struct tipc_msg *msg = buf_msg(iter);
+ skb_queue_walk(&l_ptr->outqueue, skb) {
+ struct sk_buff *outskb;
+ struct tipc_msg *msg = buf_msg(skb);
u32 length = msg_size(msg);
if (msg_user(msg) == MSG_BUNDLER)
@@ -1768,19 +1708,18 @@ void tipc_link_dup_queue_xmit(struct tipc_link *l_ptr,
msg_set_ack(msg, mod(l_ptr->next_in_no - 1)); /* Update */
msg_set_bcast_ack(msg, l_ptr->owner->bclink.last_in);
msg_set_size(&tunnel_hdr, length + INT_H_SIZE);
- outbuf = tipc_buf_acquire(length + INT_H_SIZE);
- if (outbuf == NULL) {
+ outskb = tipc_buf_acquire(length + INT_H_SIZE);
+ if (outskb == NULL) {
pr_warn("%sunable to send duplicate msg\n",
link_co_err);
return;
}
- skb_copy_to_linear_data(outbuf, &tunnel_hdr, INT_H_SIZE);
- skb_copy_to_linear_data_offset(outbuf, INT_H_SIZE, iter->data,
+ skb_copy_to_linear_data(outskb, &tunnel_hdr, INT_H_SIZE);
+ skb_copy_to_linear_data_offset(outskb, INT_H_SIZE, skb->data,
length);
- __tipc_link_xmit(tunnel, outbuf);
+ __tipc_link_xmit_skb(tunnel, outskb);
if (!tipc_link_is_up(l_ptr))
return;
- iter = iter->next;
}
}
@@ -2375,3 +2314,435 @@ static void link_print(struct tipc_link *l_ptr, const char *str)
else
pr_cont("\n");
}
+
+/* Parse and validate nested (link) properties valid for media, bearer and link
+ */
+int tipc_nl_parse_link_prop(struct nlattr *prop, struct nlattr *props[])
+{
+ int err;
+
+ err = nla_parse_nested(props, TIPC_NLA_PROP_MAX, prop,
+ tipc_nl_prop_policy);
+ if (err)
+ return err;
+
+ if (props[TIPC_NLA_PROP_PRIO]) {
+ u32 prio;
+
+ prio = nla_get_u32(props[TIPC_NLA_PROP_PRIO]);
+ if (prio > TIPC_MAX_LINK_PRI)
+ return -EINVAL;
+ }
+
+ if (props[TIPC_NLA_PROP_TOL]) {
+ u32 tol;
+
+ tol = nla_get_u32(props[TIPC_NLA_PROP_TOL]);
+ if ((tol < TIPC_MIN_LINK_TOL) || (tol > TIPC_MAX_LINK_TOL))
+ return -EINVAL;
+ }
+
+ if (props[TIPC_NLA_PROP_WIN]) {
+ u32 win;
+
+ win = nla_get_u32(props[TIPC_NLA_PROP_WIN]);
+ if ((win < TIPC_MIN_LINK_WIN) || (win > TIPC_MAX_LINK_WIN))
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int tipc_nl_link_set(struct sk_buff *skb, struct genl_info *info)
+{
+ int err;
+ int res = 0;
+ int bearer_id;
+ char *name;
+ struct tipc_link *link;
+ struct tipc_node *node;
+ struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1];
+
+ if (!info->attrs[TIPC_NLA_LINK])
+ return -EINVAL;
+
+ err = nla_parse_nested(attrs, TIPC_NLA_LINK_MAX,
+ info->attrs[TIPC_NLA_LINK],
+ tipc_nl_link_policy);
+ if (err)
+ return err;
+
+ if (!attrs[TIPC_NLA_LINK_NAME])
+ return -EINVAL;
+
+ name = nla_data(attrs[TIPC_NLA_LINK_NAME]);
+
+ node = tipc_link_find_owner(name, &bearer_id);
+ if (!node)
+ return -EINVAL;
+
+ tipc_node_lock(node);
+
+ link = node->links[bearer_id];
+ if (!link) {
+ res = -EINVAL;
+ goto out;
+ }
+
+ if (attrs[TIPC_NLA_LINK_PROP]) {
+ struct nlattr *props[TIPC_NLA_PROP_MAX + 1];
+
+ err = tipc_nl_parse_link_prop(attrs[TIPC_NLA_LINK_PROP],
+ props);
+ if (err) {
+ res = err;
+ goto out;
+ }
+
+ if (props[TIPC_NLA_PROP_TOL]) {
+ u32 tol;
+
+ tol = nla_get_u32(props[TIPC_NLA_PROP_TOL]);
+ link_set_supervision_props(link, tol);
+ tipc_link_proto_xmit(link, STATE_MSG, 0, 0, tol, 0, 0);
+ }
+ if (props[TIPC_NLA_PROP_PRIO]) {
+ u32 prio;
+
+ prio = nla_get_u32(props[TIPC_NLA_PROP_PRIO]);
+ link->priority = prio;
+ tipc_link_proto_xmit(link, STATE_MSG, 0, 0, 0, prio, 0);
+ }
+ if (props[TIPC_NLA_PROP_WIN]) {
+ u32 win;
+
+ win = nla_get_u32(props[TIPC_NLA_PROP_WIN]);
+ tipc_link_set_queue_limits(link, win);
+ }
+ }
+
+out:
+ tipc_node_unlock(node);
+
+ return res;
+}
+
+static int __tipc_nl_add_stats(struct sk_buff *skb, struct tipc_stats *s)
+{
+ int i;
+ struct nlattr *stats;
+
+ struct nla_map {
+ u32 key;
+ u32 val;
+ };
+
+ struct nla_map map[] = {
+ {TIPC_NLA_STATS_RX_INFO, s->recv_info},
+ {TIPC_NLA_STATS_RX_FRAGMENTS, s->recv_fragments},
+ {TIPC_NLA_STATS_RX_FRAGMENTED, s->recv_fragmented},
+ {TIPC_NLA_STATS_RX_BUNDLES, s->recv_bundles},
+ {TIPC_NLA_STATS_RX_BUNDLED, s->recv_bundled},
+ {TIPC_NLA_STATS_TX_INFO, s->sent_info},
+ {TIPC_NLA_STATS_TX_FRAGMENTS, s->sent_fragments},
+ {TIPC_NLA_STATS_TX_FRAGMENTED, s->sent_fragmented},
+ {TIPC_NLA_STATS_TX_BUNDLES, s->sent_bundles},
+ {TIPC_NLA_STATS_TX_BUNDLED, s->sent_bundled},
+ {TIPC_NLA_STATS_MSG_PROF_TOT, (s->msg_length_counts) ?
+ s->msg_length_counts : 1},
+ {TIPC_NLA_STATS_MSG_LEN_CNT, s->msg_length_counts},
+ {TIPC_NLA_STATS_MSG_LEN_TOT, s->msg_lengths_total},
+ {TIPC_NLA_STATS_MSG_LEN_P0, s->msg_length_profile[0]},
+ {TIPC_NLA_STATS_MSG_LEN_P1, s->msg_length_profile[1]},
+ {TIPC_NLA_STATS_MSG_LEN_P2, s->msg_length_profile[2]},
+ {TIPC_NLA_STATS_MSG_LEN_P3, s->msg_length_profile[3]},
+ {TIPC_NLA_STATS_MSG_LEN_P4, s->msg_length_profile[4]},
+ {TIPC_NLA_STATS_MSG_LEN_P5, s->msg_length_profile[5]},
+ {TIPC_NLA_STATS_MSG_LEN_P6, s->msg_length_profile[6]},
+ {TIPC_NLA_STATS_RX_STATES, s->recv_states},
+ {TIPC_NLA_STATS_RX_PROBES, s->recv_probes},
+ {TIPC_NLA_STATS_RX_NACKS, s->recv_nacks},
+ {TIPC_NLA_STATS_RX_DEFERRED, s->deferred_recv},
+ {TIPC_NLA_STATS_TX_STATES, s->sent_states},
+ {TIPC_NLA_STATS_TX_PROBES, s->sent_probes},
+ {TIPC_NLA_STATS_TX_NACKS, s->sent_nacks},
+ {TIPC_NLA_STATS_TX_ACKS, s->sent_acks},
+ {TIPC_NLA_STATS_RETRANSMITTED, s->retransmitted},
+ {TIPC_NLA_STATS_DUPLICATES, s->duplicates},
+ {TIPC_NLA_STATS_LINK_CONGS, s->link_congs},
+ {TIPC_NLA_STATS_MAX_QUEUE, s->max_queue_sz},
+ {TIPC_NLA_STATS_AVG_QUEUE, s->queue_sz_counts ?
+ (s->accu_queue_sz / s->queue_sz_counts) : 0}
+ };
+
+ stats = nla_nest_start(skb, TIPC_NLA_LINK_STATS);
+ if (!stats)
+ return -EMSGSIZE;
+
+ for (i = 0; i < ARRAY_SIZE(map); i++)
+ if (nla_put_u32(skb, map[i].key, map[i].val))
+ goto msg_full;
+
+ nla_nest_end(skb, stats);
+
+ return 0;
+msg_full:
+ nla_nest_cancel(skb, stats);
+
+ return -EMSGSIZE;
+}
+
+/* Caller should hold appropriate locks to protect the link */
+static int __tipc_nl_add_link(struct tipc_nl_msg *msg, struct tipc_link *link)
+{
+ int err;
+ void *hdr;
+ struct nlattr *attrs;
+ struct nlattr *prop;
+
+ hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_v2_family,
+ NLM_F_MULTI, TIPC_NL_LINK_GET);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ attrs = nla_nest_start(msg->skb, TIPC_NLA_LINK);
+ if (!attrs)
+ goto msg_full;
+
+ if (nla_put_string(msg->skb, TIPC_NLA_LINK_NAME, link->name))
+ goto attr_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_LINK_DEST,
+ tipc_cluster_mask(tipc_own_addr)))
+ goto attr_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_LINK_MTU, link->max_pkt))
+ goto attr_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_LINK_RX, link->next_in_no))
+ goto attr_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_LINK_TX, link->next_out_no))
+ goto attr_msg_full;
+
+ if (tipc_link_is_up(link))
+ if (nla_put_flag(msg->skb, TIPC_NLA_LINK_UP))
+ goto attr_msg_full;
+ if (tipc_link_is_active(link))
+ if (nla_put_flag(msg->skb, TIPC_NLA_LINK_ACTIVE))
+ goto attr_msg_full;
+
+ prop = nla_nest_start(msg->skb, TIPC_NLA_LINK_PROP);
+ if (!prop)
+ goto attr_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_PROP_PRIO, link->priority))
+ goto prop_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_PROP_TOL, link->tolerance))
+ goto prop_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_PROP_WIN,
+ link->queue_limit[TIPC_LOW_IMPORTANCE]))
+ goto prop_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_PROP_PRIO, link->priority))
+ goto prop_msg_full;
+ nla_nest_end(msg->skb, prop);
+
+ err = __tipc_nl_add_stats(msg->skb, &link->stats);
+ if (err)
+ goto attr_msg_full;
+
+ nla_nest_end(msg->skb, attrs);
+ genlmsg_end(msg->skb, hdr);
+
+ return 0;
+
+prop_msg_full:
+ nla_nest_cancel(msg->skb, prop);
+attr_msg_full:
+ nla_nest_cancel(msg->skb, attrs);
+msg_full:
+ genlmsg_cancel(msg->skb, hdr);
+
+ return -EMSGSIZE;
+}
+
+/* Caller should hold node lock */
+static int __tipc_nl_add_node_links(struct tipc_nl_msg *msg,
+ struct tipc_node *node,
+ u32 *prev_link)
+{
+ u32 i;
+ int err;
+
+ for (i = *prev_link; i < MAX_BEARERS; i++) {
+ *prev_link = i;
+
+ if (!node->links[i])
+ continue;
+
+ err = __tipc_nl_add_link(msg, node->links[i]);
+ if (err)
+ return err;
+ }
+ *prev_link = 0;
+
+ return 0;
+}
+
+int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ struct tipc_node *node;
+ struct tipc_nl_msg msg;
+ u32 prev_node = cb->args[0];
+ u32 prev_link = cb->args[1];
+ int done = cb->args[2];
+ int err;
+
+ if (done)
+ return 0;
+
+ msg.skb = skb;
+ msg.portid = NETLINK_CB(cb->skb).portid;
+ msg.seq = cb->nlh->nlmsg_seq;
+
+ rcu_read_lock();
+
+ if (prev_node) {
+ node = tipc_node_find(prev_node);
+ if (!node) {
+ /* We never set seq or call nl_dump_check_consistent()
+ * this means that setting prev_seq here will cause the
+ * consistence check to fail in the netlink callback
+ * handler. Resulting in the last NLMSG_DONE message
+ * having the NLM_F_DUMP_INTR flag set.
+ */
+ cb->prev_seq = 1;
+ goto out;
+ }
+
+ list_for_each_entry_continue_rcu(node, &tipc_node_list, list) {
+ tipc_node_lock(node);
+ err = __tipc_nl_add_node_links(&msg, node, &prev_link);
+ tipc_node_unlock(node);
+ if (err)
+ goto out;
+
+ prev_node = node->addr;
+ }
+ } else {
+ err = tipc_nl_add_bc_link(&msg);
+ if (err)
+ goto out;
+
+ list_for_each_entry_rcu(node, &tipc_node_list, list) {
+ tipc_node_lock(node);
+ err = __tipc_nl_add_node_links(&msg, node, &prev_link);
+ tipc_node_unlock(node);
+ if (err)
+ goto out;
+
+ prev_node = node->addr;
+ }
+ }
+ done = 1;
+out:
+ rcu_read_unlock();
+
+ cb->args[0] = prev_node;
+ cb->args[1] = prev_link;
+ cb->args[2] = done;
+
+ return skb->len;
+}
+
+int tipc_nl_link_get(struct sk_buff *skb, struct genl_info *info)
+{
+ struct sk_buff *ans_skb;
+ struct tipc_nl_msg msg;
+ struct tipc_link *link;
+ struct tipc_node *node;
+ char *name;
+ int bearer_id;
+ int err;
+
+ if (!info->attrs[TIPC_NLA_LINK_NAME])
+ return -EINVAL;
+
+ name = nla_data(info->attrs[TIPC_NLA_LINK_NAME]);
+ node = tipc_link_find_owner(name, &bearer_id);
+ if (!node)
+ return -EINVAL;
+
+ ans_skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!ans_skb)
+ return -ENOMEM;
+
+ msg.skb = ans_skb;
+ msg.portid = info->snd_portid;
+ msg.seq = info->snd_seq;
+
+ tipc_node_lock(node);
+ link = node->links[bearer_id];
+ if (!link) {
+ err = -EINVAL;
+ goto err_out;
+ }
+
+ err = __tipc_nl_add_link(&msg, link);
+ if (err)
+ goto err_out;
+
+ tipc_node_unlock(node);
+
+ return genlmsg_reply(ans_skb, info);
+
+err_out:
+ tipc_node_unlock(node);
+ nlmsg_free(ans_skb);
+
+ return err;
+}
+
+int tipc_nl_link_reset_stats(struct sk_buff *skb, struct genl_info *info)
+{
+ int err;
+ char *link_name;
+ unsigned int bearer_id;
+ struct tipc_link *link;
+ struct tipc_node *node;
+ struct nlattr *attrs[TIPC_NLA_LINK_MAX + 1];
+
+ if (!info->attrs[TIPC_NLA_LINK])
+ return -EINVAL;
+
+ err = nla_parse_nested(attrs, TIPC_NLA_LINK_MAX,
+ info->attrs[TIPC_NLA_LINK],
+ tipc_nl_link_policy);
+ if (err)
+ return err;
+
+ if (!attrs[TIPC_NLA_LINK_NAME])
+ return -EINVAL;
+
+ link_name = nla_data(attrs[TIPC_NLA_LINK_NAME]);
+
+ if (strcmp(link_name, tipc_bclink_name) == 0) {
+ err = tipc_bclink_reset_stats();
+ if (err)
+ return err;
+ return 0;
+ }
+
+ node = tipc_link_find_owner(link_name, &bearer_id);
+ if (!node)
+ return -EINVAL;
+
+ tipc_node_lock(node);
+
+ link = node->links[bearer_id];
+ if (!link) {
+ tipc_node_unlock(node);
+ return -EINVAL;
+ }
+
+ link_reset_statistics(link);
+
+ tipc_node_unlock(node);
+
+ return 0;
+}
diff --git a/net/tipc/link.h b/net/tipc/link.h
index b567a3427fda..55812e87ca1e 100644
--- a/net/tipc/link.h
+++ b/net/tipc/link.h
@@ -37,6 +37,7 @@
#ifndef _TIPC_LINK_H
#define _TIPC_LINK_H
+#include <net/genetlink.h>
#include "msg.h"
#include "node.h"
@@ -118,20 +119,13 @@ struct tipc_stats {
* @max_pkt: current maximum packet size for this link
* @max_pkt_target: desired maximum packet size for this link
* @max_pkt_probes: # of probes based on current (max_pkt, max_pkt_target)
- * @out_queue_size: # of messages in outbound message queue
- * @first_out: ptr to first outbound message in queue
- * @last_out: ptr to last outbound message in queue
+ * @outqueue: outbound message queue
* @next_out_no: next sequence number to use for outbound messages
* @last_retransmitted: sequence number of most recently retransmitted message
* @stale_count: # of identical retransmit requests made by peer
* @next_in_no: next sequence number to expect for inbound messages
- * @deferred_inqueue_sz: # of messages in inbound message queue
- * @oldest_deferred_in: ptr to first inbound message in queue
- * @newest_deferred_in: ptr to last inbound message in queue
+ * @deferred_queue: deferred queue saved OOS b'cast message received from node
* @unacked_window: # of inbound messages rx'd without ack'ing back to peer
- * @proto_msg_queue: ptr to (single) outbound control message
- * @retransm_queue_size: number of messages to retransmit
- * @retransm_queue_head: sequence number of first message to retransmit
* @next_out: ptr to first unsent outbound message in queue
* @waiting_sks: linked list of sockets waiting for link congestion to abate
* @long_msg_seq_no: next identifier to use for outbound fragmented messages
@@ -175,24 +169,17 @@ struct tipc_link {
u32 max_pkt_probes;
/* Sending */
- u32 out_queue_size;
- struct sk_buff *first_out;
- struct sk_buff *last_out;
+ struct sk_buff_head outqueue;
u32 next_out_no;
u32 last_retransmitted;
u32 stale_count;
/* Reception */
u32 next_in_no;
- u32 deferred_inqueue_sz;
- struct sk_buff *oldest_deferred_in;
- struct sk_buff *newest_deferred_in;
+ struct sk_buff_head deferred_queue;
u32 unacked_window;
/* Congestion handling */
- struct sk_buff *proto_msg_queue;
- u32 retransm_queue_size;
- u32 retransm_queue_head;
struct sk_buff *next_out;
struct sk_buff_head waiting_sks;
@@ -226,18 +213,26 @@ struct sk_buff *tipc_link_cmd_reset_stats(const void *req_tlv_area,
void tipc_link_reset_all(struct tipc_node *node);
void tipc_link_reset(struct tipc_link *l_ptr);
void tipc_link_reset_list(unsigned int bearer_id);
-int tipc_link_xmit(struct sk_buff *buf, u32 dest, u32 selector);
-int __tipc_link_xmit(struct tipc_link *link, struct sk_buff *buf);
+int tipc_link_xmit_skb(struct sk_buff *skb, u32 dest, u32 selector);
+int tipc_link_xmit(struct sk_buff_head *list, u32 dest, u32 selector);
+int __tipc_link_xmit(struct tipc_link *link, struct sk_buff_head *list);
u32 tipc_link_get_max_pkt(u32 dest, u32 selector);
void tipc_link_bundle_rcv(struct sk_buff *buf);
void tipc_link_proto_xmit(struct tipc_link *l_ptr, u32 msg_typ, int prob,
u32 gap, u32 tolerance, u32 priority, u32 acked_mtu);
-void tipc_link_push_queue(struct tipc_link *l_ptr);
-u32 tipc_link_defer_pkt(struct sk_buff **head, struct sk_buff **tail,
- struct sk_buff *buf);
+void tipc_link_push_packets(struct tipc_link *l_ptr);
+u32 tipc_link_defer_pkt(struct sk_buff_head *list, struct sk_buff *buf);
void tipc_link_set_queue_limits(struct tipc_link *l_ptr, u32 window);
void tipc_link_retransmit(struct tipc_link *l_ptr,
struct sk_buff *start, u32 retransmits);
+struct sk_buff *tipc_skb_queue_next(const struct sk_buff_head *list,
+ const struct sk_buff *skb);
+
+int tipc_nl_link_dump(struct sk_buff *skb, struct netlink_callback *cb);
+int tipc_nl_link_get(struct sk_buff *skb, struct genl_info *info);
+int tipc_nl_link_set(struct sk_buff *skb, struct genl_info *info);
+int tipc_nl_link_reset_stats(struct sk_buff *skb, struct genl_info *info);
+int tipc_nl_parse_link_prop(struct nlattr *prop, struct nlattr *props[]);
/*
* Link sequence number manipulation routines (uses modulo 2**16 arithmetic)
@@ -252,18 +247,14 @@ static inline u32 mod(u32 x)
return x & 0xffffu;
}
-static inline int between(u32 lower, u32 upper, u32 n)
+static inline int less_eq(u32 left, u32 right)
{
- if ((lower < n) && (n < upper))
- return 1;
- if ((upper < lower) && ((n > lower) || (n < upper)))
- return 1;
- return 0;
+ return mod(right - left) < 32768u;
}
-static inline int less_eq(u32 left, u32 right)
+static inline int more(u32 left, u32 right)
{
- return mod(right - left) < 32768u;
+ return !less_eq(left, right);
}
static inline int less(u32 left, u32 right)
@@ -302,7 +293,7 @@ static inline int link_reset_reset(struct tipc_link *l_ptr)
static inline int link_congested(struct tipc_link *l_ptr)
{
- return l_ptr->out_queue_size >= l_ptr->queue_limit[0];
+ return skb_queue_len(&l_ptr->outqueue) >= l_ptr->queue_limit[0];
}
#endif
diff --git a/net/tipc/msg.c b/net/tipc/msg.c
index 74745a47d72a..a687b30a699c 100644
--- a/net/tipc/msg.c
+++ b/net/tipc/msg.c
@@ -91,7 +91,7 @@ struct sk_buff *tipc_msg_create(uint user, uint type, uint hdr_sz,
* @*headbuf: in: NULL for first frag, otherwise value returned from prev call
* out: set when successful non-complete reassembly, otherwise NULL
* @*buf: in: the buffer to append. Always defined
- * out: head buf after sucessful complete reassembly, otherwise NULL
+ * out: head buf after successful complete reassembly, otherwise NULL
* Returns 1 when reassembly complete, otherwise 0
*/
int tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf)
@@ -162,15 +162,16 @@ err:
/**
* tipc_msg_build - create buffer chain containing specified header and data
* @mhdr: Message header, to be prepended to data
- * @iov: User data
+ * @m: User message
* @offset: Posision in iov to start copying from
* @dsz: Total length of user data
* @pktmax: Max packet size that can be used
- * @chain: Buffer or chain of buffers to be returned to caller
+ * @list: Buffer or chain of buffers to be returned to caller
+ *
* Returns message data size or errno: -ENOMEM, -EFAULT
*/
-int tipc_msg_build(struct tipc_msg *mhdr, struct iovec const *iov,
- int offset, int dsz, int pktmax , struct sk_buff **chain)
+int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m, int offset,
+ int dsz, int pktmax, struct sk_buff_head *list)
{
int mhsz = msg_hdr_sz(mhdr);
int msz = mhsz + dsz;
@@ -179,22 +180,22 @@ int tipc_msg_build(struct tipc_msg *mhdr, struct iovec const *iov,
int pktrem = pktmax;
int drem = dsz;
struct tipc_msg pkthdr;
- struct sk_buff *buf, *prev;
+ struct sk_buff *skb;
char *pktpos;
int rc;
- uint chain_sz = 0;
+
msg_set_size(mhdr, msz);
/* No fragmentation needed? */
if (likely(msz <= pktmax)) {
- buf = tipc_buf_acquire(msz);
- *chain = buf;
- if (unlikely(!buf))
+ skb = tipc_buf_acquire(msz);
+ if (unlikely(!skb))
return -ENOMEM;
- skb_copy_to_linear_data(buf, mhdr, mhsz);
- pktpos = buf->data + mhsz;
- TIPC_SKB_CB(buf)->chain_sz = 1;
- if (!dsz || !memcpy_fromiovecend(pktpos, iov, offset, dsz))
+ __skb_queue_tail(list, skb);
+ skb_copy_to_linear_data(skb, mhdr, mhsz);
+ pktpos = skb->data + mhsz;
+ if (!dsz || !memcpy_fromiovecend(pktpos, m->msg_iter.iov, offset,
+ dsz))
return dsz;
rc = -EFAULT;
goto error;
@@ -207,15 +208,15 @@ int tipc_msg_build(struct tipc_msg *mhdr, struct iovec const *iov,
msg_set_fragm_no(&pkthdr, pktno);
/* Prepare first fragment */
- *chain = buf = tipc_buf_acquire(pktmax);
- if (!buf)
+ skb = tipc_buf_acquire(pktmax);
+ if (!skb)
return -ENOMEM;
- chain_sz = 1;
- pktpos = buf->data;
- skb_copy_to_linear_data(buf, &pkthdr, INT_H_SIZE);
+ __skb_queue_tail(list, skb);
+ pktpos = skb->data;
+ skb_copy_to_linear_data(skb, &pkthdr, INT_H_SIZE);
pktpos += INT_H_SIZE;
pktrem -= INT_H_SIZE;
- skb_copy_to_linear_data_offset(buf, INT_H_SIZE, mhdr, mhsz);
+ skb_copy_to_linear_data_offset(skb, INT_H_SIZE, mhdr, mhsz);
pktpos += mhsz;
pktrem -= mhsz;
@@ -223,7 +224,7 @@ int tipc_msg_build(struct tipc_msg *mhdr, struct iovec const *iov,
if (drem < pktrem)
pktrem = drem;
- if (memcpy_fromiovecend(pktpos, iov, offset, pktrem)) {
+ if (memcpy_fromiovecend(pktpos, m->msg_iter.iov, offset, pktrem)) {
rc = -EFAULT;
goto error;
}
@@ -238,43 +239,41 @@ int tipc_msg_build(struct tipc_msg *mhdr, struct iovec const *iov,
pktsz = drem + INT_H_SIZE;
else
pktsz = pktmax;
- prev = buf;
- buf = tipc_buf_acquire(pktsz);
- if (!buf) {
+ skb = tipc_buf_acquire(pktsz);
+ if (!skb) {
rc = -ENOMEM;
goto error;
}
- chain_sz++;
- prev->next = buf;
+ __skb_queue_tail(list, skb);
msg_set_type(&pkthdr, FRAGMENT);
msg_set_size(&pkthdr, pktsz);
msg_set_fragm_no(&pkthdr, ++pktno);
- skb_copy_to_linear_data(buf, &pkthdr, INT_H_SIZE);
- pktpos = buf->data + INT_H_SIZE;
+ skb_copy_to_linear_data(skb, &pkthdr, INT_H_SIZE);
+ pktpos = skb->data + INT_H_SIZE;
pktrem = pktsz - INT_H_SIZE;
} while (1);
- TIPC_SKB_CB(*chain)->chain_sz = chain_sz;
- msg_set_type(buf_msg(buf), LAST_FRAGMENT);
+ msg_set_type(buf_msg(skb), LAST_FRAGMENT);
return dsz;
error:
- kfree_skb_list(*chain);
- *chain = NULL;
+ __skb_queue_purge(list);
+ __skb_queue_head_init(list);
return rc;
}
/**
* tipc_msg_bundle(): Append contents of a buffer to tail of an existing one
- * @bbuf: the existing buffer ("bundle")
- * @buf: buffer to be appended
+ * @list: the buffer chain of the existing buffer ("bundle")
+ * @skb: buffer to be appended
* @mtu: max allowable size for the bundle buffer
* Consumes buffer if successful
* Returns true if bundling could be performed, otherwise false
*/
-bool tipc_msg_bundle(struct sk_buff *bbuf, struct sk_buff *buf, u32 mtu)
+bool tipc_msg_bundle(struct sk_buff_head *list, struct sk_buff *skb, u32 mtu)
{
- struct tipc_msg *bmsg = buf_msg(bbuf);
- struct tipc_msg *msg = buf_msg(buf);
+ struct sk_buff *bskb = skb_peek_tail(list);
+ struct tipc_msg *bmsg = buf_msg(bskb);
+ struct tipc_msg *msg = buf_msg(skb);
unsigned int bsz = msg_size(bmsg);
unsigned int msz = msg_size(msg);
u32 start = align(bsz);
@@ -289,35 +288,36 @@ bool tipc_msg_bundle(struct sk_buff *bbuf, struct sk_buff *buf, u32 mtu)
return false;
if (likely(msg_user(bmsg) != MSG_BUNDLER))
return false;
- if (likely(msg_type(bmsg) != BUNDLE_OPEN))
+ if (likely(!TIPC_SKB_CB(bskb)->bundling))
return false;
- if (unlikely(skb_tailroom(bbuf) < (pad + msz)))
+ if (unlikely(skb_tailroom(bskb) < (pad + msz)))
return false;
if (unlikely(max < (start + msz)))
return false;
- skb_put(bbuf, pad + msz);
- skb_copy_to_linear_data_offset(bbuf, start, buf->data, msz);
+ skb_put(bskb, pad + msz);
+ skb_copy_to_linear_data_offset(bskb, start, skb->data, msz);
msg_set_size(bmsg, start + msz);
msg_set_msgcnt(bmsg, msg_msgcnt(bmsg) + 1);
- bbuf->next = buf->next;
- kfree_skb(buf);
+ kfree_skb(skb);
return true;
}
/**
* tipc_msg_make_bundle(): Create bundle buf and append message to its tail
- * @buf: buffer to be appended and replaced
- * @mtu: max allowable size for the bundle buffer, inclusive header
+ * @list: the buffer chain
+ * @skb: buffer to be appended and replaced
+ * @mtu: max allowable size for the bundle buffer, inclusive header
* @dnode: destination node for message. (Not always present in header)
* Replaces buffer if successful
- * Returns true if sucess, otherwise false
+ * Returns true if success, otherwise false
*/
-bool tipc_msg_make_bundle(struct sk_buff **buf, u32 mtu, u32 dnode)
+bool tipc_msg_make_bundle(struct sk_buff_head *list, struct sk_buff *skb,
+ u32 mtu, u32 dnode)
{
- struct sk_buff *bbuf;
+ struct sk_buff *bskb;
struct tipc_msg *bmsg;
- struct tipc_msg *msg = buf_msg(*buf);
+ struct tipc_msg *msg = buf_msg(skb);
u32 msz = msg_size(msg);
u32 max = mtu - INT_H_SIZE;
@@ -330,20 +330,19 @@ bool tipc_msg_make_bundle(struct sk_buff **buf, u32 mtu, u32 dnode)
if (msz > (max / 2))
return false;
- bbuf = tipc_buf_acquire(max);
- if (!bbuf)
+ bskb = tipc_buf_acquire(max);
+ if (!bskb)
return false;
- skb_trim(bbuf, INT_H_SIZE);
- bmsg = buf_msg(bbuf);
- tipc_msg_init(bmsg, MSG_BUNDLER, BUNDLE_OPEN, INT_H_SIZE, dnode);
+ skb_trim(bskb, INT_H_SIZE);
+ bmsg = buf_msg(bskb);
+ tipc_msg_init(bmsg, MSG_BUNDLER, 0, INT_H_SIZE, dnode);
msg_set_seqno(bmsg, msg_seqno(msg));
msg_set_ack(bmsg, msg_ack(msg));
msg_set_bcast_ack(bmsg, msg_bcast_ack(msg));
- bbuf->next = (*buf)->next;
- tipc_msg_bundle(bbuf, *buf, mtu);
- *buf = bbuf;
- return true;
+ TIPC_SKB_CB(bskb)->bundling = true;
+ __skb_queue_tail(list, bskb);
+ return tipc_msg_bundle(list, skb, mtu);
}
/**
@@ -429,22 +428,23 @@ int tipc_msg_eval(struct sk_buff *buf, u32 *dnode)
/* tipc_msg_reassemble() - clone a buffer chain of fragments and
* reassemble the clones into one message
*/
-struct sk_buff *tipc_msg_reassemble(struct sk_buff *chain)
+struct sk_buff *tipc_msg_reassemble(struct sk_buff_head *list)
{
- struct sk_buff *buf = chain;
- struct sk_buff *frag = buf;
+ struct sk_buff *skb;
+ struct sk_buff *frag = NULL;
struct sk_buff *head = NULL;
int hdr_sz;
/* Copy header if single buffer */
- if (!buf->next) {
- hdr_sz = skb_headroom(buf) + msg_hdr_sz(buf_msg(buf));
- return __pskb_copy(buf, hdr_sz, GFP_ATOMIC);
+ if (skb_queue_len(list) == 1) {
+ skb = skb_peek(list);
+ hdr_sz = skb_headroom(skb) + msg_hdr_sz(buf_msg(skb));
+ return __pskb_copy(skb, hdr_sz, GFP_ATOMIC);
}
/* Clone all fragments and reassemble */
- while (buf) {
- frag = skb_clone(buf, GFP_ATOMIC);
+ skb_queue_walk(list, skb) {
+ frag = skb_clone(skb, GFP_ATOMIC);
if (!frag)
goto error;
frag->next = NULL;
@@ -452,7 +452,6 @@ struct sk_buff *tipc_msg_reassemble(struct sk_buff *chain)
break;
if (!head)
goto error;
- buf = buf->next;
}
return frag;
error:
diff --git a/net/tipc/msg.h b/net/tipc/msg.h
index 0ea7b695ac4d..d5c83d7ecb47 100644
--- a/net/tipc/msg.h
+++ b/net/tipc/msg.h
@@ -464,11 +464,6 @@ static inline struct tipc_msg *msg_get_wrapped(struct tipc_msg *m)
#define FRAGMENT 1
#define LAST_FRAGMENT 2
-/* Bundling protocol message types
- */
-#define BUNDLE_OPEN 0
-#define BUNDLE_CLOSED 1
-
/*
* Link management protocol message types
*/
@@ -739,13 +734,14 @@ struct sk_buff *tipc_msg_create(uint user, uint type, uint hdr_sz,
int tipc_buf_append(struct sk_buff **headbuf, struct sk_buff **buf);
-bool tipc_msg_bundle(struct sk_buff *bbuf, struct sk_buff *buf, u32 mtu);
+bool tipc_msg_bundle(struct sk_buff_head *list, struct sk_buff *skb, u32 mtu);
-bool tipc_msg_make_bundle(struct sk_buff **buf, u32 mtu, u32 dnode);
+bool tipc_msg_make_bundle(struct sk_buff_head *list, struct sk_buff *skb,
+ u32 mtu, u32 dnode);
-int tipc_msg_build(struct tipc_msg *mhdr, struct iovec const *iov,
- int offset, int dsz, int mtu , struct sk_buff **chain);
+int tipc_msg_build(struct tipc_msg *mhdr, struct msghdr *m, int offset,
+ int dsz, int mtu, struct sk_buff_head *list);
-struct sk_buff *tipc_msg_reassemble(struct sk_buff *chain);
+struct sk_buff *tipc_msg_reassemble(struct sk_buff_head *list);
#endif
diff --git a/net/tipc/name_distr.c b/net/tipc/name_distr.c
index 376d2bb51d8d..ba6083dca95b 100644
--- a/net/tipc/name_distr.c
+++ b/net/tipc/name_distr.c
@@ -38,39 +38,6 @@
#include "link.h"
#include "name_distr.h"
-/**
- * struct publ_list - list of publications made by this node
- * @list: circular list of publications
- * @list_size: number of entries in list
- */
-struct publ_list {
- struct list_head list;
- u32 size;
-};
-
-static struct publ_list publ_zone = {
- .list = LIST_HEAD_INIT(publ_zone.list),
- .size = 0,
-};
-
-static struct publ_list publ_cluster = {
- .list = LIST_HEAD_INIT(publ_cluster.list),
- .size = 0,
-};
-
-static struct publ_list publ_node = {
- .list = LIST_HEAD_INIT(publ_node.list),
- .size = 0,
-};
-
-static struct publ_list *publ_lists[] = {
- NULL,
- &publ_zone, /* publ_lists[TIPC_ZONE_SCOPE] */
- &publ_cluster, /* publ_lists[TIPC_CLUSTER_SCOPE] */
- &publ_node /* publ_lists[TIPC_NODE_SCOPE] */
-};
-
-
int sysctl_tipc_named_timeout __read_mostly = 2000;
/**
@@ -114,9 +81,9 @@ static struct sk_buff *named_prepare_buf(u32 type, u32 size, u32 dest)
return buf;
}
-void named_cluster_distribute(struct sk_buff *buf)
+void named_cluster_distribute(struct sk_buff *skb)
{
- struct sk_buff *obuf;
+ struct sk_buff *oskb;
struct tipc_node *node;
u32 dnode;
@@ -127,15 +94,15 @@ void named_cluster_distribute(struct sk_buff *buf)
continue;
if (!tipc_node_active_links(node))
continue;
- obuf = skb_copy(buf, GFP_ATOMIC);
- if (!obuf)
+ oskb = skb_copy(skb, GFP_ATOMIC);
+ if (!oskb)
break;
- msg_set_destnode(buf_msg(obuf), dnode);
- tipc_link_xmit(obuf, dnode, dnode);
+ msg_set_destnode(buf_msg(oskb), dnode);
+ tipc_link_xmit_skb(oskb, dnode, dnode);
}
rcu_read_unlock();
- kfree_skb(buf);
+ kfree_skb(skb);
}
/**
@@ -146,8 +113,8 @@ struct sk_buff *tipc_named_publish(struct publication *publ)
struct sk_buff *buf;
struct distr_item *item;
- list_add_tail(&publ->local_list, &publ_lists[publ->scope]->list);
- publ_lists[publ->scope]->size++;
+ list_add_tail_rcu(&publ->local_list,
+ &tipc_nametbl->publ_list[publ->scope]);
if (publ->scope == TIPC_NODE_SCOPE)
return NULL;
@@ -172,7 +139,6 @@ struct sk_buff *tipc_named_withdraw(struct publication *publ)
struct distr_item *item;
list_del(&publ->local_list);
- publ_lists[publ->scope]->size--;
if (publ->scope == TIPC_NODE_SCOPE)
return NULL;
@@ -190,32 +156,28 @@ struct sk_buff *tipc_named_withdraw(struct publication *publ)
/**
* named_distribute - prepare name info for bulk distribution to another node
- * @msg_list: list of messages (buffers) to be returned from this function
+ * @list: list of messages (buffers) to be returned from this function
* @dnode: node to be updated
* @pls: linked list of publication items to be packed into buffer chain
*/
-static void named_distribute(struct list_head *msg_list, u32 dnode,
- struct publ_list *pls)
+static void named_distribute(struct sk_buff_head *list, u32 dnode,
+ struct list_head *pls)
{
struct publication *publ;
- struct sk_buff *buf = NULL;
+ struct sk_buff *skb = NULL;
struct distr_item *item = NULL;
- uint dsz = pls->size * ITEM_SIZE;
uint msg_dsz = (tipc_node_get_mtu(dnode, 0) / ITEM_SIZE) * ITEM_SIZE;
- uint rem = dsz;
- uint msg_rem = 0;
+ uint msg_rem = msg_dsz;
- list_for_each_entry(publ, &pls->list, local_list) {
+ list_for_each_entry(publ, pls, local_list) {
/* Prepare next buffer: */
- if (!buf) {
- msg_rem = min_t(uint, rem, msg_dsz);
- rem -= msg_rem;
- buf = named_prepare_buf(PUBLICATION, msg_rem, dnode);
- if (!buf) {
+ if (!skb) {
+ skb = named_prepare_buf(PUBLICATION, msg_rem, dnode);
+ if (!skb) {
pr_warn("Bulk publication failure\n");
return;
}
- item = (struct distr_item *)msg_data(buf_msg(buf));
+ item = (struct distr_item *)msg_data(buf_msg(skb));
}
/* Pack publication into message: */
@@ -225,10 +187,16 @@ static void named_distribute(struct list_head *msg_list, u32 dnode,
/* Append full buffer to list: */
if (!msg_rem) {
- list_add_tail((struct list_head *)buf, msg_list);
- buf = NULL;
+ __skb_queue_tail(list, skb);
+ skb = NULL;
+ msg_rem = msg_dsz;
}
}
+ if (skb) {
+ msg_set_size(buf_msg(skb), INT_H_SIZE + (msg_dsz - msg_rem));
+ skb_trim(skb, INT_H_SIZE + (msg_dsz - msg_rem));
+ __skb_queue_tail(list, skb);
+ }
}
/**
@@ -236,36 +204,68 @@ static void named_distribute(struct list_head *msg_list, u32 dnode,
*/
void tipc_named_node_up(u32 dnode)
{
- LIST_HEAD(msg_list);
- struct sk_buff *buf_chain;
-
- read_lock_bh(&tipc_nametbl_lock);
- named_distribute(&msg_list, dnode, &publ_cluster);
- named_distribute(&msg_list, dnode, &publ_zone);
- read_unlock_bh(&tipc_nametbl_lock);
-
- /* Convert circular list to linear list and send: */
- buf_chain = (struct sk_buff *)msg_list.next;
- ((struct sk_buff *)msg_list.prev)->next = NULL;
- tipc_link_xmit(buf_chain, dnode, dnode);
+ struct sk_buff_head head;
+
+ __skb_queue_head_init(&head);
+
+ rcu_read_lock();
+ named_distribute(&head, dnode,
+ &tipc_nametbl->publ_list[TIPC_CLUSTER_SCOPE]);
+ named_distribute(&head, dnode,
+ &tipc_nametbl->publ_list[TIPC_ZONE_SCOPE]);
+ rcu_read_unlock();
+
+ tipc_link_xmit(&head, dnode, dnode);
+}
+
+static void tipc_publ_subscribe(struct publication *publ, u32 addr)
+{
+ struct tipc_node *node;
+
+ if (in_own_node(addr))
+ return;
+
+ node = tipc_node_find(addr);
+ if (!node) {
+ pr_warn("Node subscription rejected, unknown node 0x%x\n",
+ addr);
+ return;
+ }
+
+ tipc_node_lock(node);
+ list_add_tail(&publ->nodesub_list, &node->publ_list);
+ tipc_node_unlock(node);
+}
+
+static void tipc_publ_unsubscribe(struct publication *publ, u32 addr)
+{
+ struct tipc_node *node;
+
+ node = tipc_node_find(addr);
+ if (!node)
+ return;
+
+ tipc_node_lock(node);
+ list_del_init(&publ->nodesub_list);
+ tipc_node_unlock(node);
}
/**
- * named_purge_publ - remove publication associated with a failed node
+ * tipc_publ_purge - remove publication associated with a failed node
*
* Invoked for each publication issued by a newly failed node.
* Removes publication structure from name table & deletes it.
*/
-static void named_purge_publ(struct publication *publ)
+static void tipc_publ_purge(struct publication *publ, u32 addr)
{
struct publication *p;
- write_lock_bh(&tipc_nametbl_lock);
+ spin_lock_bh(&tipc_nametbl_lock);
p = tipc_nametbl_remove_publ(publ->type, publ->lower,
publ->node, publ->ref, publ->key);
if (p)
- tipc_nodesub_unsubscribe(&p->subscr);
- write_unlock_bh(&tipc_nametbl_lock);
+ tipc_publ_unsubscribe(p, addr);
+ spin_unlock_bh(&tipc_nametbl_lock);
if (p != publ) {
pr_err("Unable to remove publication from failed node\n"
@@ -274,7 +274,15 @@ static void named_purge_publ(struct publication *publ)
publ->key);
}
- kfree(p);
+ kfree_rcu(p, rcu);
+}
+
+void tipc_publ_notify(struct list_head *nsub_list, u32 addr)
+{
+ struct publication *publ, *tmp;
+
+ list_for_each_entry_safe(publ, tmp, nsub_list, nodesub_list)
+ tipc_publ_purge(publ, addr);
}
/**
@@ -294,9 +302,7 @@ static bool tipc_update_nametbl(struct distr_item *i, u32 node, u32 dtype)
TIPC_CLUSTER_SCOPE, node,
ntohl(i->ref), ntohl(i->key));
if (publ) {
- tipc_nodesub_subscribe(&publ->subscr, node, publ,
- (net_ev_handler)
- named_purge_publ);
+ tipc_publ_subscribe(publ, node);
return true;
}
} else if (dtype == WITHDRAWAL) {
@@ -304,8 +310,8 @@ static bool tipc_update_nametbl(struct distr_item *i, u32 node, u32 dtype)
node, ntohl(i->ref),
ntohl(i->key));
if (publ) {
- tipc_nodesub_unsubscribe(&publ->subscr);
- kfree(publ);
+ tipc_publ_unsubscribe(publ, node);
+ kfree_rcu(publ, rcu);
return true;
}
} else {
@@ -370,14 +376,14 @@ void tipc_named_rcv(struct sk_buff *buf)
u32 count = msg_data_sz(msg) / ITEM_SIZE;
u32 node = msg_orignode(msg);
- write_lock_bh(&tipc_nametbl_lock);
+ spin_lock_bh(&tipc_nametbl_lock);
while (count--) {
if (!tipc_update_nametbl(item, node, msg_type(msg)))
tipc_named_add_backlog(item, msg_type(msg), node);
item++;
}
tipc_named_process_backlog();
- write_unlock_bh(&tipc_nametbl_lock);
+ spin_unlock_bh(&tipc_nametbl_lock);
kfree_skb(buf);
}
@@ -393,11 +399,12 @@ void tipc_named_reinit(void)
struct publication *publ;
int scope;
- write_lock_bh(&tipc_nametbl_lock);
+ spin_lock_bh(&tipc_nametbl_lock);
for (scope = TIPC_ZONE_SCOPE; scope <= TIPC_NODE_SCOPE; scope++)
- list_for_each_entry(publ, &publ_lists[scope]->list, local_list)
+ list_for_each_entry_rcu(publ, &tipc_nametbl->publ_list[scope],
+ local_list)
publ->node = tipc_own_addr;
- write_unlock_bh(&tipc_nametbl_lock);
+ spin_unlock_bh(&tipc_nametbl_lock);
}
diff --git a/net/tipc/name_distr.h b/net/tipc/name_distr.h
index b9e75feb3434..cef55cedcfb2 100644
--- a/net/tipc/name_distr.h
+++ b/net/tipc/name_distr.h
@@ -74,5 +74,6 @@ void tipc_named_node_up(u32 dnode);
void tipc_named_rcv(struct sk_buff *buf);
void tipc_named_reinit(void);
void tipc_named_process_backlog(void);
+void tipc_publ_notify(struct list_head *nsub_list, u32 addr);
#endif
diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c
index 3a6a0a7c0759..c8df0223371a 100644
--- a/net/tipc/name_table.c
+++ b/net/tipc/name_table.c
@@ -1,8 +1,8 @@
/*
* net/tipc/name_table.c: TIPC name table code
*
- * Copyright (c) 2000-2006, Ericsson AB
- * Copyright (c) 2004-2008, 2010-2011, Wind River Systems
+ * Copyright (c) 2000-2006, 2014, Ericsson AB
+ * Copyright (c) 2004-2008, 2010-2014, Wind River Systems
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -42,6 +42,12 @@
#define TIPC_NAMETBL_SIZE 1024 /* must be a power of 2 */
+static const struct nla_policy
+tipc_nl_name_table_policy[TIPC_NLA_NAME_TABLE_MAX + 1] = {
+ [TIPC_NLA_NAME_TABLE_UNSPEC] = { .type = NLA_UNSPEC },
+ [TIPC_NLA_NAME_TABLE_PUBL] = { .type = NLA_NESTED }
+};
+
/**
* struct name_info - name sequence publication info
* @node_list: circular list of publications made by own node
@@ -86,6 +92,7 @@ struct sub_seq {
* @ns_list: links to adjacent name sequences in hash chain
* @subscriptions: list of subscriptions for this 'type'
* @lock: spinlock controlling access to publication lists of all sub-sequences
+ * @rcu: RCU callback head used for deferred freeing
*/
struct name_seq {
u32 type;
@@ -95,21 +102,11 @@ struct name_seq {
struct hlist_node ns_list;
struct list_head subscriptions;
spinlock_t lock;
+ struct rcu_head rcu;
};
-/**
- * struct name_table - table containing all existing port name publications
- * @types: pointer to fixed-sized array of name sequence lists,
- * accessed via hashing on 'type'; name sequence lists are *not* sorted
- * @local_publ_count: number of publications issued by this node
- */
-struct name_table {
- struct hlist_head *types;
- u32 local_publ_count;
-};
-
-static struct name_table table;
-DEFINE_RWLOCK(tipc_nametbl_lock);
+struct name_table *tipc_nametbl;
+DEFINE_SPINLOCK(tipc_nametbl_lock);
static int hash(int x)
{
@@ -136,9 +133,7 @@ static struct publication *publ_create(u32 type, u32 lower, u32 upper,
publ->node = node;
publ->ref = port_ref;
publ->key = key;
- INIT_LIST_HEAD(&publ->local_list);
INIT_LIST_HEAD(&publ->pport_list);
- INIT_LIST_HEAD(&publ->subscr.nodesub_list);
return publ;
}
@@ -173,22 +168,10 @@ static struct name_seq *tipc_nameseq_create(u32 type, struct hlist_head *seq_hea
nseq->alloc = 1;
INIT_HLIST_NODE(&nseq->ns_list);
INIT_LIST_HEAD(&nseq->subscriptions);
- hlist_add_head(&nseq->ns_list, seq_head);
+ hlist_add_head_rcu(&nseq->ns_list, seq_head);
return nseq;
}
-/*
- * nameseq_delete_empty - deletes a name sequence structure if now unused
- */
-static void nameseq_delete_empty(struct name_seq *seq)
-{
- if (!seq->first_free && list_empty(&seq->subscriptions)) {
- hlist_del_init(&seq->ns_list);
- kfree(seq->sseqs);
- kfree(seq);
- }
-}
-
/**
* nameseq_find_subseq - find sub-sequence (if any) matching a name instance
*
@@ -469,8 +452,8 @@ static struct name_seq *nametbl_find_seq(u32 type)
struct hlist_head *seq_head;
struct name_seq *ns;
- seq_head = &table.types[hash(type)];
- hlist_for_each_entry(ns, seq_head, ns_list) {
+ seq_head = &tipc_nametbl->seq_hlist[hash(type)];
+ hlist_for_each_entry_rcu(ns, seq_head, ns_list) {
if (ns->type == type)
return ns;
}
@@ -481,7 +464,9 @@ static struct name_seq *nametbl_find_seq(u32 type)
struct publication *tipc_nametbl_insert_publ(u32 type, u32 lower, u32 upper,
u32 scope, u32 node, u32 port, u32 key)
{
+ struct publication *publ;
struct name_seq *seq = nametbl_find_seq(type);
+ int index = hash(type);
if ((scope < TIPC_ZONE_SCOPE) || (scope > TIPC_NODE_SCOPE) ||
(lower > upper)) {
@@ -491,12 +476,16 @@ struct publication *tipc_nametbl_insert_publ(u32 type, u32 lower, u32 upper,
}
if (!seq)
- seq = tipc_nameseq_create(type, &table.types[hash(type)]);
+ seq = tipc_nameseq_create(type,
+ &tipc_nametbl->seq_hlist[index]);
if (!seq)
return NULL;
- return tipc_nameseq_insert_publ(seq, type, lower, upper,
+ spin_lock_bh(&seq->lock);
+ publ = tipc_nameseq_insert_publ(seq, type, lower, upper,
scope, node, port, key);
+ spin_unlock_bh(&seq->lock);
+ return publ;
}
struct publication *tipc_nametbl_remove_publ(u32 type, u32 lower,
@@ -508,8 +497,16 @@ struct publication *tipc_nametbl_remove_publ(u32 type, u32 lower,
if (!seq)
return NULL;
+ spin_lock_bh(&seq->lock);
publ = tipc_nameseq_remove_publ(seq, lower, node, ref, key);
- nameseq_delete_empty(seq);
+ if (!seq->first_free && list_empty(&seq->subscriptions)) {
+ hlist_del_init_rcu(&seq->ns_list);
+ kfree(seq->sseqs);
+ spin_unlock_bh(&seq->lock);
+ kfree_rcu(seq, rcu);
+ return publ;
+ }
+ spin_unlock_bh(&seq->lock);
return publ;
}
@@ -538,14 +535,14 @@ u32 tipc_nametbl_translate(u32 type, u32 instance, u32 *destnode)
if (!tipc_in_scope(*destnode, tipc_own_addr))
return 0;
- read_lock_bh(&tipc_nametbl_lock);
+ rcu_read_lock();
seq = nametbl_find_seq(type);
if (unlikely(!seq))
goto not_found;
+ spin_lock_bh(&seq->lock);
sseq = nameseq_find_subseq(seq, instance);
if (unlikely(!sseq))
- goto not_found;
- spin_lock_bh(&seq->lock);
+ goto no_match;
info = sseq->info;
/* Closest-First Algorithm */
@@ -595,7 +592,7 @@ u32 tipc_nametbl_translate(u32 type, u32 instance, u32 *destnode)
no_match:
spin_unlock_bh(&seq->lock);
not_found:
- read_unlock_bh(&tipc_nametbl_lock);
+ rcu_read_unlock();
*destnode = node;
return ref;
}
@@ -621,13 +618,12 @@ int tipc_nametbl_mc_translate(u32 type, u32 lower, u32 upper, u32 limit,
struct name_info *info;
int res = 0;
- read_lock_bh(&tipc_nametbl_lock);
+ rcu_read_lock();
seq = nametbl_find_seq(type);
if (!seq)
goto exit;
spin_lock_bh(&seq->lock);
-
sseq = seq->sseqs + nameseq_locate_subseq(seq, lower);
sseq_stop = seq->sseqs + seq->first_free;
for (; sseq != sseq_stop; sseq++) {
@@ -645,10 +641,9 @@ int tipc_nametbl_mc_translate(u32 type, u32 lower, u32 upper, u32 limit,
if (info->cluster_list_size != info->node_list_size)
res = 1;
}
-
spin_unlock_bh(&seq->lock);
exit:
- read_unlock_bh(&tipc_nametbl_lock);
+ rcu_read_unlock();
return res;
}
@@ -661,22 +656,23 @@ struct publication *tipc_nametbl_publish(u32 type, u32 lower, u32 upper,
struct publication *publ;
struct sk_buff *buf = NULL;
- if (table.local_publ_count >= TIPC_MAX_PUBLICATIONS) {
+ spin_lock_bh(&tipc_nametbl_lock);
+ if (tipc_nametbl->local_publ_count >= TIPC_MAX_PUBLICATIONS) {
pr_warn("Publication failed, local publication limit reached (%u)\n",
TIPC_MAX_PUBLICATIONS);
+ spin_unlock_bh(&tipc_nametbl_lock);
return NULL;
}
- write_lock_bh(&tipc_nametbl_lock);
publ = tipc_nametbl_insert_publ(type, lower, upper, scope,
tipc_own_addr, port_ref, key);
if (likely(publ)) {
- table.local_publ_count++;
+ tipc_nametbl->local_publ_count++;
buf = tipc_named_publish(publ);
/* Any pending external events? */
tipc_named_process_backlog();
}
- write_unlock_bh(&tipc_nametbl_lock);
+ spin_unlock_bh(&tipc_nametbl_lock);
if (buf)
named_cluster_distribute(buf);
@@ -689,27 +685,28 @@ struct publication *tipc_nametbl_publish(u32 type, u32 lower, u32 upper,
int tipc_nametbl_withdraw(u32 type, u32 lower, u32 ref, u32 key)
{
struct publication *publ;
- struct sk_buff *buf;
+ struct sk_buff *skb = NULL;
- write_lock_bh(&tipc_nametbl_lock);
+ spin_lock_bh(&tipc_nametbl_lock);
publ = tipc_nametbl_remove_publ(type, lower, tipc_own_addr, ref, key);
if (likely(publ)) {
- table.local_publ_count--;
- buf = tipc_named_withdraw(publ);
+ tipc_nametbl->local_publ_count--;
+ skb = tipc_named_withdraw(publ);
/* Any pending external events? */
tipc_named_process_backlog();
- write_unlock_bh(&tipc_nametbl_lock);
list_del_init(&publ->pport_list);
- kfree(publ);
+ kfree_rcu(publ, rcu);
+ } else {
+ pr_err("Unable to remove local publication\n"
+ "(type=%u, lower=%u, ref=%u, key=%u)\n",
+ type, lower, ref, key);
+ }
+ spin_unlock_bh(&tipc_nametbl_lock);
- if (buf)
- named_cluster_distribute(buf);
+ if (skb) {
+ named_cluster_distribute(skb);
return 1;
}
- write_unlock_bh(&tipc_nametbl_lock);
- pr_err("Unable to remove local publication\n"
- "(type=%u, lower=%u, ref=%u, key=%u)\n",
- type, lower, ref, key);
return 0;
}
@@ -719,12 +716,14 @@ int tipc_nametbl_withdraw(u32 type, u32 lower, u32 ref, u32 key)
void tipc_nametbl_subscribe(struct tipc_subscription *s)
{
u32 type = s->seq.type;
+ int index = hash(type);
struct name_seq *seq;
- write_lock_bh(&tipc_nametbl_lock);
+ spin_lock_bh(&tipc_nametbl_lock);
seq = nametbl_find_seq(type);
if (!seq)
- seq = tipc_nameseq_create(type, &table.types[hash(type)]);
+ seq = tipc_nameseq_create(type,
+ &tipc_nametbl->seq_hlist[index]);
if (seq) {
spin_lock_bh(&seq->lock);
tipc_nameseq_subscribe(seq, s);
@@ -733,7 +732,7 @@ void tipc_nametbl_subscribe(struct tipc_subscription *s)
pr_warn("Failed to create subscription for {%u,%u,%u}\n",
s->seq.type, s->seq.lower, s->seq.upper);
}
- write_unlock_bh(&tipc_nametbl_lock);
+ spin_unlock_bh(&tipc_nametbl_lock);
}
/**
@@ -743,18 +742,23 @@ void tipc_nametbl_unsubscribe(struct tipc_subscription *s)
{
struct name_seq *seq;
- write_lock_bh(&tipc_nametbl_lock);
+ spin_lock_bh(&tipc_nametbl_lock);
seq = nametbl_find_seq(s->seq.type);
if (seq != NULL) {
spin_lock_bh(&seq->lock);
list_del_init(&s->nameseq_list);
- spin_unlock_bh(&seq->lock);
- nameseq_delete_empty(seq);
+ if (!seq->first_free && list_empty(&seq->subscriptions)) {
+ hlist_del_init_rcu(&seq->ns_list);
+ kfree(seq->sseqs);
+ spin_unlock_bh(&seq->lock);
+ kfree_rcu(seq, rcu);
+ } else {
+ spin_unlock_bh(&seq->lock);
+ }
}
- write_unlock_bh(&tipc_nametbl_lock);
+ spin_unlock_bh(&tipc_nametbl_lock);
}
-
/**
* subseq_list - print specified sub-sequence contents into the given buffer
*/
@@ -876,8 +880,8 @@ static int nametbl_list(char *buf, int len, u32 depth_info,
lowbound = 0;
upbound = ~0;
for (i = 0; i < TIPC_NAMETBL_SIZE; i++) {
- seq_head = &table.types[i];
- hlist_for_each_entry(seq, seq_head, ns_list) {
+ seq_head = &tipc_nametbl->seq_hlist[i];
+ hlist_for_each_entry_rcu(seq, seq_head, ns_list) {
ret += nameseq_list(seq, buf + ret, len - ret,
depth, seq->type,
lowbound, upbound, i);
@@ -892,8 +896,8 @@ static int nametbl_list(char *buf, int len, u32 depth_info,
}
ret += nametbl_header(buf + ret, len - ret, depth);
i = hash(type);
- seq_head = &table.types[i];
- hlist_for_each_entry(seq, seq_head, ns_list) {
+ seq_head = &tipc_nametbl->seq_hlist[i];
+ hlist_for_each_entry_rcu(seq, seq_head, ns_list) {
if (seq->type == type) {
ret += nameseq_list(seq, buf + ret, len - ret,
depth, type,
@@ -925,11 +929,11 @@ struct sk_buff *tipc_nametbl_get(const void *req_tlv_area, int req_tlv_space)
pb = TLV_DATA(rep_tlv);
pb_len = ULTRA_STRING_MAX_LEN;
argv = (struct tipc_name_table_query *)TLV_DATA(req_tlv_area);
- read_lock_bh(&tipc_nametbl_lock);
+ rcu_read_lock();
str_len = nametbl_list(pb, pb_len, ntohl(argv->depth),
ntohl(argv->type),
ntohl(argv->lowbound), ntohl(argv->upbound));
- read_unlock_bh(&tipc_nametbl_lock);
+ rcu_read_unlock();
str_len += 1; /* for "\0" */
skb_put(buf, TLV_SPACE(str_len));
TLV_SET(rep_tlv, TIPC_TLV_ULTRA_STRING, NULL, str_len);
@@ -939,12 +943,18 @@ struct sk_buff *tipc_nametbl_get(const void *req_tlv_area, int req_tlv_space)
int tipc_nametbl_init(void)
{
- table.types = kcalloc(TIPC_NAMETBL_SIZE, sizeof(struct hlist_head),
- GFP_ATOMIC);
- if (!table.types)
+ int i;
+
+ tipc_nametbl = kzalloc(sizeof(*tipc_nametbl), GFP_ATOMIC);
+ if (!tipc_nametbl)
return -ENOMEM;
- table.local_publ_count = 0;
+ for (i = 0; i < TIPC_NAMETBL_SIZE; i++)
+ INIT_HLIST_HEAD(&tipc_nametbl->seq_hlist[i]);
+
+ INIT_LIST_HEAD(&tipc_nametbl->publ_list[TIPC_ZONE_SCOPE]);
+ INIT_LIST_HEAD(&tipc_nametbl->publ_list[TIPC_CLUSTER_SCOPE]);
+ INIT_LIST_HEAD(&tipc_nametbl->publ_list[TIPC_NODE_SCOPE]);
return 0;
}
@@ -959,17 +969,19 @@ static void tipc_purge_publications(struct name_seq *seq)
struct sub_seq *sseq;
struct name_info *info;
- if (!seq->sseqs) {
- nameseq_delete_empty(seq);
- return;
- }
+ spin_lock_bh(&seq->lock);
sseq = seq->sseqs;
info = sseq->info;
list_for_each_entry_safe(publ, safe, &info->zone_list, zone_list) {
tipc_nametbl_remove_publ(publ->type, publ->lower, publ->node,
publ->ref, publ->key);
- kfree(publ);
+ kfree_rcu(publ, rcu);
}
+ hlist_del_init_rcu(&seq->ns_list);
+ kfree(seq->sseqs);
+ spin_unlock_bh(&seq->lock);
+
+ kfree_rcu(seq, rcu);
}
void tipc_nametbl_stop(void)
@@ -977,21 +989,202 @@ void tipc_nametbl_stop(void)
u32 i;
struct name_seq *seq;
struct hlist_head *seq_head;
- struct hlist_node *safe;
/* Verify name table is empty and purge any lingering
* publications, then release the name table
*/
- write_lock_bh(&tipc_nametbl_lock);
+ spin_lock_bh(&tipc_nametbl_lock);
for (i = 0; i < TIPC_NAMETBL_SIZE; i++) {
- if (hlist_empty(&table.types[i]))
+ if (hlist_empty(&tipc_nametbl->seq_hlist[i]))
continue;
- seq_head = &table.types[i];
- hlist_for_each_entry_safe(seq, safe, seq_head, ns_list) {
+ seq_head = &tipc_nametbl->seq_hlist[i];
+ hlist_for_each_entry_rcu(seq, seq_head, ns_list) {
tipc_purge_publications(seq);
}
}
- kfree(table.types);
- table.types = NULL;
- write_unlock_bh(&tipc_nametbl_lock);
+ spin_unlock_bh(&tipc_nametbl_lock);
+
+ synchronize_net();
+ kfree(tipc_nametbl);
+
+}
+
+static int __tipc_nl_add_nametable_publ(struct tipc_nl_msg *msg,
+ struct name_seq *seq,
+ struct sub_seq *sseq, u32 *last_publ)
+{
+ void *hdr;
+ struct nlattr *attrs;
+ struct nlattr *publ;
+ struct publication *p;
+
+ if (*last_publ) {
+ list_for_each_entry(p, &sseq->info->zone_list, zone_list)
+ if (p->key == *last_publ)
+ break;
+ if (p->key != *last_publ)
+ return -EPIPE;
+ } else {
+ p = list_first_entry(&sseq->info->zone_list, struct publication,
+ zone_list);
+ }
+
+ list_for_each_entry_from(p, &sseq->info->zone_list, zone_list) {
+ *last_publ = p->key;
+
+ hdr = genlmsg_put(msg->skb, msg->portid, msg->seq,
+ &tipc_genl_v2_family, NLM_F_MULTI,
+ TIPC_NL_NAME_TABLE_GET);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ attrs = nla_nest_start(msg->skb, TIPC_NLA_NAME_TABLE);
+ if (!attrs)
+ goto msg_full;
+
+ publ = nla_nest_start(msg->skb, TIPC_NLA_NAME_TABLE_PUBL);
+ if (!publ)
+ goto attr_msg_full;
+
+ if (nla_put_u32(msg->skb, TIPC_NLA_PUBL_TYPE, seq->type))
+ goto publ_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_PUBL_LOWER, sseq->lower))
+ goto publ_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_PUBL_UPPER, sseq->upper))
+ goto publ_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_PUBL_SCOPE, p->scope))
+ goto publ_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_PUBL_NODE, p->node))
+ goto publ_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_PUBL_REF, p->ref))
+ goto publ_msg_full;
+ if (nla_put_u32(msg->skb, TIPC_NLA_PUBL_KEY, p->key))
+ goto publ_msg_full;
+
+ nla_nest_end(msg->skb, publ);
+ nla_nest_end(msg->skb, attrs);
+ genlmsg_end(msg->skb, hdr);
+ }
+ *last_publ = 0;
+
+ return 0;
+
+publ_msg_full:
+ nla_nest_cancel(msg->skb, publ);
+attr_msg_full:
+ nla_nest_cancel(msg->skb, attrs);
+msg_full:
+ genlmsg_cancel(msg->skb, hdr);
+
+ return -EMSGSIZE;
+}
+
+static int __tipc_nl_subseq_list(struct tipc_nl_msg *msg, struct name_seq *seq,
+ u32 *last_lower, u32 *last_publ)
+{
+ struct sub_seq *sseq;
+ struct sub_seq *sseq_start;
+ int err;
+
+ if (*last_lower) {
+ sseq_start = nameseq_find_subseq(seq, *last_lower);
+ if (!sseq_start)
+ return -EPIPE;
+ } else {
+ sseq_start = seq->sseqs;
+ }
+
+ for (sseq = sseq_start; sseq != &seq->sseqs[seq->first_free]; sseq++) {
+ err = __tipc_nl_add_nametable_publ(msg, seq, sseq, last_publ);
+ if (err) {
+ *last_lower = sseq->lower;
+ return err;
+ }
+ }
+ *last_lower = 0;
+
+ return 0;
+}
+
+static int __tipc_nl_seq_list(struct tipc_nl_msg *msg, u32 *last_type,
+ u32 *last_lower, u32 *last_publ)
+{
+ struct hlist_head *seq_head;
+ struct name_seq *seq = NULL;
+ int err;
+ int i;
+
+ if (*last_type)
+ i = hash(*last_type);
+ else
+ i = 0;
+
+ for (; i < TIPC_NAMETBL_SIZE; i++) {
+ seq_head = &tipc_nametbl->seq_hlist[i];
+
+ if (*last_type) {
+ seq = nametbl_find_seq(*last_type);
+ if (!seq)
+ return -EPIPE;
+ } else {
+ hlist_for_each_entry_rcu(seq, seq_head, ns_list)
+ break;
+ if (!seq)
+ continue;
+ }
+
+ hlist_for_each_entry_from_rcu(seq, ns_list) {
+ spin_lock_bh(&seq->lock);
+ err = __tipc_nl_subseq_list(msg, seq, last_lower,
+ last_publ);
+
+ if (err) {
+ *last_type = seq->type;
+ spin_unlock_bh(&seq->lock);
+ return err;
+ }
+ spin_unlock_bh(&seq->lock);
+ }
+ *last_type = 0;
+ }
+ return 0;
+}
+
+int tipc_nl_name_table_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int err;
+ int done = cb->args[3];
+ u32 last_type = cb->args[0];
+ u32 last_lower = cb->args[1];
+ u32 last_publ = cb->args[2];
+ struct tipc_nl_msg msg;
+
+ if (done)
+ return 0;
+
+ msg.skb = skb;
+ msg.portid = NETLINK_CB(cb->skb).portid;
+ msg.seq = cb->nlh->nlmsg_seq;
+
+ rcu_read_lock();
+ err = __tipc_nl_seq_list(&msg, &last_type, &last_lower, &last_publ);
+ if (!err) {
+ done = 1;
+ } else if (err != -EMSGSIZE) {
+ /* We never set seq or call nl_dump_check_consistent() this
+ * means that setting prev_seq here will cause the consistence
+ * check to fail in the netlink callback handler. Resulting in
+ * the NLMSG_DONE message having the NLM_F_DUMP_INTR flag set if
+ * we got an error.
+ */
+ cb->prev_seq = 1;
+ }
+ rcu_read_unlock();
+
+ cb->args[0] = last_type;
+ cb->args[1] = last_lower;
+ cb->args[2] = last_publ;
+ cb->args[3] = done;
+
+ return skb->len;
}
diff --git a/net/tipc/name_table.h b/net/tipc/name_table.h
index f02f48b9a216..5f0dee92010d 100644
--- a/net/tipc/name_table.h
+++ b/net/tipc/name_table.h
@@ -1,7 +1,7 @@
/*
* net/tipc/name_table.h: Include file for TIPC name table code
*
- * Copyright (c) 2000-2006, Ericsson AB
+ * Copyright (c) 2000-2006, 2014, Ericsson AB
* Copyright (c) 2004-2005, 2010-2011, Wind River Systems
* All rights reserved.
*
@@ -37,15 +37,15 @@
#ifndef _TIPC_NAME_TABLE_H
#define _TIPC_NAME_TABLE_H
-#include "node_subscr.h"
-
struct tipc_subscription;
struct tipc_port_list;
/*
* TIPC name types reserved for internal TIPC use (both current and planned)
*/
-#define TIPC_ZM_SRV 3 /* zone master service name type */
+#define TIPC_ZM_SRV 3 /* zone master service name type */
+#define TIPC_PUBL_SCOPE_NUM (TIPC_NODE_SCOPE + 1)
+#define TIPC_NAMETBL_SIZE 1024 /* must be a power of 2 */
/**
* struct publication - info about a published (name or) name sequence
@@ -56,12 +56,13 @@ struct tipc_port_list;
* @node: network address of publishing port's node
* @ref: publishing port
* @key: publication key
- * @subscr: subscription to "node down" event (for off-node publications only)
+ * @nodesub_list: subscription to "node down" event (off-node publication only)
* @local_list: adjacent entries in list of publications made by this node
* @pport_list: adjacent entries in list of publications made by this port
* @node_list: adjacent matching name seq publications with >= node scope
* @cluster_list: adjacent matching name seq publications with >= cluster scope
* @zone_list: adjacent matching name seq publications with >= zone scope
+ * @rcu: RCU callback head used for deferred freeing
*
* Note that the node list, cluster list, and zone list are circular lists.
*/
@@ -73,16 +74,31 @@ struct publication {
u32 node;
u32 ref;
u32 key;
- struct tipc_node_subscr subscr;
+ struct list_head nodesub_list;
struct list_head local_list;
struct list_head pport_list;
struct list_head node_list;
struct list_head cluster_list;
struct list_head zone_list;
+ struct rcu_head rcu;
+};
+
+/**
+ * struct name_table - table containing all existing port name publications
+ * @seq_hlist: name sequence hash lists
+ * @publ_list: pulication lists
+ * @local_publ_count: number of publications issued by this node
+ */
+struct name_table {
+ struct hlist_head seq_hlist[TIPC_NAMETBL_SIZE];
+ struct list_head publ_list[TIPC_PUBL_SCOPE_NUM];
+ u32 local_publ_count;
};
+extern spinlock_t tipc_nametbl_lock;
+extern struct name_table *tipc_nametbl;
-extern rwlock_t tipc_nametbl_lock;
+int tipc_nl_name_table_dump(struct sk_buff *skb, struct netlink_callback *cb);
struct sk_buff *tipc_nametbl_get(const void *req_tlv_area, int req_tlv_space);
u32 tipc_nametbl_translate(u32 type, u32 instance, u32 *node);
diff --git a/net/tipc/net.c b/net/tipc/net.c
index 93b9944a6a8b..cf13df3cde8f 100644
--- a/net/tipc/net.c
+++ b/net/tipc/net.c
@@ -42,6 +42,11 @@
#include "node.h"
#include "config.h"
+static const struct nla_policy tipc_nl_net_policy[TIPC_NLA_NET_MAX + 1] = {
+ [TIPC_NLA_NET_UNSPEC] = { .type = NLA_UNSPEC },
+ [TIPC_NLA_NET_ID] = { .type = NLA_U32 }
+};
+
/*
* The TIPC locking policy is designed to ensure a very fine locking
* granularity, permitting complete parallel access to individual
@@ -138,3 +143,104 @@ void tipc_net_stop(void)
pr_info("Left network mode\n");
}
+
+static int __tipc_nl_add_net(struct tipc_nl_msg *msg)
+{
+ void *hdr;
+ struct nlattr *attrs;
+
+ hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_v2_family,
+ NLM_F_MULTI, TIPC_NL_NET_GET);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ attrs = nla_nest_start(msg->skb, TIPC_NLA_NET);
+ if (!attrs)
+ goto msg_full;
+
+ if (nla_put_u32(msg->skb, TIPC_NLA_NET_ID, tipc_net_id))
+ goto attr_msg_full;
+
+ nla_nest_end(msg->skb, attrs);
+ genlmsg_end(msg->skb, hdr);
+
+ return 0;
+
+attr_msg_full:
+ nla_nest_cancel(msg->skb, attrs);
+msg_full:
+ genlmsg_cancel(msg->skb, hdr);
+
+ return -EMSGSIZE;
+}
+
+int tipc_nl_net_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int err;
+ int done = cb->args[0];
+ struct tipc_nl_msg msg;
+
+ if (done)
+ return 0;
+
+ msg.skb = skb;
+ msg.portid = NETLINK_CB(cb->skb).portid;
+ msg.seq = cb->nlh->nlmsg_seq;
+
+ err = __tipc_nl_add_net(&msg);
+ if (err)
+ goto out;
+
+ done = 1;
+out:
+ cb->args[0] = done;
+
+ return skb->len;
+}
+
+int tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info)
+{
+ int err;
+ struct nlattr *attrs[TIPC_NLA_NET_MAX + 1];
+
+ if (!info->attrs[TIPC_NLA_NET])
+ return -EINVAL;
+
+ err = nla_parse_nested(attrs, TIPC_NLA_NET_MAX,
+ info->attrs[TIPC_NLA_NET],
+ tipc_nl_net_policy);
+ if (err)
+ return err;
+
+ if (attrs[TIPC_NLA_NET_ID]) {
+ u32 val;
+
+ /* Can't change net id once TIPC has joined a network */
+ if (tipc_own_addr)
+ return -EPERM;
+
+ val = nla_get_u32(attrs[TIPC_NLA_NET_ID]);
+ if (val < 1 || val > 9999)
+ return -EINVAL;
+
+ tipc_net_id = val;
+ }
+
+ if (attrs[TIPC_NLA_NET_ADDR]) {
+ u32 addr;
+
+ /* Can't change net addr once TIPC has joined a network */
+ if (tipc_own_addr)
+ return -EPERM;
+
+ addr = nla_get_u32(attrs[TIPC_NLA_NET_ADDR]);
+ if (!tipc_addr_node_valid(addr))
+ return -EINVAL;
+
+ rtnl_lock();
+ tipc_net_start(addr);
+ rtnl_unlock();
+ }
+
+ return 0;
+}
diff --git a/net/tipc/net.h b/net/tipc/net.h
index 59ef3388be2c..a81c1b9eb150 100644
--- a/net/tipc/net.h
+++ b/net/tipc/net.h
@@ -1,7 +1,7 @@
/*
* net/tipc/net.h: Include file for TIPC network routing code
*
- * Copyright (c) 1995-2006, Ericsson AB
+ * Copyright (c) 1995-2006, 2014, Ericsson AB
* Copyright (c) 2005, 2010-2011, Wind River Systems
* All rights reserved.
*
@@ -37,7 +37,13 @@
#ifndef _TIPC_NET_H
#define _TIPC_NET_H
+#include <net/genetlink.h>
+
int tipc_net_start(u32 addr);
+
void tipc_net_stop(void);
+int tipc_nl_net_dump(struct sk_buff *skb, struct netlink_callback *cb);
+int tipc_nl_net_set(struct sk_buff *skb, struct genl_info *info);
+
#endif
diff --git a/net/tipc/netlink.c b/net/tipc/netlink.c
index ad844d365340..b891e3905bc4 100644
--- a/net/tipc/netlink.c
+++ b/net/tipc/netlink.c
@@ -1,7 +1,7 @@
/*
* net/tipc/netlink.c: TIPC configuration handling
*
- * Copyright (c) 2005-2006, Ericsson AB
+ * Copyright (c) 2005-2006, 2014, Ericsson AB
* Copyright (c) 2005-2007, Wind River Systems
* All rights reserved.
*
@@ -36,6 +36,12 @@
#include "core.h"
#include "config.h"
+#include "socket.h"
+#include "name_table.h"
+#include "bearer.h"
+#include "link.h"
+#include "node.h"
+#include "net.h"
#include <net/genetlink.h>
static int handle_cmd(struct sk_buff *skb, struct genl_info *info)
@@ -68,6 +74,19 @@ static int handle_cmd(struct sk_buff *skb, struct genl_info *info)
return 0;
}
+static const struct nla_policy tipc_nl_policy[TIPC_NLA_MAX + 1] = {
+ [TIPC_NLA_UNSPEC] = { .type = NLA_UNSPEC, },
+ [TIPC_NLA_BEARER] = { .type = NLA_NESTED, },
+ [TIPC_NLA_SOCK] = { .type = NLA_NESTED, },
+ [TIPC_NLA_PUBL] = { .type = NLA_NESTED, },
+ [TIPC_NLA_LINK] = { .type = NLA_NESTED, },
+ [TIPC_NLA_MEDIA] = { .type = NLA_NESTED, },
+ [TIPC_NLA_NODE] = { .type = NLA_NESTED, },
+ [TIPC_NLA_NET] = { .type = NLA_NESTED, },
+ [TIPC_NLA_NAME_TABLE] = { .type = NLA_NESTED, }
+};
+
+/* Legacy ASCII API */
static struct genl_family tipc_genl_family = {
.id = GENL_ID_GENERATE,
.name = TIPC_GENL_NAME,
@@ -76,6 +95,7 @@ static struct genl_family tipc_genl_family = {
.maxattr = 0,
};
+/* Legacy ASCII API */
static struct genl_ops tipc_genl_ops[] = {
{
.cmd = TIPC_GENL_CMD,
@@ -83,12 +103,122 @@ static struct genl_ops tipc_genl_ops[] = {
},
};
+/* Users of the legacy API (tipc-config) can't handle that we add operations,
+ * so we have a separate genl handling for the new API.
+ */
+struct genl_family tipc_genl_v2_family = {
+ .id = GENL_ID_GENERATE,
+ .name = TIPC_GENL_V2_NAME,
+ .version = TIPC_GENL_V2_VERSION,
+ .hdrsize = 0,
+ .maxattr = TIPC_NLA_MAX,
+};
+
+static const struct genl_ops tipc_genl_v2_ops[] = {
+ {
+ .cmd = TIPC_NL_BEARER_DISABLE,
+ .doit = tipc_nl_bearer_disable,
+ .policy = tipc_nl_policy,
+ },
+ {
+ .cmd = TIPC_NL_BEARER_ENABLE,
+ .doit = tipc_nl_bearer_enable,
+ .policy = tipc_nl_policy,
+ },
+ {
+ .cmd = TIPC_NL_BEARER_GET,
+ .doit = tipc_nl_bearer_get,
+ .dumpit = tipc_nl_bearer_dump,
+ .policy = tipc_nl_policy,
+ },
+ {
+ .cmd = TIPC_NL_BEARER_SET,
+ .doit = tipc_nl_bearer_set,
+ .policy = tipc_nl_policy,
+ },
+ {
+ .cmd = TIPC_NL_SOCK_GET,
+ .dumpit = tipc_nl_sk_dump,
+ .policy = tipc_nl_policy,
+ },
+ {
+ .cmd = TIPC_NL_PUBL_GET,
+ .dumpit = tipc_nl_publ_dump,
+ .policy = tipc_nl_policy,
+ },
+ {
+ .cmd = TIPC_NL_LINK_GET,
+ .doit = tipc_nl_link_get,
+ .dumpit = tipc_nl_link_dump,
+ .policy = tipc_nl_policy,
+ },
+ {
+ .cmd = TIPC_NL_LINK_SET,
+ .doit = tipc_nl_link_set,
+ .policy = tipc_nl_policy,
+ },
+ {
+ .cmd = TIPC_NL_LINK_RESET_STATS,
+ .doit = tipc_nl_link_reset_stats,
+ .policy = tipc_nl_policy,
+ },
+ {
+ .cmd = TIPC_NL_MEDIA_GET,
+ .doit = tipc_nl_media_get,
+ .dumpit = tipc_nl_media_dump,
+ .policy = tipc_nl_policy,
+ },
+ {
+ .cmd = TIPC_NL_MEDIA_SET,
+ .doit = tipc_nl_media_set,
+ .policy = tipc_nl_policy,
+ },
+ {
+ .cmd = TIPC_NL_NODE_GET,
+ .dumpit = tipc_nl_node_dump,
+ .policy = tipc_nl_policy,
+ },
+ {
+ .cmd = TIPC_NL_NET_GET,
+ .dumpit = tipc_nl_net_dump,
+ .policy = tipc_nl_policy,
+ },
+ {
+ .cmd = TIPC_NL_NET_SET,
+ .doit = tipc_nl_net_set,
+ .policy = tipc_nl_policy,
+ },
+ {
+ .cmd = TIPC_NL_NAME_TABLE_GET,
+ .dumpit = tipc_nl_name_table_dump,
+ .policy = tipc_nl_policy,
+ }
+};
+
+int tipc_nlmsg_parse(const struct nlmsghdr *nlh, struct nlattr ***attr)
+{
+ u32 maxattr = tipc_genl_v2_family.maxattr;
+
+ *attr = tipc_genl_v2_family.attrbuf;
+ if (!*attr)
+ return -EOPNOTSUPP;
+
+ return nlmsg_parse(nlh, GENL_HDRLEN, *attr, maxattr, tipc_nl_policy);
+}
+
int tipc_netlink_start(void)
{
int res;
res = genl_register_family_with_ops(&tipc_genl_family, tipc_genl_ops);
if (res) {
+ pr_err("Failed to register legacy interface\n");
+ return res;
+ }
+
+ res = genl_register_family_with_ops(&tipc_genl_v2_family,
+ tipc_genl_v2_ops);
+ if (res) {
pr_err("Failed to register netlink interface\n");
return res;
}
@@ -98,4 +228,5 @@ int tipc_netlink_start(void)
void tipc_netlink_stop(void)
{
genl_unregister_family(&tipc_genl_family);
+ genl_unregister_family(&tipc_genl_v2_family);
}
diff --git a/net/tipc/node_subscr.h b/net/tipc/netlink.h
index d91b8cc81e3d..1425c6869de0 100644
--- a/net/tipc/node_subscr.h
+++ b/net/tipc/netlink.h
@@ -1,8 +1,7 @@
/*
- * net/tipc/node_subscr.h: Include file for TIPC "node down" subscription handling
+ * net/tipc/netlink.h: Include file for TIPC netlink code
*
- * Copyright (c) 1995-2006, Ericsson AB
- * Copyright (c) 2005, 2010-2011, Wind River Systems
+ * Copyright (c) 2014, Ericsson AB
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -34,30 +33,16 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef _TIPC_NODE_SUBSCR_H
-#define _TIPC_NODE_SUBSCR_H
+#ifndef _TIPC_NETLINK_H
+#define _TIPC_NETLINK_H
-#include "addr.h"
+extern struct genl_family tipc_genl_v2_family;
+int tipc_nlmsg_parse(const struct nlmsghdr *nlh, struct nlattr ***buf);
-typedef void (*net_ev_handler) (void *usr_handle);
-
-/**
- * struct tipc_node_subscr - "node down" subscription entry
- * @node: ptr to node structure of interest (or NULL, if none)
- * @handle_node_down: routine to invoke when node fails
- * @usr_handle: argument to pass to routine when node fails
- * @nodesub_list: adjacent entries in list of subscriptions for the node
- */
-struct tipc_node_subscr {
- struct tipc_node *node;
- net_ev_handler handle_node_down;
- void *usr_handle;
- struct list_head nodesub_list;
+struct tipc_nl_msg {
+ struct sk_buff *skb;
+ u32 portid;
+ u32 seq;
};
-void tipc_nodesub_subscribe(struct tipc_node_subscr *node_sub, u32 addr,
- void *usr_handle, net_ev_handler handle_down);
-void tipc_nodesub_unsubscribe(struct tipc_node_subscr *node_sub);
-void tipc_nodesub_notify(struct list_head *nsub_list);
-
#endif
diff --git a/net/tipc/node.c b/net/tipc/node.c
index 5781634e957d..8d353ec77a66 100644
--- a/net/tipc/node.c
+++ b/net/tipc/node.c
@@ -58,6 +58,12 @@ struct tipc_sock_conn {
struct list_head list;
};
+static const struct nla_policy tipc_nl_node_policy[TIPC_NLA_NODE_MAX + 1] = {
+ [TIPC_NLA_NODE_UNSPEC] = { .type = NLA_UNSPEC },
+ [TIPC_NLA_NODE_ADDR] = { .type = NLA_U32 },
+ [TIPC_NLA_NODE_UP] = { .type = NLA_FLAG }
+};
+
/*
* A trivial power-of-two bitmask technique is used for speed, since this
* operation is done for every incoming TIPC packet. The number of hash table
@@ -107,9 +113,10 @@ struct tipc_node *tipc_node_create(u32 addr)
spin_lock_init(&n_ptr->lock);
INIT_HLIST_NODE(&n_ptr->hash);
INIT_LIST_HEAD(&n_ptr->list);
- INIT_LIST_HEAD(&n_ptr->nsub);
+ INIT_LIST_HEAD(&n_ptr->publ_list);
INIT_LIST_HEAD(&n_ptr->conn_sks);
- __skb_queue_head_init(&n_ptr->waiting_sks);
+ skb_queue_head_init(&n_ptr->waiting_sks);
+ __skb_queue_head_init(&n_ptr->bclink.deferred_queue);
hlist_add_head_rcu(&n_ptr->hash, &node_htable[tipc_hashfn(addr)]);
@@ -375,8 +382,7 @@ static void node_lost_contact(struct tipc_node *n_ptr)
/* Flush broadcast link info associated with lost node */
if (n_ptr->bclink.recv_permitted) {
- kfree_skb_list(n_ptr->bclink.deferred_head);
- n_ptr->bclink.deferred_size = 0;
+ __skb_queue_purge(&n_ptr->bclink.deferred_queue);
if (n_ptr->bclink.reasm_buf) {
kfree_skb(n_ptr->bclink.reasm_buf);
@@ -568,7 +574,7 @@ void tipc_node_unlock(struct tipc_node *node)
skb_queue_splice_init(&node->waiting_sks, &waiting_sks);
if (flags & TIPC_NOTIFY_NODE_DOWN) {
- list_replace_init(&node->nsub, &nsub_list);
+ list_replace_init(&node->publ_list, &nsub_list);
list_replace_init(&node->conn_sks, &conn_sks);
}
node->action_flags &= ~(TIPC_WAKEUP_USERS | TIPC_NOTIFY_NODE_DOWN |
@@ -585,7 +591,7 @@ void tipc_node_unlock(struct tipc_node *node)
tipc_node_abort_sock_conns(&conn_sks);
if (!list_empty(&nsub_list))
- tipc_nodesub_notify(&nsub_list);
+ tipc_publ_notify(&nsub_list, addr);
if (flags & TIPC_WAKEUP_BCAST_USERS)
tipc_bclink_wakeup_users();
@@ -601,3 +607,93 @@ void tipc_node_unlock(struct tipc_node *node)
tipc_nametbl_withdraw(TIPC_LINK_STATE, addr,
link_id, addr);
}
+
+/* Caller should hold node lock for the passed node */
+static int __tipc_nl_add_node(struct tipc_nl_msg *msg, struct tipc_node *node)
+{
+ void *hdr;
+ struct nlattr *attrs;
+
+ hdr = genlmsg_put(msg->skb, msg->portid, msg->seq, &tipc_genl_v2_family,
+ NLM_F_MULTI, TIPC_NL_NODE_GET);
+ if (!hdr)
+ return -EMSGSIZE;
+
+ attrs = nla_nest_start(msg->skb, TIPC_NLA_NODE);
+ if (!attrs)
+ goto msg_full;
+
+ if (nla_put_u32(msg->skb, TIPC_NLA_NODE_ADDR, node->addr))
+ goto attr_msg_full;
+ if (tipc_node_is_up(node))
+ if (nla_put_flag(msg->skb, TIPC_NLA_NODE_UP))
+ goto attr_msg_full;
+
+ nla_nest_end(msg->skb, attrs);
+ genlmsg_end(msg->skb, hdr);
+
+ return 0;
+
+attr_msg_full:
+ nla_nest_cancel(msg->skb, attrs);
+msg_full:
+ genlmsg_cancel(msg->skb, hdr);
+
+ return -EMSGSIZE;
+}
+
+int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int err;
+ int done = cb->args[0];
+ int last_addr = cb->args[1];
+ struct tipc_node *node;
+ struct tipc_nl_msg msg;
+
+ if (done)
+ return 0;
+
+ msg.skb = skb;
+ msg.portid = NETLINK_CB(cb->skb).portid;
+ msg.seq = cb->nlh->nlmsg_seq;
+
+ rcu_read_lock();
+
+ if (last_addr && !tipc_node_find(last_addr)) {
+ rcu_read_unlock();
+ /* We never set seq or call nl_dump_check_consistent() this
+ * means that setting prev_seq here will cause the consistence
+ * check to fail in the netlink callback handler. Resulting in
+ * the NLMSG_DONE message having the NLM_F_DUMP_INTR flag set if
+ * the node state changed while we released the lock.
+ */
+ cb->prev_seq = 1;
+ return -EPIPE;
+ }
+
+ list_for_each_entry_rcu(node, &tipc_node_list, list) {
+ if (last_addr) {
+ if (node->addr == last_addr)
+ last_addr = 0;
+ else
+ continue;
+ }
+
+ tipc_node_lock(node);
+ err = __tipc_nl_add_node(&msg, node);
+ if (err) {
+ last_addr = node->addr;
+ tipc_node_unlock(node);
+ goto out;
+ }
+
+ tipc_node_unlock(node);
+ }
+ done = 1;
+out:
+ cb->args[0] = done;
+ cb->args[1] = last_addr;
+ rcu_read_unlock();
+
+ return skb->len;
+}
diff --git a/net/tipc/node.h b/net/tipc/node.h
index 04e91458bb29..cbe0e950f1cc 100644
--- a/net/tipc/node.h
+++ b/net/tipc/node.h
@@ -1,7 +1,7 @@
/*
* net/tipc/node.h: Include file for TIPC node management routines
*
- * Copyright (c) 2000-2006, Ericsson AB
+ * Copyright (c) 2000-2006, 2014, Ericsson AB
* Copyright (c) 2005, 2010-2014, Wind River Systems
* All rights reserved.
*
@@ -37,7 +37,6 @@
#ifndef _TIPC_NODE_H
#define _TIPC_NODE_H
-#include "node_subscr.h"
#include "addr.h"
#include "net.h"
#include "bearer.h"
@@ -72,9 +71,7 @@ enum {
* @last_in: sequence # of last in-sequence b'cast message received from node
* @last_sent: sequence # of last b'cast message sent by node
* @oos_state: state tracker for handling OOS b'cast messages
- * @deferred_size: number of OOS b'cast messages in deferred queue
- * @deferred_head: oldest OOS b'cast message received from node
- * @deferred_tail: newest OOS b'cast message received from node
+ * @deferred_queue: deferred queue saved OOS b'cast message received from node
* @reasm_buf: broadcast reassembly queue head from node
* @recv_permitted: true if node is allowed to receive b'cast messages
*/
@@ -84,8 +81,7 @@ struct tipc_node_bclink {
u32 last_sent;
u32 oos_state;
u32 deferred_size;
- struct sk_buff *deferred_head;
- struct sk_buff *deferred_tail;
+ struct sk_buff_head deferred_queue;
struct sk_buff *reasm_buf;
bool recv_permitted;
};
@@ -104,7 +100,7 @@ struct tipc_node_bclink {
* @link_cnt: number of links to node
* @signature: node instance identifier
* @link_id: local and remote bearer ids of changing link, if any
- * @nsub: list of "node down" subscriptions monitoring node
+ * @publ_list: list of publications
* @rcu: rcu struct for tipc_node
*/
struct tipc_node {
@@ -121,7 +117,7 @@ struct tipc_node {
int working_links;
u32 signature;
u32 link_id;
- struct list_head nsub;
+ struct list_head publ_list;
struct sk_buff_head waiting_sks;
struct list_head conn_sks;
struct rcu_head rcu;
@@ -145,6 +141,8 @@ void tipc_node_unlock(struct tipc_node *node);
int tipc_node_add_conn(u32 dnode, u32 port, u32 peer_port);
void tipc_node_remove_conn(u32 dnode, u32 port);
+int tipc_nl_node_dump(struct sk_buff *skb, struct netlink_callback *cb);
+
static inline void tipc_node_lock(struct tipc_node *node)
{
spin_lock_bh(&node->lock);
diff --git a/net/tipc/node_subscr.c b/net/tipc/node_subscr.c
deleted file mode 100644
index 2d13eea8574a..000000000000
--- a/net/tipc/node_subscr.c
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * net/tipc/node_subscr.c: TIPC "node down" subscription handling
- *
- * Copyright (c) 1995-2006, Ericsson AB
- * Copyright (c) 2005, 2010-2011, Wind River Systems
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the names of the copyright holders nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL") version 2 as published by the Free
- * Software Foundation.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "core.h"
-#include "node_subscr.h"
-#include "node.h"
-
-/**
- * tipc_nodesub_subscribe - create "node down" subscription for specified node
- */
-void tipc_nodesub_subscribe(struct tipc_node_subscr *node_sub, u32 addr,
- void *usr_handle, net_ev_handler handle_down)
-{
- if (in_own_node(addr)) {
- node_sub->node = NULL;
- return;
- }
-
- node_sub->node = tipc_node_find(addr);
- if (!node_sub->node) {
- pr_warn("Node subscription rejected, unknown node 0x%x\n",
- addr);
- return;
- }
- node_sub->handle_node_down = handle_down;
- node_sub->usr_handle = usr_handle;
-
- tipc_node_lock(node_sub->node);
- list_add_tail(&node_sub->nodesub_list, &node_sub->node->nsub);
- tipc_node_unlock(node_sub->node);
-}
-
-/**
- * tipc_nodesub_unsubscribe - cancel "node down" subscription (if any)
- */
-void tipc_nodesub_unsubscribe(struct tipc_node_subscr *node_sub)
-{
- if (!node_sub->node)
- return;
-
- tipc_node_lock(node_sub->node);
- list_del_init(&node_sub->nodesub_list);
- tipc_node_unlock(node_sub->node);
-}
-
-/**
- * tipc_nodesub_notify - notify subscribers that a node is unreachable
- *
- * Note: node is locked by caller
- */
-void tipc_nodesub_notify(struct list_head *nsub_list)
-{
- struct tipc_node_subscr *ns, *safe;
- net_ev_handler handle_node_down;
-
- list_for_each_entry_safe(ns, safe, nsub_list, nodesub_list) {
- handle_node_down = ns->handle_node_down;
- if (handle_node_down) {
- ns->handle_node_down = NULL;
- handle_node_down(ns->usr_handle);
- }
- }
-}
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index 51bddc236a15..4731cad99d1c 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -121,6 +121,14 @@ static const struct proto_ops msg_ops;
static struct proto tipc_proto;
static struct proto tipc_proto_kern;
+static const struct nla_policy tipc_nl_sock_policy[TIPC_NLA_SOCK_MAX + 1] = {
+ [TIPC_NLA_SOCK_UNSPEC] = { .type = NLA_UNSPEC },
+ [TIPC_NLA_SOCK_ADDR] = { .type = NLA_U32 },
+ [TIPC_NLA_SOCK_REF] = { .type = NLA_U32 },
+ [TIPC_NLA_SOCK_CON] = { .type = NLA_NESTED },
+ [TIPC_NLA_SOCK_HAS_PUBL] = { .type = NLA_FLAG }
+};
+
/*
* Revised TIPC socket locking policy:
*
@@ -236,12 +244,12 @@ static void tsk_advance_rx_queue(struct sock *sk)
*/
static void tsk_rej_rx_queue(struct sock *sk)
{
- struct sk_buff *buf;
+ struct sk_buff *skb;
u32 dnode;
- while ((buf = __skb_dequeue(&sk->sk_receive_queue))) {
- if (tipc_msg_reverse(buf, &dnode, TIPC_ERR_NO_PORT))
- tipc_link_xmit(buf, dnode, 0);
+ while ((skb = __skb_dequeue(&sk->sk_receive_queue))) {
+ if (tipc_msg_reverse(skb, &dnode, TIPC_ERR_NO_PORT))
+ tipc_link_xmit_skb(skb, dnode, 0);
}
}
@@ -454,7 +462,7 @@ static int tipc_release(struct socket *sock)
{
struct sock *sk = sock->sk;
struct tipc_sock *tsk;
- struct sk_buff *buf;
+ struct sk_buff *skb;
u32 dnode;
/*
@@ -473,11 +481,11 @@ static int tipc_release(struct socket *sock)
*/
dnode = tsk_peer_node(tsk);
while (sock->state != SS_DISCONNECTING) {
- buf = __skb_dequeue(&sk->sk_receive_queue);
- if (buf == NULL)
+ skb = __skb_dequeue(&sk->sk_receive_queue);
+ if (skb == NULL)
break;
- if (TIPC_SKB_CB(buf)->handle != NULL)
- kfree_skb(buf);
+ if (TIPC_SKB_CB(skb)->handle != NULL)
+ kfree_skb(skb);
else {
if ((sock->state == SS_CONNECTING) ||
(sock->state == SS_CONNECTED)) {
@@ -485,8 +493,8 @@ static int tipc_release(struct socket *sock)
tsk->connected = 0;
tipc_node_remove_conn(dnode, tsk->ref);
}
- if (tipc_msg_reverse(buf, &dnode, TIPC_ERR_NO_PORT))
- tipc_link_xmit(buf, dnode, 0);
+ if (tipc_msg_reverse(skb, &dnode, TIPC_ERR_NO_PORT))
+ tipc_link_xmit_skb(skb, dnode, 0);
}
}
@@ -494,12 +502,12 @@ static int tipc_release(struct socket *sock)
tipc_sk_ref_discard(tsk->ref);
k_cancel_timer(&tsk->timer);
if (tsk->connected) {
- buf = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG,
+ skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG,
SHORT_H_SIZE, 0, dnode, tipc_own_addr,
tsk_peer_port(tsk),
tsk->ref, TIPC_ERR_NO_PORT);
- if (buf)
- tipc_link_xmit(buf, dnode, tsk->ref);
+ if (skb)
+ tipc_link_xmit_skb(skb, dnode, tsk->ref);
tipc_node_remove_conn(dnode, tsk->ref);
}
k_term_timer(&tsk->timer);
@@ -692,7 +700,7 @@ static unsigned int tipc_poll(struct file *file, struct socket *sock,
* tipc_sendmcast - send multicast message
* @sock: socket structure
* @seq: destination address
- * @iov: message data to send
+ * @msg: message to send
* @dsz: total length of message data
* @timeo: timeout to wait for wakeup
*
@@ -700,11 +708,11 @@ static unsigned int tipc_poll(struct file *file, struct socket *sock,
* Returns the number of bytes sent on success, or errno
*/
static int tipc_sendmcast(struct socket *sock, struct tipc_name_seq *seq,
- struct iovec *iov, size_t dsz, long timeo)
+ struct msghdr *msg, size_t dsz, long timeo)
{
struct sock *sk = sock->sk;
struct tipc_msg *mhdr = &tipc_sk(sk)->phdr;
- struct sk_buff *buf;
+ struct sk_buff_head head;
uint mtu;
int rc;
@@ -719,12 +727,13 @@ static int tipc_sendmcast(struct socket *sock, struct tipc_name_seq *seq,
new_mtu:
mtu = tipc_bclink_get_mtu();
- rc = tipc_msg_build(mhdr, iov, 0, dsz, mtu, &buf);
+ __skb_queue_head_init(&head);
+ rc = tipc_msg_build(mhdr, msg, 0, dsz, mtu, &head);
if (unlikely(rc < 0))
return rc;
do {
- rc = tipc_bclink_xmit(buf);
+ rc = tipc_bclink_xmit(&head);
if (likely(rc >= 0)) {
rc = dsz;
break;
@@ -736,7 +745,7 @@ new_mtu:
tipc_sk(sk)->link_cong = 1;
rc = tipc_wait_for_sndmsg(sock, &timeo);
if (rc)
- kfree_skb_list(buf);
+ __skb_queue_purge(&head);
} while (!rc);
return rc;
}
@@ -818,39 +827,6 @@ exit:
return TIPC_OK;
}
-/**
- * dest_name_check - verify user is permitted to send to specified port name
- * @dest: destination address
- * @m: descriptor for message to be sent
- *
- * Prevents restricted configuration commands from being issued by
- * unauthorized users.
- *
- * Returns 0 if permission is granted, otherwise errno
- */
-static int dest_name_check(struct sockaddr_tipc *dest, struct msghdr *m)
-{
- struct tipc_cfg_msg_hdr hdr;
-
- if (unlikely(dest->addrtype == TIPC_ADDR_ID))
- return 0;
- if (likely(dest->addr.name.name.type >= TIPC_RESERVED_TYPES))
- return 0;
- if (likely(dest->addr.name.name.type == TIPC_TOP_SRV))
- return 0;
- if (likely(dest->addr.name.name.type != TIPC_CFG_SRV))
- return -EACCES;
-
- if (!m->msg_iovlen || (m->msg_iov[0].iov_len < sizeof(hdr)))
- return -EMSGSIZE;
- if (copy_from_user(&hdr, m->msg_iov[0].iov_base, sizeof(hdr)))
- return -EFAULT;
- if ((ntohs(hdr.tcm_type) & 0xC000) && (!capable(CAP_NET_ADMIN)))
- return -EACCES;
-
- return 0;
-}
-
static int tipc_wait_for_sndmsg(struct socket *sock, long *timeo_p)
{
struct sock *sk = sock->sk;
@@ -897,13 +873,13 @@ static int tipc_sendmsg(struct kiocb *iocb, struct socket *sock,
struct sock *sk = sock->sk;
struct tipc_sock *tsk = tipc_sk(sk);
struct tipc_msg *mhdr = &tsk->phdr;
- struct iovec *iov = m->msg_iov;
u32 dnode, dport;
- struct sk_buff *buf;
+ struct sk_buff_head head;
+ struct sk_buff *skb;
struct tipc_name_seq *seq = &dest->addr.nameseq;
u32 mtu;
long timeo;
- int rc = -EINVAL;
+ int rc;
if (unlikely(!dest))
return -EDESTADDRREQ;
@@ -936,14 +912,11 @@ static int tipc_sendmsg(struct kiocb *iocb, struct socket *sock,
tsk->conn_instance = dest->addr.name.name.instance;
}
}
- rc = dest_name_check(dest, m);
- if (rc)
- goto exit;
timeo = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT);
if (dest->addrtype == TIPC_ADDR_MCAST) {
- rc = tipc_sendmcast(sock, seq, iov, dsz, timeo);
+ rc = tipc_sendmcast(sock, seq, m, dsz, timeo);
goto exit;
} else if (dest->addrtype == TIPC_ADDR_NAME) {
u32 type = dest->addr.name.name.type;
@@ -974,13 +947,15 @@ static int tipc_sendmsg(struct kiocb *iocb, struct socket *sock,
new_mtu:
mtu = tipc_node_get_mtu(dnode, tsk->ref);
- rc = tipc_msg_build(mhdr, iov, 0, dsz, mtu, &buf);
+ __skb_queue_head_init(&head);
+ rc = tipc_msg_build(mhdr, m, 0, dsz, mtu, &head);
if (rc < 0)
goto exit;
do {
- TIPC_SKB_CB(buf)->wakeup_pending = tsk->link_cong;
- rc = tipc_link_xmit(buf, dnode, tsk->ref);
+ skb = skb_peek(&head);
+ TIPC_SKB_CB(skb)->wakeup_pending = tsk->link_cong;
+ rc = tipc_link_xmit(&head, dnode, tsk->ref);
if (likely(rc >= 0)) {
if (sock->state != SS_READY)
sock->state = SS_CONNECTING;
@@ -994,7 +969,7 @@ new_mtu:
tsk->link_cong = 1;
rc = tipc_wait_for_sndmsg(sock, &timeo);
if (rc)
- kfree_skb_list(buf);
+ __skb_queue_purge(&head);
} while (!rc);
exit:
if (iocb)
@@ -1051,7 +1026,7 @@ static int tipc_send_stream(struct kiocb *iocb, struct socket *sock,
struct sock *sk = sock->sk;
struct tipc_sock *tsk = tipc_sk(sk);
struct tipc_msg *mhdr = &tsk->phdr;
- struct sk_buff *buf;
+ struct sk_buff_head head;
DECLARE_SOCKADDR(struct sockaddr_tipc *, dest, m->msg_name);
u32 ref = tsk->ref;
int rc = -EINVAL;
@@ -1086,12 +1061,13 @@ static int tipc_send_stream(struct kiocb *iocb, struct socket *sock,
next:
mtu = tsk->max_pkt;
send = min_t(uint, dsz - sent, TIPC_MAX_USER_MSG_SIZE);
- rc = tipc_msg_build(mhdr, m->msg_iov, sent, send, mtu, &buf);
+ __skb_queue_head_init(&head);
+ rc = tipc_msg_build(mhdr, m, sent, send, mtu, &head);
if (unlikely(rc < 0))
goto exit;
do {
if (likely(!tsk_conn_cong(tsk))) {
- rc = tipc_link_xmit(buf, dnode, ref);
+ rc = tipc_link_xmit(&head, dnode, ref);
if (likely(!rc)) {
tsk->sent_unacked++;
sent += send;
@@ -1109,7 +1085,7 @@ next:
}
rc = tipc_wait_for_sndpkt(sock, &timeo);
if (rc)
- kfree_skb_list(buf);
+ __skb_queue_purge(&head);
} while (!rc);
exit:
if (iocb)
@@ -1254,20 +1230,20 @@ static int tipc_sk_anc_data_recv(struct msghdr *m, struct tipc_msg *msg,
static void tipc_sk_send_ack(struct tipc_sock *tsk, uint ack)
{
- struct sk_buff *buf = NULL;
+ struct sk_buff *skb = NULL;
struct tipc_msg *msg;
u32 peer_port = tsk_peer_port(tsk);
u32 dnode = tsk_peer_node(tsk);
if (!tsk->connected)
return;
- buf = tipc_msg_create(CONN_MANAGER, CONN_ACK, INT_H_SIZE, 0, dnode,
+ skb = tipc_msg_create(CONN_MANAGER, CONN_ACK, INT_H_SIZE, 0, dnode,
tipc_own_addr, peer_port, tsk->ref, TIPC_OK);
- if (!buf)
+ if (!skb)
return;
- msg = buf_msg(buf);
+ msg = buf_msg(skb);
msg_set_msgcnt(msg, ack);
- tipc_link_xmit(buf, dnode, msg_link_selector(msg));
+ tipc_link_xmit_skb(skb, dnode, msg_link_selector(msg));
}
static int tipc_wait_for_rcvmsg(struct socket *sock, long *timeop)
@@ -1372,8 +1348,7 @@ restart:
sz = buf_len;
m->msg_flags |= MSG_TRUNC;
}
- res = skb_copy_datagram_iovec(buf, msg_hdr_sz(msg),
- m->msg_iov, sz);
+ res = skb_copy_datagram_msg(buf, msg_hdr_sz(msg), m, sz);
if (res)
goto exit;
res = sz;
@@ -1473,8 +1448,8 @@ restart:
needed = (buf_len - sz_copied);
sz_to_copy = (sz <= needed) ? sz : needed;
- res = skb_copy_datagram_iovec(buf, msg_hdr_sz(msg) + offset,
- m->msg_iov, sz_to_copy);
+ res = skb_copy_datagram_msg(buf, msg_hdr_sz(msg) + offset,
+ m, sz_to_copy);
if (res)
goto exit;
@@ -1556,7 +1531,7 @@ static void tipc_data_ready(struct sock *sk)
* @tsk: TIPC socket
* @msg: message
*
- * Returns 0 (TIPC_OK) if everyting ok, -TIPC_ERR_NO_PORT otherwise
+ * Returns 0 (TIPC_OK) if everything ok, -TIPC_ERR_NO_PORT otherwise
*/
static int filter_connect(struct tipc_sock *tsk, struct sk_buff **buf)
{
@@ -1723,20 +1698,20 @@ static int filter_rcv(struct sock *sk, struct sk_buff *buf)
/**
* tipc_backlog_rcv - handle incoming message from backlog queue
* @sk: socket
- * @buf: message
+ * @skb: message
*
* Caller must hold socket lock, but not port lock.
*
* Returns 0
*/
-static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *buf)
+static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *skb)
{
int rc;
u32 onode;
struct tipc_sock *tsk = tipc_sk(sk);
- uint truesize = buf->truesize;
+ uint truesize = skb->truesize;
- rc = filter_rcv(sk, buf);
+ rc = filter_rcv(sk, skb);
if (likely(!rc)) {
if (atomic_read(&tsk->dupl_rcvcnt) < TIPC_CONN_OVERLOAD_LIMIT)
@@ -1744,25 +1719,25 @@ static int tipc_backlog_rcv(struct sock *sk, struct sk_buff *buf)
return 0;
}
- if ((rc < 0) && !tipc_msg_reverse(buf, &onode, -rc))
+ if ((rc < 0) && !tipc_msg_reverse(skb, &onode, -rc))
return 0;
- tipc_link_xmit(buf, onode, 0);
+ tipc_link_xmit_skb(skb, onode, 0);
return 0;
}
/**
* tipc_sk_rcv - handle incoming message
- * @buf: buffer containing arriving message
+ * @skb: buffer containing arriving message
* Consumes buffer
* Returns 0 if success, or errno: -EHOSTUNREACH
*/
-int tipc_sk_rcv(struct sk_buff *buf)
+int tipc_sk_rcv(struct sk_buff *skb)
{
struct tipc_sock *tsk;
struct sock *sk;
- u32 dport = msg_destport(buf_msg(buf));
+ u32 dport = msg_destport(buf_msg(skb));
int rc = TIPC_OK;
uint limit;
u32 dnode;
@@ -1770,7 +1745,7 @@ int tipc_sk_rcv(struct sk_buff *buf)
/* Validate destination and message */
tsk = tipc_sk_get(dport);
if (unlikely(!tsk)) {
- rc = tipc_msg_eval(buf, &dnode);
+ rc = tipc_msg_eval(skb, &dnode);
goto exit;
}
sk = &tsk->sk;
@@ -1779,12 +1754,12 @@ int tipc_sk_rcv(struct sk_buff *buf)
spin_lock_bh(&sk->sk_lock.slock);
if (!sock_owned_by_user(sk)) {
- rc = filter_rcv(sk, buf);
+ rc = filter_rcv(sk, skb);
} else {
if (sk->sk_backlog.len == 0)
atomic_set(&tsk->dupl_rcvcnt, 0);
- limit = rcvbuf_limit(sk, buf) + atomic_read(&tsk->dupl_rcvcnt);
- if (sk_add_backlog(sk, buf, limit))
+ limit = rcvbuf_limit(sk, skb) + atomic_read(&tsk->dupl_rcvcnt);
+ if (sk_add_backlog(sk, skb, limit))
rc = -TIPC_ERR_OVERLOAD;
}
spin_unlock_bh(&sk->sk_lock.slock);
@@ -1792,10 +1767,10 @@ int tipc_sk_rcv(struct sk_buff *buf)
if (likely(!rc))
return 0;
exit:
- if ((rc < 0) && !tipc_msg_reverse(buf, &dnode, -rc))
+ if ((rc < 0) && !tipc_msg_reverse(skb, &dnode, -rc))
return -EHOSTUNREACH;
- tipc_link_xmit(buf, dnode, 0);
+ tipc_link_xmit_skb(skb, dnode, 0);
return (rc < 0) ? -EHOSTUNREACH : 0;
}
@@ -2053,7 +2028,7 @@ static int tipc_shutdown(struct socket *sock, int how)
{
struct sock *sk = sock->sk;
struct tipc_sock *tsk = tipc_sk(sk);
- struct sk_buff *buf;
+ struct sk_buff *skb;
u32 dnode;
int res;
@@ -2068,23 +2043,23 @@ static int tipc_shutdown(struct socket *sock, int how)
restart:
/* Disconnect and send a 'FIN+' or 'FIN-' message to peer */
- buf = __skb_dequeue(&sk->sk_receive_queue);
- if (buf) {
- if (TIPC_SKB_CB(buf)->handle != NULL) {
- kfree_skb(buf);
+ skb = __skb_dequeue(&sk->sk_receive_queue);
+ if (skb) {
+ if (TIPC_SKB_CB(skb)->handle != NULL) {
+ kfree_skb(skb);
goto restart;
}
- if (tipc_msg_reverse(buf, &dnode, TIPC_CONN_SHUTDOWN))
- tipc_link_xmit(buf, dnode, tsk->ref);
+ if (tipc_msg_reverse(skb, &dnode, TIPC_CONN_SHUTDOWN))
+ tipc_link_xmit_skb(skb, dnode, tsk->ref);
tipc_node_remove_conn(dnode, tsk->ref);
} else {
dnode = tsk_peer_node(tsk);
- buf = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE,
+ skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE,
TIPC_CONN_MSG, SHORT_H_SIZE,
0, dnode, tipc_own_addr,
tsk_peer_port(tsk),
tsk->ref, TIPC_CONN_SHUTDOWN);
- tipc_link_xmit(buf, dnode, tsk->ref);
+ tipc_link_xmit_skb(skb, dnode, tsk->ref);
}
tsk->connected = 0;
sock->state = SS_DISCONNECTING;
@@ -2113,7 +2088,7 @@ static void tipc_sk_timeout(unsigned long ref)
{
struct tipc_sock *tsk;
struct sock *sk;
- struct sk_buff *buf = NULL;
+ struct sk_buff *skb = NULL;
u32 peer_port, peer_node;
tsk = tipc_sk_get(ref);
@@ -2131,20 +2106,20 @@ static void tipc_sk_timeout(unsigned long ref)
if (tsk->probing_state == TIPC_CONN_PROBING) {
/* Previous probe not answered -> self abort */
- buf = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG,
+ skb = tipc_msg_create(TIPC_CRITICAL_IMPORTANCE, TIPC_CONN_MSG,
SHORT_H_SIZE, 0, tipc_own_addr,
peer_node, ref, peer_port,
TIPC_ERR_NO_PORT);
} else {
- buf = tipc_msg_create(CONN_MANAGER, CONN_PROBE, INT_H_SIZE,
+ skb = tipc_msg_create(CONN_MANAGER, CONN_PROBE, INT_H_SIZE,
0, peer_node, tipc_own_addr,
peer_port, ref, TIPC_OK);
tsk->probing_state = TIPC_CONN_PROBING;
k_start_timer(&tsk->timer, tsk->probing_interval);
}
bh_unlock_sock(sk);
- if (buf)
- tipc_link_xmit(buf, peer_node, ref);
+ if (skb)
+ tipc_link_xmit_skb(skb, peer_node, ref);
exit:
tipc_sk_put(tsk);
}
@@ -2802,3 +2777,233 @@ void tipc_socket_stop(void)
sock_unregister(tipc_family_ops.family);
proto_unregister(&tipc_proto);
}
+
+/* Caller should hold socket lock for the passed tipc socket. */
+static int __tipc_nl_add_sk_con(struct sk_buff *skb, struct tipc_sock *tsk)
+{
+ u32 peer_node;
+ u32 peer_port;
+ struct nlattr *nest;
+
+ peer_node = tsk_peer_node(tsk);
+ peer_port = tsk_peer_port(tsk);
+
+ nest = nla_nest_start(skb, TIPC_NLA_SOCK_CON);
+
+ if (nla_put_u32(skb, TIPC_NLA_CON_NODE, peer_node))
+ goto msg_full;
+ if (nla_put_u32(skb, TIPC_NLA_CON_SOCK, peer_port))
+ goto msg_full;
+
+ if (tsk->conn_type != 0) {
+ if (nla_put_flag(skb, TIPC_NLA_CON_FLAG))
+ goto msg_full;
+ if (nla_put_u32(skb, TIPC_NLA_CON_TYPE, tsk->conn_type))
+ goto msg_full;
+ if (nla_put_u32(skb, TIPC_NLA_CON_INST, tsk->conn_instance))
+ goto msg_full;
+ }
+ nla_nest_end(skb, nest);
+
+ return 0;
+
+msg_full:
+ nla_nest_cancel(skb, nest);
+
+ return -EMSGSIZE;
+}
+
+/* Caller should hold socket lock for the passed tipc socket. */
+static int __tipc_nl_add_sk(struct sk_buff *skb, struct netlink_callback *cb,
+ struct tipc_sock *tsk)
+{
+ int err;
+ void *hdr;
+ struct nlattr *attrs;
+
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
+ &tipc_genl_v2_family, NLM_F_MULTI, TIPC_NL_SOCK_GET);
+ if (!hdr)
+ goto msg_cancel;
+
+ attrs = nla_nest_start(skb, TIPC_NLA_SOCK);
+ if (!attrs)
+ goto genlmsg_cancel;
+ if (nla_put_u32(skb, TIPC_NLA_SOCK_REF, tsk->ref))
+ goto attr_msg_cancel;
+ if (nla_put_u32(skb, TIPC_NLA_SOCK_ADDR, tipc_own_addr))
+ goto attr_msg_cancel;
+
+ if (tsk->connected) {
+ err = __tipc_nl_add_sk_con(skb, tsk);
+ if (err)
+ goto attr_msg_cancel;
+ } else if (!list_empty(&tsk->publications)) {
+ if (nla_put_flag(skb, TIPC_NLA_SOCK_HAS_PUBL))
+ goto attr_msg_cancel;
+ }
+ nla_nest_end(skb, attrs);
+ genlmsg_end(skb, hdr);
+
+ return 0;
+
+attr_msg_cancel:
+ nla_nest_cancel(skb, attrs);
+genlmsg_cancel:
+ genlmsg_cancel(skb, hdr);
+msg_cancel:
+ return -EMSGSIZE;
+}
+
+int tipc_nl_sk_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int err;
+ struct tipc_sock *tsk;
+ u32 prev_ref = cb->args[0];
+ u32 ref = prev_ref;
+
+ tsk = tipc_sk_get_next(&ref);
+ for (; tsk; tsk = tipc_sk_get_next(&ref)) {
+ lock_sock(&tsk->sk);
+ err = __tipc_nl_add_sk(skb, cb, tsk);
+ release_sock(&tsk->sk);
+ tipc_sk_put(tsk);
+ if (err)
+ break;
+
+ prev_ref = ref;
+ }
+
+ cb->args[0] = prev_ref;
+
+ return skb->len;
+}
+
+/* Caller should hold socket lock for the passed tipc socket. */
+static int __tipc_nl_add_sk_publ(struct sk_buff *skb,
+ struct netlink_callback *cb,
+ struct publication *publ)
+{
+ void *hdr;
+ struct nlattr *attrs;
+
+ hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
+ &tipc_genl_v2_family, NLM_F_MULTI, TIPC_NL_PUBL_GET);
+ if (!hdr)
+ goto msg_cancel;
+
+ attrs = nla_nest_start(skb, TIPC_NLA_PUBL);
+ if (!attrs)
+ goto genlmsg_cancel;
+
+ if (nla_put_u32(skb, TIPC_NLA_PUBL_KEY, publ->key))
+ goto attr_msg_cancel;
+ if (nla_put_u32(skb, TIPC_NLA_PUBL_TYPE, publ->type))
+ goto attr_msg_cancel;
+ if (nla_put_u32(skb, TIPC_NLA_PUBL_LOWER, publ->lower))
+ goto attr_msg_cancel;
+ if (nla_put_u32(skb, TIPC_NLA_PUBL_UPPER, publ->upper))
+ goto attr_msg_cancel;
+
+ nla_nest_end(skb, attrs);
+ genlmsg_end(skb, hdr);
+
+ return 0;
+
+attr_msg_cancel:
+ nla_nest_cancel(skb, attrs);
+genlmsg_cancel:
+ genlmsg_cancel(skb, hdr);
+msg_cancel:
+ return -EMSGSIZE;
+}
+
+/* Caller should hold socket lock for the passed tipc socket. */
+static int __tipc_nl_list_sk_publ(struct sk_buff *skb,
+ struct netlink_callback *cb,
+ struct tipc_sock *tsk, u32 *last_publ)
+{
+ int err;
+ struct publication *p;
+
+ if (*last_publ) {
+ list_for_each_entry(p, &tsk->publications, pport_list) {
+ if (p->key == *last_publ)
+ break;
+ }
+ if (p->key != *last_publ) {
+ /* We never set seq or call nl_dump_check_consistent()
+ * this means that setting prev_seq here will cause the
+ * consistence check to fail in the netlink callback
+ * handler. Resulting in the last NLMSG_DONE message
+ * having the NLM_F_DUMP_INTR flag set.
+ */
+ cb->prev_seq = 1;
+ *last_publ = 0;
+ return -EPIPE;
+ }
+ } else {
+ p = list_first_entry(&tsk->publications, struct publication,
+ pport_list);
+ }
+
+ list_for_each_entry_from(p, &tsk->publications, pport_list) {
+ err = __tipc_nl_add_sk_publ(skb, cb, p);
+ if (err) {
+ *last_publ = p->key;
+ return err;
+ }
+ }
+ *last_publ = 0;
+
+ return 0;
+}
+
+int tipc_nl_publ_dump(struct sk_buff *skb, struct netlink_callback *cb)
+{
+ int err;
+ u32 tsk_ref = cb->args[0];
+ u32 last_publ = cb->args[1];
+ u32 done = cb->args[2];
+ struct tipc_sock *tsk;
+
+ if (!tsk_ref) {
+ struct nlattr **attrs;
+ struct nlattr *sock[TIPC_NLA_SOCK_MAX + 1];
+
+ err = tipc_nlmsg_parse(cb->nlh, &attrs);
+ if (err)
+ return err;
+
+ err = nla_parse_nested(sock, TIPC_NLA_SOCK_MAX,
+ attrs[TIPC_NLA_SOCK],
+ tipc_nl_sock_policy);
+ if (err)
+ return err;
+
+ if (!sock[TIPC_NLA_SOCK_REF])
+ return -EINVAL;
+
+ tsk_ref = nla_get_u32(sock[TIPC_NLA_SOCK_REF]);
+ }
+
+ if (done)
+ return 0;
+
+ tsk = tipc_sk_get(tsk_ref);
+ if (!tsk)
+ return -EINVAL;
+
+ lock_sock(&tsk->sk);
+ err = __tipc_nl_list_sk_publ(skb, cb, tsk, &last_publ);
+ if (!err)
+ done = 1;
+ release_sock(&tsk->sk);
+ tipc_sk_put(tsk);
+
+ cb->args[0] = tsk_ref;
+ cb->args[1] = last_publ;
+ cb->args[2] = done;
+
+ return skb->len;
+}
diff --git a/net/tipc/socket.h b/net/tipc/socket.h
index baa43d03901e..d34089387006 100644
--- a/net/tipc/socket.h
+++ b/net/tipc/socket.h
@@ -36,6 +36,7 @@
#define _TIPC_SOCK_H
#include <net/sock.h>
+#include <net/genetlink.h>
#define TIPC_CONNACK_INTV 256
#define TIPC_FLOWCTRL_WIN (TIPC_CONNACK_INTV * 2)
@@ -47,5 +48,7 @@ void tipc_sk_mcast_rcv(struct sk_buff *buf);
void tipc_sk_reinit(void);
int tipc_sk_ref_table_init(u32 requested_size, u32 start);
void tipc_sk_ref_table_stop(void);
+int tipc_nl_sk_dump(struct sk_buff *skb, struct netlink_callback *cb);
+int tipc_nl_publ_dump(struct sk_buff *skb, struct netlink_callback *cb);
#endif
diff --git a/net/tipc/subscr.c b/net/tipc/subscr.c
index 31b5cb232a43..0344206b984f 100644
--- a/net/tipc/subscr.c
+++ b/net/tipc/subscr.c
@@ -305,7 +305,6 @@ static int subscr_subscribe(struct tipc_subscr *s,
kfree(sub);
return -EINVAL;
}
- INIT_LIST_HEAD(&sub->nameseq_list);
list_add(&sub->subscription_list, &subscriber->subscription_list);
sub->subscriber = subscriber;
sub->swap = swap;
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index e96884380732..8e1b10274b02 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1516,7 +1516,7 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock,
skb_put(skb, len - data_len);
skb->data_len = data_len;
skb->len = len;
- err = skb_copy_datagram_from_iovec(skb, 0, msg->msg_iov, 0, len);
+ err = skb_copy_datagram_from_iter(skb, 0, &msg->msg_iter, len);
if (err)
goto out_free;
@@ -1694,8 +1694,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
skb_put(skb, size - data_len);
skb->data_len = data_len;
skb->len = size;
- err = skb_copy_datagram_from_iovec(skb, 0, msg->msg_iov,
- sent, size);
+ err = skb_copy_datagram_from_iter(skb, 0, &msg->msg_iter, size);
if (err) {
kfree_skb(skb);
goto out_err;
@@ -1825,7 +1824,7 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock,
else if (size < skb->len - skip)
msg->msg_flags |= MSG_TRUNC;
- err = skb_copy_datagram_iovec(skb, skip, msg->msg_iov, size);
+ err = skb_copy_datagram_msg(skb, skip, msg, size);
if (err)
goto out_free;
@@ -2030,8 +2029,8 @@ again:
}
chunk = min_t(unsigned int, unix_skb_len(skb) - skip, size);
- if (skb_copy_datagram_iovec(skb, UNIXCB(skb).consumed + skip,
- msg->msg_iov, chunk)) {
+ if (skb_copy_datagram_msg(skb, UNIXCB(skb).consumed + skip,
+ msg, chunk)) {
if (copied == 0)
copied = -EFAULT;
break;
diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
index 85d232bed87d..1d0e39c9a3e2 100644
--- a/net/vmw_vsock/af_vsock.c
+++ b/net/vmw_vsock/af_vsock.c
@@ -1013,7 +1013,7 @@ static int vsock_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock,
goto out;
}
- err = transport->dgram_enqueue(vsk, remote_addr, msg->msg_iov, len);
+ err = transport->dgram_enqueue(vsk, remote_addr, msg, len);
out:
release_sock(sk);
@@ -1617,7 +1617,7 @@ static int vsock_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
*/
written = transport->stream_enqueue(
- vsk, msg->msg_iov,
+ vsk, msg,
len - total_written);
if (written < 0) {
err = -ENOMEM;
@@ -1739,7 +1739,7 @@ vsock_stream_recvmsg(struct kiocb *kiocb,
break;
read = transport->stream_dequeue(
- vsk, msg->msg_iov,
+ vsk, msg,
len - copied, flags);
if (read < 0) {
err = -ENOMEM;
diff --git a/net/vmw_vsock/vmci_transport.c b/net/vmw_vsock/vmci_transport.c
index 9bb63ffec4f2..02d2e5229240 100644
--- a/net/vmw_vsock/vmci_transport.c
+++ b/net/vmw_vsock/vmci_transport.c
@@ -1697,7 +1697,7 @@ static int vmci_transport_dgram_bind(struct vsock_sock *vsk,
static int vmci_transport_dgram_enqueue(
struct vsock_sock *vsk,
struct sockaddr_vm *remote_addr,
- struct iovec *iov,
+ struct msghdr *msg,
size_t len)
{
int err;
@@ -1714,7 +1714,7 @@ static int vmci_transport_dgram_enqueue(
if (!dg)
return -ENOMEM;
- memcpy_fromiovec(VMCI_DG_PAYLOAD(dg), iov, len);
+ memcpy_from_msg(VMCI_DG_PAYLOAD(dg), msg, len);
dg->dst = vmci_make_handle(remote_addr->svm_cid,
remote_addr->svm_port);
@@ -1773,8 +1773,7 @@ static int vmci_transport_dgram_dequeue(struct kiocb *kiocb,
}
/* Place the datagram payload in the user's iovec. */
- err = skb_copy_datagram_iovec(skb, sizeof(*dg), msg->msg_iov,
- payload_len);
+ err = skb_copy_datagram_msg(skb, sizeof(*dg), msg, payload_len);
if (err)
goto out;
@@ -1836,22 +1835,23 @@ static int vmci_transport_connect(struct vsock_sock *vsk)
static ssize_t vmci_transport_stream_dequeue(
struct vsock_sock *vsk,
- struct iovec *iov,
+ struct msghdr *msg,
size_t len,
int flags)
{
if (flags & MSG_PEEK)
- return vmci_qpair_peekv(vmci_trans(vsk)->qpair, iov, len, 0);
+ return vmci_qpair_peekv(vmci_trans(vsk)->qpair, msg, len, 0);
else
- return vmci_qpair_dequev(vmci_trans(vsk)->qpair, iov, len, 0);
+ return vmci_qpair_dequev(vmci_trans(vsk)->qpair, msg, len, 0);
}
static ssize_t vmci_transport_stream_enqueue(
struct vsock_sock *vsk,
- struct iovec *iov,
+ struct msghdr *msg,
size_t len)
{
- return vmci_qpair_enquev(vmci_trans(vsk)->qpair, iov, len, 0);
+ /* XXX: stripping const */
+ return vmci_qpair_enquev(vmci_trans(vsk)->qpair, (struct iovec *)msg->msg_iter.iov, len, 0);
}
static s64 vmci_transport_stream_has_data(struct vsock_sock *vsk)
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig
index 29c8675f9a11..22ba971741e5 100644
--- a/net/wireless/Kconfig
+++ b/net/wireless/Kconfig
@@ -175,7 +175,7 @@ config CFG80211_INTERNAL_REGDB
Most distributions have a CRDA package. So if unsure, say N.
config CFG80211_WEXT
- bool "cfg80211 wireless extensions compatibility"
+ bool
depends on CFG80211
select WEXT_CORE
help
diff --git a/net/wireless/Makefile b/net/wireless/Makefile
index a761670af31d..4c9e39f04ef8 100644
--- a/net/wireless/Makefile
+++ b/net/wireless/Makefile
@@ -10,7 +10,7 @@ obj-$(CONFIG_WEXT_SPY) += wext-spy.o
obj-$(CONFIG_WEXT_PRIV) += wext-priv.o
cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o
-cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o
+cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o ap.o trace.o ocb.o
cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o
cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o
cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 72d81e2154d5..85506f1d0789 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -115,7 +115,7 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
EXPORT_SYMBOL(cfg80211_chandef_valid);
static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
- int *pri40, int *pri80)
+ u32 *pri40, u32 *pri80)
{
int tmp;
@@ -366,6 +366,7 @@ int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
break;
case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_OCB:
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_AP_VLAN:
@@ -892,6 +893,13 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
*radar_detect |= BIT(wdev->chandef.width);
}
return;
+ case NL80211_IFTYPE_OCB:
+ if (wdev->chandef.chan) {
+ *chan = wdev->chandef.chan;
+ *chanmode = CHAN_MODE_SHARED;
+ return;
+ }
+ break;
case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_AP_VLAN:
case NL80211_IFTYPE_WDS:
diff --git a/net/wireless/core.c b/net/wireless/core.c
index f52a4cd7017c..53dda7728f86 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -86,11 +86,11 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx)
return &rdev->wiphy;
}
-int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
- char *newname)
+static int cfg80211_dev_check_name(struct cfg80211_registered_device *rdev,
+ const char *newname)
{
struct cfg80211_registered_device *rdev2;
- int wiphy_idx, taken = -1, result, digits;
+ int wiphy_idx, taken = -1, digits;
ASSERT_RTNL();
@@ -109,15 +109,28 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
return -EINVAL;
}
+ /* Ensure another device does not already have this name. */
+ list_for_each_entry(rdev2, &cfg80211_rdev_list, list)
+ if (strcmp(newname, wiphy_name(&rdev2->wiphy)) == 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
+ char *newname)
+{
+ int result;
+
+ ASSERT_RTNL();
/* Ignore nop renames */
- if (strcmp(newname, dev_name(&rdev->wiphy.dev)) == 0)
+ if (strcmp(newname, wiphy_name(&rdev->wiphy)) == 0)
return 0;
- /* Ensure another device does not already have this name. */
- list_for_each_entry(rdev2, &cfg80211_rdev_list, list)
- if (strcmp(newname, dev_name(&rdev2->wiphy.dev)) == 0)
- return -EINVAL;
+ result = cfg80211_dev_check_name(rdev, newname);
+ if (result < 0)
+ return result;
result = device_rename(&rdev->wiphy.dev, newname);
if (result)
@@ -309,7 +322,8 @@ static void cfg80211_destroy_iface_wk(struct work_struct *work)
/* exported functions */
-struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
+struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
+ const char *requested_name)
{
static atomic_t wiphy_counter = ATOMIC_INIT(0);
@@ -346,7 +360,31 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
rdev->wiphy_idx--;
/* give it a proper name */
- dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx);
+ if (requested_name && requested_name[0]) {
+ int rv;
+
+ rtnl_lock();
+ rv = cfg80211_dev_check_name(rdev, requested_name);
+
+ if (rv < 0) {
+ rtnl_unlock();
+ goto use_default_name;
+ }
+
+ rv = dev_set_name(&rdev->wiphy.dev, "%s", requested_name);
+ rtnl_unlock();
+ if (rv)
+ goto use_default_name;
+ } else {
+use_default_name:
+ /* NOTE: This is *probably* safe w/out holding rtnl because of
+ * the restrictions on phy names. Probably this call could
+ * fail if some other part of the kernel (re)named a device
+ * phyX. But, might should add some locking and check return
+ * value, and use a different name if this one exists?
+ */
+ dev_set_name(&rdev->wiphy.dev, PHY_NAME "%d", rdev->wiphy_idx);
+ }
INIT_LIST_HEAD(&rdev->wdev_list);
INIT_LIST_HEAD(&rdev->beacon_registrations);
@@ -406,7 +444,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
return &rdev->wiphy;
}
-EXPORT_SYMBOL(wiphy_new);
+EXPORT_SYMBOL(wiphy_new_nm);
static int wiphy_verify_combinations(struct wiphy *wiphy)
{
@@ -503,6 +541,24 @@ int wiphy_register(struct wiphy *wiphy)
!wiphy->wowlan->tcp))
return -EINVAL;
#endif
+ if (WARN_ON((wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) &&
+ (!rdev->ops->tdls_channel_switch ||
+ !rdev->ops->tdls_cancel_channel_switch)))
+ return -EINVAL;
+
+ /*
+ * if a wiphy has unsupported modes for regulatory channel enforcement,
+ * opt-out of enforcement checking
+ */
+ if (wiphy->interface_modes & ~(BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_P2P_GO) |
+ BIT(NL80211_IFTYPE_ADHOC) |
+ BIT(NL80211_IFTYPE_P2P_DEVICE) |
+ BIT(NL80211_IFTYPE_AP_VLAN) |
+ BIT(NL80211_IFTYPE_MONITOR)))
+ wiphy->regulatory_flags |= REGULATORY_IGNORE_STALE_KICKOFF;
if (WARN_ON(wiphy->coalesce &&
(!wiphy->coalesce->n_rules ||
@@ -831,7 +887,22 @@ void __cfg80211_leave(struct cfg80211_registered_device *rdev,
case NL80211_IFTYPE_P2P_GO:
__cfg80211_stop_ap(rdev, dev, true);
break;
- default:
+ case NL80211_IFTYPE_OCB:
+ __cfg80211_leave_ocb(rdev, dev);
+ break;
+ case NL80211_IFTYPE_WDS:
+ /* must be handled by mac80211/driver, has no APIs */
+ break;
+ case NL80211_IFTYPE_P2P_DEVICE:
+ /* cannot happen, has no netdev */
+ break;
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_MONITOR:
+ /* nothing to do */
+ break;
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NUM_NL80211_IFTYPES:
+ /* invalid */
break;
}
}
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 7e3a3cef7df9..faa5b1609aae 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -111,6 +111,7 @@ cfg80211_rdev_free_wowlan(struct cfg80211_registered_device *rdev)
rdev->wiphy.wowlan_config->tcp->sock)
sock_release(rdev->wiphy.wowlan_config->tcp->sock);
kfree(rdev->wiphy.wowlan_config->tcp);
+ kfree(rdev->wiphy.wowlan_config->nd_config);
kfree(rdev->wiphy.wowlan_config);
#endif
}
@@ -290,6 +291,18 @@ int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
struct cfg80211_chan_def *chandef);
+/* OCB */
+int __cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ocb_setup *setup);
+int cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ocb_setup *setup);
+int __cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev);
+int cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev);
+
/* AP */
int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev,
struct net_device *dev, bool notify);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 5839c85075f1..a17d6bc6b22c 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -209,7 +209,7 @@ cfg80211_get_dev_from_info(struct net *netns, struct genl_info *info)
}
/* policy for the attributes */
-static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
+static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
.len = 20-1 },
@@ -388,13 +388,14 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_MAC_HINT] = { .len = ETH_ALEN },
[NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 },
[NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 },
- [NL80211_ATTR_IFACE_SOCKET_OWNER] = { .type = NLA_FLAG },
+ [NL80211_ATTR_SOCKET_OWNER] = { .type = NLA_FLAG },
[NL80211_ATTR_CSA_C_OFFSETS_TX] = { .type = NLA_BINARY },
[NL80211_ATTR_USE_RRM] = { .type = NLA_FLAG },
[NL80211_ATTR_TSID] = { .type = NLA_U8 },
[NL80211_ATTR_USER_PRIO] = { .type = NLA_U8 },
[NL80211_ATTR_ADMITTED_TIME] = { .type = NLA_U16 },
[NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 },
+ [NL80211_ATTR_MAC_MASK] = { .len = ETH_ALEN },
};
/* policy for the key attributes */
@@ -428,6 +429,7 @@ nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG },
[NL80211_WOWLAN_TRIG_TCP_CONNECTION] = { .type = NLA_NESTED },
+ [NL80211_WOWLAN_TRIG_NET_DETECT] = { .type = NLA_NESTED },
};
static const struct nla_policy
@@ -884,7 +886,12 @@ static int nl80211_key_allowed(struct wireless_dev *wdev)
if (!wdev->current_bss)
return -ENOLINK;
break;
- default:
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NL80211_IFTYPE_OCB:
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_P2P_DEVICE:
+ case NL80211_IFTYPE_WDS:
+ case NUM_NL80211_IFTYPES:
return -EINVAL;
}
@@ -1083,6 +1090,8 @@ static int nl80211_send_wowlan(struct sk_buff *msg,
if (large && nl80211_send_wowlan_tcp_caps(rdev, msg))
return -ENOBUFS;
+ /* TODO: send wowlan net detect */
+
nla_nest_end(msg, nl_wowlan);
return 0;
@@ -1514,8 +1523,8 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)
CMD(channel_switch, CHANNEL_SWITCH);
CMD(set_qos_map, SET_QOS_MAP);
- if (rdev->wiphy.flags &
- WIPHY_FLAG_SUPPORTS_WMM_ADMISSION)
+ if (rdev->wiphy.features &
+ NL80211_FEATURE_SUPPORTS_WMM_ADMISSION)
CMD(add_tx_ts, ADD_TX_TS);
}
/* add into the if now */
@@ -2308,7 +2317,8 @@ static inline u64 wdev_id(struct wireless_dev *wdev)
static int nl80211_send_chandef(struct sk_buff *msg,
const struct cfg80211_chan_def *chandef)
{
- WARN_ON(!cfg80211_chandef_valid(chandef));
+ if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+ return -EINVAL;
if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
chandef->chan->center_freq))
@@ -2336,12 +2346,16 @@ static int nl80211_send_chandef(struct sk_buff *msg,
static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
struct cfg80211_registered_device *rdev,
- struct wireless_dev *wdev)
+ struct wireless_dev *wdev, bool removal)
{
struct net_device *dev = wdev->netdev;
+ u8 cmd = NL80211_CMD_NEW_INTERFACE;
void *hdr;
- hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_INTERFACE);
+ if (removal)
+ cmd = NL80211_CMD_DEL_INTERFACE;
+
+ hdr = nl80211hdr_put(msg, portid, seq, flags, cmd);
if (!hdr)
return -1;
@@ -2408,7 +2422,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
}
if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
- rdev, wdev) < 0) {
+ rdev, wdev, false) < 0) {
goto out;
}
if_idx++;
@@ -2436,7 +2450,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
return -ENOMEM;
if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
- rdev, wdev) < 0) {
+ rdev, wdev, false) < 0) {
nlmsg_free(msg);
return -ENOBUFS;
}
@@ -2582,7 +2596,7 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct vif_params params;
struct wireless_dev *wdev;
- struct sk_buff *msg;
+ struct sk_buff *msg, *event;
int err;
enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
u32 flags;
@@ -2605,7 +2619,9 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
!(rdev->wiphy.interface_modes & (1 << type)))
return -EOPNOTSUPP;
- if (type == NL80211_IFTYPE_P2P_DEVICE && info->attrs[NL80211_ATTR_MAC]) {
+ if ((type == NL80211_IFTYPE_P2P_DEVICE ||
+ rdev->wiphy.features & NL80211_FEATURE_MAC_ON_CREATE) &&
+ info->attrs[NL80211_ATTR_MAC]) {
nla_memcpy(params.macaddr, info->attrs[NL80211_ATTR_MAC],
ETH_ALEN);
if (!is_valid_ether_addr(params.macaddr))
@@ -2634,12 +2650,15 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
wdev = rdev_add_virtual_intf(rdev,
nla_data(info->attrs[NL80211_ATTR_IFNAME]),
type, err ? NULL : &flags, &params);
- if (IS_ERR(wdev)) {
+ if (WARN_ON(!wdev)) {
+ nlmsg_free(msg);
+ return -EPROTO;
+ } else if (IS_ERR(wdev)) {
nlmsg_free(msg);
return PTR_ERR(wdev);
}
- if (info->attrs[NL80211_ATTR_IFACE_SOCKET_OWNER])
+ if (info->attrs[NL80211_ATTR_SOCKET_OWNER])
wdev->owner_nlportid = info->snd_portid;
switch (type) {
@@ -2675,11 +2694,25 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
}
if (nl80211_send_iface(msg, info->snd_portid, info->snd_seq, 0,
- rdev, wdev) < 0) {
+ rdev, wdev, false) < 0) {
nlmsg_free(msg);
return -ENOBUFS;
}
+ event = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (event) {
+ if (nl80211_send_iface(event, 0, 0, 0,
+ rdev, wdev, false) < 0) {
+ nlmsg_free(event);
+ goto out;
+ }
+
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy),
+ event, 0, NL80211_MCGRP_CONFIG,
+ GFP_KERNEL);
+ }
+
+out:
return genlmsg_reply(msg, info);
}
@@ -2687,10 +2720,18 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct wireless_dev *wdev = info->user_ptr[1];
+ struct sk_buff *msg;
+ int status;
if (!rdev->ops->del_virtual_intf)
return -EOPNOTSUPP;
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (msg && nl80211_send_iface(msg, 0, 0, 0, rdev, wdev, true) < 0) {
+ nlmsg_free(msg);
+ msg = NULL;
+ }
+
/*
* If we remove a wireless device without a netdev then clear
* user_ptr[1] so that nl80211_post_doit won't dereference it
@@ -2701,7 +2742,15 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
if (!wdev->netdev)
info->user_ptr[1] = NULL;
- return rdev_del_virtual_intf(rdev, wdev);
+ status = rdev_del_virtual_intf(rdev, wdev);
+ if (status >= 0 && msg)
+ genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy),
+ msg, 0, NL80211_MCGRP_CONFIG,
+ GFP_KERNEL);
+ else
+ nlmsg_free(msg);
+
+ return status;
}
static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info)
@@ -4398,10 +4447,12 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1];
- u8 *mac_addr = NULL;
+ struct station_del_parameters params;
+
+ memset(&params, 0, sizeof(params));
if (info->attrs[NL80211_ATTR_MAC])
- mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ params.mac = nla_data(info->attrs[NL80211_ATTR_MAC]);
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
@@ -4412,7 +4463,28 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
if (!rdev->ops->del_station)
return -EOPNOTSUPP;
- return rdev_del_station(rdev, dev, mac_addr);
+ if (info->attrs[NL80211_ATTR_MGMT_SUBTYPE]) {
+ params.subtype =
+ nla_get_u8(info->attrs[NL80211_ATTR_MGMT_SUBTYPE]);
+ if (params.subtype != IEEE80211_STYPE_DISASSOC >> 4 &&
+ params.subtype != IEEE80211_STYPE_DEAUTH >> 4)
+ return -EINVAL;
+ } else {
+ /* Default to Deauthentication frame */
+ params.subtype = IEEE80211_STYPE_DEAUTH >> 4;
+ }
+
+ if (info->attrs[NL80211_ATTR_REASON_CODE]) {
+ params.reason_code =
+ nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
+ if (params.reason_code == 0)
+ return -EINVAL; /* 0 is reserved */
+ } else {
+ /* Default to reason code 2 */
+ params.reason_code = WLAN_REASON_PREV_AUTH_NOT_VALID;
+ }
+
+ return rdev_del_station(rdev, dev, &params);
}
static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq,
@@ -4423,7 +4495,7 @@ static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq,
void *hdr;
struct nlattr *pinfoattr;
- hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_STATION);
+ hdr = nl80211hdr_put(msg, portid, seq, flags, NL80211_CMD_NEW_MPATH);
if (!hdr)
return -1;
@@ -4624,6 +4696,96 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
return rdev_del_mpath(rdev, dev, dst);
}
+static int nl80211_get_mpp(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ int err;
+ struct net_device *dev = info->user_ptr[1];
+ struct mpath_info pinfo;
+ struct sk_buff *msg;
+ u8 *dst = NULL;
+ u8 mpp[ETH_ALEN];
+
+ memset(&pinfo, 0, sizeof(pinfo));
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ if (!rdev->ops->get_mpp)
+ return -EOPNOTSUPP;
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
+ return -EOPNOTSUPP;
+
+ err = rdev_get_mpp(rdev, dev, dst, mpp, &pinfo);
+ if (err)
+ return err;
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ if (nl80211_send_mpath(msg, info->snd_portid, info->snd_seq, 0,
+ dev, dst, mpp, &pinfo) < 0) {
+ nlmsg_free(msg);
+ return -ENOBUFS;
+ }
+
+ return genlmsg_reply(msg, info);
+}
+
+static int nl80211_dump_mpp(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ struct mpath_info pinfo;
+ struct cfg80211_registered_device *rdev;
+ struct wireless_dev *wdev;
+ u8 dst[ETH_ALEN];
+ u8 mpp[ETH_ALEN];
+ int path_idx = cb->args[2];
+ int err;
+
+ err = nl80211_prepare_wdev_dump(skb, cb, &rdev, &wdev);
+ if (err)
+ return err;
+
+ if (!rdev->ops->dump_mpp) {
+ err = -EOPNOTSUPP;
+ goto out_err;
+ }
+
+ if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) {
+ err = -EOPNOTSUPP;
+ goto out_err;
+ }
+
+ while (1) {
+ err = rdev_dump_mpp(rdev, wdev->netdev, path_idx, dst,
+ mpp, &pinfo);
+ if (err == -ENOENT)
+ break;
+ if (err)
+ goto out_err;
+
+ if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
+ wdev->netdev, dst, mpp,
+ &pinfo) < 0)
+ goto out;
+
+ path_idx++;
+ }
+
+ out:
+ cb->args[2] = path_idx;
+ err = skb->len;
+ out_err:
+ nl80211_finish_wdev_dump(rdev);
+ return err;
+}
+
static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -5260,11 +5422,11 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
{
struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
struct nlattr *nl_reg_rule;
- char *alpha2 = NULL;
- int rem_reg_rules = 0, r = 0;
+ char *alpha2;
+ int rem_reg_rules, r;
u32 num_rules = 0, rule_idx = 0, size_of_regd;
enum nl80211_dfs_regions dfs_region = NL80211_DFS_UNSET;
- struct ieee80211_regdomain *rd = NULL;
+ struct ieee80211_regdomain *rd;
if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
return -EINVAL;
@@ -5358,6 +5520,43 @@ static int validate_scan_freqs(struct nlattr *freqs)
return n_channels;
}
+static int nl80211_parse_random_mac(struct nlattr **attrs,
+ u8 *mac_addr, u8 *mac_addr_mask)
+{
+ int i;
+
+ if (!attrs[NL80211_ATTR_MAC] && !attrs[NL80211_ATTR_MAC_MASK]) {
+ memset(mac_addr, 0, ETH_ALEN);
+ memset(mac_addr_mask, 0, ETH_ALEN);
+ mac_addr[0] = 0x2;
+ mac_addr_mask[0] = 0x3;
+
+ return 0;
+ }
+
+ /* need both or none */
+ if (!attrs[NL80211_ATTR_MAC] || !attrs[NL80211_ATTR_MAC_MASK])
+ return -EINVAL;
+
+ memcpy(mac_addr, nla_data(attrs[NL80211_ATTR_MAC]), ETH_ALEN);
+ memcpy(mac_addr_mask, nla_data(attrs[NL80211_ATTR_MAC_MASK]), ETH_ALEN);
+
+ /* don't allow or configure an mcast address */
+ if (!is_multicast_ether_addr(mac_addr_mask) ||
+ is_multicast_ether_addr(mac_addr))
+ return -EINVAL;
+
+ /*
+ * allow users to pass a MAC address that has bits set outside
+ * of the mask, but don't bother drivers with having to deal
+ * with such bits
+ */
+ for (i = 0; i < ETH_ALEN; i++)
+ mac_addr[i] &= mac_addr_mask[i];
+
+ return 0;
+}
+
static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -5535,6 +5734,25 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
err = -EOPNOTSUPP;
goto out_free;
}
+
+ if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
+ if (!(wiphy->features &
+ NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR)) {
+ err = -EOPNOTSUPP;
+ goto out_free;
+ }
+
+ if (wdev->current_bss) {
+ err = -EOPNOTSUPP;
+ goto out_free;
+ }
+
+ err = nl80211_parse_random_mac(info->attrs,
+ request->mac_addr,
+ request->mac_addr_mask);
+ if (err)
+ goto out_free;
+ }
}
request->no_cck =
@@ -5561,14 +5779,12 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
return err;
}
-static int nl80211_start_sched_scan(struct sk_buff *skb,
- struct genl_info *info)
+static struct cfg80211_sched_scan_request *
+nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev,
+ struct nlattr **attrs)
{
struct cfg80211_sched_scan_request *request;
- struct cfg80211_registered_device *rdev = info->user_ptr[0];
- struct net_device *dev = info->user_ptr[1];
struct nlattr *attr;
- struct wiphy *wiphy;
int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i;
u32 interval;
enum ieee80211_band band;
@@ -5576,38 +5792,32 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
s32 default_match_rssi = NL80211_SCAN_RSSI_THOLD_OFF;
- if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
- !rdev->ops->sched_scan_start)
- return -EOPNOTSUPP;
-
- if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
- return -EINVAL;
+ if (!is_valid_ie_attr(attrs[NL80211_ATTR_IE]))
+ return ERR_PTR(-EINVAL);
- if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
- return -EINVAL;
+ if (!attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
+ return ERR_PTR(-EINVAL);
- interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
+ interval = nla_get_u32(attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
if (interval == 0)
- return -EINVAL;
-
- wiphy = &rdev->wiphy;
+ return ERR_PTR(-EINVAL);
- if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
+ if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
n_channels = validate_scan_freqs(
- info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
+ attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
if (!n_channels)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
} else {
n_channels = ieee80211_get_num_supported_channels(wiphy);
}
- if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
- nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
+ if (attrs[NL80211_ATTR_SCAN_SSIDS])
+ nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS],
tmp)
n_ssids++;
if (n_ssids > wiphy->max_sched_scan_ssids)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
/*
* First, count the number of 'real' matchsets. Due to an issue with
@@ -5618,9 +5828,9 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
* older userspace that treated a matchset with only the RSSI as the
* global RSSI for all other matchsets - if there are other matchsets.
*/
- if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
+ if (attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
nla_for_each_nested(attr,
- info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
+ attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
tmp) {
struct nlattr *rssi;
@@ -5628,7 +5838,7 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
nla_data(attr), nla_len(attr),
nl80211_match_policy);
if (err)
- return err;
+ return ERR_PTR(err);
/* add other standalone attributes here */
if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]) {
n_match_sets++;
@@ -5645,30 +5855,23 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
n_match_sets = 1;
if (n_match_sets > wiphy->max_match_sets)
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
- if (info->attrs[NL80211_ATTR_IE])
- ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ if (attrs[NL80211_ATTR_IE])
+ ie_len = nla_len(attrs[NL80211_ATTR_IE]);
else
ie_len = 0;
if (ie_len > wiphy->max_sched_scan_ie_len)
- return -EINVAL;
-
- if (rdev->sched_scan_req) {
- err = -EINPROGRESS;
- goto out;
- }
+ return ERR_PTR(-EINVAL);
request = kzalloc(sizeof(*request)
+ sizeof(*request->ssids) * n_ssids
+ sizeof(*request->match_sets) * n_match_sets
+ sizeof(*request->channels) * n_channels
+ ie_len, GFP_KERNEL);
- if (!request) {
- err = -ENOMEM;
- goto out;
- }
+ if (!request)
+ return ERR_PTR(-ENOMEM);
if (n_ssids)
request->ssids = (void *)&request->channels[n_channels];
@@ -5693,10 +5896,10 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
request->n_match_sets = n_match_sets;
i = 0;
- if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
+ if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
/* user specified, bail out if channel not found */
nla_for_each_nested(attr,
- info->attrs[NL80211_ATTR_SCAN_FREQUENCIES],
+ attrs[NL80211_ATTR_SCAN_FREQUENCIES],
tmp) {
struct ieee80211_channel *chan;
@@ -5742,8 +5945,8 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
request->n_channels = i;
i = 0;
- if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
- nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
+ if (attrs[NL80211_ATTR_SCAN_SSIDS]) {
+ nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS],
tmp) {
if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
err = -EINVAL;
@@ -5757,9 +5960,9 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
}
i = 0;
- if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
+ if (attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
nla_for_each_nested(attr,
- info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
+ attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
tmp) {
struct nlattr *ssid, *rssi;
@@ -5814,36 +6017,88 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
if (ie_len) {
request->ie_len = ie_len;
memcpy((void *)request->ie,
- nla_data(info->attrs[NL80211_ATTR_IE]),
+ nla_data(attrs[NL80211_ATTR_IE]),
request->ie_len);
}
- if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
+ if (attrs[NL80211_ATTR_SCAN_FLAGS]) {
request->flags = nla_get_u32(
- info->attrs[NL80211_ATTR_SCAN_FLAGS]);
+ attrs[NL80211_ATTR_SCAN_FLAGS]);
if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
!(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) {
err = -EOPNOTSUPP;
goto out_free;
}
+
+ if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) {
+ u32 flg = NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
+
+ if (!wdev) /* must be net-detect */
+ flg = NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
+
+ if (!(wiphy->features & flg)) {
+ err = -EOPNOTSUPP;
+ goto out_free;
+ }
+
+ if (wdev && wdev->current_bss) {
+ err = -EOPNOTSUPP;
+ goto out_free;
+ }
+
+ err = nl80211_parse_random_mac(attrs, request->mac_addr,
+ request->mac_addr_mask);
+ if (err)
+ goto out_free;
+ }
}
- request->dev = dev;
- request->wiphy = &rdev->wiphy;
request->interval = interval;
request->scan_start = jiffies;
- err = rdev_sched_scan_start(rdev, dev, request);
- if (!err) {
- rdev->sched_scan_req = request;
- nl80211_send_sched_scan(rdev, dev,
- NL80211_CMD_START_SCHED_SCAN);
- goto out;
- }
+ return request;
out_free:
kfree(request);
-out:
+ return ERR_PTR(err);
+}
+
+static int nl80211_start_sched_scan(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
+ !rdev->ops->sched_scan_start)
+ return -EOPNOTSUPP;
+
+ if (rdev->sched_scan_req)
+ return -EINPROGRESS;
+
+ rdev->sched_scan_req = nl80211_parse_sched_scan(&rdev->wiphy, wdev,
+ info->attrs);
+ err = PTR_ERR_OR_ZERO(rdev->sched_scan_req);
+ if (err)
+ goto out_err;
+
+ err = rdev_sched_scan_start(rdev, dev, rdev->sched_scan_req);
+ if (err)
+ goto out_free;
+
+ rdev->sched_scan_req->dev = dev;
+ rdev->sched_scan_req->wiphy = &rdev->wiphy;
+
+ nl80211_send_sched_scan(rdev, dev,
+ NL80211_CMD_START_SCHED_SCAN);
+ return 0;
+
+out_free:
+ kfree(rdev->sched_scan_req);
+out_err:
+ rdev->sched_scan_req = NULL;
return err;
}
@@ -5923,7 +6178,6 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
* function is called under RTNL lock, so this should not be a problem.
*/
static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];
- u8 radar_detect_width = 0;
int err;
bool need_new_beacon = false;
int len, i;
@@ -6059,10 +6313,8 @@ skip_beacons:
if (err < 0)
return err;
- if (err > 0) {
- radar_detect_width = BIT(params.chandef.width);
+ if (err > 0)
params.radar_required = true;
- }
if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX])
params.block_tx = true;
@@ -6311,8 +6563,6 @@ static int nl80211_dump_survey(struct sk_buff *skb,
}
while (1) {
- struct ieee80211_channel *chan;
-
res = rdev_dump_survey(rdev, wdev->netdev, survey_idx, &survey);
if (res == -ENOENT)
break;
@@ -6325,9 +6575,7 @@ static int nl80211_dump_survey(struct sk_buff *skb,
goto out;
}
- chan = ieee80211_get_channel(&rdev->wiphy,
- survey.channel->center_freq);
- if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {
+ if (survey.channel->flags & IEEE80211_CHAN_DISABLED) {
survey_idx++;
continue;
}
@@ -8159,6 +8407,28 @@ static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
}
+static int nl80211_join_ocb(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct ocb_setup setup = {};
+ int err;
+
+ err = nl80211_parse_chandef(rdev, info, &setup.chandef);
+ if (err)
+ return err;
+
+ return cfg80211_join_ocb(rdev, dev, &setup);
+}
+
+static int nl80211_leave_ocb(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+
+ return cfg80211_leave_ocb(rdev, dev);
+}
+
static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -8542,6 +8812,39 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
return 0;
}
+static int nl80211_parse_wowlan_nd(struct cfg80211_registered_device *rdev,
+ const struct wiphy_wowlan_support *wowlan,
+ struct nlattr *attr,
+ struct cfg80211_wowlan *trig)
+{
+ struct nlattr **tb;
+ int err;
+
+ tb = kzalloc(NUM_NL80211_ATTR * sizeof(*tb), GFP_KERNEL);
+ if (!tb)
+ return -ENOMEM;
+
+ if (!(wowlan->flags & WIPHY_WOWLAN_NET_DETECT)) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ err = nla_parse(tb, NL80211_ATTR_MAX,
+ nla_data(attr), nla_len(attr),
+ nl80211_policy);
+ if (err)
+ goto out;
+
+ trig->nd_config = nl80211_parse_sched_scan(&rdev->wiphy, NULL, tb);
+ err = PTR_ERR_OR_ZERO(trig->nd_config);
+ if (err)
+ trig->nd_config = NULL;
+
+out:
+ kfree(tb);
+ return err;
+}
+
static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -8687,6 +8990,14 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
goto error;
}
+ if (tb[NL80211_WOWLAN_TRIG_NET_DETECT]) {
+ err = nl80211_parse_wowlan_nd(
+ rdev, wowlan, tb[NL80211_WOWLAN_TRIG_NET_DETECT],
+ &new_triggers);
+ if (err)
+ goto error;
+ }
+
ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL);
if (!ntrig) {
err = -ENOMEM;
@@ -9444,7 +9755,7 @@ static int nl80211_add_tx_ts(struct sk_buff *skb, struct genl_info *info)
u16 admitted_time = 0;
int err;
- if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_WMM_ADMISSION))
+ if (!(rdev->wiphy.features & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION))
return -EOPNOTSUPP;
if (!info->attrs[NL80211_ATTR_TSID] || !info->attrs[NL80211_ATTR_MAC] ||
@@ -9460,12 +9771,10 @@ static int nl80211_add_tx_ts(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
/* WMM uses TIDs 0-7 even for TSPEC */
- if (tsid < IEEE80211_FIRST_TSPEC_TSID) {
- if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_WMM_ADMISSION))
- return -EINVAL;
- } else {
+ if (tsid >= IEEE80211_FIRST_TSPEC_TSID) {
/* TODO: handle 802.11 TSPEC/admission control
- * need more attributes for that (e.g. BA session requirement)
+ * need more attributes for that (e.g. BA session requirement);
+ * change the WMM adminssion test above to allow both then
*/
return -EINVAL;
}
@@ -9521,6 +9830,98 @@ static int nl80211_del_tx_ts(struct sk_buff *skb, struct genl_info *info)
return err;
}
+static int nl80211_tdls_channel_switch(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct cfg80211_chan_def chandef = {};
+ const u8 *addr;
+ u8 oper_class;
+ int err;
+
+ if (!rdev->ops->tdls_channel_switch ||
+ !(rdev->wiphy.features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH))
+ return -EOPNOTSUPP;
+
+ switch (dev->ieee80211_ptr->iftype) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ if (!info->attrs[NL80211_ATTR_MAC] ||
+ !info->attrs[NL80211_ATTR_OPER_CLASS])
+ return -EINVAL;
+
+ err = nl80211_parse_chandef(rdev, info, &chandef);
+ if (err)
+ return err;
+
+ /*
+ * Don't allow wide channels on the 2.4Ghz band, as per IEEE802.11-2012
+ * section 10.22.6.2.1. Disallow 5/10Mhz channels as well for now, the
+ * specification is not defined for them.
+ */
+ if (chandef.chan->band == IEEE80211_BAND_2GHZ &&
+ chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
+ chandef.width != NL80211_CHAN_WIDTH_20)
+ return -EINVAL;
+
+ /* we will be active on the TDLS link */
+ if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, wdev->iftype))
+ return -EINVAL;
+
+ /* don't allow switching to DFS channels */
+ if (cfg80211_chandef_dfs_required(wdev->wiphy, &chandef, wdev->iftype))
+ return -EINVAL;
+
+ addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ oper_class = nla_get_u8(info->attrs[NL80211_ATTR_OPER_CLASS]);
+
+ wdev_lock(wdev);
+ err = rdev_tdls_channel_switch(rdev, dev, addr, oper_class, &chandef);
+ wdev_unlock(wdev);
+
+ return err;
+}
+
+static int nl80211_tdls_cancel_channel_switch(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ const u8 *addr;
+
+ if (!rdev->ops->tdls_channel_switch ||
+ !rdev->ops->tdls_cancel_channel_switch ||
+ !(rdev->wiphy.features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH))
+ return -EOPNOTSUPP;
+
+ switch (dev->ieee80211_ptr->iftype) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ if (!info->attrs[NL80211_ATTR_MAC])
+ return -EINVAL;
+
+ addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+
+ wdev_lock(wdev);
+ rdev_tdls_cancel_channel_switch(rdev, dev, addr);
+ wdev_unlock(wdev);
+
+ return 0;
+}
+
#define NL80211_FLAG_NEED_WIPHY 0x01
#define NL80211_FLAG_NEED_NETDEV 0x02
#define NL80211_FLAG_NEED_RTNL 0x04
@@ -9782,6 +10183,15 @@ static const struct genl_ops nl80211_ops[] = {
NL80211_FLAG_NEED_RTNL,
},
{
+ .cmd = NL80211_CMD_GET_MPP,
+ .doit = nl80211_get_mpp,
+ .dumpit = nl80211_dump_mpp,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
.cmd = NL80211_CMD_SET_MPATH,
.doit = nl80211_set_mpath,
.policy = nl80211_policy,
@@ -10095,6 +10505,22 @@ static const struct genl_ops nl80211_ops[] = {
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
+ {
+ .cmd = NL80211_CMD_JOIN_OCB,
+ .doit = nl80211_join_ocb,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_LEAVE_OCB,
+ .doit = nl80211_leave_ocb,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
#ifdef CONFIG_PM
{
.cmd = NL80211_CMD_GET_WOWLAN,
@@ -10294,6 +10720,22 @@ static const struct genl_ops nl80211_ops[] = {
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
+ {
+ .cmd = NL80211_CMD_TDLS_CHANNEL_SWITCH,
+ .doit = nl80211_tdls_channel_switch,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
+ {
+ .cmd = NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH,
+ .doit = nl80211_tdls_cancel_channel_switch,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+ NL80211_FLAG_NEED_RTNL,
+ },
};
/* notification functions */
@@ -11325,55 +11767,155 @@ void cfg80211_mgmt_tx_status(struct wireless_dev *wdev, u64 cookie,
}
EXPORT_SYMBOL(cfg80211_mgmt_tx_status);
-void cfg80211_cqm_rssi_notify(struct net_device *dev,
- enum nl80211_cqm_rssi_threshold_event rssi_event,
- gfp_t gfp)
+static struct sk_buff *cfg80211_prepare_cqm(struct net_device *dev,
+ const char *mac, gfp_t gfp)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
- struct wiphy *wiphy = wdev->wiphy;
- struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
- struct sk_buff *msg;
- struct nlattr *pinfoattr;
- void *hdr;
-
- trace_cfg80211_cqm_rssi_notify(dev, rssi_event);
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
+ struct sk_buff *msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+ void **cb;
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
if (!msg)
- return;
+ return NULL;
- hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
- if (!hdr) {
+ cb = (void **)msg->cb;
+
+ cb[0] = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
+ if (!cb[0]) {
nlmsg_free(msg);
- return;
+ return NULL;
}
if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
goto nla_put_failure;
- pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
- if (!pinfoattr)
+ if (mac && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac))
goto nla_put_failure;
- if (nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
- rssi_event))
+ cb[1] = nla_nest_start(msg, NL80211_ATTR_CQM);
+ if (!cb[1])
goto nla_put_failure;
- nla_nest_end(msg, pinfoattr);
+ cb[2] = rdev;
- genlmsg_end(msg, hdr);
+ return msg;
+ nla_put_failure:
+ nlmsg_free(msg);
+ return NULL;
+}
+
+static void cfg80211_send_cqm(struct sk_buff *msg, gfp_t gfp)
+{
+ void **cb = (void **)msg->cb;
+ struct cfg80211_registered_device *rdev = cb[2];
+
+ nla_nest_end(msg, cb[1]);
+ genlmsg_end(msg, cb[0]);
+
+ memset(msg->cb, 0, sizeof(msg->cb));
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
NL80211_MCGRP_MLME, gfp);
+}
+
+void cfg80211_cqm_rssi_notify(struct net_device *dev,
+ enum nl80211_cqm_rssi_threshold_event rssi_event,
+ gfp_t gfp)
+{
+ struct sk_buff *msg;
+
+ trace_cfg80211_cqm_rssi_notify(dev, rssi_event);
+
+ if (WARN_ON(rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW &&
+ rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH))
+ return;
+
+ msg = cfg80211_prepare_cqm(dev, NULL, gfp);
+ if (!msg)
+ return;
+
+ if (nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
+ rssi_event))
+ goto nla_put_failure;
+
+ cfg80211_send_cqm(msg, gfp);
+
return;
nla_put_failure:
- genlmsg_cancel(msg, hdr);
nlmsg_free(msg);
}
EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
+void cfg80211_cqm_txe_notify(struct net_device *dev,
+ const u8 *peer, u32 num_packets,
+ u32 rate, u32 intvl, gfp_t gfp)
+{
+ struct sk_buff *msg;
+
+ msg = cfg80211_prepare_cqm(dev, peer, gfp);
+ if (!msg)
+ return;
+
+ if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_PKTS, num_packets))
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_RATE, rate))
+ goto nla_put_failure;
+
+ if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_INTVL, intvl))
+ goto nla_put_failure;
+
+ cfg80211_send_cqm(msg, gfp);
+ return;
+
+ nla_put_failure:
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
+
+void cfg80211_cqm_pktloss_notify(struct net_device *dev,
+ const u8 *peer, u32 num_packets, gfp_t gfp)
+{
+ struct sk_buff *msg;
+
+ trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets);
+
+ msg = cfg80211_prepare_cqm(dev, peer, gfp);
+ if (!msg)
+ return;
+
+ if (nla_put_u32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets))
+ goto nla_put_failure;
+
+ cfg80211_send_cqm(msg, gfp);
+ return;
+
+ nla_put_failure:
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
+
+void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp)
+{
+ struct sk_buff *msg;
+
+ msg = cfg80211_prepare_cqm(dev, NULL, gfp);
+ if (!msg)
+ return;
+
+ if (nla_put_flag(msg, NL80211_ATTR_CQM_BEACON_LOSS_EVENT))
+ goto nla_put_failure;
+
+ cfg80211_send_cqm(msg, gfp);
+ return;
+
+ nla_put_failure:
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_cqm_beacon_loss_notify);
+
static void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev, const u8 *bssid,
const u8 *replay_ctr, gfp_t gfp)
@@ -11491,7 +12033,9 @@ EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify);
static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
struct net_device *netdev,
struct cfg80211_chan_def *chandef,
- gfp_t gfp)
+ gfp_t gfp,
+ enum nl80211_commands notif,
+ u8 count)
{
struct sk_buff *msg;
void *hdr;
@@ -11500,7 +12044,7 @@ static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
if (!msg)
return;
- hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CH_SWITCH_NOTIFY);
+ hdr = nl80211hdr_put(msg, 0, 0, 0, notif);
if (!hdr) {
nlmsg_free(msg);
return;
@@ -11512,6 +12056,10 @@ static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
if (nl80211_send_chandef(msg, chandef))
goto nla_put_failure;
+ if ((notif == NL80211_CMD_CH_SWITCH_STARTED_NOTIFY) &&
+ (nla_put_u32(msg, NL80211_ATTR_CH_SWITCH_COUNT, count)))
+ goto nla_put_failure;
+
genlmsg_end(msg, hdr);
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
@@ -11534,70 +12082,27 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
trace_cfg80211_ch_switch_notify(dev, chandef);
- if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
- wdev->iftype != NL80211_IFTYPE_P2P_GO &&
- wdev->iftype != NL80211_IFTYPE_ADHOC &&
- wdev->iftype != NL80211_IFTYPE_MESH_POINT))
- return;
-
wdev->chandef = *chandef;
wdev->preset_chandef = *chandef;
- nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
+ nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL,
+ NL80211_CMD_CH_SWITCH_NOTIFY, 0);
}
EXPORT_SYMBOL(cfg80211_ch_switch_notify);
-void cfg80211_cqm_txe_notify(struct net_device *dev,
- const u8 *peer, u32 num_packets,
- u32 rate, u32 intvl, gfp_t gfp)
+void cfg80211_ch_switch_started_notify(struct net_device *dev,
+ struct cfg80211_chan_def *chandef,
+ u8 count)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
- struct sk_buff *msg;
- struct nlattr *pinfoattr;
- void *hdr;
-
- msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
- if (!msg)
- return;
-
- hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
- if (!hdr) {
- nlmsg_free(msg);
- return;
- }
-
- if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
- nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
- nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
- goto nla_put_failure;
-
- pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
- if (!pinfoattr)
- goto nla_put_failure;
- if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_PKTS, num_packets))
- goto nla_put_failure;
-
- if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_RATE, rate))
- goto nla_put_failure;
-
- if (nla_put_u32(msg, NL80211_ATTR_CQM_TXE_INTVL, intvl))
- goto nla_put_failure;
-
- nla_nest_end(msg, pinfoattr);
+ trace_cfg80211_ch_switch_started_notify(dev, chandef);
- genlmsg_end(msg, hdr);
-
- genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
- NL80211_MCGRP_MLME, gfp);
- return;
-
- nla_put_failure:
- genlmsg_cancel(msg, hdr);
- nlmsg_free(msg);
+ nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL,
+ NL80211_CMD_CH_SWITCH_STARTED_NOTIFY, count);
}
-EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
+EXPORT_SYMBOL(cfg80211_ch_switch_started_notify);
void
nl80211_radar_notify(struct cfg80211_registered_device *rdev,
@@ -11647,54 +12152,6 @@ nl80211_radar_notify(struct cfg80211_registered_device *rdev,
nlmsg_free(msg);
}
-void cfg80211_cqm_pktloss_notify(struct net_device *dev,
- const u8 *peer, u32 num_packets, gfp_t gfp)
-{
- struct wireless_dev *wdev = dev->ieee80211_ptr;
- struct wiphy *wiphy = wdev->wiphy;
- struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
- struct sk_buff *msg;
- struct nlattr *pinfoattr;
- void *hdr;
-
- trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets);
-
- msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
- if (!msg)
- return;
-
- hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
- if (!hdr) {
- nlmsg_free(msg);
- return;
- }
-
- if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
- nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
- nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
- goto nla_put_failure;
-
- pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
- if (!pinfoattr)
- goto nla_put_failure;
-
- if (nla_put_u32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets))
- goto nla_put_failure;
-
- nla_nest_end(msg, pinfoattr);
-
- genlmsg_end(msg, hdr);
-
- genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
- NL80211_MCGRP_MLME, gfp);
- return;
-
- nla_put_failure:
- genlmsg_cancel(msg, hdr);
- nlmsg_free(msg);
-}
-EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify);
-
void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
u64 cookie, bool acked, gfp_t gfp)
{
@@ -11782,6 +12239,67 @@ void cfg80211_report_obss_beacon(struct wiphy *wiphy,
EXPORT_SYMBOL(cfg80211_report_obss_beacon);
#ifdef CONFIG_PM
+static int cfg80211_net_detect_results(struct sk_buff *msg,
+ struct cfg80211_wowlan_wakeup *wakeup)
+{
+ struct cfg80211_wowlan_nd_info *nd = wakeup->net_detect;
+ struct nlattr *nl_results, *nl_match, *nl_freqs;
+ int i, j;
+
+ nl_results = nla_nest_start(
+ msg, NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS);
+ if (!nl_results)
+ return -EMSGSIZE;
+
+ for (i = 0; i < nd->n_matches; i++) {
+ struct cfg80211_wowlan_nd_match *match = nd->matches[i];
+
+ nl_match = nla_nest_start(msg, i);
+ if (!nl_match)
+ break;
+
+ /* The SSID attribute is optional in nl80211, but for
+ * simplicity reasons it's always present in the
+ * cfg80211 structure. If a driver can't pass the
+ * SSID, that needs to be changed. A zero length SSID
+ * is still a valid SSID (wildcard), so it cannot be
+ * used for this purpose.
+ */
+ if (nla_put(msg, NL80211_ATTR_SSID, match->ssid.ssid_len,
+ match->ssid.ssid)) {
+ nla_nest_cancel(msg, nl_match);
+ goto out;
+ }
+
+ if (match->n_channels) {
+ nl_freqs = nla_nest_start(
+ msg, NL80211_ATTR_SCAN_FREQUENCIES);
+ if (!nl_freqs) {
+ nla_nest_cancel(msg, nl_match);
+ goto out;
+ }
+
+ for (j = 0; j < match->n_channels; j++) {
+ if (nla_put_u32(msg,
+ NL80211_ATTR_WIPHY_FREQ,
+ match->channels[j])) {
+ nla_nest_cancel(msg, nl_freqs);
+ nla_nest_cancel(msg, nl_match);
+ goto out;
+ }
+ }
+
+ nla_nest_end(msg, nl_freqs);
+ }
+
+ nla_nest_end(msg, nl_match);
+ }
+
+out:
+ nla_nest_end(msg, nl_results);
+ return 0;
+}
+
void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
struct cfg80211_wowlan_wakeup *wakeup,
gfp_t gfp)
@@ -11876,6 +12394,10 @@ void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev,
goto free_msg;
}
+ if (wakeup->net_detect &&
+ cfg80211_net_detect_results(msg, wakeup))
+ goto free_msg;
+
nla_nest_end(msg, reasons);
}
diff --git a/net/wireless/ocb.c b/net/wireless/ocb.c
new file mode 100644
index 000000000000..c00d4a792319
--- /dev/null
+++ b/net/wireless/ocb.c
@@ -0,0 +1,88 @@
+/*
+ * OCB mode implementation
+ *
+ * Copyright: (c) 2014 Czech Technical University in Prague
+ * (c) 2014 Volkswagen Group Research
+ * Author: Rostislav Lisovy <rostislav.lisovy@fel.cvut.cz>
+ * Funded by: Volkswagen Group Research
+ *
+ * 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.
+ */
+
+#include <linux/ieee80211.h>
+#include <net/cfg80211.h>
+#include "nl80211.h"
+#include "core.h"
+#include "rdev-ops.h"
+
+int __cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ocb_setup *setup)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB)
+ return -EOPNOTSUPP;
+
+ if (WARN_ON(!setup->chandef.chan))
+ return -EINVAL;
+
+ err = rdev_join_ocb(rdev, dev, setup);
+ if (!err)
+ wdev->chandef = setup->chandef;
+
+ return err;
+}
+
+int cfg80211_join_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ocb_setup *setup)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ wdev_lock(wdev);
+ err = __cfg80211_join_ocb(rdev, dev, setup);
+ wdev_unlock(wdev);
+
+ return err;
+}
+
+int __cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ ASSERT_WDEV_LOCK(wdev);
+
+ if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB)
+ return -EOPNOTSUPP;
+
+ if (!rdev->ops->leave_ocb)
+ return -EOPNOTSUPP;
+
+ err = rdev_leave_ocb(rdev, dev);
+ if (!err)
+ memset(&wdev->chandef, 0, sizeof(wdev->chandef));
+
+ return err;
+}
+
+int cfg80211_leave_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ wdev_lock(wdev);
+ err = __cfg80211_leave_ocb(rdev, dev);
+ wdev_unlock(wdev);
+
+ return err;
+}
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index f6d457d6a558..35cfb7134bdb 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -178,11 +178,12 @@ static inline int rdev_add_station(struct cfg80211_registered_device *rdev,
}
static inline int rdev_del_station(struct cfg80211_registered_device *rdev,
- struct net_device *dev, u8 *mac)
+ struct net_device *dev,
+ struct station_del_parameters *params)
{
int ret;
- trace_rdev_del_station(&rdev->wiphy, dev, mac);
- ret = rdev->ops->del_station(&rdev->wiphy, dev, mac);
+ trace_rdev_del_station(&rdev->wiphy, dev, params);
+ ret = rdev->ops->del_station(&rdev->wiphy, dev, params);
trace_rdev_return_int(&rdev->wiphy, ret);
return ret;
}
@@ -263,6 +264,18 @@ static inline int rdev_get_mpath(struct cfg80211_registered_device *rdev,
}
+static inline int rdev_get_mpp(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, u8 *dst, u8 *mpp,
+ struct mpath_info *pinfo)
+{
+ int ret;
+
+ trace_rdev_get_mpp(&rdev->wiphy, dev, dst, mpp);
+ ret = rdev->ops->get_mpp(&rdev->wiphy, dev, dst, mpp, pinfo);
+ trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo);
+ return ret;
+}
+
static inline int rdev_dump_mpath(struct cfg80211_registered_device *rdev,
struct net_device *dev, int idx, u8 *dst,
u8 *next_hop, struct mpath_info *pinfo)
@@ -271,7 +284,20 @@ static inline int rdev_dump_mpath(struct cfg80211_registered_device *rdev,
int ret;
trace_rdev_dump_mpath(&rdev->wiphy, dev, idx, dst, next_hop);
ret = rdev->ops->dump_mpath(&rdev->wiphy, dev, idx, dst, next_hop,
- pinfo);
+ pinfo);
+ trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo);
+ return ret;
+}
+
+static inline int rdev_dump_mpp(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, int idx, u8 *dst,
+ u8 *mpp, struct mpath_info *pinfo)
+
+{
+ int ret;
+
+ trace_rdev_dump_mpp(&rdev->wiphy, dev, idx, dst, mpp);
+ ret = rdev->ops->dump_mpp(&rdev->wiphy, dev, idx, dst, mpp, pinfo);
trace_rdev_return_int_mpath_info(&rdev->wiphy, ret, pinfo);
return ret;
}
@@ -322,6 +348,27 @@ static inline int rdev_leave_mesh(struct cfg80211_registered_device *rdev,
return ret;
}
+static inline int rdev_join_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev,
+ struct ocb_setup *setup)
+{
+ int ret;
+ trace_rdev_join_ocb(&rdev->wiphy, dev, setup);
+ ret = rdev->ops->join_ocb(&rdev->wiphy, dev, setup);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline int rdev_leave_ocb(struct cfg80211_registered_device *rdev,
+ struct net_device *dev)
+{
+ int ret;
+ trace_rdev_leave_ocb(&rdev->wiphy, dev);
+ ret = rdev->ops->leave_ocb(&rdev->wiphy, dev);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
static inline int rdev_change_bss(struct cfg80211_registered_device *rdev,
struct net_device *dev,
struct bss_parameters *params)
@@ -946,4 +993,28 @@ rdev_del_tx_ts(struct cfg80211_registered_device *rdev,
return ret;
}
+static inline int
+rdev_tdls_channel_switch(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *addr,
+ u8 oper_class, struct cfg80211_chan_def *chandef)
+{
+ int ret;
+
+ trace_rdev_tdls_channel_switch(&rdev->wiphy, dev, addr, oper_class,
+ chandef);
+ ret = rdev->ops->tdls_channel_switch(&rdev->wiphy, dev, addr,
+ oper_class, chandef);
+ trace_rdev_return_int(&rdev->wiphy, ret);
+ return ret;
+}
+
+static inline void
+rdev_tdls_cancel_channel_switch(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *addr)
+{
+ trace_rdev_tdls_cancel_channel_switch(&rdev->wiphy, dev, addr);
+ rdev->ops->tdls_cancel_channel_switch(&rdev->wiphy, dev, addr);
+ trace_rdev_return_void(&rdev->wiphy);
+}
+
#endif /* __CFG80211_RDEV_OPS */
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index b725a31a4751..47be6163381c 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -56,6 +56,7 @@
#include <net/cfg80211.h>
#include "core.h"
#include "reg.h"
+#include "rdev-ops.h"
#include "regdb.h"
#include "nl80211.h"
@@ -66,6 +67,12 @@
#define REG_DBG_PRINT(args...)
#endif
+/*
+ * Grace period we give before making sure all current interfaces reside on
+ * channels allowed by the current regulatory domain.
+ */
+#define REG_ENFORCE_GRACE_MS 60000
+
/**
* enum reg_request_treatment - regulatory request treatment
*
@@ -210,6 +217,9 @@ struct reg_beacon {
struct ieee80211_channel chan;
};
+static void reg_check_chans_work(struct work_struct *work);
+static DECLARE_DELAYED_WORK(reg_check_chans, reg_check_chans_work);
+
static void reg_todo(struct work_struct *work);
static DECLARE_WORK(reg_work, reg_todo);
@@ -573,8 +583,9 @@ static const struct ieee80211_regdomain *reg_get_regdomain(struct wiphy *wiphy)
return get_cfg80211_regdom();
}
-unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,
- const struct ieee80211_reg_rule *rule)
+static unsigned int
+reg_get_max_bandwidth_from_range(const struct ieee80211_regdomain *rd,
+ const struct ieee80211_reg_rule *rule)
{
const struct ieee80211_freq_range *freq_range = &rule->freq_range;
const struct ieee80211_freq_range *freq_range_tmp;
@@ -622,6 +633,27 @@ unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,
return end_freq - start_freq;
}
+unsigned int reg_get_max_bandwidth(const struct ieee80211_regdomain *rd,
+ const struct ieee80211_reg_rule *rule)
+{
+ unsigned int bw = reg_get_max_bandwidth_from_range(rd, rule);
+
+ if (rule->flags & NL80211_RRF_NO_160MHZ)
+ bw = min_t(unsigned int, bw, MHZ_TO_KHZ(80));
+ if (rule->flags & NL80211_RRF_NO_80MHZ)
+ bw = min_t(unsigned int, bw, MHZ_TO_KHZ(40));
+
+ /*
+ * HT40+/HT40- limits are handled per-channel. Only limit BW if both
+ * are not allowed.
+ */
+ if (rule->flags & NL80211_RRF_NO_HT40MINUS &&
+ rule->flags & NL80211_RRF_NO_HT40PLUS)
+ bw = min_t(unsigned int, bw, MHZ_TO_KHZ(20));
+
+ return bw;
+}
+
/* Sanity check on a regulatory rule */
static bool is_valid_reg_rule(const struct ieee80211_reg_rule *rule)
{
@@ -946,6 +978,16 @@ static u32 map_regdom_flags(u32 rd_flags)
channel_flags |= IEEE80211_CHAN_NO_OFDM;
if (rd_flags & NL80211_RRF_NO_OUTDOOR)
channel_flags |= IEEE80211_CHAN_INDOOR_ONLY;
+ if (rd_flags & NL80211_RRF_GO_CONCURRENT)
+ channel_flags |= IEEE80211_CHAN_GO_CONCURRENT;
+ if (rd_flags & NL80211_RRF_NO_HT40MINUS)
+ channel_flags |= IEEE80211_CHAN_NO_HT40MINUS;
+ if (rd_flags & NL80211_RRF_NO_HT40PLUS)
+ channel_flags |= IEEE80211_CHAN_NO_HT40PLUS;
+ if (rd_flags & NL80211_RRF_NO_80MHZ)
+ channel_flags |= IEEE80211_CHAN_NO_80MHZ;
+ if (rd_flags & NL80211_RRF_NO_160MHZ)
+ channel_flags |= IEEE80211_CHAN_NO_160MHZ;
return channel_flags;
}
@@ -1486,6 +1528,96 @@ static void reg_call_notifier(struct wiphy *wiphy,
wiphy->reg_notifier(wiphy, request);
}
+static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)
+{
+ struct ieee80211_channel *ch;
+ struct cfg80211_chan_def chandef;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+ bool ret = true;
+
+ wdev_lock(wdev);
+
+ if (!wdev->netdev || !netif_running(wdev->netdev))
+ goto out;
+
+ switch (wdev->iftype) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_P2P_GO:
+ if (!wdev->beacon_interval)
+ goto out;
+
+ ret = cfg80211_reg_can_beacon(wiphy,
+ &wdev->chandef, wdev->iftype);
+ break;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_ADHOC:
+ if (!wdev->current_bss ||
+ !wdev->current_bss->pub.channel)
+ goto out;
+
+ ch = wdev->current_bss->pub.channel;
+ if (rdev->ops->get_channel &&
+ !rdev_get_channel(rdev, wdev, &chandef))
+ ret = cfg80211_chandef_usable(wiphy, &chandef,
+ IEEE80211_CHAN_DISABLED);
+ else
+ ret = !(ch->flags & IEEE80211_CHAN_DISABLED);
+ break;
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_AP_VLAN:
+ case NL80211_IFTYPE_P2P_DEVICE:
+ /* no enforcement required */
+ break;
+ default:
+ /* others not implemented for now */
+ WARN_ON(1);
+ break;
+ }
+
+out:
+ wdev_unlock(wdev);
+ return ret;
+}
+
+static void reg_leave_invalid_chans(struct wiphy *wiphy)
+{
+ struct wireless_dev *wdev;
+ struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+ ASSERT_RTNL();
+
+ list_for_each_entry(wdev, &rdev->wdev_list, list)
+ if (!reg_wdev_chan_valid(wiphy, wdev))
+ cfg80211_leave(rdev, wdev);
+}
+
+static void reg_check_chans_work(struct work_struct *work)
+{
+ struct cfg80211_registered_device *rdev;
+
+ REG_DBG_PRINT("Verifying active interfaces after reg change\n");
+ rtnl_lock();
+
+ list_for_each_entry(rdev, &cfg80211_rdev_list, list)
+ if (!(rdev->wiphy.regulatory_flags &
+ REGULATORY_IGNORE_STALE_KICKOFF))
+ reg_leave_invalid_chans(&rdev->wiphy);
+
+ rtnl_unlock();
+}
+
+static void reg_check_channels(void)
+{
+ /*
+ * Give usermode a chance to do something nicer (move to another
+ * channel, orderly disconnection), before forcing a disconnection.
+ */
+ mod_delayed_work(system_power_efficient_wq,
+ &reg_check_chans,
+ msecs_to_jiffies(REG_ENFORCE_GRACE_MS));
+}
+
static void wiphy_update_regulatory(struct wiphy *wiphy,
enum nl80211_reg_initiator initiator)
{
@@ -1525,6 +1657,8 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
wiphy = &rdev->wiphy;
wiphy_update_regulatory(wiphy, initiator);
}
+
+ reg_check_channels();
}
static void handle_channel_custom(struct wiphy *wiphy,
@@ -1565,10 +1699,23 @@ static void handle_channel_custom(struct wiphy *wiphy,
if (max_bandwidth_khz < MHZ_TO_KHZ(160))
bw_flags |= IEEE80211_CHAN_NO_160MHZ;
+ chan->dfs_state_entered = jiffies;
+ chan->dfs_state = NL80211_DFS_USABLE;
+
+ chan->beacon_found = false;
chan->flags |= map_regdom_flags(reg_rule->flags) | bw_flags;
chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain);
chan->max_reg_power = chan->max_power =
(int) MBM_TO_DBM(power_rule->max_eirp);
+
+ if (chan->flags & IEEE80211_CHAN_RADAR) {
+ if (reg_rule->dfs_cac_ms)
+ chan->dfs_cac_ms = reg_rule->dfs_cac_ms;
+ else
+ chan->dfs_cac_ms = IEEE80211_DFS_MIN_CAC_TIME_MS;
+ }
+
+ chan->max_power = chan->max_reg_power;
}
static void handle_band_custom(struct wiphy *wiphy,
@@ -1931,8 +2078,10 @@ static void reg_process_hint(struct regulatory_request *reg_request)
/* This is required so that the orig_* parameters are saved */
if (treatment == REG_REQ_ALREADY_SET && wiphy &&
- wiphy->regulatory_flags & REGULATORY_STRICT_REG)
+ wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
wiphy_update_regulatory(wiphy, reg_request->initiator);
+ reg_check_channels();
+ }
return;
@@ -2813,6 +2962,7 @@ void regulatory_exit(void)
cancel_work_sync(&reg_work);
cancel_delayed_work_sync(&reg_timeout);
+ cancel_delayed_work_sync(&reg_check_chans);
/* Lock to suppress warnings */
rtnl_lock();
diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index dc1668ff543b..0ab3711c79a0 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -80,9 +80,18 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
if (!request)
return -ENOMEM;
- if (wdev->conn->params.channel)
+ if (wdev->conn->params.channel) {
+ enum ieee80211_band band = wdev->conn->params.channel->band;
+ struct ieee80211_supported_band *sband =
+ wdev->wiphy->bands[band];
+
+ if (!sband) {
+ kfree(request);
+ return -EINVAL;
+ }
request->channels[0] = wdev->conn->params.channel;
- else {
+ request->rates[band] = (1 << sband->n_bitrates) - 1;
+ } else {
int i = 0, j;
enum ieee80211_band band;
struct ieee80211_supported_band *bands;
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index 625a6e6d1168..ad38910f7036 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -600,6 +600,11 @@ DEFINE_EVENT(wiphy_netdev_evt, rdev_leave_ibss,
TP_ARGS(wiphy, netdev)
);
+DEFINE_EVENT(wiphy_netdev_evt, rdev_leave_ocb,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
+ TP_ARGS(wiphy, netdev)
+);
+
DEFINE_EVENT(wiphy_netdev_evt, rdev_flush_pmksa,
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev),
TP_ARGS(wiphy, netdev)
@@ -680,9 +685,34 @@ DECLARE_EVENT_CLASS(wiphy_netdev_mac_evt,
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac))
);
-DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_del_station,
- TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *mac),
- TP_ARGS(wiphy, netdev, mac)
+DECLARE_EVENT_CLASS(station_del,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct station_del_parameters *params),
+ TP_ARGS(wiphy, netdev, params),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(sta_mac)
+ __field(u8, subtype)
+ __field(u16, reason_code)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(sta_mac, params->mac);
+ __entry->subtype = params->subtype;
+ __entry->reason_code = params->reason_code;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT
+ ", subtype: %u, reason_code: %u",
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac),
+ __entry->subtype, __entry->reason_code)
+);
+
+DEFINE_EVENT(station_del, rdev_del_station,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ struct station_del_parameters *params),
+ TP_ARGS(wiphy, netdev, params)
);
DEFINE_EVENT(wiphy_netdev_mac_evt, rdev_get_station,
@@ -801,6 +831,51 @@ TRACE_EVENT(rdev_dump_mpath,
MAC_PR_ARG(next_hop))
);
+TRACE_EVENT(rdev_get_mpp,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ u8 *dst, u8 *mpp),
+ TP_ARGS(wiphy, netdev, dst, mpp),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(dst)
+ MAC_ENTRY(mpp)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(dst, dst);
+ MAC_ASSIGN(mpp, mpp);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", destination: " MAC_PR_FMT
+ ", mpp: " MAC_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG,
+ MAC_PR_ARG(dst), MAC_PR_ARG(mpp))
+);
+
+TRACE_EVENT(rdev_dump_mpp,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, int idx,
+ u8 *dst, u8 *mpp),
+ TP_ARGS(wiphy, netdev, idx, mpp, dst),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(dst)
+ MAC_ENTRY(mpp)
+ __field(int, idx)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(dst, dst);
+ MAC_ASSIGN(mpp, mpp);
+ __entry->idx = idx;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d, destination: "
+ MAC_PR_FMT ", mpp: " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->idx, MAC_PR_ARG(dst),
+ MAC_PR_ARG(mpp))
+);
+
TRACE_EVENT(rdev_return_int_mpath_info,
TP_PROTO(struct wiphy *wiphy, int ret, struct mpath_info *pinfo),
TP_ARGS(wiphy, ret, pinfo),
@@ -1246,6 +1321,22 @@ TRACE_EVENT(rdev_join_ibss,
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->ssid)
);
+TRACE_EVENT(rdev_join_ocb,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ const struct ocb_setup *setup),
+ TP_ARGS(wiphy, netdev, setup),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG)
+);
+
TRACE_EVENT(rdev_set_wiphy_params,
TP_PROTO(struct wiphy *wiphy, u32 changed),
TP_ARGS(wiphy, changed),
@@ -1941,6 +2032,48 @@ TRACE_EVENT(rdev_del_tx_ts,
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->tsid)
);
+TRACE_EVENT(rdev_tdls_channel_switch,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ const u8 *addr, u8 oper_class,
+ struct cfg80211_chan_def *chandef),
+ TP_ARGS(wiphy, netdev, addr, oper_class, chandef),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(addr)
+ __field(u8, oper_class)
+ CHAN_DEF_ENTRY
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(addr, addr);
+ CHAN_DEF_ASSIGN(chandef);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT
+ " oper class %d, " CHAN_DEF_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(addr),
+ __entry->oper_class, CHAN_DEF_PR_ARG)
+);
+
+TRACE_EVENT(rdev_tdls_cancel_channel_switch,
+ TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+ const u8 *addr),
+ TP_ARGS(wiphy, netdev, addr),
+ TP_STRUCT__entry(
+ WIPHY_ENTRY
+ NETDEV_ENTRY
+ MAC_ENTRY(addr)
+ ),
+ TP_fast_assign(
+ WIPHY_ASSIGN;
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(addr, addr);
+ ),
+ TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT,
+ WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(addr))
+);
+
/*************************************************************
* cfg80211 exported functions traces *
*************************************************************/
@@ -2264,6 +2397,22 @@ TRACE_EVENT(cfg80211_ch_switch_notify,
NETDEV_PR_ARG, CHAN_DEF_PR_ARG)
);
+TRACE_EVENT(cfg80211_ch_switch_started_notify,
+ TP_PROTO(struct net_device *netdev,
+ struct cfg80211_chan_def *chandef),
+ TP_ARGS(netdev, chandef),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ CHAN_DEF_ENTRY
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ CHAN_DEF_ASSIGN(chandef);
+ ),
+ TP_printk(NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT,
+ NETDEV_PR_ARG, CHAN_DEF_PR_ARG)
+);
+
TRACE_EVENT(cfg80211_radar_event,
TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef),
TP_ARGS(wiphy, chandef),
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 5e233a577d0f..d0ac795445b7 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -442,7 +442,8 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
break;
case cpu_to_le16(0):
if (iftype != NL80211_IFTYPE_ADHOC &&
- iftype != NL80211_IFTYPE_STATION)
+ iftype != NL80211_IFTYPE_STATION &&
+ iftype != NL80211_IFTYPE_OCB)
return -1;
break;
}
@@ -519,6 +520,7 @@ int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr,
memcpy(hdr.addr3, skb->data, ETH_ALEN);
hdrlen = 24;
break;
+ case NL80211_IFTYPE_OCB:
case NL80211_IFTYPE_ADHOC:
/* DA SA BSSID */
memcpy(hdr.addr1, skb->data, ETH_ALEN);
@@ -937,6 +939,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
if (dev->ieee80211_ptr->use_4addr)
break;
/* fall through */
+ case NL80211_IFTYPE_OCB:
case NL80211_IFTYPE_P2P_CLIENT:
case NL80211_IFTYPE_ADHOC:
dev->priv_flags |= IFF_DONT_BRIDGE;
diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c
index 5ad4418ef093..d9149b68b9bc 100644
--- a/net/x25/af_x25.c
+++ b/net/x25/af_x25.c
@@ -1170,7 +1170,7 @@ static int x25_sendmsg(struct kiocb *iocb, struct socket *sock,
skb_reset_transport_header(skb);
skb_put(skb, len);
- rc = memcpy_fromiovec(skb_transport_header(skb), msg->msg_iov, len);
+ rc = memcpy_from_msg(skb_transport_header(skb), msg, len);
if (rc)
goto out_kfree_skb;
@@ -1335,7 +1335,7 @@ static int x25_recvmsg(struct kiocb *iocb, struct socket *sock,
/* Currently, each datagram always contains a complete record */
msg->msg_flags |= MSG_EOR;
- rc = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+ rc = skb_copy_datagram_msg(skb, 0, msg, copied);
if (rc)
goto out_free_dgram;
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 88bf289abdc9..cee479bc655c 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -55,6 +55,7 @@ static int stale_bundle(struct dst_entry *dst);
static int xfrm_bundle_ok(struct xfrm_dst *xdst);
static void xfrm_policy_queue_process(unsigned long arg);
+static void __xfrm_policy_link(struct xfrm_policy *pol, int dir);
static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol,
int dir);
@@ -561,7 +562,7 @@ static void xfrm_hash_resize(struct work_struct *work)
mutex_lock(&hash_resize_mutex);
total = 0;
- for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) {
+ for (dir = 0; dir < XFRM_POLICY_MAX; dir++) {
if (xfrm_bydst_should_resize(net, dir, &total))
xfrm_bydst_resize(net, dir);
}
@@ -601,7 +602,7 @@ static void xfrm_hash_rebuild(struct work_struct *work)
write_lock_bh(&net->xfrm.xfrm_policy_lock);
/* reset the bydst and inexact table in all directions */
- for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) {
+ for (dir = 0; dir < XFRM_POLICY_MAX; dir++) {
INIT_HLIST_HEAD(&net->xfrm.policy_inexact[dir]);
hmask = net->xfrm.policy_bydst[dir].hmask;
odst = net->xfrm.policy_bydst[dir].table;
@@ -779,8 +780,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
hlist_add_behind(&policy->bydst, newpos);
else
hlist_add_head(&policy->bydst, chain);
- xfrm_pol_hold(policy);
- net->xfrm.policy_count[dir]++;
+ __xfrm_policy_link(policy, dir);
atomic_inc(&net->xfrm.flow_cache_genid);
/* After previous checking, family can either be AF_INET or AF_INET6 */
@@ -799,7 +799,6 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
policy->curlft.use_time = 0;
if (!mod_timer(&policy->timer, jiffies + HZ))
xfrm_pol_hold(policy);
- list_add(&policy->walk.all, &net->xfrm.policy_all);
write_unlock_bh(&net->xfrm.xfrm_policy_lock);
if (delpol)
@@ -1247,17 +1246,10 @@ out:
static void __xfrm_policy_link(struct xfrm_policy *pol, int dir)
{
struct net *net = xp_net(pol);
- struct hlist_head *chain = policy_hash_bysel(net, &pol->selector,
- pol->family, dir);
list_add(&pol->walk.all, &net->xfrm.policy_all);
- hlist_add_head(&pol->bydst, chain);
- hlist_add_head(&pol->byidx, net->xfrm.policy_byidx+idx_hash(net, pol->index));
net->xfrm.policy_count[dir]++;
xfrm_pol_hold(pol);
-
- if (xfrm_bydst_should_resize(net, dir, NULL))
- schedule_work(&net->xfrm.policy_hash_work);
}
static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol,
@@ -1265,17 +1257,31 @@ static struct xfrm_policy *__xfrm_policy_unlink(struct xfrm_policy *pol,
{
struct net *net = xp_net(pol);
- if (hlist_unhashed(&pol->bydst))
+ if (list_empty(&pol->walk.all))
return NULL;
- hlist_del_init(&pol->bydst);
- hlist_del(&pol->byidx);
- list_del(&pol->walk.all);
+ /* Socket policies are not hashed. */
+ if (!hlist_unhashed(&pol->bydst)) {
+ hlist_del(&pol->bydst);
+ hlist_del(&pol->byidx);
+ }
+
+ list_del_init(&pol->walk.all);
net->xfrm.policy_count[dir]--;
return pol;
}
+static void xfrm_sk_policy_link(struct xfrm_policy *pol, int dir)
+{
+ __xfrm_policy_link(pol, XFRM_POLICY_MAX + dir);
+}
+
+static void xfrm_sk_policy_unlink(struct xfrm_policy *pol, int dir)
+{
+ __xfrm_policy_unlink(pol, XFRM_POLICY_MAX + dir);
+}
+
int xfrm_policy_delete(struct xfrm_policy *pol, int dir)
{
struct net *net = xp_net(pol);
@@ -1307,7 +1313,7 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol)
if (pol) {
pol->curlft.add_time = get_seconds();
pol->index = xfrm_gen_index(net, XFRM_POLICY_MAX+dir, 0);
- __xfrm_policy_link(pol, XFRM_POLICY_MAX+dir);
+ xfrm_sk_policy_link(pol, dir);
}
if (old_pol) {
if (pol)
@@ -1316,7 +1322,7 @@ int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol)
/* Unlinking succeeds always. This is the only function
* allowed to delete or replace socket policy.
*/
- __xfrm_policy_unlink(old_pol, XFRM_POLICY_MAX+dir);
+ xfrm_sk_policy_unlink(old_pol, dir);
}
write_unlock_bh(&net->xfrm.xfrm_policy_lock);
@@ -1349,7 +1355,7 @@ static struct xfrm_policy *clone_policy(const struct xfrm_policy *old, int dir)
memcpy(newp->xfrm_vec, old->xfrm_vec,
newp->xfrm_nr*sizeof(struct xfrm_tmpl));
write_lock_bh(&net->xfrm.xfrm_policy_lock);
- __xfrm_policy_link(newp, XFRM_POLICY_MAX+dir);
+ xfrm_sk_policy_link(newp, dir);
write_unlock_bh(&net->xfrm.xfrm_policy_lock);
xfrm_pol_put(newp);
}
@@ -1878,7 +1884,6 @@ xfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols,
static void xfrm_policy_queue_process(unsigned long arg)
{
- int err = 0;
struct sk_buff *skb;
struct sock *sk;
struct dst_entry *dst;
@@ -1941,7 +1946,7 @@ static void xfrm_policy_queue_process(unsigned long arg)
skb_dst_drop(skb);
skb_dst_set(skb, dst);
- err = dst_output(skb);
+ dst_output(skb);
}
out:
@@ -2966,10 +2971,11 @@ static int __net_init xfrm_policy_init(struct net *net)
goto out_byidx;
net->xfrm.policy_idx_hmask = hmask;
- for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) {
+ for (dir = 0; dir < XFRM_POLICY_MAX; dir++) {
struct xfrm_policy_hash *htab;
net->xfrm.policy_count[dir] = 0;
+ net->xfrm.policy_count[XFRM_POLICY_MAX + dir] = 0;
INIT_HLIST_HEAD(&net->xfrm.policy_inexact[dir]);
htab = &net->xfrm.policy_bydst[dir];
@@ -3021,7 +3027,7 @@ static void xfrm_policy_fini(struct net *net)
WARN_ON(!list_empty(&net->xfrm.policy_all));
- for (dir = 0; dir < XFRM_POLICY_MAX * 2; dir++) {
+ for (dir = 0; dir < XFRM_POLICY_MAX; dir++) {
struct xfrm_policy_hash *htab;
WARN_ON(!hlist_empty(&net->xfrm.policy_inexact[dir]));
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index e812e988c111..8128594ab379 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -824,13 +824,15 @@ static int copy_to_user_state_extra(struct xfrm_state *x,
ret = xfrm_mark_put(skb, &x->mark);
if (ret)
goto out;
- if (x->replay_esn) {
+ if (x->replay_esn)
ret = nla_put(skb, XFRMA_REPLAY_ESN_VAL,
xfrm_replay_state_esn_len(x->replay_esn),
x->replay_esn);
- if (ret)
- goto out;
- }
+ else
+ ret = nla_put(skb, XFRMA_REPLAY_VAL, sizeof(x->replay),
+ &x->replay);
+ if (ret)
+ goto out;
if (x->security)
ret = copy_sec_ctx(x->security, skb);
out:
@@ -2569,6 +2571,8 @@ static inline size_t xfrm_sa_len(struct xfrm_state *x)
l += nla_total_size(sizeof(x->tfcpad));
if (x->replay_esn)
l += nla_total_size(xfrm_replay_state_esn_len(x->replay_esn));
+ else
+ l += nla_total_size(sizeof(struct xfrm_replay_state));
if (x->security)
l += nla_total_size(sizeof(struct xfrm_user_sec_ctx) +
x->security->ctx_len);