summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
Diffstat (limited to 'net')
-rw-r--r--net/802/hippi.c4
-rw-r--r--net/batman-adv/main.h2
-rw-r--r--net/bluetooth/af_bluetooth.c2
-rw-r--r--net/bluetooth/bnep/bnep.h3
-rw-r--r--net/bluetooth/hci_core.c26
-rw-r--r--net/bluetooth/hci_event.c72
-rw-r--r--net/bluetooth/l2cap_core.c744
-rw-r--r--net/bluetooth/l2cap_sock.c181
-rw-r--r--net/bluetooth/mgmt.c17
-rw-r--r--net/bluetooth/smp.c56
-rw-r--r--net/bluetooth/smp.h1
-rw-r--r--net/bridge/br_netlink.c4
-rw-r--r--net/bridge/netfilter/ebt_vlan.c3
-rw-r--r--net/core/dev.c164
-rw-r--r--net/core/dev_addr_lists.c97
-rw-r--r--net/core/dev_ioctl.c2
-rw-r--r--net/core/flow_dissector.c6
-rw-r--r--net/core/neighbour.c466
-rw-r--r--net/core/net-sysfs.c4
-rw-r--r--net/core/netprio_cgroup.c2
-rw-r--r--net/core/skbuff.c3
-rw-r--r--net/dcb/dcbevent.c3
-rw-r--r--net/dcb/dcbnl.c3
-rw-r--r--net/decnet/dn_dev.c13
-rw-r--r--net/decnet/dn_neigh.c28
-rw-r--r--net/dns_resolver/dns_key.c3
-rw-r--r--net/dns_resolver/dns_query.c3
-rw-r--r--net/dns_resolver/internal.h3
-rw-r--r--net/hsr/hsr_framereg.c13
-rw-r--r--net/ipv4/af_inet.c25
-rw-r--r--net/ipv4/arp.c39
-rw-r--r--net/ipv4/cipso_ipv4.c3
-rw-r--r--net/ipv4/devinet.c62
-rw-r--r--net/ipv4/ip_fragment.c2
-rw-r--r--net/ipv4/ip_sockglue.c2
-rw-r--r--net/ipv4/ip_tunnel_core.c5
-rw-r--r--net/ipv4/ipmr.c2
-rw-r--r--net/ipv4/netfilter/nf_nat_snmp_basic.c3
-rw-r--r--net/ipv4/proc.c1
-rw-r--r--net/ipv4/sysctl_net_ipv4.c9
-rw-r--r--net/ipv4/tcp.c63
-rw-r--r--net/ipv4/tcp_offload.c9
-rw-r--r--net/ipv4/tcp_output.c139
-rw-r--r--net/ipv6/addrconf.c217
-rw-r--r--net/ipv6/af_inet6.c3
-rw-r--r--net/ipv6/ah6.c3
-rw-r--r--net/ipv6/esp6.c3
-rw-r--r--net/ipv6/ip6_gre.c3
-rw-r--r--net/ipv6/ip6_offload.c51
-rw-r--r--net/ipv6/ip6_output.c25
-rw-r--r--net/ipv6/ip6_tunnel.c3
-rw-r--r--net/ipv6/ipcomp6.c3
-rw-r--r--net/ipv6/ipv6_sockglue.c7
-rw-r--r--net/ipv6/mip6.c3
-rw-r--r--net/ipv6/ndisc.c55
-rw-r--r--net/ipv6/route.c16
-rw-r--r--net/ipv6/tcp_ipv6.c8
-rw-r--r--net/ipv6/tcpv6_offload.c6
-rw-r--r--net/ipv6/tunnel6.c3
-rw-r--r--net/ipv6/xfrm6_mode_ro.c3
-rw-r--r--net/ipv6/xfrm6_tunnel.c3
-rw-r--r--net/irda/af_irda.c4
-rw-r--r--net/irda/discovery.c4
-rw-r--r--net/irda/ircomm/ircomm_core.c4
-rw-r--r--net/irda/ircomm/ircomm_event.c4
-rw-r--r--net/irda/ircomm/ircomm_lmp.c4
-rw-r--r--net/irda/ircomm/ircomm_param.c4
-rw-r--r--net/irda/ircomm/ircomm_ttp.c4
-rw-r--r--net/irda/ircomm/ircomm_tty.c4
-rw-r--r--net/irda/ircomm/ircomm_tty_attach.c4
-rw-r--r--net/irda/ircomm/ircomm_tty_ioctl.c4
-rw-r--r--net/irda/irda_device.c4
-rw-r--r--net/irda/irlap.c4
-rw-r--r--net/irda/parameters.c4
-rw-r--r--net/irda/qos.c4
-rw-r--r--net/mac80211/cfg.c123
-rw-r--r--net/mac80211/chan.c141
-rw-r--r--net/mac80211/debugfs.c168
-rw-r--r--net/mac80211/debugfs_sta.c134
-rw-r--r--net/mac80211/ibss.c6
-rw-r--r--net/mac80211/ieee80211_i.h43
-rw-r--r--net/mac80211/iface.c14
-rw-r--r--net/mac80211/key.c62
-rw-r--r--net/mac80211/key.h13
-rw-r--r--net/mac80211/main.c147
-rw-r--r--net/mac80211/mesh.c7
-rw-r--r--net/mac80211/mesh.h6
-rw-r--r--net/mac80211/mesh_hwmp.c91
-rw-r--r--net/mac80211/mesh_pathtbl.c7
-rw-r--r--net/mac80211/mesh_plink.c708
-rw-r--r--net/mac80211/mesh_ps.c3
-rw-r--r--net/mac80211/mesh_sync.c28
-rw-r--r--net/mac80211/mlme.c43
-rw-r--r--net/mac80211/rate.h4
-rw-r--r--net/mac80211/rc80211_minstrel.c3
-rw-r--r--net/mac80211/rc80211_minstrel_ht.c22
-rw-r--r--net/mac80211/rc80211_minstrel_ht_debugfs.c3
-rw-r--r--net/mac80211/rx.c88
-rw-r--r--net/mac80211/scan.c10
-rw-r--r--net/mac80211/sta_info.c40
-rw-r--r--net/mac80211/sta_info.h38
-rw-r--r--net/mac80211/status.c78
-rw-r--r--net/mac80211/trace.h21
-rw-r--r--net/mac80211/tx.c41
-rw-r--r--net/mac80211/util.c95
-rw-r--r--net/mac80211/vht.c7
-rw-r--r--net/mac80211/wpa.c116
-rw-r--r--net/mac80211/wpa.h2
-rw-r--r--net/netfilter/ipvs/ip_vs_nfct.c3
-rw-r--r--net/netfilter/xt_connmark.c3
-rw-r--r--net/netfilter/xt_osf.c3
-rw-r--r--net/netlabel/netlabel_addrlist.c3
-rw-r--r--net/netlabel/netlabel_addrlist.h3
-rw-r--r--net/netlabel/netlabel_cipso_v4.c3
-rw-r--r--net/netlabel/netlabel_cipso_v4.h3
-rw-r--r--net/netlabel/netlabel_domainhash.c3
-rw-r--r--net/netlabel/netlabel_domainhash.h3
-rw-r--r--net/netlabel/netlabel_kapi.c3
-rw-r--r--net/netlabel/netlabel_mgmt.c3
-rw-r--r--net/netlabel/netlabel_mgmt.h3
-rw-r--r--net/netlabel/netlabel_unlabeled.c3
-rw-r--r--net/netlabel/netlabel_unlabeled.h3
-rw-r--r--net/netlabel/netlabel_user.c3
-rw-r--r--net/netlabel/netlabel_user.h3
-rw-r--r--net/nfc/af_nfc.c4
-rw-r--r--net/nfc/core.c4
-rw-r--r--net/nfc/hci/command.c4
-rw-r--r--net/nfc/hci/core.c4
-rw-r--r--net/nfc/hci/hci.h4
-rw-r--r--net/nfc/hci/hcp.c4
-rw-r--r--net/nfc/hci/llc.c4
-rw-r--r--net/nfc/hci/llc.h4
-rw-r--r--net/nfc/hci/llc_nop.c4
-rw-r--r--net/nfc/hci/llc_shdlc.c4
-rw-r--r--net/nfc/llcp.h4
-rw-r--r--net/nfc/llcp_commands.c4
-rw-r--r--net/nfc/llcp_core.c4
-rw-r--r--net/nfc/llcp_sock.c4
-rw-r--r--net/nfc/nci/core.c3
-rw-r--r--net/nfc/nci/data.c3
-rw-r--r--net/nfc/nci/lib.c3
-rw-r--r--net/nfc/nci/ntf.c3
-rw-r--r--net/nfc/nci/rsp.c3
-rw-r--r--net/nfc/netlink.c4
-rw-r--r--net/nfc/nfc.h4
-rw-r--r--net/nfc/rawsock.c4
-rw-r--r--net/openvswitch/actions.c10
-rw-r--r--net/openvswitch/flow_table.c4
-rw-r--r--net/packet/af_packet.c129
-rw-r--r--net/packet/internal.h1
-rw-r--r--net/rfkill/core.c4
-rw-r--r--net/sched/act_api.c201
-rw-r--r--net/sched/act_csum.c13
-rw-r--r--net/sched/act_gact.c13
-rw-r--r--net/sched/act_ipt.c21
-rw-r--r--net/sched/act_mirred.c16
-rw-r--r--net/sched/act_nat.c12
-rw-r--r--net/sched/act_pedit.c12
-rw-r--r--net/sched/act_police.c63
-rw-r--r--net/sched/act_simple.c20
-rw-r--r--net/sched/act_skbedit.c16
-rw-r--r--net/sched/cls_api.c99
-rw-r--r--net/sched/cls_basic.c13
-rw-r--r--net/sched/cls_bpf.c15
-rw-r--r--net/sched/cls_cgroup.c14
-rw-r--r--net/sched/cls_flow.c15
-rw-r--r--net/sched/cls_fw.c13
-rw-r--r--net/sched/cls_route.c13
-rw-r--r--net/sched/cls_rsvp.h13
-rw-r--r--net/sched/cls_tcindex.c17
-rw-r--r--net/sched/cls_u32.c15
-rw-r--r--net/sched/em_meta.c159
-rw-r--r--net/sched/sch_api.c8
-rw-r--r--net/sched/sch_cbq.c3
-rw-r--r--net/sched/sch_dsmark.c2
-rw-r--r--net/sched/sch_fq.c36
-rw-r--r--net/sched/sch_generic.c6
-rw-r--r--net/sched/sch_htb.c16
-rw-r--r--net/sched/sch_mq.c13
-rw-r--r--net/sched/sch_mqprio.c10
-rw-r--r--net/sched/sch_multiq.c3
-rw-r--r--net/sched/sch_netem.c2
-rw-r--r--net/sched/sch_sfq.c10
-rw-r--r--net/sctp/associola.c70
-rw-r--r--net/sctp/auth.c5
-rw-r--r--net/sctp/bind_addr.c5
-rw-r--r--net/sctp/chunk.c5
-rw-r--r--net/sctp/command.c5
-rw-r--r--net/sctp/debug.c5
-rw-r--r--net/sctp/endpointola.c5
-rw-r--r--net/sctp/input.c11
-rw-r--r--net/sctp/inqueue.c5
-rw-r--r--net/sctp/ipv6.c5
-rw-r--r--net/sctp/objcnt.c5
-rw-r--r--net/sctp/output.c5
-rw-r--r--net/sctp/outqueue.c5
-rw-r--r--net/sctp/primitive.c5
-rw-r--r--net/sctp/proc.c5
-rw-r--r--net/sctp/protocol.c5
-rw-r--r--net/sctp/sm_make_chunk.c5
-rw-r--r--net/sctp/sm_sideeffect.c5
-rw-r--r--net/sctp/sm_statefuns.c5
-rw-r--r--net/sctp/sm_statetable.c5
-rw-r--r--net/sctp/socket.c5
-rw-r--r--net/sctp/ssnmap.c5
-rw-r--r--net/sctp/sysctl.c5
-rw-r--r--net/sctp/transport.c5
-rw-r--r--net/sctp/tsnmap.c5
-rw-r--r--net/sctp/ulpevent.c5
-rw-r--r--net/sctp/ulpqueue.c5
-rw-r--r--net/socket.c106
-rw-r--r--net/tipc/bcast.c6
-rw-r--r--net/tipc/bearer.c323
-rw-r--r--net/tipc/bearer.h52
-rw-r--r--net/tipc/core.c8
-rw-r--r--net/tipc/core.h2
-rw-r--r--net/tipc/discover.c17
-rw-r--r--net/tipc/eth_media.c326
-rw-r--r--net/tipc/ib_media.c319
-rw-r--r--net/tipc/link.c124
-rw-r--r--net/tipc/link.h7
-rw-r--r--net/tipc/name_table.c3
-rw-r--r--net/tipc/node.c6
-rw-r--r--net/tipc/port.c9
-rw-r--r--net/tipc/socket.c33
-rw-r--r--net/unix/af_unix.c9
-rw-r--r--net/wireless/chan.c203
-rw-r--r--net/wireless/core.c6
-rw-r--r--net/wireless/core.h18
-rw-r--r--net/wireless/genregdb.awk45
-rw-r--r--net/wireless/ibss.c4
-rw-r--r--net/wireless/mesh.c3
-rw-r--r--net/wireless/mlme.c20
-rw-r--r--net/wireless/nl80211.c107
-rw-r--r--net/wireless/nl80211.h2
-rw-r--r--net/wireless/rdev-ops.h12
-rw-r--r--net/wireless/reg.c802
-rw-r--r--net/wireless/reg.h3
-rw-r--r--net/wireless/trace.h15
-rw-r--r--net/x25/af_x25.c4
-rw-r--r--net/x25/x25_dev.c10
-rw-r--r--net/x25/x25_facilities.c14
-rw-r--r--net/x25/x25_forward.c5
-rw-r--r--net/x25/x25_in.c4
-rw-r--r--net/x25/x25_link.c6
-rw-r--r--net/x25/x25_subr.c6
246 files changed, 5401 insertions, 3680 deletions
diff --git a/net/802/hippi.c b/net/802/hippi.c
index 51a1f530417d..a97a3bde77bb 100644
--- a/net/802/hippi.c
+++ b/net/802/hippi.c
@@ -172,14 +172,14 @@ EXPORT_SYMBOL(hippi_mac_addr);
int hippi_neigh_setup_dev(struct net_device *dev, struct neigh_parms *p)
{
/* Never send broadcast/multicast ARP messages */
- p->mcast_probes = 0;
+ NEIGH_VAR_SET(p, MCAST_PROBES, 0);
/* In IPv6 unicast probes are valid even on NBMA,
* because they are encapsulated in normal IPv6 protocol.
* Should be a generic flag.
*/
if (p->tbl->family != AF_INET6)
- p->ucast_probes = 0;
+ NEIGH_VAR_SET(p, UCAST_PROBES, 0);
return 0;
}
EXPORT_SYMBOL(hippi_neigh_setup_dev);
diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h
index f94f287b8670..322dd7323732 100644
--- a/net/batman-adv/main.h
+++ b/net/batman-adv/main.h
@@ -266,7 +266,7 @@ static inline void batadv_dbg(int type __always_unused,
*/
static inline int batadv_compare_eth(const void *data1, const void *data2)
{
- return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0);
+ return ether_addr_equal_unaligned(data1, data2);
}
/**
diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
index 56ca494621c6..0c5866bb49b6 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.17"
+#define VERSION "2.18"
/* Bluetooth sockets */
#define BT_MAX_PROTO 8
diff --git a/net/bluetooth/bnep/bnep.h b/net/bluetooth/bnep/bnep.h
index e7ee5314f39a..5a5b16f365e9 100644
--- a/net/bluetooth/bnep/bnep.h
+++ b/net/bluetooth/bnep/bnep.h
@@ -12,8 +12,7 @@
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _BNEP_H
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 6ccc4eb9e55e..8b8b5f80dd89 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1228,7 +1228,7 @@ static void hci_set_event_mask_page_2(struct hci_request *req)
/* If Connectionless Slave Broadcast master role is supported
* enable all necessary events for it.
*/
- if (hdev->features[2][0] & 0x01) {
+ if (lmp_csb_master_capable(hdev)) {
events[1] |= 0x40; /* Triggered Clock Capture */
events[1] |= 0x80; /* Synchronization Train Complete */
events[2] |= 0x10; /* Slave Page Response Timeout */
@@ -1238,7 +1238,7 @@ static void hci_set_event_mask_page_2(struct hci_request *req)
/* If Connectionless Slave Broadcast slave role is supported
* enable all necessary events for it.
*/
- if (hdev->features[2][0] & 0x02) {
+ if (lmp_csb_slave_capable(hdev)) {
events[2] |= 0x01; /* Synchronization Train Received */
events[2] |= 0x02; /* CSB Receive */
events[2] |= 0x04; /* CSB Timeout */
@@ -1275,15 +1275,17 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
hci_setup_link_policy(req);
if (lmp_le_capable(hdev)) {
- /* If the controller has a public BD_ADDR, then by
- * default use that one. If this is a LE only
- * controller without one, default to the random
- * address.
- */
- if (bacmp(&hdev->bdaddr, BDADDR_ANY))
- hdev->own_addr_type = ADDR_LE_DEV_PUBLIC;
- else
- hdev->own_addr_type = ADDR_LE_DEV_RANDOM;
+ if (test_bit(HCI_SETUP, &hdev->dev_flags)) {
+ /* If the controller has a public BD_ADDR, then
+ * by default use that one. If this is a LE only
+ * controller without a public address, default
+ * to the random address.
+ */
+ if (bacmp(&hdev->bdaddr, BDADDR_ANY))
+ hdev->own_addr_type = ADDR_LE_DEV_PUBLIC;
+ else
+ hdev->own_addr_type = ADDR_LE_DEV_RANDOM;
+ }
hci_set_le_support(req);
}
@@ -1307,7 +1309,7 @@ static void hci_init4_req(struct hci_request *req, unsigned long opt)
hci_set_event_mask_page_2(req);
/* Check for Synchronization Train support */
- if (hdev->features[2][0] & 0x04)
+ if (lmp_sync_train_capable(hdev))
hci_req_add(req, HCI_OP_READ_SYNC_TRAIN_PARAMS, 0, NULL);
}
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 5935f748c0f9..5fb3df66c2cd 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -486,7 +486,10 @@ static void hci_cc_read_local_commands(struct hci_dev *hdev,
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
- if (!rp->status)
+ if (rp->status)
+ return;
+
+ if (test_bit(HCI_SETUP, &hdev->dev_flags))
memcpy(hdev->commands, rp->commands, sizeof(hdev->commands));
}
@@ -538,12 +541,6 @@ static void hci_cc_read_local_features(struct hci_dev *hdev,
if (hdev->features[0][5] & LMP_EDR_3S_ESCO)
hdev->esco_type |= (ESCO_2EV5 | ESCO_3EV5);
-
- BT_DBG("%s features 0x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x", hdev->name,
- hdev->features[0][0], hdev->features[0][1],
- hdev->features[0][2], hdev->features[0][3],
- hdev->features[0][4], hdev->features[0][5],
- hdev->features[0][6], hdev->features[0][7]);
}
static void hci_cc_read_local_ext_features(struct hci_dev *hdev,
@@ -1782,7 +1779,9 @@ static u8 hci_to_mgmt_reason(u8 err)
static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_disconn_complete *ev = (void *) skb->data;
+ u8 reason = hci_to_mgmt_reason(ev->reason);
struct hci_conn *conn;
+ u8 type;
BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
@@ -1792,43 +1791,38 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
if (!conn)
goto unlock;
- if (ev->status == 0)
- conn->state = BT_CLOSED;
+ if (ev->status) {
+ mgmt_disconnect_failed(hdev, &conn->dst, conn->type,
+ conn->dst_type, ev->status);
+ goto unlock;
+ }
- if (test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags) &&
- (conn->type == ACL_LINK || conn->type == LE_LINK)) {
- if (ev->status) {
- mgmt_disconnect_failed(hdev, &conn->dst, conn->type,
- conn->dst_type, ev->status);
- } else {
- u8 reason = hci_to_mgmt_reason(ev->reason);
+ conn->state = BT_CLOSED;
- mgmt_device_disconnected(hdev, &conn->dst, conn->type,
- conn->dst_type, reason);
- }
- }
+ if (test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
+ mgmt_device_disconnected(hdev, &conn->dst, conn->type,
+ conn->dst_type, reason);
- if (ev->status == 0) {
- u8 type = conn->type;
+ if (conn->type == ACL_LINK && conn->flush_key)
+ hci_remove_link_key(hdev, &conn->dst);
- if (type == ACL_LINK && conn->flush_key)
- hci_remove_link_key(hdev, &conn->dst);
- hci_proto_disconn_cfm(conn, ev->reason);
- hci_conn_del(conn);
+ type = conn->type;
- /* Re-enable advertising if necessary, since it might
- * have been disabled by the connection. From the
- * HCI_LE_Set_Advertise_Enable command description in
- * the core specification (v4.0):
- * "The Controller shall continue advertising until the Host
- * issues an LE_Set_Advertise_Enable command with
- * Advertising_Enable set to 0x00 (Advertising is disabled)
- * or until a connection is created or until the Advertising
- * is timed out due to Directed Advertising."
- */
- if (type == LE_LINK)
- mgmt_reenable_advertising(hdev);
- }
+ hci_proto_disconn_cfm(conn, ev->reason);
+ hci_conn_del(conn);
+
+ /* Re-enable advertising if necessary, since it might
+ * have been disabled by the connection. From the
+ * HCI_LE_Set_Advertise_Enable command description in
+ * the core specification (v4.0):
+ * "The Controller shall continue advertising until the Host
+ * issues an LE_Set_Advertise_Enable command with
+ * Advertising_Enable set to 0x00 (Advertising is disabled)
+ * or until a connection is created or until the Advertising
+ * is timed out due to Directed Advertising."
+ */
+ if (type == LE_LINK)
+ mgmt_reenable_advertising(hdev);
unlock:
hci_dev_unlock(hdev);
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 4af3821df880..b6bca64b320d 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -49,6 +49,9 @@ static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP | L2CAP_FC_CONNLESS, };
static LIST_HEAD(chan_list);
static DEFINE_RWLOCK(chan_list_lock);
+static u16 le_max_credits = L2CAP_LE_MAX_CREDITS;
+static u16 le_default_mps = L2CAP_LE_DEFAULT_MPS;
+
static struct sk_buff *l2cap_build_cmd(struct l2cap_conn *conn,
u8 code, u8 ident, u16 dlen, void *data);
static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len,
@@ -213,9 +216,14 @@ int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid)
static u16 l2cap_alloc_cid(struct l2cap_conn *conn)
{
- u16 cid = L2CAP_CID_DYN_START;
+ u16 cid, dyn_end;
+
+ if (conn->hcon->type == LE_LINK)
+ dyn_end = L2CAP_CID_LE_DYN_END;
+ else
+ dyn_end = L2CAP_CID_DYN_END;
- for (; cid < L2CAP_CID_DYN_END; cid++) {
+ for (cid = L2CAP_CID_DYN_START; cid < dyn_end; cid++) {
if (!__l2cap_get_chan_by_scid(conn, cid))
return cid;
}
@@ -490,6 +498,18 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan)
set_bit(FLAG_FORCE_ACTIVE, &chan->flags);
}
+static void l2cap_le_flowctl_init(struct l2cap_chan *chan)
+{
+ chan->sdu = NULL;
+ chan->sdu_last_frag = NULL;
+ chan->sdu_len = 0;
+ chan->tx_credits = 0;
+ chan->rx_credits = le_max_credits;
+ chan->mps = min_t(u16, chan->imtu, L2CAP_LE_DEFAULT_MPS);
+
+ skb_queue_head_init(&chan->tx_q);
+}
+
void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
{
BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn,
@@ -502,12 +522,12 @@ void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan)
switch (chan->chan_type) {
case L2CAP_CHAN_CONN_ORIENTED:
if (conn->hcon->type == LE_LINK) {
- /* LE connection */
- chan->omtu = L2CAP_DEFAULT_MTU;
- if (chan->dcid == L2CAP_CID_ATT)
+ if (chan->dcid == L2CAP_CID_ATT) {
+ chan->omtu = L2CAP_DEFAULT_MTU;
chan->scid = L2CAP_CID_ATT;
- else
+ } else {
chan->scid = l2cap_alloc_cid(conn);
+ }
} else {
/* Alloc CID for connection-oriented socket */
chan->scid = l2cap_alloc_cid(conn);
@@ -597,6 +617,10 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
case L2CAP_MODE_BASIC:
break;
+ case L2CAP_MODE_LE_FLOWCTL:
+ skb_queue_purge(&chan->tx_q);
+ break;
+
case L2CAP_MODE_ERTM:
__clear_retrans_timer(chan);
__clear_monitor_timer(chan);
@@ -617,6 +641,50 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err)
return;
}
+static void l2cap_chan_le_connect_reject(struct l2cap_chan *chan)
+{
+ struct l2cap_conn *conn = chan->conn;
+ struct l2cap_le_conn_rsp rsp;
+ u16 result;
+
+ if (test_bit(FLAG_DEFER_SETUP, &chan->flags))
+ result = L2CAP_CR_AUTHORIZATION;
+ else
+ result = L2CAP_CR_BAD_PSM;
+
+ l2cap_state_change(chan, BT_DISCONN);
+
+ rsp.dcid = cpu_to_le16(chan->scid);
+ rsp.mtu = cpu_to_le16(chan->imtu);
+ rsp.mps = cpu_to_le16(chan->mps);
+ rsp.credits = cpu_to_le16(chan->rx_credits);
+ rsp.result = cpu_to_le16(result);
+
+ l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_RSP, sizeof(rsp),
+ &rsp);
+}
+
+static void l2cap_chan_connect_reject(struct l2cap_chan *chan)
+{
+ struct l2cap_conn *conn = chan->conn;
+ struct l2cap_conn_rsp rsp;
+ u16 result;
+
+ if (test_bit(FLAG_DEFER_SETUP, &chan->flags))
+ result = L2CAP_CR_SEC_BLOCK;
+ else
+ result = L2CAP_CR_BAD_PSM;
+
+ l2cap_state_change(chan, BT_DISCONN);
+
+ rsp.scid = cpu_to_le16(chan->dcid);
+ rsp.dcid = cpu_to_le16(chan->scid);
+ rsp.result = cpu_to_le16(result);
+ rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
+
+ l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp);
+}
+
void l2cap_chan_close(struct l2cap_chan *chan, int reason)
{
struct l2cap_conn *conn = chan->conn;
@@ -630,8 +698,10 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
case BT_CONNECTED:
case BT_CONFIG:
- if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED &&
- conn->hcon->type == ACL_LINK) {
+ /* ATT uses L2CAP_CHAN_CONN_ORIENTED so we must also
+ * check for chan->psm.
+ */
+ if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED && chan->psm) {
__set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
l2cap_send_disconn_req(chan, reason);
} else
@@ -639,24 +709,11 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason)
break;
case BT_CONNECT2:
- if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED &&
- conn->hcon->type == ACL_LINK) {
- struct l2cap_conn_rsp rsp;
- __u16 result;
-
- if (test_bit(FLAG_DEFER_SETUP, &chan->flags))
- result = L2CAP_CR_SEC_BLOCK;
- else
- result = L2CAP_CR_BAD_PSM;
-
- l2cap_state_change(chan, BT_DISCONN);
-
- rsp.scid = cpu_to_le16(chan->dcid);
- rsp.dcid = cpu_to_le16(chan->scid);
- rsp.result = cpu_to_le16(result);
- rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO);
- l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP,
- sizeof(rsp), &rsp);
+ if (chan->chan_type == L2CAP_CHAN_CONN_ORIENTED) {
+ if (conn->hcon->type == ACL_LINK)
+ l2cap_chan_connect_reject(chan);
+ else if (conn->hcon->type == LE_LINK)
+ l2cap_chan_le_connect_reject(chan);
}
l2cap_chan_del(chan, reason);
@@ -726,6 +783,9 @@ int l2cap_chan_check_security(struct l2cap_chan *chan)
struct l2cap_conn *conn = chan->conn;
__u8 auth_type;
+ if (conn->hcon->type == LE_LINK)
+ return smp_conn_security(conn->hcon, chan->sec_level);
+
auth_type = l2cap_get_auth_type(chan);
return hci_conn_security(conn->hcon, chan->sec_level, auth_type);
@@ -1152,16 +1212,57 @@ static void l2cap_chan_ready(struct l2cap_chan *chan)
chan->conf_state = 0;
__clear_chan_timer(chan);
+ if (chan->mode == L2CAP_MODE_LE_FLOWCTL && !chan->tx_credits)
+ chan->ops->suspend(chan);
+
chan->state = BT_CONNECTED;
chan->ops->ready(chan);
}
+static void l2cap_le_connect(struct l2cap_chan *chan)
+{
+ struct l2cap_conn *conn = chan->conn;
+ struct l2cap_le_conn_req req;
+
+ if (test_and_set_bit(FLAG_LE_CONN_REQ_SENT, &chan->flags))
+ return;
+
+ req.psm = chan->psm;
+ req.scid = cpu_to_le16(chan->scid);
+ req.mtu = cpu_to_le16(chan->imtu);
+ req.mps = cpu_to_le16(chan->mps);
+ req.credits = cpu_to_le16(chan->rx_credits);
+
+ chan->ident = l2cap_get_ident(conn);
+
+ l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_REQ,
+ sizeof(req), &req);
+}
+
+static void l2cap_le_start(struct l2cap_chan *chan)
+{
+ struct l2cap_conn *conn = chan->conn;
+
+ if (!smp_conn_security(conn->hcon, chan->sec_level))
+ return;
+
+ if (!chan->psm) {
+ l2cap_chan_ready(chan);
+ return;
+ }
+
+ if (chan->state == BT_CONNECT)
+ l2cap_le_connect(chan);
+}
+
static void l2cap_start_connection(struct l2cap_chan *chan)
{
if (__amp_capable(chan)) {
BT_DBG("chan %p AMP capable: discover AMPs", chan);
a2mp_discover_amp(chan);
+ } else if (chan->conn->hcon->type == LE_LINK) {
+ l2cap_le_start(chan);
} else {
l2cap_send_conn_req(chan);
}
@@ -1172,7 +1273,7 @@ static void l2cap_do_start(struct l2cap_chan *chan)
struct l2cap_conn *conn = chan->conn;
if (conn->hcon->type == LE_LINK) {
- l2cap_chan_ready(chan);
+ l2cap_le_start(chan);
return;
}
@@ -1430,9 +1531,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
}
if (hcon->type == LE_LINK) {
- if (smp_conn_security(hcon, chan->sec_level))
- l2cap_chan_ready(chan);
-
+ l2cap_le_start(chan);
} else if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
l2cap_chan_ready(chan);
@@ -1703,7 +1802,8 @@ EXPORT_SYMBOL(l2cap_conn_put);
*/
static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
bdaddr_t *src,
- bdaddr_t *dst)
+ bdaddr_t *dst,
+ u8 link_type)
{
struct l2cap_chan *c, *c1 = NULL;
@@ -1713,6 +1813,12 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
if (state && c->state != state)
continue;
+ if (link_type == ACL_LINK && c->src_type != BDADDR_BREDR)
+ continue;
+
+ if (link_type == LE_LINK && c->src_type == BDADDR_BREDR)
+ continue;
+
if (c->psm == psm) {
int src_match, dst_match;
int src_any, dst_any;
@@ -1739,6 +1845,18 @@ static struct l2cap_chan *l2cap_global_chan_by_psm(int state, __le16 psm,
return c1;
}
+static bool is_valid_psm(u16 psm, u8 dst_type)
+{
+ if (!psm)
+ return false;
+
+ if (bdaddr_type_is_le(dst_type))
+ return (psm <= 0x00ff);
+
+ /* PSM must be odd and lsb of upper byte must be 0 */
+ return ((psm & 0x0101) == 0x0001);
+}
+
int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
bdaddr_t *dst, u8 dst_type)
{
@@ -1759,8 +1877,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
l2cap_chan_lock(chan);
- /* PSM must be odd and lsb of upper byte must be 0 */
- if ((__le16_to_cpu(psm) & 0x0101) != 0x0001 && !cid &&
+ if (!is_valid_psm(__le16_to_cpu(psm), dst_type) && !cid &&
chan->chan_type != L2CAP_CHAN_RAW) {
err = -EINVAL;
goto done;
@@ -1774,6 +1891,9 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
switch (chan->mode) {
case L2CAP_MODE_BASIC:
break;
+ case L2CAP_MODE_LE_FLOWCTL:
+ l2cap_le_flowctl_init(chan);
+ break;
case L2CAP_MODE_ERTM:
case L2CAP_MODE_STREAMING:
if (!disable_ertm)
@@ -2432,6 +2552,89 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan,
return 0;
}
+static struct sk_buff *l2cap_create_le_flowctl_pdu(struct l2cap_chan *chan,
+ struct msghdr *msg,
+ size_t len, u16 sdulen)
+{
+ struct l2cap_conn *conn = chan->conn;
+ struct sk_buff *skb;
+ int err, count, hlen;
+ struct l2cap_hdr *lh;
+
+ BT_DBG("chan %p len %zu", chan, len);
+
+ if (!conn)
+ return ERR_PTR(-ENOTCONN);
+
+ hlen = L2CAP_HDR_SIZE;
+
+ if (sdulen)
+ hlen += L2CAP_SDULEN_SIZE;
+
+ count = min_t(unsigned int, (conn->mtu - hlen), len);
+
+ skb = chan->ops->alloc_skb(chan, count + hlen,
+ msg->msg_flags & MSG_DONTWAIT);
+ if (IS_ERR(skb))
+ return skb;
+
+ /* Create L2CAP header */
+ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
+ lh->cid = cpu_to_le16(chan->dcid);
+ lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
+
+ if (sdulen)
+ put_unaligned_le16(sdulen, skb_put(skb, L2CAP_SDULEN_SIZE));
+
+ err = l2cap_skbuff_fromiovec(chan, msg, len, count, skb);
+ if (unlikely(err < 0)) {
+ kfree_skb(skb);
+ return ERR_PTR(err);
+ }
+
+ return skb;
+}
+
+static int l2cap_segment_le_sdu(struct l2cap_chan *chan,
+ struct sk_buff_head *seg_queue,
+ struct msghdr *msg, size_t len)
+{
+ struct sk_buff *skb;
+ size_t pdu_len;
+ u16 sdu_len;
+
+ BT_DBG("chan %p, msg %p, len %zu", chan, msg, len);
+
+ pdu_len = chan->conn->mtu - L2CAP_HDR_SIZE;
+
+ pdu_len = min_t(size_t, pdu_len, chan->remote_mps);
+
+ sdu_len = len;
+ pdu_len -= L2CAP_SDULEN_SIZE;
+
+ while (len > 0) {
+ if (len <= pdu_len)
+ pdu_len = len;
+
+ skb = l2cap_create_le_flowctl_pdu(chan, msg, pdu_len, sdu_len);
+ if (IS_ERR(skb)) {
+ __skb_queue_purge(seg_queue);
+ return PTR_ERR(skb);
+ }
+
+ __skb_queue_tail(seg_queue, skb);
+
+ len -= pdu_len;
+
+ if (sdu_len) {
+ sdu_len = 0;
+ pdu_len += L2CAP_SDULEN_SIZE;
+ }
+ }
+
+ return 0;
+}
+
int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
u32 priority)
{
@@ -2453,6 +2656,40 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len,
}
switch (chan->mode) {
+ case L2CAP_MODE_LE_FLOWCTL:
+ /* Check outgoing MTU */
+ if (len > chan->omtu)
+ return -EMSGSIZE;
+
+ if (!chan->tx_credits)
+ return -EAGAIN;
+
+ __skb_queue_head_init(&seg_queue);
+
+ err = l2cap_segment_le_sdu(chan, &seg_queue, msg, len);
+
+ if (chan->state != BT_CONNECTED) {
+ __skb_queue_purge(&seg_queue);
+ err = -ENOTCONN;
+ }
+
+ if (err)
+ return err;
+
+ skb_queue_splice_tail_init(&seg_queue, &chan->tx_q);
+
+ while (chan->tx_credits && !skb_queue_empty(&chan->tx_q)) {
+ l2cap_do_send(chan, skb_dequeue(&chan->tx_q));
+ chan->tx_credits--;
+ }
+
+ if (!chan->tx_credits)
+ chan->ops->suspend(chan);
+
+ err = len;
+
+ break;
+
case L2CAP_MODE_BASIC:
/* Check outgoing MTU */
if (len > chan->omtu)
@@ -3592,6 +3829,23 @@ static int l2cap_build_conf_rsp(struct l2cap_chan *chan, void *data,
return ptr - data;
}
+void __l2cap_le_connect_rsp_defer(struct l2cap_chan *chan)
+{
+ struct l2cap_le_conn_rsp rsp;
+ struct l2cap_conn *conn = chan->conn;
+
+ BT_DBG("chan %p", chan);
+
+ rsp.dcid = cpu_to_le16(chan->scid);
+ rsp.mtu = cpu_to_le16(chan->imtu);
+ rsp.mps = cpu_to_le16(chan->mps);
+ rsp.credits = cpu_to_le16(chan->rx_credits);
+ rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS);
+
+ l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CONN_RSP, sizeof(rsp),
+ &rsp);
+}
+
void __l2cap_connect_rsp_defer(struct l2cap_chan *chan)
{
struct l2cap_conn_rsp rsp;
@@ -3713,7 +3967,7 @@ static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn,
/* Check if we have socket listening on psm */
pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, &conn->hcon->src,
- &conn->hcon->dst);
+ &conn->hcon->dst, ACL_LINK);
if (!pchan) {
result = L2CAP_CR_BAD_PSM;
goto sendresp;
@@ -5155,18 +5409,17 @@ static inline int l2cap_check_conn_param(u16 min, u16 max, u16 latency,
static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd,
- u8 *data)
+ u16 cmd_len, u8 *data)
{
struct hci_conn *hcon = conn->hcon;
struct l2cap_conn_param_update_req *req;
struct l2cap_conn_param_update_rsp rsp;
- u16 min, max, latency, to_multiplier, cmd_len;
+ u16 min, max, latency, to_multiplier;
int err;
if (!(hcon->link_mode & HCI_LM_MASTER))
return -EINVAL;
- cmd_len = __le16_to_cpu(cmd->len);
if (cmd_len != sizeof(struct l2cap_conn_param_update_req))
return -EPROTO;
@@ -5196,6 +5449,65 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn,
return 0;
}
+static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+ u8 *data)
+{
+ struct l2cap_le_conn_rsp *rsp = (struct l2cap_le_conn_rsp *) data;
+ u16 dcid, mtu, mps, credits, result;
+ struct l2cap_chan *chan;
+ int err;
+
+ if (cmd_len < sizeof(*rsp))
+ return -EPROTO;
+
+ dcid = __le16_to_cpu(rsp->dcid);
+ mtu = __le16_to_cpu(rsp->mtu);
+ mps = __le16_to_cpu(rsp->mps);
+ credits = __le16_to_cpu(rsp->credits);
+ result = __le16_to_cpu(rsp->result);
+
+ if (result == L2CAP_CR_SUCCESS && (mtu < 23 || mps < 23))
+ return -EPROTO;
+
+ BT_DBG("dcid 0x%4.4x mtu %u mps %u credits %u result 0x%2.2x",
+ dcid, mtu, mps, credits, result);
+
+ mutex_lock(&conn->chan_lock);
+
+ chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
+ if (!chan) {
+ err = -EBADSLT;
+ goto unlock;
+ }
+
+ err = 0;
+
+ l2cap_chan_lock(chan);
+
+ switch (result) {
+ case L2CAP_CR_SUCCESS:
+ chan->ident = 0;
+ chan->dcid = dcid;
+ chan->omtu = mtu;
+ chan->remote_mps = mps;
+ chan->tx_credits = credits;
+ l2cap_chan_ready(chan);
+ break;
+
+ default:
+ l2cap_chan_del(chan, ECONNREFUSED);
+ break;
+ }
+
+ l2cap_chan_unlock(chan);
+
+unlock:
+ mutex_unlock(&conn->chan_lock);
+
+ return err;
+}
+
static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
struct l2cap_cmd_hdr *cmd, u16 cmd_len,
u8 *data)
@@ -5276,23 +5588,235 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn,
return err;
}
+static int l2cap_le_connect_req(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+ u8 *data)
+{
+ struct l2cap_le_conn_req *req = (struct l2cap_le_conn_req *) data;
+ struct l2cap_le_conn_rsp rsp;
+ struct l2cap_chan *chan, *pchan;
+ u16 dcid, scid, credits, mtu, mps;
+ __le16 psm;
+ u8 result;
+
+ if (cmd_len != sizeof(*req))
+ return -EPROTO;
+
+ scid = __le16_to_cpu(req->scid);
+ mtu = __le16_to_cpu(req->mtu);
+ mps = __le16_to_cpu(req->mps);
+ psm = req->psm;
+ dcid = 0;
+ credits = 0;
+
+ if (mtu < 23 || mps < 23)
+ return -EPROTO;
+
+ BT_DBG("psm 0x%2.2x scid 0x%4.4x mtu %u mps %u", __le16_to_cpu(psm),
+ scid, mtu, mps);
+
+ /* Check if we have socket listening on psm */
+ pchan = l2cap_global_chan_by_psm(BT_LISTEN, psm, &conn->hcon->src,
+ &conn->hcon->dst, LE_LINK);
+ if (!pchan) {
+ result = L2CAP_CR_BAD_PSM;
+ chan = NULL;
+ goto response;
+ }
+
+ mutex_lock(&conn->chan_lock);
+ l2cap_chan_lock(pchan);
+
+ if (!smp_sufficient_security(conn->hcon, pchan->sec_level)) {
+ result = L2CAP_CR_AUTHENTICATION;
+ chan = NULL;
+ goto response_unlock;
+ }
+
+ /* Check if we already have channel with that dcid */
+ if (__l2cap_get_chan_by_dcid(conn, scid)) {
+ result = L2CAP_CR_NO_MEM;
+ chan = NULL;
+ goto response_unlock;
+ }
+
+ chan = pchan->ops->new_connection(pchan);
+ if (!chan) {
+ result = L2CAP_CR_NO_MEM;
+ goto response_unlock;
+ }
+
+ l2cap_le_flowctl_init(chan);
+
+ bacpy(&chan->src, &conn->hcon->src);
+ bacpy(&chan->dst, &conn->hcon->dst);
+ chan->src_type = bdaddr_type(conn->hcon, conn->hcon->src_type);
+ chan->dst_type = bdaddr_type(conn->hcon, conn->hcon->dst_type);
+ chan->psm = psm;
+ chan->dcid = scid;
+ chan->omtu = mtu;
+ chan->remote_mps = mps;
+ chan->tx_credits = __le16_to_cpu(req->credits);
+
+ __l2cap_chan_add(conn, chan);
+ dcid = chan->scid;
+ credits = chan->rx_credits;
+
+ __set_chan_timer(chan, chan->ops->get_sndtimeo(chan));
+
+ chan->ident = cmd->ident;
+
+ if (test_bit(FLAG_DEFER_SETUP, &chan->flags)) {
+ l2cap_state_change(chan, BT_CONNECT2);
+ result = L2CAP_CR_PEND;
+ chan->ops->defer(chan);
+ } else {
+ l2cap_chan_ready(chan);
+ result = L2CAP_CR_SUCCESS;
+ }
+
+response_unlock:
+ l2cap_chan_unlock(pchan);
+ mutex_unlock(&conn->chan_lock);
+
+ if (result == L2CAP_CR_PEND)
+ return 0;
+
+response:
+ if (chan) {
+ rsp.mtu = cpu_to_le16(chan->imtu);
+ rsp.mps = cpu_to_le16(chan->mps);
+ } else {
+ rsp.mtu = 0;
+ rsp.mps = 0;
+ }
+
+ rsp.dcid = cpu_to_le16(dcid);
+ rsp.credits = cpu_to_le16(credits);
+ rsp.result = cpu_to_le16(result);
+
+ l2cap_send_cmd(conn, cmd->ident, L2CAP_LE_CONN_RSP, sizeof(rsp), &rsp);
+
+ return 0;
+}
+
+static inline int l2cap_le_credits(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+ u8 *data)
+{
+ struct l2cap_le_credits *pkt;
+ struct l2cap_chan *chan;
+ u16 cid, credits;
+
+ if (cmd_len != sizeof(*pkt))
+ return -EPROTO;
+
+ pkt = (struct l2cap_le_credits *) data;
+ cid = __le16_to_cpu(pkt->cid);
+ credits = __le16_to_cpu(pkt->credits);
+
+ BT_DBG("cid 0x%4.4x credits 0x%4.4x", cid, credits);
+
+ chan = l2cap_get_chan_by_dcid(conn, cid);
+ if (!chan)
+ return -EBADSLT;
+
+ chan->tx_credits += credits;
+
+ while (chan->tx_credits && !skb_queue_empty(&chan->tx_q)) {
+ l2cap_do_send(chan, skb_dequeue(&chan->tx_q));
+ chan->tx_credits--;
+ }
+
+ if (chan->tx_credits)
+ chan->ops->resume(chan);
+
+ l2cap_chan_unlock(chan);
+
+ return 0;
+}
+
+static inline int l2cap_le_command_rej(struct l2cap_conn *conn,
+ struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+ u8 *data)
+{
+ struct l2cap_cmd_rej_unk *rej = (struct l2cap_cmd_rej_unk *) data;
+ struct l2cap_chan *chan;
+
+ if (cmd_len < sizeof(*rej))
+ return -EPROTO;
+
+ mutex_lock(&conn->chan_lock);
+
+ chan = __l2cap_get_chan_by_ident(conn, cmd->ident);
+ if (!chan)
+ goto done;
+
+ l2cap_chan_lock(chan);
+ l2cap_chan_del(chan, ECONNREFUSED);
+ l2cap_chan_unlock(chan);
+
+done:
+ mutex_unlock(&conn->chan_lock);
+ return 0;
+}
+
static inline int l2cap_le_sig_cmd(struct l2cap_conn *conn,
- struct l2cap_cmd_hdr *cmd, u8 *data)
+ struct l2cap_cmd_hdr *cmd, u16 cmd_len,
+ u8 *data)
{
+ int err = 0;
+
+ if (!enable_lecoc) {
+ switch (cmd->code) {
+ case L2CAP_LE_CONN_REQ:
+ case L2CAP_LE_CONN_RSP:
+ case L2CAP_LE_CREDITS:
+ case L2CAP_DISCONN_REQ:
+ case L2CAP_DISCONN_RSP:
+ return -EINVAL;
+ }
+ }
+
switch (cmd->code) {
case L2CAP_COMMAND_REJ:
- return 0;
+ l2cap_le_command_rej(conn, cmd, cmd_len, data);
+ break;
case L2CAP_CONN_PARAM_UPDATE_REQ:
- return l2cap_conn_param_update_req(conn, cmd, data);
+ err = l2cap_conn_param_update_req(conn, cmd, cmd_len, data);
+ break;
case L2CAP_CONN_PARAM_UPDATE_RSP:
- return 0;
+ break;
+
+ case L2CAP_LE_CONN_RSP:
+ l2cap_le_connect_rsp(conn, cmd, cmd_len, data);
+ break;
+
+ case L2CAP_LE_CONN_REQ:
+ err = l2cap_le_connect_req(conn, cmd, cmd_len, data);
+ break;
+
+ case L2CAP_LE_CREDITS:
+ err = l2cap_le_credits(conn, cmd, cmd_len, data);
+ break;
+
+ case L2CAP_DISCONN_REQ:
+ err = l2cap_disconnect_req(conn, cmd, cmd_len, data);
+ break;
+
+ case L2CAP_DISCONN_RSP:
+ l2cap_disconnect_rsp(conn, cmd, cmd_len, data);
+ break;
default:
BT_ERR("Unknown LE signaling command 0x%2.2x", cmd->code);
- return -EINVAL;
+ err = -EINVAL;
+ break;
}
+
+ return err;
}
static inline void l2cap_le_sig_channel(struct l2cap_conn *conn,
@@ -5321,7 +5845,7 @@ static inline void l2cap_le_sig_channel(struct l2cap_conn *conn,
goto drop;
}
- err = l2cap_le_sig_cmd(conn, cmd, skb->data);
+ err = l2cap_le_sig_cmd(conn, cmd, len, skb->data);
if (err) {
struct l2cap_cmd_rej_unk rej;
@@ -6312,6 +6836,121 @@ drop:
return 0;
}
+static void l2cap_chan_le_send_credits(struct l2cap_chan *chan)
+{
+ struct l2cap_conn *conn = chan->conn;
+ struct l2cap_le_credits pkt;
+ u16 return_credits;
+
+ /* We return more credits to the sender only after the amount of
+ * credits falls below half of the initial amount.
+ */
+ if (chan->rx_credits >= (le_max_credits + 1) / 2)
+ return;
+
+ return_credits = le_max_credits - chan->rx_credits;
+
+ BT_DBG("chan %p returning %u credits to sender", chan, return_credits);
+
+ chan->rx_credits += return_credits;
+
+ pkt.cid = cpu_to_le16(chan->scid);
+ pkt.credits = cpu_to_le16(return_credits);
+
+ chan->ident = l2cap_get_ident(conn);
+
+ l2cap_send_cmd(conn, chan->ident, L2CAP_LE_CREDITS, sizeof(pkt), &pkt);
+}
+
+static int l2cap_le_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb)
+{
+ int err;
+
+ if (!chan->rx_credits) {
+ BT_ERR("No credits to receive LE L2CAP data");
+ return -ENOBUFS;
+ }
+
+ if (chan->imtu < skb->len) {
+ BT_ERR("Too big LE L2CAP PDU");
+ return -ENOBUFS;
+ }
+
+ chan->rx_credits--;
+ BT_DBG("rx_credits %u -> %u", chan->rx_credits + 1, chan->rx_credits);
+
+ l2cap_chan_le_send_credits(chan);
+
+ err = 0;
+
+ if (!chan->sdu) {
+ u16 sdu_len;
+
+ sdu_len = get_unaligned_le16(skb->data);
+ skb_pull(skb, L2CAP_SDULEN_SIZE);
+
+ BT_DBG("Start of new SDU. sdu_len %u skb->len %u imtu %u",
+ sdu_len, skb->len, chan->imtu);
+
+ if (sdu_len > chan->imtu) {
+ BT_ERR("Too big LE L2CAP SDU length received");
+ err = -EMSGSIZE;
+ goto failed;
+ }
+
+ if (skb->len > sdu_len) {
+ BT_ERR("Too much LE L2CAP data received");
+ err = -EINVAL;
+ goto failed;
+ }
+
+ if (skb->len == sdu_len)
+ return chan->ops->recv(chan, skb);
+
+ chan->sdu = skb;
+ chan->sdu_len = sdu_len;
+ chan->sdu_last_frag = skb;
+
+ return 0;
+ }
+
+ BT_DBG("SDU fragment. chan->sdu->len %u skb->len %u chan->sdu_len %u",
+ chan->sdu->len, skb->len, chan->sdu_len);
+
+ if (chan->sdu->len + skb->len > chan->sdu_len) {
+ BT_ERR("Too much LE L2CAP data received");
+ err = -EINVAL;
+ goto failed;
+ }
+
+ append_skb_frag(chan->sdu, skb, &chan->sdu_last_frag);
+ skb = NULL;
+
+ if (chan->sdu->len == chan->sdu_len) {
+ err = chan->ops->recv(chan, chan->sdu);
+ if (!err) {
+ chan->sdu = NULL;
+ chan->sdu_last_frag = NULL;
+ chan->sdu_len = 0;
+ }
+ }
+
+failed:
+ if (err) {
+ kfree_skb(skb);
+ kfree_skb(chan->sdu);
+ chan->sdu = NULL;
+ chan->sdu_last_frag = NULL;
+ chan->sdu_len = 0;
+ }
+
+ /* We can't return an error here since we took care of the skb
+ * freeing internally. An error return would cause the caller to
+ * do a double-free of the skb.
+ */
+ return 0;
+}
+
static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid,
struct sk_buff *skb)
{
@@ -6341,6 +6980,12 @@ static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid,
goto drop;
switch (chan->mode) {
+ case L2CAP_MODE_LE_FLOWCTL:
+ if (l2cap_le_data_rcv(chan, skb) < 0)
+ goto drop;
+
+ goto done;
+
case L2CAP_MODE_BASIC:
/* If socket recv buffers overflows we drop data here
* which is *bad* because L2CAP has to be reliable.
@@ -6380,7 +7025,8 @@ static void l2cap_conless_channel(struct l2cap_conn *conn, __le16 psm,
if (hcon->type != ACL_LINK)
goto drop;
- chan = l2cap_global_chan_by_psm(0, psm, &hcon->src, &hcon->dst);
+ chan = l2cap_global_chan_by_psm(0, psm, &hcon->src, &hcon->dst,
+ ACL_LINK);
if (!chan)
goto drop;
@@ -6612,11 +7258,10 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
}
if (chan->state == BT_CONNECT) {
- if (!status) {
+ if (!status)
l2cap_start_connection(chan);
- } else {
+ else
__set_chan_timer(chan, L2CAP_DISC_TIMEOUT);
- }
} else if (chan->state == BT_CONNECT2) {
struct l2cap_conn_rsp rsp;
__u16 res, stat;
@@ -6817,6 +7462,11 @@ int __init l2cap_init(void)
l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs,
NULL, &l2cap_debugfs_fops);
+ debugfs_create_u16("l2cap_le_max_credits", 0466, bt_debugfs,
+ &le_max_credits);
+ debugfs_create_u16("l2cap_le_default_mps", 0466, bt_debugfs,
+ &le_default_mps);
+
return 0;
}
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index 7cc24d263caa..e7806e6d282c 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -27,6 +27,7 @@
/* Bluetooth L2CAP sockets. */
+#include <linux/module.h>
#include <linux/export.h>
#include <net/bluetooth/bluetooth.h>
@@ -35,6 +36,8 @@
#include "smp.h"
+bool enable_lecoc;
+
static struct bt_sock_list l2cap_sk_list = {
.lock = __RW_LOCK_UNLOCKED(l2cap_sk_list.lock)
};
@@ -50,6 +53,32 @@ bool l2cap_is_socket(struct socket *sock)
}
EXPORT_SYMBOL(l2cap_is_socket);
+static int l2cap_validate_bredr_psm(u16 psm)
+{
+ /* PSM must be odd and lsb of upper byte must be 0 */
+ if ((psm & 0x0101) != 0x0001)
+ return -EINVAL;
+
+ /* Restrict usage of well-known PSMs */
+ if (psm < 0x1001 && !capable(CAP_NET_BIND_SERVICE))
+ return -EACCES;
+
+ return 0;
+}
+
+static int l2cap_validate_le_psm(u16 psm)
+{
+ /* Valid LE_PSM ranges are defined only until 0x00ff */
+ if (psm > 0x00ff)
+ return -EINVAL;
+
+ /* Restrict fixed, SIG assigned PSM values to CAP_NET_BIND_SERVICE */
+ if (psm <= 0x007f && !capable(CAP_NET_BIND_SERVICE))
+ return -EACCES;
+
+ return 0;
+}
+
static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
{
struct sock *sk = sock->sk;
@@ -73,11 +102,11 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
return -EINVAL;
if (bdaddr_type_is_le(la.l2_bdaddr_type)) {
- /* Connection oriented channels are not supported on LE */
- if (la.l2_psm)
+ if (!enable_lecoc && la.l2_psm)
return -EINVAL;
/* We only allow ATT user space socket */
- if (la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
+ if (la.l2_cid &&
+ la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
return -EINVAL;
}
@@ -91,17 +120,13 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
if (la.l2_psm) {
__u16 psm = __le16_to_cpu(la.l2_psm);
- /* PSM must be odd and lsb of upper byte must be 0 */
- if ((psm & 0x0101) != 0x0001) {
- err = -EINVAL;
- goto done;
- }
+ if (la.l2_bdaddr_type == BDADDR_BREDR)
+ err = l2cap_validate_bredr_psm(psm);
+ else
+ err = l2cap_validate_le_psm(psm);
- /* Restrict usage of well-known PSMs */
- if (psm < 0x1001 && !capable(CAP_NET_BIND_SERVICE)) {
- err = -EACCES;
+ if (err)
goto done;
- }
}
if (la.l2_cid)
@@ -127,6 +152,9 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
bacpy(&chan->src, &la.l2_bdaddr);
chan->src_type = la.l2_bdaddr_type;
+ if (chan->psm && bdaddr_type_is_le(chan->src_type))
+ chan->mode = L2CAP_MODE_LE_FLOWCTL;
+
chan->state = BT_BOUND;
sk->sk_state = BT_BOUND;
@@ -189,14 +217,17 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr,
return -EINVAL;
if (bdaddr_type_is_le(la.l2_bdaddr_type)) {
- /* Connection oriented channels are not supported on LE */
- if (la.l2_psm)
+ if (!enable_lecoc && la.l2_psm)
return -EINVAL;
/* We only allow ATT user space socket */
- if (la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
+ if (la.l2_cid &&
+ la.l2_cid != __constant_cpu_to_le16(L2CAP_CID_ATT))
return -EINVAL;
}
+ if (chan->psm && bdaddr_type_is_le(chan->src_type))
+ chan->mode = L2CAP_MODE_LE_FLOWCTL;
+
err = l2cap_chan_connect(chan, la.l2_psm, __le16_to_cpu(la.l2_cid),
&la.l2_bdaddr, la.l2_bdaddr_type);
if (err)
@@ -234,6 +265,7 @@ static int l2cap_sock_listen(struct socket *sock, int backlog)
switch (chan->mode) {
case L2CAP_MODE_BASIC:
+ case L2CAP_MODE_LE_FLOWCTL:
break;
case L2CAP_MODE_ERTM:
case L2CAP_MODE_STREAMING:
@@ -360,6 +392,16 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname,
switch (optname) {
case L2CAP_OPTIONS:
+ /* LE sockets should use BT_SNDMTU/BT_RCVMTU, but since
+ * legacy ATT code depends on getsockopt for
+ * L2CAP_OPTIONS we need to let this pass.
+ */
+ if (bdaddr_type_is_le(chan->src_type) &&
+ chan->scid != L2CAP_CID_ATT) {
+ err = -EINVAL;
+ break;
+ }
+
memset(&opts, 0, sizeof(opts));
opts.imtu = chan->imtu;
opts.omtu = chan->omtu;
@@ -514,6 +556,41 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname,
err = -EFAULT;
break;
+ case BT_SNDMTU:
+ if (!enable_lecoc) {
+ err = -EPROTONOSUPPORT;
+ break;
+ }
+
+ if (!bdaddr_type_is_le(chan->src_type)) {
+ err = -EINVAL;
+ break;
+ }
+
+ if (sk->sk_state != BT_CONNECTED) {
+ err = -ENOTCONN;
+ break;
+ }
+
+ if (put_user(chan->omtu, (u16 __user *) optval))
+ err = -EFAULT;
+ break;
+
+ case BT_RCVMTU:
+ if (!enable_lecoc) {
+ err = -EPROTONOSUPPORT;
+ break;
+ }
+
+ if (!bdaddr_type_is_le(chan->src_type)) {
+ err = -EINVAL;
+ break;
+ }
+
+ if (put_user(chan->imtu, (u16 __user *) optval))
+ err = -EFAULT;
+ break;
+
default:
err = -ENOPROTOOPT;
break;
@@ -554,6 +631,11 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
switch (optname) {
case L2CAP_OPTIONS:
+ if (bdaddr_type_is_le(chan->src_type)) {
+ err = -EINVAL;
+ break;
+ }
+
if (sk->sk_state == BT_CONNECTED) {
err = -EINVAL;
break;
@@ -585,6 +667,8 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname,
chan->mode = opts.mode;
switch (chan->mode) {
+ case L2CAP_MODE_LE_FLOWCTL:
+ break;
case L2CAP_MODE_BASIC:
clear_bit(CONF_STATE2_DEVICE, &chan->conf_state);
break;
@@ -807,6 +891,47 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
break;
+ case BT_SNDMTU:
+ if (!enable_lecoc) {
+ err = -EPROTONOSUPPORT;
+ break;
+ }
+
+ if (!bdaddr_type_is_le(chan->src_type)) {
+ err = -EINVAL;
+ break;
+ }
+
+ /* Setting is not supported as it's the remote side that
+ * decides this.
+ */
+ err = -EPERM;
+ break;
+
+ case BT_RCVMTU:
+ if (!enable_lecoc) {
+ err = -EPROTONOSUPPORT;
+ break;
+ }
+
+ if (!bdaddr_type_is_le(chan->src_type)) {
+ err = -EINVAL;
+ break;
+ }
+
+ if (sk->sk_state == BT_CONNECTED) {
+ err = -EISCONN;
+ break;
+ }
+
+ if (get_user(opt, (u32 __user *) optval)) {
+ err = -EFAULT;
+ break;
+ }
+
+ chan->imtu = opt;
+ break;
+
default:
err = -ENOPROTOOPT;
break;
@@ -859,10 +984,16 @@ static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
if (sk->sk_state == BT_CONNECT2 && test_bit(BT_SK_DEFER_SETUP,
&bt_sk(sk)->flags)) {
- sk->sk_state = BT_CONFIG;
- pi->chan->state = BT_CONFIG;
+ if (bdaddr_type_is_le(pi->chan->src_type)) {
+ sk->sk_state = BT_CONNECTED;
+ pi->chan->state = BT_CONNECTED;
+ __l2cap_le_connect_rsp_defer(pi->chan);
+ } else {
+ sk->sk_state = BT_CONFIG;
+ pi->chan->state = BT_CONFIG;
+ __l2cap_connect_rsp_defer(pi->chan);
+ }
- __l2cap_connect_rsp_defer(pi->chan);
err = 0;
goto done;
}
@@ -1236,6 +1367,14 @@ static long l2cap_sock_get_sndtimeo_cb(struct l2cap_chan *chan)
return sk->sk_sndtimeo;
}
+static void l2cap_sock_suspend_cb(struct l2cap_chan *chan)
+{
+ struct sock *sk = chan->data;
+
+ set_bit(BT_SK_SUSPEND, &bt_sk(sk)->flags);
+ sk->sk_state_change(sk);
+}
+
static struct l2cap_ops l2cap_chan_ops = {
.name = "L2CAP Socket Interface",
.new_connection = l2cap_sock_new_connection_cb,
@@ -1246,6 +1385,7 @@ static struct l2cap_ops l2cap_chan_ops = {
.ready = l2cap_sock_ready_cb,
.defer = l2cap_sock_defer_cb,
.resume = l2cap_sock_resume_cb,
+ .suspend = l2cap_sock_suspend_cb,
.set_shutdown = l2cap_sock_set_shutdown_cb,
.get_sndtimeo = l2cap_sock_get_sndtimeo_cb,
.alloc_skb = l2cap_sock_alloc_skb_cb,
@@ -1303,6 +1443,8 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
chan->tx_win_max = pchan->tx_win_max;
chan->sec_level = pchan->sec_level;
chan->flags = pchan->flags;
+ chan->tx_credits = pchan->tx_credits;
+ chan->rx_credits = pchan->rx_credits;
security_sk_clone(parent, sk);
} else {
@@ -1469,3 +1611,6 @@ void l2cap_cleanup_sockets(void)
bt_sock_unregister(BTPROTO_L2CAP);
proto_unregister(&l2cap_proto);
}
+
+module_param(enable_lecoc, bool, 0644);
+MODULE_PARM_DESC(enable_lecoc, "Enable support for LE CoC");
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 074d83690a41..a03ca3ca91bf 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -1264,7 +1264,7 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data,
if (cp->val == 0x02) {
/* Limited discoverable mode */
- hci_cp.num_iac = 2;
+ hci_cp.num_iac = min_t(u8, hdev->num_iac, 2);
hci_cp.iac_lap[0] = 0x00; /* LIAC */
hci_cp.iac_lap[1] = 0x8b;
hci_cp.iac_lap[2] = 0x9e;
@@ -4595,6 +4595,9 @@ void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
struct mgmt_ev_device_disconnected ev;
struct sock *sk = NULL;
+ if (link_type != ACL_LINK && link_type != LE_LINK)
+ return;
+
mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk);
bacpy(&ev.addr.bdaddr, bdaddr);
@@ -4613,6 +4616,8 @@ void mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr,
void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
u8 link_type, u8 addr_type, u8 status)
{
+ u8 bdaddr_type = link_to_bdaddr(link_type, addr_type);
+ struct mgmt_cp_disconnect *cp;
struct mgmt_rp_disconnect rp;
struct pending_cmd *cmd;
@@ -4623,8 +4628,16 @@ void mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr,
if (!cmd)
return;
+ cp = cmd->param;
+
+ if (bacmp(bdaddr, &cp->addr.bdaddr))
+ return;
+
+ if (cp->addr.type != bdaddr_type)
+ return;
+
bacpy(&rp.addr.bdaddr, bdaddr);
- rp.addr.type = link_to_bdaddr(link_type, addr_type);
+ rp.addr.type = bdaddr_type;
cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT,
mgmt_status(status), &rp, sizeof(rp));
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 4b07acb8293c..45007362683b 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -53,8 +53,7 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
{
struct blkcipher_desc desc;
struct scatterlist sg;
- int err, iv_len;
- unsigned char iv[128];
+ int err;
if (tfm == NULL) {
BT_ERR("tfm %p", tfm);
@@ -72,12 +71,6 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r)
sg_init_one(&sg, r, 16);
- iv_len = crypto_blkcipher_ivsize(tfm);
- if (iv_len) {
- memset(&iv, 0xff, iv_len);
- crypto_blkcipher_set_iv(tfm, iv, iv_len);
- }
-
err = crypto_blkcipher_encrypt(&desc, &sg, &sg, 16);
if (err)
BT_ERR("Encrypt data error %d", err);
@@ -143,13 +136,6 @@ static int smp_s1(struct crypto_blkcipher *tfm, u8 k[16], u8 r1[16],
return err;
}
-static int smp_rand(u8 *buf)
-{
- get_random_bytes(buf, 16);
-
- return 0;
-}
-
static struct sk_buff *smp_build_cmd(struct l2cap_conn *conn, u8 code,
u16 dlen, void *data)
{
@@ -257,11 +243,11 @@ static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
return 0;
}
-static void smp_failure(struct l2cap_conn *conn, u8 reason, u8 send)
+static void smp_failure(struct l2cap_conn *conn, u8 reason)
{
struct hci_conn *hcon = conn->hcon;
- if (send)
+ if (reason)
smp_send_cmd(conn, SMP_CMD_PAIRING_FAIL, sizeof(reason),
&reason);
@@ -406,7 +392,7 @@ static void confirm_work(struct work_struct *work)
return;
error:
- smp_failure(conn, reason, 1);
+ smp_failure(conn, reason);
}
static void random_work(struct work_struct *work)
@@ -490,7 +476,7 @@ static void random_work(struct work_struct *work)
return;
error:
- smp_failure(conn, reason, 1);
+ smp_failure(conn, reason);
}
static struct smp_chan *smp_chan_create(struct l2cap_conn *conn)
@@ -555,10 +541,10 @@ int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey)
break;
case MGMT_OP_USER_PASSKEY_NEG_REPLY:
case MGMT_OP_USER_CONFIRM_NEG_REPLY:
- smp_failure(conn, SMP_PASSKEY_ENTRY_FAILED, 1);
+ smp_failure(conn, SMP_PASSKEY_ENTRY_FAILED);
return 0;
default:
- smp_failure(conn, SMP_PASSKEY_ENTRY_FAILED, 1);
+ smp_failure(conn, SMP_PASSKEY_ENTRY_FAILED);
return -EOPNOTSUPP;
}
@@ -606,9 +592,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
if (check_enc_key_size(conn, key_size))
return SMP_ENC_KEY_SIZE;
- ret = smp_rand(smp->prnd);
- if (ret)
- return SMP_UNSPECIFIED;
+ get_random_bytes(smp->prnd, sizeof(smp->prnd));
smp->prsp[0] = SMP_CMD_PAIRING_RSP;
memcpy(&smp->prsp[1], &rsp, sizeof(rsp));
@@ -644,9 +628,7 @@ 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;
- ret = smp_rand(smp->prnd);
- if (ret)
- return SMP_UNSPECIFIED;
+ get_random_bytes(smp->prnd, sizeof(smp->prnd));
smp->prsp[0] = SMP_CMD_PAIRING_RSP;
memcpy(&smp->prsp[1], rsp, sizeof(*rsp));
@@ -768,6 +750,17 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb)
return 0;
}
+bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level)
+{
+ if (sec_level == BT_SECURITY_LOW)
+ return true;
+
+ if (hcon->sec_level >= sec_level)
+ return true;
+
+ return false;
+}
+
int smp_conn_security(struct hci_conn *hcon, __u8 sec_level)
{
struct l2cap_conn *conn = hcon->l2cap_data;
@@ -779,10 +772,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 (sec_level == BT_SECURITY_LOW)
- return 1;
-
- if (hcon->sec_level >= sec_level)
+ if (smp_sufficient_security(hcon, sec_level))
return 1;
if (hcon->link_mode & HCI_LM_MASTER)
@@ -895,7 +885,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
break;
case SMP_CMD_PAIRING_FAIL:
- smp_failure(conn, skb->data[0], 0);
+ smp_failure(conn, 0);
reason = 0;
err = -EPERM;
break;
@@ -941,7 +931,7 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb)
done:
if (reason)
- smp_failure(conn, reason, 1);
+ smp_failure(conn, reason);
kfree_skb(skb);
return err;
diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h
index f8ba07f3e5fa..a700bcb490d7 100644
--- a/net/bluetooth/smp.h
+++ b/net/bluetooth/smp.h
@@ -136,6 +136,7 @@ struct smp_chan {
};
/* SMP Commands */
+bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level);
int smp_conn_security(struct hci_conn *hcon, __u8 sec_level);
int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb);
int smp_distribute_keys(struct l2cap_conn *conn, __u8 force);
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index f75d92e4f96b..c2fd6bc5f080 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -373,7 +373,7 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh)
p = br_port_get_rtnl(dev);
/* We want to accept dev as bridge itself if the AF_SPEC
- * is set to see if someone is setting vlan info on the brigde
+ * is set to see if someone is setting vlan info on the bridge
*/
if (!p && !afspec)
return -EINVAL;
@@ -389,7 +389,7 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh)
err = br_setport(p, tb);
spin_unlock_bh(&p->br->lock);
} else {
- /* Binary compatability with old RSTP */
+ /* Binary compatibility with old RSTP */
if (nla_len(protinfo) < sizeof(u8))
return -EINVAL;
diff --git a/net/bridge/netfilter/ebt_vlan.c b/net/bridge/netfilter/ebt_vlan.c
index eae67bf0446c..8d3f8c7651f0 100644
--- a/net/bridge/netfilter/ebt_vlan.c
+++ b/net/bridge/netfilter/ebt_vlan.c
@@ -14,8 +14,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/if_ether.h>
diff --git a/net/core/dev.c b/net/core/dev.c
index ba3b7ea5ebb3..c482fe8abf87 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2145,30 +2145,42 @@ void __netif_schedule(struct Qdisc *q)
}
EXPORT_SYMBOL(__netif_schedule);
-void dev_kfree_skb_irq(struct sk_buff *skb)
+struct dev_kfree_skb_cb {
+ enum skb_free_reason reason;
+};
+
+static struct dev_kfree_skb_cb *get_kfree_skb_cb(const struct sk_buff *skb)
+{
+ return (struct dev_kfree_skb_cb *)skb->cb;
+}
+
+void __dev_kfree_skb_irq(struct sk_buff *skb, enum skb_free_reason reason)
{
- if (atomic_dec_and_test(&skb->users)) {
- struct softnet_data *sd;
- unsigned long flags;
+ unsigned long flags;
- local_irq_save(flags);
- sd = &__get_cpu_var(softnet_data);
- skb->next = sd->completion_queue;
- sd->completion_queue = skb;
- raise_softirq_irqoff(NET_TX_SOFTIRQ);
- local_irq_restore(flags);
+ if (likely(atomic_read(&skb->users) == 1)) {
+ smp_rmb();
+ atomic_set(&skb->users, 0);
+ } else if (likely(!atomic_dec_and_test(&skb->users))) {
+ return;
}
+ get_kfree_skb_cb(skb)->reason = reason;
+ local_irq_save(flags);
+ skb->next = __this_cpu_read(softnet_data.completion_queue);
+ __this_cpu_write(softnet_data.completion_queue, skb);
+ raise_softirq_irqoff(NET_TX_SOFTIRQ);
+ local_irq_restore(flags);
}
-EXPORT_SYMBOL(dev_kfree_skb_irq);
+EXPORT_SYMBOL(__dev_kfree_skb_irq);
-void dev_kfree_skb_any(struct sk_buff *skb)
+void __dev_kfree_skb_any(struct sk_buff *skb, enum skb_free_reason reason)
{
if (in_irq() || irqs_disabled())
- dev_kfree_skb_irq(skb);
+ __dev_kfree_skb_irq(skb, reason);
else
dev_kfree_skb(skb);
}
-EXPORT_SYMBOL(dev_kfree_skb_any);
+EXPORT_SYMBOL(__dev_kfree_skb_any);
/**
@@ -2523,21 +2535,6 @@ netdev_features_t netif_skb_features(struct sk_buff *skb)
}
EXPORT_SYMBOL(netif_skb_features);
-/*
- * Returns true if either:
- * 1. skb has frag_list and the device doesn't support FRAGLIST, or
- * 2. skb is fragmented and the device does not support SG.
- */
-static inline int skb_needs_linearize(struct sk_buff *skb,
- netdev_features_t features)
-{
- return skb_is_nonlinear(skb) &&
- ((skb_has_frag_list(skb) &&
- !(features & NETIF_F_FRAGLIST)) ||
- (skb_shinfo(skb)->nr_frags &&
- !(features & NETIF_F_SG)));
-}
-
int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev,
struct netdev_queue *txq, void *accel_priv)
{
@@ -3009,7 +3006,7 @@ static int get_rps_cpu(struct net_device *dev, struct sk_buff *skb,
}
skb_reset_network_header(skb);
- if (!skb_get_rxhash(skb))
+ if (!skb_get_hash(skb))
goto done;
flow_table = rcu_dereference(rxqueue->rps_flow_table);
@@ -3154,7 +3151,7 @@ static bool skb_flow_limit(struct sk_buff *skb, unsigned int qlen)
rcu_read_lock();
fl = rcu_dereference(sd->flow_limit);
if (fl) {
- new_flow = skb_get_rxhash(skb) & (fl->num_buckets - 1);
+ new_flow = skb_get_hash(skb) & (fl->num_buckets - 1);
old_flow = fl->history[fl->history_head];
fl->history[fl->history_head] = new_flow;
@@ -3306,7 +3303,10 @@ static void net_tx_action(struct softirq_action *h)
clist = clist->next;
WARN_ON(atomic_read(&skb->users));
- trace_kfree_skb(skb, net_tx_action);
+ if (likely(get_kfree_skb_cb(skb)->reason == SKB_REASON_CONSUMED))
+ trace_consume_skb(skb);
+ else
+ trace_kfree_skb(skb, net_tx_action);
__kfree_skb(skb);
}
}
@@ -3752,7 +3752,7 @@ static int napi_gro_complete(struct sk_buff *skb)
if (ptype->type != type || !ptype->callbacks.gro_complete)
continue;
- err = ptype->callbacks.gro_complete(skb);
+ err = ptype->callbacks.gro_complete(skb, 0);
break;
}
rcu_read_unlock();
@@ -3818,6 +3818,23 @@ static void gro_list_prepare(struct napi_struct *napi, struct sk_buff *skb)
}
}
+static void skb_gro_reset_offset(struct sk_buff *skb)
+{
+ const struct skb_shared_info *pinfo = skb_shinfo(skb);
+ const skb_frag_t *frag0 = &pinfo->frags[0];
+
+ NAPI_GRO_CB(skb)->data_offset = 0;
+ NAPI_GRO_CB(skb)->frag0 = NULL;
+ NAPI_GRO_CB(skb)->frag0_len = 0;
+
+ if (skb_mac_header(skb) == skb_tail_pointer(skb) &&
+ pinfo->nr_frags &&
+ !PageHighMem(skb_frag_page(frag0))) {
+ NAPI_GRO_CB(skb)->frag0 = skb_frag_address(frag0);
+ NAPI_GRO_CB(skb)->frag0_len = skb_frag_size(frag0);
+ }
+}
+
static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
{
struct sk_buff **pp = NULL;
@@ -3833,6 +3850,7 @@ static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff
if (skb_is_gso(skb) || skb_has_frag_list(skb))
goto normal;
+ skb_gro_reset_offset(skb);
gro_list_prepare(napi, skb);
rcu_read_lock();
@@ -3938,27 +3956,8 @@ static gro_result_t napi_skb_finish(gro_result_t ret, struct sk_buff *skb)
return ret;
}
-static void skb_gro_reset_offset(struct sk_buff *skb)
-{
- const struct skb_shared_info *pinfo = skb_shinfo(skb);
- const skb_frag_t *frag0 = &pinfo->frags[0];
-
- NAPI_GRO_CB(skb)->data_offset = 0;
- NAPI_GRO_CB(skb)->frag0 = NULL;
- NAPI_GRO_CB(skb)->frag0_len = 0;
-
- if (skb_mac_header(skb) == skb_tail_pointer(skb) &&
- pinfo->nr_frags &&
- !PageHighMem(skb_frag_page(frag0))) {
- NAPI_GRO_CB(skb)->frag0 = skb_frag_address(frag0);
- NAPI_GRO_CB(skb)->frag0_len = skb_frag_size(frag0);
- }
-}
-
gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb)
{
- skb_gro_reset_offset(skb);
-
return napi_skb_finish(dev_gro_receive(napi, skb), skb);
}
EXPORT_SYMBOL(napi_gro_receive);
@@ -3981,8 +3980,7 @@ struct sk_buff *napi_get_frags(struct napi_struct *napi)
if (!skb) {
skb = netdev_alloc_skb_ip_align(napi->dev, GRO_MAX_HEAD);
- if (skb)
- napi->skb = skb;
+ napi->skb = skb;
}
return skb;
}
@@ -3993,12 +3991,7 @@ static gro_result_t napi_frags_finish(struct napi_struct *napi, struct sk_buff *
{
switch (ret) {
case GRO_NORMAL:
- case GRO_HELD:
- skb->protocol = eth_type_trans(skb, skb->dev);
-
- if (ret == GRO_HELD)
- skb_gro_pull(skb, -ETH_HLEN);
- else if (netif_receive_skb(skb))
+ if (netif_receive_skb(skb))
ret = GRO_DROP;
break;
@@ -4007,6 +4000,7 @@ static gro_result_t napi_frags_finish(struct napi_struct *napi, struct sk_buff *
napi_reuse_skb(napi, skb);
break;
+ case GRO_HELD:
case GRO_MERGED:
break;
}
@@ -4017,36 +4011,15 @@ static gro_result_t napi_frags_finish(struct napi_struct *napi, struct sk_buff *
static struct sk_buff *napi_frags_skb(struct napi_struct *napi)
{
struct sk_buff *skb = napi->skb;
- struct ethhdr *eth;
- unsigned int hlen;
- unsigned int off;
napi->skb = NULL;
- skb_reset_mac_header(skb);
- skb_gro_reset_offset(skb);
-
- off = skb_gro_offset(skb);
- hlen = off + sizeof(*eth);
- eth = skb_gro_header_fast(skb, off);
- if (skb_gro_header_hard(skb, hlen)) {
- eth = skb_gro_header_slow(skb, hlen, off);
- if (unlikely(!eth)) {
- napi_reuse_skb(napi, skb);
- skb = NULL;
- goto out;
- }
+ if (unlikely(!pskb_may_pull(skb, sizeof(struct ethhdr)))) {
+ napi_reuse_skb(napi, skb);
+ return NULL;
}
+ skb->protocol = eth_type_trans(skb, skb->dev);
- skb_gro_pull(skb, sizeof(*eth));
-
- /*
- * This works because the only protocols we care about don't require
- * special handling. We'll fix it up properly at the end.
- */
- skb->protocol = eth->h_proto;
-
-out:
return skb;
}
@@ -4571,6 +4544,27 @@ void *netdev_lower_get_next_private_rcu(struct net_device *dev,
EXPORT_SYMBOL(netdev_lower_get_next_private_rcu);
/**
+ * netdev_lower_get_first_private_rcu - Get the first ->private from the
+ * lower neighbour list, RCU
+ * variant
+ * @dev: device
+ *
+ * Gets the first netdev_adjacent->private from the dev's lower neighbour
+ * list. The caller must hold RCU read lock.
+ */
+void *netdev_lower_get_first_private_rcu(struct net_device *dev)
+{
+ struct netdev_adjacent *lower;
+
+ lower = list_first_or_null_rcu(&dev->adj_list.lower,
+ struct netdev_adjacent, list);
+ if (lower)
+ return lower->private;
+ return NULL;
+}
+EXPORT_SYMBOL(netdev_lower_get_first_private_rcu);
+
+/**
* netdev_master_upper_dev_get_rcu - Get master upper device
* @dev: device
*
diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c
index ec40a849fc42..bb504a919e33 100644
--- a/net/core/dev_addr_lists.c
+++ b/net/core/dev_addr_lists.c
@@ -186,47 +186,6 @@ static int __hw_addr_sync_multiple(struct netdev_hw_addr_list *to_list,
return err;
}
-int __hw_addr_add_multiple(struct netdev_hw_addr_list *to_list,
- struct netdev_hw_addr_list *from_list,
- int addr_len, unsigned char addr_type)
-{
- int err;
- struct netdev_hw_addr *ha, *ha2;
- unsigned char type;
-
- list_for_each_entry(ha, &from_list->list, list) {
- type = addr_type ? addr_type : ha->type;
- err = __hw_addr_add(to_list, ha->addr, addr_len, type);
- if (err)
- goto unroll;
- }
- return 0;
-
-unroll:
- list_for_each_entry(ha2, &from_list->list, list) {
- if (ha2 == ha)
- break;
- type = addr_type ? addr_type : ha2->type;
- __hw_addr_del(to_list, ha2->addr, addr_len, type);
- }
- return err;
-}
-EXPORT_SYMBOL(__hw_addr_add_multiple);
-
-void __hw_addr_del_multiple(struct netdev_hw_addr_list *to_list,
- struct netdev_hw_addr_list *from_list,
- int addr_len, unsigned char addr_type)
-{
- struct netdev_hw_addr *ha;
- unsigned char type;
-
- list_for_each_entry(ha, &from_list->list, list) {
- type = addr_type ? addr_type : ha->type;
- __hw_addr_del(to_list, ha->addr, addr_len, type);
- }
-}
-EXPORT_SYMBOL(__hw_addr_del_multiple);
-
/* This function only works where there is a strict 1-1 relationship
* between source and destionation of they synch. If you ever need to
* sync addresses to more then 1 destination, you need to use
@@ -264,7 +223,7 @@ void __hw_addr_unsync(struct netdev_hw_addr_list *to_list,
}
EXPORT_SYMBOL(__hw_addr_unsync);
-void __hw_addr_flush(struct netdev_hw_addr_list *list)
+static void __hw_addr_flush(struct netdev_hw_addr_list *list)
{
struct netdev_hw_addr *ha, *tmp;
@@ -274,7 +233,6 @@ void __hw_addr_flush(struct netdev_hw_addr_list *list)
}
list->count = 0;
}
-EXPORT_SYMBOL(__hw_addr_flush);
void __hw_addr_init(struct netdev_hw_addr_list *list)
{
@@ -400,59 +358,6 @@ int dev_addr_del(struct net_device *dev, const unsigned char *addr,
}
EXPORT_SYMBOL(dev_addr_del);
-/**
- * dev_addr_add_multiple - Add device addresses from another device
- * @to_dev: device to which addresses will be added
- * @from_dev: device from which addresses will be added
- * @addr_type: address type - 0 means type will be used from from_dev
- *
- * Add device addresses of the one device to another.
- **
- * The caller must hold the rtnl_mutex.
- */
-int dev_addr_add_multiple(struct net_device *to_dev,
- struct net_device *from_dev,
- unsigned char addr_type)
-{
- int err;
-
- ASSERT_RTNL();
-
- if (from_dev->addr_len != to_dev->addr_len)
- return -EINVAL;
- err = __hw_addr_add_multiple(&to_dev->dev_addrs, &from_dev->dev_addrs,
- to_dev->addr_len, addr_type);
- if (!err)
- call_netdevice_notifiers(NETDEV_CHANGEADDR, to_dev);
- return err;
-}
-EXPORT_SYMBOL(dev_addr_add_multiple);
-
-/**
- * dev_addr_del_multiple - Delete device addresses by another device
- * @to_dev: device where the addresses will be deleted
- * @from_dev: device supplying the addresses to be deleted
- * @addr_type: address type - 0 means type will be used from from_dev
- *
- * Deletes addresses in to device by the list of addresses in from device.
- *
- * The caller must hold the rtnl_mutex.
- */
-int dev_addr_del_multiple(struct net_device *to_dev,
- struct net_device *from_dev,
- unsigned char addr_type)
-{
- ASSERT_RTNL();
-
- if (from_dev->addr_len != to_dev->addr_len)
- return -EINVAL;
- __hw_addr_del_multiple(&to_dev->dev_addrs, &from_dev->dev_addrs,
- to_dev->addr_len, addr_type);
- call_netdevice_notifiers(NETDEV_CHANGEADDR, to_dev);
- return 0;
-}
-EXPORT_SYMBOL(dev_addr_del_multiple);
-
/*
* Unicast list handling functions
*/
diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c
index 5b7d0e1d0664..cf999e09bcd2 100644
--- a/net/core/dev_ioctl.c
+++ b/net/core/dev_ioctl.c
@@ -327,6 +327,7 @@ static int dev_ifsioc(struct net *net, struct ifreq *ifr, unsigned int cmd)
cmd == SIOCBRADDIF ||
cmd == SIOCBRDELIF ||
cmd == SIOCSHWTSTAMP ||
+ cmd == SIOCGHWTSTAMP ||
cmd == SIOCWANDEV) {
err = -EOPNOTSUPP;
if (ops->ndo_do_ioctl) {
@@ -546,6 +547,7 @@ int dev_ioctl(struct net *net, unsigned int cmd, void __user *arg)
*/
default:
if (cmd == SIOCWANDEV ||
+ cmd == SIOCGHWTSTAMP ||
(cmd >= SIOCDEVPRIVATE &&
cmd <= SIOCDEVPRIVATE + 15)) {
dev_load(net, ifr.ifr_name);
diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c
index d6ef17322500..b324bfa3485c 100644
--- a/net/core/flow_dissector.c
+++ b/net/core/flow_dissector.c
@@ -202,12 +202,12 @@ static __always_inline u32 __flow_hash_1word(u32 a)
}
/*
- * __skb_get_rxhash: calculate a flow hash based on src/dst addresses
+ * __skb_get_hash: calculate a flow hash based on src/dst addresses
* and src/dst port numbers. Sets rxhash in skb to non-zero hash value
* on success, zero indicates no valid hash. Also, sets l4_rxhash in skb
* if hash is a canonical 4-tuple hash over transport ports.
*/
-void __skb_get_rxhash(struct sk_buff *skb)
+void __skb_get_hash(struct sk_buff *skb)
{
struct flow_keys keys;
u32 hash;
@@ -234,7 +234,7 @@ void __skb_get_rxhash(struct sk_buff *skb)
skb->rxhash = hash;
}
-EXPORT_SYMBOL(__skb_get_rxhash);
+EXPORT_SYMBOL(__skb_get_hash);
/*
* Returns a Tx hash based on the given packet descriptor a Tx queues' number
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 36b1443f9ae4..a666740051dc 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -38,6 +38,8 @@
#include <linux/random.h>
#include <linux/string.h>
#include <linux/log2.h>
+#include <linux/inetdevice.h>
+#include <net/addrconf.h>
#define DEBUG
#define NEIGH_DEBUG 1
@@ -497,7 +499,7 @@ struct neighbour *__neigh_create(struct neigh_table *tbl, const void *pkey,
goto out_neigh_release;
}
- n->confirmed = jiffies - (n->parms->base_reachable_time << 1);
+ n->confirmed = jiffies - (NEIGH_VAR(n->parms, BASE_REACHABLE_TIME) << 1);
write_lock_bh(&tbl->lock);
nht = rcu_dereference_protected(tbl->nht,
@@ -776,7 +778,7 @@ static void neigh_periodic_work(struct work_struct *work)
tbl->last_rand = jiffies;
for (p = &tbl->parms; p; p = p->next)
p->reachable_time =
- neigh_rand_reach_time(p->base_reachable_time);
+ neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME));
}
for (i = 0 ; i < (1 << nht->hash_shift); i++) {
@@ -799,7 +801,7 @@ static void neigh_periodic_work(struct work_struct *work)
if (atomic_read(&n->refcnt) == 1 &&
(state == NUD_FAILED ||
- time_after(jiffies, n->used + n->parms->gc_staletime))) {
+ time_after(jiffies, n->used + NEIGH_VAR(n->parms, GC_STALETIME)))) {
*np = n->next;
n->dead = 1;
write_unlock(&n->lock);
@@ -822,12 +824,12 @@ next_elt:
lockdep_is_held(&tbl->lock));
}
out:
- /* Cycle through all hash buckets every base_reachable_time/2 ticks.
- * ARP entry timeouts range from 1/2 base_reachable_time to 3/2
- * base_reachable_time.
+ /* Cycle through all hash buckets every BASE_REACHABLE_TIME/2 ticks.
+ * ARP entry timeouts range from 1/2 BASE_REACHABLE_TIME to 3/2
+ * BASE_REACHABLE_TIME.
*/
schedule_delayed_work(&tbl->gc_work,
- tbl->parms.base_reachable_time >> 1);
+ NEIGH_VAR(&tbl->parms, BASE_REACHABLE_TIME) >> 1);
write_unlock_bh(&tbl->lock);
}
@@ -835,8 +837,9 @@ static __inline__ int neigh_max_probes(struct neighbour *n)
{
struct neigh_parms *p = n->parms;
return (n->nud_state & NUD_PROBE) ?
- p->ucast_probes :
- p->ucast_probes + p->app_probes + p->mcast_probes;
+ NEIGH_VAR(p, UCAST_PROBES) :
+ NEIGH_VAR(p, UCAST_PROBES) + NEIGH_VAR(p, APP_PROBES) +
+ NEIGH_VAR(p, MCAST_PROBES);
}
static void neigh_invalidate(struct neighbour *neigh)
@@ -901,12 +904,13 @@ static void neigh_timer_handler(unsigned long arg)
neigh_dbg(2, "neigh %p is still alive\n", neigh);
next = neigh->confirmed + neigh->parms->reachable_time;
} else if (time_before_eq(now,
- neigh->used + neigh->parms->delay_probe_time)) {
+ neigh->used +
+ NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME))) {
neigh_dbg(2, "neigh %p is delayed\n", neigh);
neigh->nud_state = NUD_DELAY;
neigh->updated = jiffies;
neigh_suspect(neigh);
- next = now + neigh->parms->delay_probe_time;
+ next = now + NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME);
} else {
neigh_dbg(2, "neigh %p is suspected\n", neigh);
neigh->nud_state = NUD_STALE;
@@ -916,7 +920,8 @@ static void neigh_timer_handler(unsigned long arg)
}
} else if (state & NUD_DELAY) {
if (time_before_eq(now,
- neigh->confirmed + neigh->parms->delay_probe_time)) {
+ neigh->confirmed +
+ NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME))) {
neigh_dbg(2, "neigh %p is now reachable\n", neigh);
neigh->nud_state = NUD_REACHABLE;
neigh->updated = jiffies;
@@ -928,11 +933,11 @@ static void neigh_timer_handler(unsigned long arg)
neigh->nud_state = NUD_PROBE;
neigh->updated = jiffies;
atomic_set(&neigh->probes, 0);
- next = now + neigh->parms->retrans_time;
+ next = now + NEIGH_VAR(neigh->parms, RETRANS_TIME);
}
} else {
/* NUD_PROBE|NUD_INCOMPLETE */
- next = now + neigh->parms->retrans_time;
+ next = now + NEIGH_VAR(neigh->parms, RETRANS_TIME);
}
if ((neigh->nud_state & (NUD_INCOMPLETE | NUD_PROBE)) &&
@@ -973,13 +978,16 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
goto out_unlock_bh;
if (!(neigh->nud_state & (NUD_STALE | NUD_INCOMPLETE))) {
- if (neigh->parms->mcast_probes + neigh->parms->app_probes) {
+ if (NEIGH_VAR(neigh->parms, MCAST_PROBES) +
+ NEIGH_VAR(neigh->parms, APP_PROBES)) {
unsigned long next, now = jiffies;
- atomic_set(&neigh->probes, neigh->parms->ucast_probes);
+ atomic_set(&neigh->probes,
+ NEIGH_VAR(neigh->parms, UCAST_PROBES));
neigh->nud_state = NUD_INCOMPLETE;
neigh->updated = now;
- next = now + max(neigh->parms->retrans_time, HZ/2);
+ next = now + max(NEIGH_VAR(neigh->parms, RETRANS_TIME),
+ HZ/2);
neigh_add_timer(neigh, next);
immediate_probe = true;
} else {
@@ -994,14 +1002,14 @@ int __neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
neigh_dbg(2, "neigh %p is delayed\n", neigh);
neigh->nud_state = NUD_DELAY;
neigh->updated = jiffies;
- neigh_add_timer(neigh,
- jiffies + neigh->parms->delay_probe_time);
+ neigh_add_timer(neigh, jiffies +
+ NEIGH_VAR(neigh->parms, DELAY_PROBE_TIME));
}
if (neigh->nud_state == NUD_INCOMPLETE) {
if (skb) {
while (neigh->arp_queue_len_bytes + skb->truesize >
- neigh->parms->queue_len_bytes) {
+ NEIGH_VAR(neigh->parms, QUEUE_LEN_BYTES)) {
struct sk_buff *buff;
buff = __skb_dequeue(&neigh->arp_queue);
@@ -1171,7 +1179,7 @@ int neigh_update(struct neighbour *neigh, const u8 *lladdr, u8 new,
neigh_update_hhs(neigh);
if (!(new & NUD_CONNECTED))
neigh->confirmed = jiffies -
- (neigh->parms->base_reachable_time << 1);
+ (NEIGH_VAR(neigh->parms, BASE_REACHABLE_TIME) << 1);
notify = 1;
}
if (new == old)
@@ -1231,6 +1239,21 @@ out:
}
EXPORT_SYMBOL(neigh_update);
+/* Update the neigh to listen temporarily for probe responses, even if it is
+ * in a NUD_FAILED state. The caller has to hold neigh->lock for writing.
+ */
+void __neigh_set_probe_once(struct neighbour *neigh)
+{
+ neigh->updated = jiffies;
+ if (!(neigh->nud_state & NUD_FAILED))
+ return;
+ neigh->nud_state = NUD_PROBE;
+ atomic_set(&neigh->probes, NEIGH_VAR(neigh->parms, UCAST_PROBES));
+ neigh_add_timer(neigh,
+ jiffies + NEIGH_VAR(neigh->parms, RETRANS_TIME));
+}
+EXPORT_SYMBOL(__neigh_set_probe_once);
+
struct neighbour *neigh_event_ns(struct neigh_table *tbl,
u8 *lladdr, void *saddr,
struct net_device *dev)
@@ -1392,9 +1415,10 @@ void pneigh_enqueue(struct neigh_table *tbl, struct neigh_parms *p,
struct sk_buff *skb)
{
unsigned long now = jiffies;
- unsigned long sched_next = now + (net_random() % p->proxy_delay);
+ unsigned long sched_next = now + (net_random() %
+ NEIGH_VAR(p, PROXY_DELAY));
- if (tbl->proxy_queue.qlen > p->proxy_qlen) {
+ if (tbl->proxy_queue.qlen > NEIGH_VAR(p, PROXY_QLEN)) {
kfree_skb(skb);
return;
}
@@ -1441,7 +1465,7 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev,
p->tbl = tbl;
atomic_set(&p->refcnt, 1);
p->reachable_time =
- neigh_rand_reach_time(p->base_reachable_time);
+ neigh_rand_reach_time(NEIGH_VAR(p, BASE_REACHABLE_TIME));
dev_hold(dev);
p->dev = dev;
write_pnet(&p->net, hold_net(net));
@@ -1458,6 +1482,8 @@ struct neigh_parms *neigh_parms_alloc(struct net_device *dev,
p->next = tbl->parms.next;
tbl->parms.next = p;
write_unlock_bh(&tbl->lock);
+
+ neigh_parms_data_state_cleanall(p);
}
return p;
}
@@ -1510,7 +1536,7 @@ static void neigh_table_init_no_netlink(struct neigh_table *tbl)
write_pnet(&tbl->parms.net, &init_net);
atomic_set(&tbl->parms.refcnt, 1);
tbl->parms.reachable_time =
- neigh_rand_reach_time(tbl->parms.base_reachable_time);
+ neigh_rand_reach_time(NEIGH_VAR(&tbl->parms, BASE_REACHABLE_TIME));
tbl->stats = alloc_percpu(struct neigh_statistics);
if (!tbl->stats)
@@ -1778,24 +1804,32 @@ static int neightbl_fill_parms(struct sk_buff *skb, struct neigh_parms *parms)
if ((parms->dev &&
nla_put_u32(skb, NDTPA_IFINDEX, parms->dev->ifindex)) ||
nla_put_u32(skb, NDTPA_REFCNT, atomic_read(&parms->refcnt)) ||
- nla_put_u32(skb, NDTPA_QUEUE_LENBYTES, parms->queue_len_bytes) ||
+ nla_put_u32(skb, NDTPA_QUEUE_LENBYTES,
+ NEIGH_VAR(parms, QUEUE_LEN_BYTES)) ||
/* approximative value for deprecated QUEUE_LEN (in packets) */
nla_put_u32(skb, NDTPA_QUEUE_LEN,
- parms->queue_len_bytes / SKB_TRUESIZE(ETH_FRAME_LEN)) ||
- nla_put_u32(skb, NDTPA_PROXY_QLEN, parms->proxy_qlen) ||
- nla_put_u32(skb, NDTPA_APP_PROBES, parms->app_probes) ||
- nla_put_u32(skb, NDTPA_UCAST_PROBES, parms->ucast_probes) ||
- nla_put_u32(skb, NDTPA_MCAST_PROBES, parms->mcast_probes) ||
+ NEIGH_VAR(parms, QUEUE_LEN_BYTES) / SKB_TRUESIZE(ETH_FRAME_LEN)) ||
+ nla_put_u32(skb, NDTPA_PROXY_QLEN, NEIGH_VAR(parms, PROXY_QLEN)) ||
+ nla_put_u32(skb, NDTPA_APP_PROBES, NEIGH_VAR(parms, APP_PROBES)) ||
+ nla_put_u32(skb, NDTPA_UCAST_PROBES,
+ NEIGH_VAR(parms, UCAST_PROBES)) ||
+ nla_put_u32(skb, NDTPA_MCAST_PROBES,
+ NEIGH_VAR(parms, MCAST_PROBES)) ||
nla_put_msecs(skb, NDTPA_REACHABLE_TIME, parms->reachable_time) ||
nla_put_msecs(skb, NDTPA_BASE_REACHABLE_TIME,
- parms->base_reachable_time) ||
- nla_put_msecs(skb, NDTPA_GC_STALETIME, parms->gc_staletime) ||
+ NEIGH_VAR(parms, BASE_REACHABLE_TIME)) ||
+ nla_put_msecs(skb, NDTPA_GC_STALETIME,
+ NEIGH_VAR(parms, GC_STALETIME)) ||
nla_put_msecs(skb, NDTPA_DELAY_PROBE_TIME,
- parms->delay_probe_time) ||
- nla_put_msecs(skb, NDTPA_RETRANS_TIME, parms->retrans_time) ||
- nla_put_msecs(skb, NDTPA_ANYCAST_DELAY, parms->anycast_delay) ||
- nla_put_msecs(skb, NDTPA_PROXY_DELAY, parms->proxy_delay) ||
- nla_put_msecs(skb, NDTPA_LOCKTIME, parms->locktime))
+ NEIGH_VAR(parms, DELAY_PROBE_TIME)) ||
+ nla_put_msecs(skb, NDTPA_RETRANS_TIME,
+ NEIGH_VAR(parms, RETRANS_TIME)) ||
+ nla_put_msecs(skb, NDTPA_ANYCAST_DELAY,
+ NEIGH_VAR(parms, ANYCAST_DELAY)) ||
+ nla_put_msecs(skb, NDTPA_PROXY_DELAY,
+ NEIGH_VAR(parms, PROXY_DELAY)) ||
+ nla_put_msecs(skb, NDTPA_LOCKTIME,
+ NEIGH_VAR(parms, LOCKTIME)))
goto nla_put_failure;
return nla_nest_end(skb, nest);
@@ -2011,44 +2045,54 @@ static int neightbl_set(struct sk_buff *skb, struct nlmsghdr *nlh)
switch (i) {
case NDTPA_QUEUE_LEN:
- p->queue_len_bytes = nla_get_u32(tbp[i]) *
- SKB_TRUESIZE(ETH_FRAME_LEN);
+ NEIGH_VAR_SET(p, QUEUE_LEN_BYTES,
+ nla_get_u32(tbp[i]) *
+ SKB_TRUESIZE(ETH_FRAME_LEN));
break;
case NDTPA_QUEUE_LENBYTES:
- p->queue_len_bytes = nla_get_u32(tbp[i]);
+ NEIGH_VAR_SET(p, QUEUE_LEN_BYTES,
+ nla_get_u32(tbp[i]));
break;
case NDTPA_PROXY_QLEN:
- p->proxy_qlen = nla_get_u32(tbp[i]);
+ NEIGH_VAR_SET(p, PROXY_QLEN,
+ nla_get_u32(tbp[i]));
break;
case NDTPA_APP_PROBES:
- p->app_probes = nla_get_u32(tbp[i]);
+ NEIGH_VAR_SET(p, APP_PROBES,
+ nla_get_u32(tbp[i]));
break;
case NDTPA_UCAST_PROBES:
- p->ucast_probes = nla_get_u32(tbp[i]);
+ NEIGH_VAR_SET(p, UCAST_PROBES,
+ nla_get_u32(tbp[i]));
break;
case NDTPA_MCAST_PROBES:
- p->mcast_probes = nla_get_u32(tbp[i]);
+ NEIGH_VAR_SET(p, MCAST_PROBES,
+ nla_get_u32(tbp[i]));
break;
case NDTPA_BASE_REACHABLE_TIME:
- p->base_reachable_time = nla_get_msecs(tbp[i]);
+ NEIGH_VAR_SET(p, BASE_REACHABLE_TIME,
+ nla_get_msecs(tbp[i]));
break;
case NDTPA_GC_STALETIME:
- p->gc_staletime = nla_get_msecs(tbp[i]);
+ NEIGH_VAR_SET(p, GC_STALETIME,
+ nla_get_msecs(tbp[i]));
break;
case NDTPA_DELAY_PROBE_TIME:
- p->delay_probe_time = nla_get_msecs(tbp[i]);
+ NEIGH_VAR_SET(p, DELAY_PROBE_TIME,
+ nla_get_msecs(tbp[i]));
break;
case NDTPA_RETRANS_TIME:
- p->retrans_time = nla_get_msecs(tbp[i]);
+ NEIGH_VAR_SET(p, RETRANS_TIME,
+ nla_get_msecs(tbp[i]));
break;
case NDTPA_ANYCAST_DELAY:
- p->anycast_delay = nla_get_msecs(tbp[i]);
+ NEIGH_VAR_SET(p, ANYCAST_DELAY, nla_get_msecs(tbp[i]));
break;
case NDTPA_PROXY_DELAY:
- p->proxy_delay = nla_get_msecs(tbp[i]);
+ NEIGH_VAR_SET(p, PROXY_DELAY, nla_get_msecs(tbp[i]));
break;
case NDTPA_LOCKTIME:
- p->locktime = nla_get_msecs(tbp[i]);
+ NEIGH_VAR_SET(p, LOCKTIME, nla_get_msecs(tbp[i]));
break;
}
}
@@ -2789,133 +2833,167 @@ static int proc_unres_qlen(struct ctl_table *ctl, int write,
return ret;
}
-enum {
- NEIGH_VAR_MCAST_PROBE,
- NEIGH_VAR_UCAST_PROBE,
- NEIGH_VAR_APP_PROBE,
- NEIGH_VAR_RETRANS_TIME,
- NEIGH_VAR_BASE_REACHABLE_TIME,
- NEIGH_VAR_DELAY_PROBE_TIME,
- NEIGH_VAR_GC_STALETIME,
- NEIGH_VAR_QUEUE_LEN,
- NEIGH_VAR_QUEUE_LEN_BYTES,
- NEIGH_VAR_PROXY_QLEN,
- NEIGH_VAR_ANYCAST_DELAY,
- NEIGH_VAR_PROXY_DELAY,
- NEIGH_VAR_LOCKTIME,
- NEIGH_VAR_RETRANS_TIME_MS,
- NEIGH_VAR_BASE_REACHABLE_TIME_MS,
- NEIGH_VAR_GC_INTERVAL,
- NEIGH_VAR_GC_THRESH1,
- NEIGH_VAR_GC_THRESH2,
- NEIGH_VAR_GC_THRESH3,
- NEIGH_VAR_MAX
-};
+static struct neigh_parms *neigh_get_dev_parms_rcu(struct net_device *dev,
+ int family)
+{
+ switch (family) {
+ case AF_INET:
+ return __in_dev_arp_parms_get_rcu(dev);
+ case AF_INET6:
+ return __in6_dev_nd_parms_get_rcu(dev);
+ }
+ return NULL;
+}
+
+static void neigh_copy_dflt_parms(struct net *net, struct neigh_parms *p,
+ int index)
+{
+ struct net_device *dev;
+ int family = neigh_parms_family(p);
+
+ rcu_read_lock();
+ for_each_netdev_rcu(net, dev) {
+ struct neigh_parms *dst_p =
+ neigh_get_dev_parms_rcu(dev, family);
+
+ if (dst_p && !test_bit(index, dst_p->data_state))
+ dst_p->data[index] = p->data[index];
+ }
+ rcu_read_unlock();
+}
+
+static void neigh_proc_update(struct ctl_table *ctl, int write)
+{
+ struct net_device *dev = ctl->extra1;
+ struct neigh_parms *p = ctl->extra2;
+ struct net *net = neigh_parms_net(p);
+ int index = (int *) ctl->data - p->data;
+
+ if (!write)
+ return;
+
+ set_bit(index, p->data_state);
+ if (!dev) /* NULL dev means this is default value */
+ neigh_copy_dflt_parms(net, p, index);
+}
+
+static int neigh_proc_dointvec_zero_intmax(struct ctl_table *ctl, int write,
+ void __user *buffer,
+ size_t *lenp, loff_t *ppos)
+{
+ struct ctl_table tmp = *ctl;
+ int ret;
+
+ tmp.extra1 = &zero;
+ tmp.extra2 = &int_max;
+
+ ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
+ neigh_proc_update(ctl, write);
+ return ret;
+}
+
+int neigh_proc_dointvec(struct ctl_table *ctl, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
+
+ neigh_proc_update(ctl, write);
+ return ret;
+}
+EXPORT_SYMBOL(neigh_proc_dointvec);
+
+int neigh_proc_dointvec_jiffies(struct ctl_table *ctl, int write,
+ void __user *buffer,
+ size_t *lenp, loff_t *ppos)
+{
+ int ret = proc_dointvec_jiffies(ctl, write, buffer, lenp, ppos);
+
+ neigh_proc_update(ctl, write);
+ return ret;
+}
+EXPORT_SYMBOL(neigh_proc_dointvec_jiffies);
+
+static int neigh_proc_dointvec_userhz_jiffies(struct ctl_table *ctl, int write,
+ void __user *buffer,
+ size_t *lenp, loff_t *ppos)
+{
+ int ret = proc_dointvec_userhz_jiffies(ctl, write, buffer, lenp, ppos);
+
+ neigh_proc_update(ctl, write);
+ return ret;
+}
+
+int neigh_proc_dointvec_ms_jiffies(struct ctl_table *ctl, int write,
+ void __user *buffer,
+ size_t *lenp, loff_t *ppos)
+{
+ int ret = proc_dointvec_ms_jiffies(ctl, write, buffer, lenp, ppos);
+
+ neigh_proc_update(ctl, write);
+ return ret;
+}
+EXPORT_SYMBOL(neigh_proc_dointvec_ms_jiffies);
+
+static int neigh_proc_dointvec_unres_qlen(struct ctl_table *ctl, int write,
+ void __user *buffer,
+ size_t *lenp, loff_t *ppos)
+{
+ int ret = proc_unres_qlen(ctl, write, buffer, lenp, ppos);
+
+ neigh_proc_update(ctl, write);
+ return ret;
+}
+
+#define NEIGH_PARMS_DATA_OFFSET(index) \
+ (&((struct neigh_parms *) 0)->data[index])
+
+#define NEIGH_SYSCTL_ENTRY(attr, data_attr, name, mval, proc) \
+ [NEIGH_VAR_ ## attr] = { \
+ .procname = name, \
+ .data = NEIGH_PARMS_DATA_OFFSET(NEIGH_VAR_ ## data_attr), \
+ .maxlen = sizeof(int), \
+ .mode = mval, \
+ .proc_handler = proc, \
+ }
+
+#define NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(attr, name) \
+ NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_zero_intmax)
+
+#define NEIGH_SYSCTL_JIFFIES_ENTRY(attr, name) \
+ NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_jiffies)
+
+#define NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(attr, name) \
+ NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_userhz_jiffies)
+
+#define NEIGH_SYSCTL_MS_JIFFIES_ENTRY(attr, name) \
+ NEIGH_SYSCTL_ENTRY(attr, attr, name, 0644, neigh_proc_dointvec_ms_jiffies)
+
+#define NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(attr, data_attr, name) \
+ NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, neigh_proc_dointvec_ms_jiffies)
+
+#define NEIGH_SYSCTL_UNRES_QLEN_REUSED_ENTRY(attr, data_attr, name) \
+ NEIGH_SYSCTL_ENTRY(attr, data_attr, name, 0644, neigh_proc_dointvec_unres_qlen)
static struct neigh_sysctl_table {
struct ctl_table_header *sysctl_header;
struct ctl_table neigh_vars[NEIGH_VAR_MAX + 1];
} neigh_sysctl_template __read_mostly = {
.neigh_vars = {
- [NEIGH_VAR_MCAST_PROBE] = {
- .procname = "mcast_solicit",
- .maxlen = sizeof(int),
- .mode = 0644,
- .extra1 = &zero,
- .extra2 = &int_max,
- .proc_handler = proc_dointvec_minmax,
- },
- [NEIGH_VAR_UCAST_PROBE] = {
- .procname = "ucast_solicit",
- .maxlen = sizeof(int),
- .mode = 0644,
- .extra1 = &zero,
- .extra2 = &int_max,
- .proc_handler = proc_dointvec_minmax,
- },
- [NEIGH_VAR_APP_PROBE] = {
- .procname = "app_solicit",
- .maxlen = sizeof(int),
- .mode = 0644,
- .extra1 = &zero,
- .extra2 = &int_max,
- .proc_handler = proc_dointvec_minmax,
- },
- [NEIGH_VAR_RETRANS_TIME] = {
- .procname = "retrans_time",
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_userhz_jiffies,
- },
- [NEIGH_VAR_BASE_REACHABLE_TIME] = {
- .procname = "base_reachable_time",
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_jiffies,
- },
- [NEIGH_VAR_DELAY_PROBE_TIME] = {
- .procname = "delay_first_probe_time",
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_jiffies,
- },
- [NEIGH_VAR_GC_STALETIME] = {
- .procname = "gc_stale_time",
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_jiffies,
- },
- [NEIGH_VAR_QUEUE_LEN] = {
- .procname = "unres_qlen",
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_unres_qlen,
- },
- [NEIGH_VAR_QUEUE_LEN_BYTES] = {
- .procname = "unres_qlen_bytes",
- .maxlen = sizeof(int),
- .mode = 0644,
- .extra1 = &zero,
- .proc_handler = proc_dointvec_minmax,
- },
- [NEIGH_VAR_PROXY_QLEN] = {
- .procname = "proxy_qlen",
- .maxlen = sizeof(int),
- .mode = 0644,
- .extra1 = &zero,
- .extra2 = &int_max,
- .proc_handler = proc_dointvec_minmax,
- },
- [NEIGH_VAR_ANYCAST_DELAY] = {
- .procname = "anycast_delay",
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_userhz_jiffies,
- },
- [NEIGH_VAR_PROXY_DELAY] = {
- .procname = "proxy_delay",
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_userhz_jiffies,
- },
- [NEIGH_VAR_LOCKTIME] = {
- .procname = "locktime",
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_userhz_jiffies,
- },
- [NEIGH_VAR_RETRANS_TIME_MS] = {
- .procname = "retrans_time_ms",
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_ms_jiffies,
- },
- [NEIGH_VAR_BASE_REACHABLE_TIME_MS] = {
- .procname = "base_reachable_time_ms",
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = proc_dointvec_ms_jiffies,
- },
+ NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(MCAST_PROBES, "mcast_solicit"),
+ NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(UCAST_PROBES, "ucast_solicit"),
+ NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(APP_PROBES, "app_solicit"),
+ NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(RETRANS_TIME, "retrans_time"),
+ NEIGH_SYSCTL_JIFFIES_ENTRY(BASE_REACHABLE_TIME, "base_reachable_time"),
+ NEIGH_SYSCTL_JIFFIES_ENTRY(DELAY_PROBE_TIME, "delay_first_probe_time"),
+ NEIGH_SYSCTL_JIFFIES_ENTRY(GC_STALETIME, "gc_stale_time"),
+ NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(QUEUE_LEN_BYTES, "unres_qlen_bytes"),
+ NEIGH_SYSCTL_ZERO_INTMAX_ENTRY(PROXY_QLEN, "proxy_qlen"),
+ NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(ANYCAST_DELAY, "anycast_delay"),
+ NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(PROXY_DELAY, "proxy_delay"),
+ NEIGH_SYSCTL_USERHZ_JIFFIES_ENTRY(LOCKTIME, "locktime"),
+ NEIGH_SYSCTL_UNRES_QLEN_REUSED_ENTRY(QUEUE_LEN, QUEUE_LEN_BYTES, "unres_qlen"),
+ NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(RETRANS_TIME_MS, RETRANS_TIME, "retrans_time_ms"),
+ NEIGH_SYSCTL_MS_JIFFIES_REUSED_ENTRY(BASE_REACHABLE_TIME_MS, BASE_REACHABLE_TIME, "base_reachable_time_ms"),
[NEIGH_VAR_GC_INTERVAL] = {
.procname = "gc_interval",
.maxlen = sizeof(int),
@@ -2951,31 +3029,23 @@ static struct neigh_sysctl_table {
};
int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,
- char *p_name, proc_handler *handler)
+ proc_handler *handler)
{
+ int i;
struct neigh_sysctl_table *t;
- const char *dev_name_source = NULL;
+ const char *dev_name_source;
char neigh_path[ sizeof("net//neigh/") + IFNAMSIZ + IFNAMSIZ ];
+ char *p_name;
t = kmemdup(&neigh_sysctl_template, sizeof(*t), GFP_KERNEL);
if (!t)
goto err;
- t->neigh_vars[NEIGH_VAR_MCAST_PROBE].data = &p->mcast_probes;
- t->neigh_vars[NEIGH_VAR_UCAST_PROBE].data = &p->ucast_probes;
- t->neigh_vars[NEIGH_VAR_APP_PROBE].data = &p->app_probes;
- t->neigh_vars[NEIGH_VAR_RETRANS_TIME].data = &p->retrans_time;
- t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME].data = &p->base_reachable_time;
- t->neigh_vars[NEIGH_VAR_DELAY_PROBE_TIME].data = &p->delay_probe_time;
- t->neigh_vars[NEIGH_VAR_GC_STALETIME].data = &p->gc_staletime;
- t->neigh_vars[NEIGH_VAR_QUEUE_LEN].data = &p->queue_len_bytes;
- t->neigh_vars[NEIGH_VAR_QUEUE_LEN_BYTES].data = &p->queue_len_bytes;
- t->neigh_vars[NEIGH_VAR_PROXY_QLEN].data = &p->proxy_qlen;
- t->neigh_vars[NEIGH_VAR_ANYCAST_DELAY].data = &p->anycast_delay;
- t->neigh_vars[NEIGH_VAR_PROXY_DELAY].data = &p->proxy_delay;
- t->neigh_vars[NEIGH_VAR_LOCKTIME].data = &p->locktime;
- t->neigh_vars[NEIGH_VAR_RETRANS_TIME_MS].data = &p->retrans_time;
- t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME_MS].data = &p->base_reachable_time;
+ for (i = 0; i < ARRAY_SIZE(t->neigh_vars); i++) {
+ t->neigh_vars[i].data += (long) p;
+ t->neigh_vars[i].extra1 = dev;
+ t->neigh_vars[i].extra2 = p;
+ }
if (dev) {
dev_name_source = dev->name;
@@ -2990,26 +3060,32 @@ int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,
t->neigh_vars[NEIGH_VAR_GC_THRESH3].data = (int *)(p + 1) + 3;
}
-
if (handler) {
/* RetransTime */
t->neigh_vars[NEIGH_VAR_RETRANS_TIME].proc_handler = handler;
- t->neigh_vars[NEIGH_VAR_RETRANS_TIME].extra1 = dev;
/* ReachableTime */
t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME].proc_handler = handler;
- t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME].extra1 = dev;
/* RetransTime (in milliseconds)*/
t->neigh_vars[NEIGH_VAR_RETRANS_TIME_MS].proc_handler = handler;
- t->neigh_vars[NEIGH_VAR_RETRANS_TIME_MS].extra1 = dev;
/* ReachableTime (in milliseconds) */
t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME_MS].proc_handler = handler;
- t->neigh_vars[NEIGH_VAR_BASE_REACHABLE_TIME_MS].extra1 = dev;
}
/* Don't export sysctls to unprivileged users */
if (neigh_parms_net(p)->user_ns != &init_user_ns)
t->neigh_vars[0].procname = NULL;
+ switch (neigh_parms_family(p)) {
+ case AF_INET:
+ p_name = "ipv4";
+ break;
+ case AF_INET6:
+ p_name = "ipv6";
+ break;
+ default:
+ BUG();
+ }
+
snprintf(neigh_path, sizeof(neigh_path), "net/%s/neigh/%s",
p_name, dev_name_source);
t->sysctl_header =
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index f3edf9635e02..1a7b7b1df0d1 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -676,8 +676,8 @@ static ssize_t store_rps_dev_flow_table_cnt(struct netdev_rx_queue *queue,
while ((mask | (mask >> 1)) != mask)
mask |= (mask >> 1);
/* On 64 bit arches, must check mask fits in table->mask (u32),
- * and on 32bit arches, must check RPS_DEV_FLOW_TABLE_SIZE(mask + 1)
- * doesnt overflow.
+ * and on 32bit arches, must check
+ * RPS_DEV_FLOW_TABLE_SIZE(mask + 1) doesn't overflow.
*/
#if BITS_PER_LONG > 32
if (mask > (unsigned long)(u32)mask)
diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c
index 9b7cf6c85f82..1dda50c2e705 100644
--- a/net/core/netprio_cgroup.c
+++ b/net/core/netprio_cgroup.c
@@ -30,7 +30,7 @@
#define PRIOMAP_MIN_SZ 128
/*
- * Extend @dev->priomap so that it's large enough to accomodate
+ * Extend @dev->priomap so that it's large enough to accommodate
* @target_idx. @dev->priomap.priomap_len > @target_idx after successful
* return. Must be called under rtnl lock.
*/
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 06e72d3cdf60..2b6b863f51f2 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -712,9 +712,8 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old)
new->inner_network_header = old->inner_network_header;
new->inner_mac_header = old->inner_mac_header;
skb_dst_copy(new, old);
- new->rxhash = old->rxhash;
+ skb_copy_hash(new, old);
new->ooo_okay = old->ooo_okay;
- new->l4_rxhash = old->l4_rxhash;
new->no_fcs = old->no_fcs;
new->encapsulation = old->encapsulation;
#ifdef CONFIG_XFRM
diff --git a/net/dcb/dcbevent.c b/net/dcb/dcbevent.c
index 4f72fc40bf02..a520d8004d89 100644
--- a/net/dcb/dcbevent.c
+++ b/net/dcb/dcbevent.c
@@ -11,8 +11,7 @@
* 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., 59 Temple
- * Place - Suite 330, Boston, MA 02111-1307 USA.
+ * this program; if not, see <http://www.gnu.org/licenses/>.
*
* Author: John Fastabend <john.r.fastabend@intel.com>
*/
diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c
index 40d5829ed36a..66fbe1948fb5 100644
--- a/net/dcb/dcbnl.c
+++ b/net/dcb/dcbnl.c
@@ -11,8 +11,7 @@
* 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., 59 Temple
- * Place - Suite 330, Boston, MA 02111-1307 USA.
+ * this program; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Lucy Liu <lucy.liu@intel.com>
*/
diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c
index dd0dfb25f4b1..a603823a3e27 100644
--- a/net/decnet/dn_dev.c
+++ b/net/decnet/dn_dev.c
@@ -561,6 +561,7 @@ static const struct nla_policy dn_ifa_policy[IFA_MAX+1] = {
[IFA_LOCAL] = { .type = NLA_U16 },
[IFA_LABEL] = { .type = NLA_STRING,
.len = IFNAMSIZ - 1 },
+ [IFA_FLAGS] = { .type = NLA_U32 },
};
static int dn_nl_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)
@@ -648,7 +649,8 @@ static int dn_nl_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
ifa->ifa_local = nla_get_le16(tb[IFA_LOCAL]);
ifa->ifa_address = nla_get_le16(tb[IFA_ADDRESS]);
- ifa->ifa_flags = ifm->ifa_flags;
+ ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) :
+ ifm->ifa_flags;
ifa->ifa_scope = ifm->ifa_scope;
ifa->ifa_dev = dn_db;
@@ -669,7 +671,8 @@ static inline size_t dn_ifaddr_nlmsg_size(void)
return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
+ nla_total_size(IFNAMSIZ) /* IFA_LABEL */
+ nla_total_size(2) /* IFA_ADDRESS */
- + nla_total_size(2); /* IFA_LOCAL */
+ + nla_total_size(2) /* IFA_LOCAL */
+ + nla_total_size(4); /* IFA_FLAGS */
}
static int dn_nl_fill_ifaddr(struct sk_buff *skb, struct dn_ifaddr *ifa,
@@ -677,6 +680,7 @@ static int dn_nl_fill_ifaddr(struct sk_buff *skb, struct dn_ifaddr *ifa,
{
struct ifaddrmsg *ifm;
struct nlmsghdr *nlh;
+ u32 ifa_flags = ifa->ifa_flags | IFA_F_PERMANENT;
nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), flags);
if (nlh == NULL)
@@ -685,7 +689,7 @@ static int dn_nl_fill_ifaddr(struct sk_buff *skb, struct dn_ifaddr *ifa,
ifm = nlmsg_data(nlh);
ifm->ifa_family = AF_DECnet;
ifm->ifa_prefixlen = 16;
- ifm->ifa_flags = ifa->ifa_flags | IFA_F_PERMANENT;
+ ifm->ifa_flags = ifa_flags;
ifm->ifa_scope = ifa->ifa_scope;
ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
@@ -694,7 +698,8 @@ static int dn_nl_fill_ifaddr(struct sk_buff *skb, struct dn_ifaddr *ifa,
(ifa->ifa_local &&
nla_put_le16(skb, IFA_LOCAL, ifa->ifa_local)) ||
(ifa->ifa_label[0] &&
- nla_put_string(skb, IFA_LABEL, ifa->ifa_label)))
+ nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
+ nla_put_u32(skb, IFA_FLAGS, ifa_flags))
goto nla_put_failure;
return nlmsg_end(skb, nlh);
diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c
index f8637f93d318..c8121ceddb9e 100644
--- a/net/decnet/dn_neigh.c
+++ b/net/decnet/dn_neigh.c
@@ -102,19 +102,21 @@ struct neigh_table dn_neigh_table = {
.id = "dn_neigh_cache",
.parms ={
.tbl = &dn_neigh_table,
- .base_reachable_time = 30 * HZ,
- .retrans_time = 1 * HZ,
- .gc_staletime = 60 * HZ,
- .reachable_time = 30 * HZ,
- .delay_probe_time = 5 * HZ,
- .queue_len_bytes = 64*1024,
- .ucast_probes = 0,
- .app_probes = 0,
- .mcast_probes = 0,
- .anycast_delay = 0,
- .proxy_delay = 0,
- .proxy_qlen = 0,
- .locktime = 1 * HZ,
+ .reachable_time = 30 * HZ,
+ .data = {
+ [NEIGH_VAR_MCAST_PROBES] = 0,
+ [NEIGH_VAR_UCAST_PROBES] = 0,
+ [NEIGH_VAR_APP_PROBES] = 0,
+ [NEIGH_VAR_RETRANS_TIME] = 1 * HZ,
+ [NEIGH_VAR_BASE_REACHABLE_TIME] = 30 * HZ,
+ [NEIGH_VAR_DELAY_PROBE_TIME] = 5 * HZ,
+ [NEIGH_VAR_GC_STALETIME] = 60 * HZ,
+ [NEIGH_VAR_QUEUE_LEN_BYTES] = 64*1024,
+ [NEIGH_VAR_PROXY_QLEN] = 0,
+ [NEIGH_VAR_ANYCAST_DELAY] = 0,
+ [NEIGH_VAR_PROXY_DELAY] = 0,
+ [NEIGH_VAR_LOCKTIME] = 1 * HZ,
+ },
},
.gc_interval = 30 * HZ,
.gc_thresh1 = 128,
diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c
index f347a2ca7d7e..bf8584339048 100644
--- a/net/dns_resolver/dns_key.c
+++ b/net/dns_resolver/dns_key.c
@@ -19,8 +19,7 @@
* the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
diff --git a/net/dns_resolver/dns_query.c b/net/dns_resolver/dns_query.c
index c32be292c7e3..e7b6d53eef88 100644
--- a/net/dns_resolver/dns_query.c
+++ b/net/dns_resolver/dns_query.c
@@ -32,8 +32,7 @@
* the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/module.h>
diff --git a/net/dns_resolver/internal.h b/net/dns_resolver/internal.h
index 17c7886b5b3a..7af1ed39c009 100644
--- a/net/dns_resolver/internal.h
+++ b/net/dns_resolver/internal.h
@@ -15,8 +15,7 @@
* the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/compiler.h>
diff --git a/net/hsr/hsr_framereg.c b/net/hsr/hsr_framereg.c
index 4bdab1521878..327060c6c874 100644
--- a/net/hsr/hsr_framereg.c
+++ b/net/hsr/hsr_framereg.c
@@ -127,11 +127,6 @@ int hsr_create_self_node(struct list_head *self_node_db,
return 0;
}
-static void node_entry_reclaim(struct rcu_head *rh)
-{
- kfree(container_of(rh, struct node_entry, rcu_head));
-}
-
/* Add/merge node to the database of nodes. 'skb' must contain an HSR
* supervision frame.
@@ -175,7 +170,7 @@ struct node_entry *hsr_merge_node(struct hsr_priv *hsr_priv,
if (node && !ether_addr_equal(node->MacAddressA, hsr_sp->MacAddressA)) {
/* Node has changed its AddrA, frame was received from SlaveB */
list_del_rcu(&node->mac_list);
- call_rcu(&node->rcu_head, node_entry_reclaim);
+ kfree_rcu(node, rcu_head);
node = NULL;
}
@@ -183,7 +178,7 @@ struct node_entry *hsr_merge_node(struct hsr_priv *hsr_priv,
!ether_addr_equal(node->MacAddressB, hsr_ethsup->ethhdr.h_source)) {
/* Cables have been swapped */
list_del_rcu(&node->mac_list);
- call_rcu(&node->rcu_head, node_entry_reclaim);
+ kfree_rcu(node, rcu_head);
node = NULL;
}
@@ -192,7 +187,7 @@ struct node_entry *hsr_merge_node(struct hsr_priv *hsr_priv,
!ether_addr_equal(node->MacAddressA, hsr_ethsup->ethhdr.h_source)) {
/* Cables have been swapped */
list_del_rcu(&node->mac_list);
- call_rcu(&node->rcu_head, node_entry_reclaim);
+ kfree_rcu(node, rcu_head);
node = NULL;
}
@@ -417,7 +412,7 @@ void hsr_prune_nodes(struct hsr_priv *hsr_priv)
hsr_nl_nodedown(hsr_priv, node->MacAddressA);
list_del_rcu(&node->mac_list);
/* Note that we need to free this entry later: */
- call_rcu(&node->rcu_head, node_entry_reclaim);
+ kfree_rcu(node, rcu_head);
}
}
rcu_read_unlock();
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 70011e029ac1..ef4f9df6d698 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1377,8 +1377,12 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
if (!NAPI_GRO_CB(p)->same_flow)
continue;
- iph2 = ip_hdr(p);
-
+ iph2 = (struct iphdr *)(p->data + off);
+ /* The above works because, with the exception of the top
+ * (inner most) layer, we only aggregate pkts with the same
+ * hdr length so all the hdrs we'll need to verify will start
+ * at the same offset.
+ */
if ((iph->protocol ^ iph2->protocol) |
((__force u32)iph->saddr ^ (__force u32)iph2->saddr) |
((__force u32)iph->daddr ^ (__force u32)iph2->daddr)) {
@@ -1397,6 +1401,11 @@ static struct sk_buff **inet_gro_receive(struct sk_buff **head,
}
NAPI_GRO_CB(skb)->flush |= flush;
+ skb_set_network_header(skb, off);
+ /* The above will be needed by the transport layer if there is one
+ * immediately following this IP hdr.
+ */
+
skb_gro_pull(skb, sizeof(*iph));
skb_set_transport_header(skb, skb_gro_offset(skb));
@@ -1411,10 +1420,10 @@ out:
return pp;
}
-static int inet_gro_complete(struct sk_buff *skb)
+static int inet_gro_complete(struct sk_buff *skb, int nhoff)
{
- __be16 newlen = htons(skb->len - skb_network_offset(skb));
- struct iphdr *iph = ip_hdr(skb);
+ __be16 newlen = htons(skb->len - nhoff);
+ struct iphdr *iph = (struct iphdr *)(skb->data + nhoff);
const struct net_offload *ops;
int proto = iph->protocol;
int err = -ENOSYS;
@@ -1427,7 +1436,11 @@ static int inet_gro_complete(struct sk_buff *skb)
if (WARN_ON(!ops || !ops->callbacks.gro_complete))
goto out_unlock;
- err = ops->callbacks.gro_complete(skb);
+ /* Only need to add sizeof(*iph) to get to the next hdr below
+ * because any hdr with option will have been flushed in
+ * inet_gro_receive().
+ */
+ err = ops->callbacks.gro_complete(skb, nhoff + sizeof(*iph));
out_unlock:
rcu_read_unlock();
diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
index 7808093cede6..b67cf805910e 100644
--- a/net/ipv4/arp.c
+++ b/net/ipv4/arp.c
@@ -166,18 +166,20 @@ struct neigh_table arp_tbl = {
.id = "arp_cache",
.parms = {
.tbl = &arp_tbl,
- .base_reachable_time = 30 * HZ,
- .retrans_time = 1 * HZ,
- .gc_staletime = 60 * HZ,
.reachable_time = 30 * HZ,
- .delay_probe_time = 5 * HZ,
- .queue_len_bytes = 64*1024,
- .ucast_probes = 3,
- .mcast_probes = 3,
- .anycast_delay = 1 * HZ,
- .proxy_delay = (8 * HZ) / 10,
- .proxy_qlen = 64,
- .locktime = 1 * HZ,
+ .data = {
+ [NEIGH_VAR_MCAST_PROBES] = 3,
+ [NEIGH_VAR_UCAST_PROBES] = 3,
+ [NEIGH_VAR_RETRANS_TIME] = 1 * HZ,
+ [NEIGH_VAR_BASE_REACHABLE_TIME] = 30 * HZ,
+ [NEIGH_VAR_DELAY_PROBE_TIME] = 5 * HZ,
+ [NEIGH_VAR_GC_STALETIME] = 60 * HZ,
+ [NEIGH_VAR_QUEUE_LEN_BYTES] = 64 * 1024,
+ [NEIGH_VAR_PROXY_QLEN] = 64,
+ [NEIGH_VAR_ANYCAST_DELAY] = 1 * HZ,
+ [NEIGH_VAR_PROXY_DELAY] = (8 * HZ) / 10,
+ [NEIGH_VAR_LOCKTIME] = 1 * HZ,
+ },
},
.gc_interval = 30 * HZ,
.gc_thresh1 = 128,
@@ -359,14 +361,14 @@ static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb)
if (!saddr)
saddr = inet_select_addr(dev, target, RT_SCOPE_LINK);
- probes -= neigh->parms->ucast_probes;
+ probes -= NEIGH_VAR(neigh->parms, UCAST_PROBES);
if (probes < 0) {
if (!(neigh->nud_state & NUD_VALID))
pr_debug("trying to ucast probe in NUD_INVALID\n");
neigh_ha_snapshot(dst_ha, neigh, dev);
dst_hw = dst_ha;
} else {
- probes -= neigh->parms->app_probes;
+ probes -= NEIGH_VAR(neigh->parms, APP_PROBES);
if (probes < 0) {
neigh_app_ns(neigh);
return;
@@ -379,6 +381,7 @@ static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb)
static int arp_ignore(struct in_device *in_dev, __be32 sip, __be32 tip)
{
+ struct net *net = dev_net(in_dev->dev);
int scope;
switch (IN_DEV_ARP_IGNORE(in_dev)) {
@@ -397,6 +400,7 @@ static int arp_ignore(struct in_device *in_dev, __be32 sip, __be32 tip)
case 3: /* Do not reply for scope host addresses */
sip = 0;
scope = RT_SCOPE_LINK;
+ in_dev = NULL;
break;
case 4: /* Reserved */
case 5:
@@ -408,7 +412,7 @@ static int arp_ignore(struct in_device *in_dev, __be32 sip, __be32 tip)
default:
return 0;
}
- return !inet_confirm_addr(in_dev, sip, tip, scope);
+ return !inet_confirm_addr(net, in_dev, sip, tip, scope);
}
static int arp_filter(__be32 sip, __be32 tip, struct net_device *dev)
@@ -871,7 +875,7 @@ static int arp_process(struct sk_buff *skb)
if (NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED ||
skb->pkt_type == PACKET_HOST ||
- in_dev->arp_parms->proxy_delay == 0) {
+ NEIGH_VAR(in_dev->arp_parms, PROXY_DELAY) == 0) {
arp_send(ARPOP_REPLY, ETH_P_ARP, sip,
dev, tip, sha, dev->dev_addr,
sha);
@@ -910,7 +914,8 @@ static int arp_process(struct sk_buff *skb)
agents are active. Taking the first reply prevents
arp trashing and chooses the fastest router.
*/
- override = time_after(jiffies, n->updated + n->parms->locktime);
+ override = time_after(jiffies, n->updated +
+ NEIGH_VAR(n->parms, LOCKTIME));
/* Broadcast replies and request packets
do not assert neighbour reachability.
@@ -1284,7 +1289,7 @@ void __init arp_init(void)
dev_add_pack(&arp_packet_type);
arp_proc_init();
#ifdef CONFIG_SYSCTL
- neigh_sysctl_register(NULL, &arp_tbl.parms, "ipv4", NULL);
+ neigh_sysctl_register(NULL, &arp_tbl.parms, NULL);
#endif
register_netdevice_notifier(&arp_netdev_notifier);
}
diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c
index 667c1d4ca984..4b59b6e488d3 100644
--- a/net/ipv4/cipso_ipv4.c
+++ b/net/ipv4/cipso_ipv4.c
@@ -31,8 +31,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index a1b5bcbd04ae..de03fe7002d0 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -99,6 +99,7 @@ static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
[IFA_BROADCAST] = { .type = NLA_U32 },
[IFA_LABEL] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
[IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) },
+ [IFA_FLAGS] = { .type = NLA_U32 },
};
#define IN4_ADDR_HSIZE_SHIFT 8
@@ -500,6 +501,7 @@ static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
return -ENOBUFS;
}
ipv4_devconf_setall(in_dev);
+ neigh_parms_data_state_setall(in_dev->arp_parms);
if (ifa->ifa_dev != in_dev) {
WARN_ON(ifa->ifa_dev);
in_dev_hold(in_dev);
@@ -747,6 +749,7 @@ static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh,
goto errout;
ipv4_devconf_setall(in_dev);
+ neigh_parms_data_state_setall(in_dev->arp_parms);
in_dev_hold(in_dev);
if (tb[IFA_ADDRESS] == NULL)
@@ -755,7 +758,8 @@ static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh,
INIT_HLIST_NODE(&ifa->hash);
ifa->ifa_prefixlen = ifm->ifa_prefixlen;
ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
- ifa->ifa_flags = ifm->ifa_flags;
+ ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) :
+ ifm->ifa_flags;
ifa->ifa_scope = ifm->ifa_scope;
ifa->ifa_dev = in_dev;
@@ -1236,22 +1240,21 @@ static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
/*
* Confirm that local IP address exists using wildcards:
- * - in_dev: only on this interface, 0=any interface
+ * - net: netns to check, cannot be NULL
+ * - in_dev: only on this interface, NULL=any interface
* - dst: only in the same subnet as dst, 0=any dst
* - local: address, 0=autoselect the local address
* - scope: maximum allowed scope value for the local address
*/
-__be32 inet_confirm_addr(struct in_device *in_dev,
+__be32 inet_confirm_addr(struct net *net, struct in_device *in_dev,
__be32 dst, __be32 local, int scope)
{
__be32 addr = 0;
struct net_device *dev;
- struct net *net;
- if (scope != RT_SCOPE_LINK)
+ if (in_dev != NULL)
return confirm_addr_indev(in_dev, dst, local, scope);
- net = dev_net(in_dev->dev);
rcu_read_lock();
for_each_netdev_rcu(net, dev) {
in_dev = __in_dev_get_rcu(dev);
@@ -1435,7 +1438,8 @@ static size_t inet_nlmsg_size(void)
+ nla_total_size(4) /* IFA_ADDRESS */
+ nla_total_size(4) /* IFA_LOCAL */
+ nla_total_size(4) /* IFA_BROADCAST */
- + nla_total_size(IFNAMSIZ); /* IFA_LABEL */
+ + nla_total_size(IFNAMSIZ) /* IFA_LABEL */
+ + nla_total_size(4); /* IFA_FLAGS */
}
static inline u32 cstamp_delta(unsigned long cstamp)
@@ -1503,6 +1507,7 @@ static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
nla_put_be32(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
(ifa->ifa_label[0] &&
nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) ||
+ nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) ||
put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp,
preferred, valid))
goto nla_put_failure;
@@ -1691,6 +1696,8 @@ static int inet_netconf_msgsize_devconf(int type)
size += nla_total_size(4);
if (type == -1 || type == NETCONFA_MC_FORWARDING)
size += nla_total_size(4);
+ if (type == -1 || type == NETCONFA_PROXY_ARP)
+ size += nla_total_size(4);
return size;
}
@@ -1727,6 +1734,10 @@ static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex,
nla_put_s32(skb, NETCONFA_MC_FORWARDING,
IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0)
goto nla_put_failure;
+ if ((type == -1 || type == NETCONFA_PROXY_ARP) &&
+ nla_put_s32(skb, NETCONFA_PROXY_ARP,
+ IPV4_DEVCONF(*devconf, PROXY_ARP)) < 0)
+ goto nla_put_failure;
return nlmsg_end(skb, nlh);
@@ -1764,6 +1775,7 @@ static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = {
[NETCONFA_IFINDEX] = { .len = sizeof(int) },
[NETCONFA_FORWARDING] = { .len = sizeof(int) },
[NETCONFA_RP_FILTER] = { .len = sizeof(int) },
+ [NETCONFA_PROXY_ARP] = { .len = sizeof(int) },
};
static int inet_netconf_get_devconf(struct sk_buff *in_skb,
@@ -1945,6 +1957,19 @@ static void inet_forward_change(struct net *net)
}
}
+static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf)
+{
+ if (cnf == net->ipv4.devconf_dflt)
+ return NETCONFA_IFINDEX_DEFAULT;
+ else if (cnf == net->ipv4.devconf_all)
+ return NETCONFA_IFINDEX_ALL;
+ else {
+ struct in_device *idev
+ = container_of(cnf, struct in_device, cnf);
+ return idev->dev->ifindex;
+ }
+}
+
static int devinet_conf_proc(struct ctl_table *ctl, int write,
void __user *buffer,
size_t *lenp, loff_t *ppos)
@@ -1957,6 +1982,7 @@ static int devinet_conf_proc(struct ctl_table *ctl, int write,
struct ipv4_devconf *cnf = ctl->extra1;
struct net *net = ctl->extra2;
int i = (int *)ctl->data - cnf->data;
+ int ifindex;
set_bit(i, cnf->state);
@@ -1966,23 +1992,19 @@ static int devinet_conf_proc(struct ctl_table *ctl, int write,
i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
if ((new_value == 0) && (old_value != 0))
rt_cache_flush(net);
+
if (i == IPV4_DEVCONF_RP_FILTER - 1 &&
new_value != old_value) {
- int ifindex;
-
- if (cnf == net->ipv4.devconf_dflt)
- ifindex = NETCONFA_IFINDEX_DEFAULT;
- else if (cnf == net->ipv4.devconf_all)
- ifindex = NETCONFA_IFINDEX_ALL;
- else {
- struct in_device *idev =
- container_of(cnf, struct in_device,
- cnf);
- ifindex = idev->dev->ifindex;
- }
+ ifindex = devinet_conf_ifindex(net, cnf);
inet_netconf_notify_devconf(net, NETCONFA_RP_FILTER,
ifindex, cnf);
}
+ if (i == IPV4_DEVCONF_PROXY_ARP - 1 &&
+ new_value != old_value) {
+ ifindex = devinet_conf_ifindex(net, cnf);
+ inet_netconf_notify_devconf(net, NETCONFA_PROXY_ARP,
+ ifindex, cnf);
+ }
}
return ret;
@@ -2160,7 +2182,7 @@ static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf)
static void devinet_sysctl_register(struct in_device *idev)
{
- neigh_sysctl_register(idev->dev, idev->arp_parms, "ipv4", NULL);
+ neigh_sysctl_register(idev->dev, idev->arp_parms, NULL);
__devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
&idev->cnf);
}
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index 2481993a4970..c10a3ce5cbff 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -704,7 +704,7 @@ struct sk_buff *ip_check_defrag(struct sk_buff *skb, u32 user)
memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
if (ip_defrag(skb, user))
return NULL;
- skb->rxhash = 0;
+ skb_clear_hash(skb);
}
}
return skb;
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index ddf32a6bc415..a9fc435dc89f 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -1051,7 +1051,7 @@ e_inval:
*
* To support IP_CMSG_PKTINFO option, we store rt_iif and specific
* destination in skb->cb[] before dst drop.
- * This way, receiver doesnt make cache line misses to read rtable.
+ * This way, receiver doesn't make cache line misses to read rtable.
*/
void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb)
{
diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c
index 42ffbc8d65c6..6156f4ef5e91 100644
--- a/net/ipv4/ip_tunnel_core.c
+++ b/net/ipv4/ip_tunnel_core.c
@@ -56,7 +56,7 @@ int iptunnel_xmit(struct rtable *rt, struct sk_buff *skb,
skb_scrub_packet(skb, xnet);
- skb->rxhash = 0;
+ skb_clear_hash(skb);
skb_dst_set(skb, &rt->dst);
memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
@@ -107,8 +107,7 @@ int iptunnel_pull_header(struct sk_buff *skb, int hdr_len, __be16 inner_proto)
nf_reset(skb);
secpath_reset(skb);
- if (!skb->l4_rxhash)
- skb->rxhash = 0;
+ skb_clear_hash_if_not_l4(skb);
skb_dst_drop(skb);
skb->vlan_tci = 0;
skb_set_queue_mapping(skb, 0);
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 62212c772a4b..421a24934ffd 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -425,6 +425,7 @@ struct net_device *ipmr_new_tunnel(struct net *net, struct vifctl *v)
goto failure;
ipv4_devconf_setall(in_dev);
+ neigh_parms_data_state_setall(in_dev->arp_parms);
IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0;
if (dev_open(dev))
@@ -517,6 +518,7 @@ static struct net_device *ipmr_reg_vif(struct net *net, struct mr_table *mrt)
}
ipv4_devconf_setall(in_dev);
+ neigh_parms_data_state_setall(in_dev->arp_parms);
IPV4_DEVCONF(in_dev->cnf, RP_FILTER) = 0;
rcu_read_unlock();
diff --git a/net/ipv4/netfilter/nf_nat_snmp_basic.c b/net/ipv4/netfilter/nf_nat_snmp_basic.c
index 5f011cc89cd9..61a942265e8a 100644
--- a/net/ipv4/netfilter/nf_nat_snmp_basic.c
+++ b/net/ipv4/netfilter/nf_nat_snmp_basic.c
@@ -34,8 +34,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
* Author: James Morris <jmorris@intercode.com.au>
*
diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c
index 4a0335854b89..8ecd7ad959b4 100644
--- a/net/ipv4/proc.c
+++ b/net/ipv4/proc.c
@@ -279,6 +279,7 @@ static const struct snmp_mib snmp4_net_list[] = {
SNMP_MIB_ITEM("TCPFastOpenCookieReqd", LINUX_MIB_TCPFASTOPENCOOKIEREQD),
SNMP_MIB_ITEM("TCPSpuriousRtxHostQueues", LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES),
SNMP_MIB_ITEM("BusyPollRxPackets", LINUX_MIB_BUSYPOLLRXPACKETS),
+ SNMP_MIB_ITEM("TCPAutoCorking", LINUX_MIB_TCPAUTOCORKING),
SNMP_MIB_SENTINEL
};
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 3d69ec8dac57..38c8ec90ff68 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -733,6 +733,15 @@ static struct ctl_table ipv4_table[] = {
.extra2 = &gso_max_segs,
},
{
+ .procname = "tcp_autocorking",
+ .data = &sysctl_tcp_autocorking,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec_minmax,
+ .extra1 = &zero,
+ .extra2 = &one,
+ },
+ {
.procname = "udp_mem",
.data = &sysctl_udp_mem,
.maxlen = sizeof(sysctl_udp_mem),
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index c4638e6f0238..0ca87547becb 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -285,6 +285,8 @@ int sysctl_tcp_fin_timeout __read_mostly = TCP_FIN_TIMEOUT;
int sysctl_tcp_min_tso_segs __read_mostly = 2;
+int sysctl_tcp_autocorking __read_mostly = 1;
+
struct percpu_counter tcp_orphan_count;
EXPORT_SYMBOL_GPL(tcp_orphan_count);
@@ -619,19 +621,52 @@ static inline void tcp_mark_urg(struct tcp_sock *tp, int flags)
tp->snd_up = tp->write_seq;
}
-static inline void tcp_push(struct sock *sk, int flags, int mss_now,
- int nonagle)
+/* If a not yet filled skb is pushed, do not send it if
+ * we have packets in Qdisc or NIC queues :
+ * Because TX completion will happen shortly, it gives a chance
+ * to coalesce future sendmsg() payload into this skb, without
+ * need for a timer, and with no latency trade off.
+ * As packets containing data payload have a bigger truesize
+ * than pure acks (dataless) packets, the last check prevents
+ * autocorking if we only have an ACK in Qdisc/NIC queues.
+ */
+static bool tcp_should_autocork(struct sock *sk, struct sk_buff *skb,
+ int size_goal)
{
- if (tcp_send_head(sk)) {
- struct tcp_sock *tp = tcp_sk(sk);
+ return skb->len < size_goal &&
+ sysctl_tcp_autocorking &&
+ atomic_read(&sk->sk_wmem_alloc) > skb->truesize;
+}
+
+static void tcp_push(struct sock *sk, int flags, int mss_now,
+ int nonagle, int size_goal)
+{
+ struct tcp_sock *tp = tcp_sk(sk);
+ struct sk_buff *skb;
- if (!(flags & MSG_MORE) || forced_push(tp))
- tcp_mark_push(tp, tcp_write_queue_tail(sk));
+ if (!tcp_send_head(sk))
+ return;
+
+ skb = tcp_write_queue_tail(sk);
+ if (!(flags & MSG_MORE) || forced_push(tp))
+ tcp_mark_push(tp, skb);
+
+ tcp_mark_urg(tp, flags);
+
+ if (tcp_should_autocork(sk, skb, size_goal)) {
- tcp_mark_urg(tp, flags);
- __tcp_push_pending_frames(sk, mss_now,
- (flags & MSG_MORE) ? TCP_NAGLE_CORK : nonagle);
+ /* avoid atomic op if TSQ_THROTTLED bit is already set */
+ if (!test_bit(TSQ_THROTTLED, &tp->tsq_flags)) {
+ NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAUTOCORKING);
+ set_bit(TSQ_THROTTLED, &tp->tsq_flags);
+ }
+ return;
}
+
+ if (flags & MSG_MORE)
+ nonagle = TCP_NAGLE_CORK;
+
+ __tcp_push_pending_frames(sk, mss_now, nonagle);
}
static int tcp_splice_data_recv(read_descriptor_t *rd_desc, struct sk_buff *skb,
@@ -934,7 +969,8 @@ new_segment:
wait_for_sndbuf:
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
wait_for_memory:
- tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH);
+ tcp_push(sk, flags & ~MSG_MORE, mss_now,
+ TCP_NAGLE_PUSH, size_goal);
if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
goto do_error;
@@ -944,7 +980,7 @@ wait_for_memory:
out:
if (copied && !(flags & MSG_SENDPAGE_NOTLAST))
- tcp_push(sk, flags, mss_now, tp->nonagle);
+ tcp_push(sk, flags, mss_now, tp->nonagle, size_goal);
return copied;
do_error:
@@ -1225,7 +1261,8 @@ wait_for_sndbuf:
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
wait_for_memory:
if (copied)
- tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH);
+ tcp_push(sk, flags & ~MSG_MORE, mss_now,
+ TCP_NAGLE_PUSH, size_goal);
if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
goto do_error;
@@ -1236,7 +1273,7 @@ wait_for_memory:
out:
if (copied)
- tcp_push(sk, flags, mss_now, tp->nonagle);
+ tcp_push(sk, flags, mss_now, tp->nonagle, size_goal);
release_sock(sk);
return copied + copied_syn;
diff --git a/net/ipv4/tcp_offload.c b/net/ipv4/tcp_offload.c
index 05606353c7e7..2658a27f540d 100644
--- a/net/ipv4/tcp_offload.c
+++ b/net/ipv4/tcp_offload.c
@@ -240,7 +240,7 @@ int tcp_gro_complete(struct sk_buff *skb)
{
struct tcphdr *th = tcp_hdr(skb);
- skb->csum_start = skb_transport_header(skb) - skb->head;
+ skb->csum_start = (unsigned char *)th - skb->head;
skb->csum_offset = offsetof(struct tcphdr, check);
skb->ip_summed = CHECKSUM_PARTIAL;
@@ -272,6 +272,7 @@ static int tcp_v4_gso_send_check(struct sk_buff *skb)
static struct sk_buff **tcp4_gro_receive(struct sk_buff **head, struct sk_buff *skb)
{
+ /* Use the IP hdr immediately proceeding for this transport */
const struct iphdr *iph = skb_gro_network_header(skb);
__wsum wsum;
@@ -303,13 +304,13 @@ skip_csum:
return tcp_gro_receive(head, skb);
}
-static int tcp4_gro_complete(struct sk_buff *skb)
+static int tcp4_gro_complete(struct sk_buff *skb, int thoff)
{
const struct iphdr *iph = ip_hdr(skb);
struct tcphdr *th = tcp_hdr(skb);
- th->check = ~tcp_v4_check(skb->len - skb_transport_offset(skb),
- iph->saddr, iph->daddr, 0);
+ th->check = ~tcp_v4_check(skb->len - thoff, iph->saddr,
+ iph->daddr, 0);
skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
return tcp_gro_complete(skb);
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 7820f3a7dd70..9e7aec7ee67e 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -363,15 +363,17 @@ static inline void TCP_ECN_send(struct sock *sk, struct sk_buff *skb,
*/
static void tcp_init_nondata_skb(struct sk_buff *skb, u32 seq, u8 flags)
{
+ struct skb_shared_info *shinfo = skb_shinfo(skb);
+
skb->ip_summed = CHECKSUM_PARTIAL;
skb->csum = 0;
TCP_SKB_CB(skb)->tcp_flags = flags;
TCP_SKB_CB(skb)->sacked = 0;
- skb_shinfo(skb)->gso_segs = 1;
- skb_shinfo(skb)->gso_size = 0;
- skb_shinfo(skb)->gso_type = 0;
+ shinfo->gso_segs = 1;
+ shinfo->gso_size = 0;
+ shinfo->gso_type = 0;
TCP_SKB_CB(skb)->seq = seq;
if (flags & (TCPHDR_SYN | TCPHDR_FIN))
@@ -406,7 +408,7 @@ struct tcp_out_options {
* Beware: Something in the Internet is very sensitive to the ordering of
* TCP options, we learned this through the hard way, so be careful here.
* Luckily we can at least blame others for their non-compliance but from
- * inter-operatibility perspective it seems that we're somewhat stuck with
+ * inter-operability perspective it seems that we're somewhat stuck with
* the ordering which we have been using if we want to keep working with
* those broken things (not that it currently hurts anybody as there isn't
* particular reason why the ordering would need to be changed).
@@ -679,7 +681,7 @@ static unsigned int tcp_established_options(struct sock *sk, struct sk_buff *skb
*
* Its important tcp_wfree() can be replaced by sock_wfree() in the event skb
* needs to be reallocated in a driver.
- * The invariant being skb->truesize substracted from sk->sk_wmem_alloc
+ * The invariant being skb->truesize subtracted from sk->sk_wmem_alloc
*
* Since transmit from skb destructor is forbidden, we use a tasklet
* to process all sockets that eventually need to send more skbs.
@@ -699,9 +701,9 @@ static void tcp_tsq_handler(struct sock *sk)
tcp_write_xmit(sk, tcp_current_mss(sk), 0, 0, GFP_ATOMIC);
}
/*
- * One tasklest per cpu tries to send more skbs.
+ * One tasklet per cpu tries to send more skbs.
* We run in tasklet context but need to disable irqs when
- * transfering tsq->head because tcp_wfree() might
+ * transferring tsq->head because tcp_wfree() might
* interrupt us (non NAPI drivers)
*/
static void tcp_tasklet_func(unsigned long data)
@@ -795,7 +797,7 @@ void __init tcp_tasklet_init(void)
/*
* Write buffer destructor automatically called from kfree_skb.
- * We cant xmit new skbs from this context, as we might already
+ * We can't xmit new skbs from this context, as we might already
* hold qdisc lock.
*/
void tcp_wfree(struct sk_buff *skb)
@@ -986,6 +988,8 @@ static void tcp_queue_skb(struct sock *sk, struct sk_buff *skb)
static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb,
unsigned int mss_now)
{
+ struct skb_shared_info *shinfo = skb_shinfo(skb);
+
/* Make sure we own this skb before messing gso_size/gso_segs */
WARN_ON_ONCE(skb_cloned(skb));
@@ -993,13 +997,13 @@ static void tcp_set_skb_tso_segs(const struct sock *sk, struct sk_buff *skb,
/* Avoid the costly divide in the normal
* non-TSO case.
*/
- skb_shinfo(skb)->gso_segs = 1;
- skb_shinfo(skb)->gso_size = 0;
- skb_shinfo(skb)->gso_type = 0;
+ shinfo->gso_segs = 1;
+ shinfo->gso_size = 0;
+ shinfo->gso_type = 0;
} else {
- skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss_now);
- skb_shinfo(skb)->gso_size = mss_now;
- skb_shinfo(skb)->gso_type = sk->sk_gso_type;
+ shinfo->gso_segs = DIV_ROUND_UP(skb->len, mss_now);
+ shinfo->gso_size = mss_now;
+ shinfo->gso_type = sk->sk_gso_type;
}
}
@@ -1146,6 +1150,7 @@ int tcp_fragment(struct sock *sk, struct sk_buff *skb, u32 len,
*/
static void __pskb_trim_head(struct sk_buff *skb, int len)
{
+ struct skb_shared_info *shinfo;
int i, k, eat;
eat = min_t(int, len, skb_headlen(skb));
@@ -1157,23 +1162,24 @@ static void __pskb_trim_head(struct sk_buff *skb, int len)
}
eat = len;
k = 0;
- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- int size = skb_frag_size(&skb_shinfo(skb)->frags[i]);
+ shinfo = skb_shinfo(skb);
+ for (i = 0; i < shinfo->nr_frags; i++) {
+ int size = skb_frag_size(&shinfo->frags[i]);
if (size <= eat) {
skb_frag_unref(skb, i);
eat -= size;
} else {
- skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i];
+ shinfo->frags[k] = shinfo->frags[i];
if (eat) {
- skb_shinfo(skb)->frags[k].page_offset += eat;
- skb_frag_size_sub(&skb_shinfo(skb)->frags[k], eat);
+ shinfo->frags[k].page_offset += eat;
+ skb_frag_size_sub(&shinfo->frags[k], eat);
eat = 0;
}
k++;
}
}
- skb_shinfo(skb)->nr_frags = k;
+ shinfo->nr_frags = k;
skb_reset_tail_pointer(skb);
skb->data_len -= len;
@@ -1378,23 +1384,51 @@ static void tcp_cwnd_validate(struct sock *sk)
}
}
-/* Returns the portion of skb which can be sent right away without
- * introducing MSS oddities to segment boundaries. In rare cases where
- * mss_now != mss_cache, we will request caller to create a small skb
- * per input skb which could be mostly avoided here (if desired).
- *
- * We explicitly want to create a request for splitting write queue tail
- * to a small skb for Nagle purposes while avoiding unnecessary modulos,
- * thus all the complexity (cwnd_len is always MSS multiple which we
- * return whenever allowed by the other factors). Basically we need the
- * modulo only when the receiver window alone is the limiting factor or
- * when we would be allowed to send the split-due-to-Nagle skb fully.
+/* Minshall's variant of the Nagle send check. */
+static bool tcp_minshall_check(const struct tcp_sock *tp)
+{
+ return after(tp->snd_sml, tp->snd_una) &&
+ !after(tp->snd_sml, tp->snd_nxt);
+}
+
+/* Update snd_sml if this skb is under mss
+ * Note that a TSO packet might end with a sub-mss segment
+ * The test is really :
+ * if ((skb->len % mss) != 0)
+ * tp->snd_sml = TCP_SKB_CB(skb)->end_seq;
+ * But we can avoid doing the divide again given we already have
+ * skb_pcount = skb->len / mss_now
+ */
+static void tcp_minshall_update(struct tcp_sock *tp, unsigned int mss_now,
+ const struct sk_buff *skb)
+{
+ if (skb->len < tcp_skb_pcount(skb) * mss_now)
+ tp->snd_sml = TCP_SKB_CB(skb)->end_seq;
+}
+
+/* Return false, if packet can be sent now without violation Nagle's rules:
+ * 1. It is full sized. (provided by caller in %partial bool)
+ * 2. Or it contains FIN. (already checked by caller)
+ * 3. Or TCP_CORK is not set, and TCP_NODELAY is set.
+ * 4. Or TCP_CORK is not set, and all sent packets are ACKed.
+ * With Minshall's modification: all sent small packets are ACKed.
*/
-static unsigned int tcp_mss_split_point(const struct sock *sk, const struct sk_buff *skb,
- unsigned int mss_now, unsigned int max_segs)
+static bool tcp_nagle_check(bool partial, const struct tcp_sock *tp,
+ unsigned int mss_now, int nonagle)
+{
+ return partial &&
+ ((nonagle & TCP_NAGLE_CORK) ||
+ (!nonagle && tp->packets_out && tcp_minshall_check(tp)));
+}
+/* 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,
+ unsigned int mss_now,
+ unsigned int max_segs,
+ int nonagle)
{
const struct tcp_sock *tp = tcp_sk(sk);
- u32 needed, window, max_len;
+ u32 partial, needed, window, max_len;
window = tcp_wnd_end(tp) - TCP_SKB_CB(skb)->seq;
max_len = mss_now * max_segs;
@@ -1407,7 +1441,15 @@ static unsigned int tcp_mss_split_point(const struct sock *sk, const struct sk_b
if (max_len <= needed)
return max_len;
- return needed - needed % mss_now;
+ partial = needed % mss_now;
+ /* If last segment is not a full MSS, check if Nagle rules allow us
+ * to include this last segment in this skb.
+ * Otherwise, we'll split the skb at last MSS boundary
+ */
+ if (tcp_nagle_check(partial != 0, tp, mss_now, nonagle))
+ return needed - partial;
+
+ return needed;
}
/* Can at least one segment of SKB be sent right now, according to the
@@ -1447,28 +1489,6 @@ static int tcp_init_tso_segs(const struct sock *sk, struct sk_buff *skb,
return tso_segs;
}
-/* Minshall's variant of the Nagle send check. */
-static inline bool tcp_minshall_check(const struct tcp_sock *tp)
-{
- return after(tp->snd_sml, tp->snd_una) &&
- !after(tp->snd_sml, tp->snd_nxt);
-}
-
-/* Return false, if packet can be sent now without violation Nagle's rules:
- * 1. It is full sized.
- * 2. Or it contains FIN. (already checked by caller)
- * 3. Or TCP_CORK is not set, and TCP_NODELAY is set.
- * 4. Or TCP_CORK is not set, and all sent packets are ACKed.
- * With Minshall's modification: all sent small packets are ACKed.
- */
-static inline bool tcp_nagle_check(const struct tcp_sock *tp,
- const struct sk_buff *skb,
- unsigned int mss_now, int nonagle)
-{
- return skb->len < mss_now &&
- ((nonagle & TCP_NAGLE_CORK) ||
- (!nonagle && tp->packets_out && tcp_minshall_check(tp)));
-}
/* Return true if the Nagle test allows this packet to be
* sent now.
@@ -1489,7 +1509,7 @@ static inline bool tcp_nagle_test(const struct tcp_sock *tp, const struct sk_buf
if (tcp_urg_mode(tp) || (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN))
return true;
- if (!tcp_nagle_check(tp, skb, cur_mss, nonagle))
+ if (!tcp_nagle_check(skb->len < cur_mss, tp, cur_mss, nonagle))
return true;
return false;
@@ -1892,7 +1912,8 @@ static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
limit = tcp_mss_split_point(sk, skb, mss_now,
min_t(unsigned int,
cwnd_quota,
- sk->sk_gso_max_segs));
+ sk->sk_gso_max_segs),
+ nonagle);
if (skb->len > limit &&
unlikely(tso_fragment(sk, skb, limit, mss_now, gfp)))
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index d5fa5b8c443e..3c3425ee6a2b 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -834,6 +834,8 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
goto out;
}
+ neigh_parms_data_state_setall(idev->nd_parms);
+
ifa->addr = *addr;
if (peer_addr)
ifa->peer_addr = *peer_addr;
@@ -1024,7 +1026,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i
u32 addr_flags;
unsigned long now = jiffies;
- write_lock(&idev->lock);
+ write_lock_bh(&idev->lock);
if (ift) {
spin_lock_bh(&ift->lock);
memcpy(&addr.s6_addr[8], &ift->addr.s6_addr[8], 8);
@@ -1036,7 +1038,7 @@ static int ipv6_create_tempaddr(struct inet6_ifaddr *ifp, struct inet6_ifaddr *i
retry:
in6_dev_hold(idev);
if (idev->cnf.use_tempaddr <= 0) {
- write_unlock(&idev->lock);
+ write_unlock_bh(&idev->lock);
pr_info("%s: use_tempaddr is disabled\n", __func__);
in6_dev_put(idev);
ret = -1;
@@ -1046,7 +1048,7 @@ retry:
if (ifp->regen_count++ >= idev->cnf.regen_max_retry) {
idev->cnf.use_tempaddr = -1; /*XXX*/
spin_unlock_bh(&ifp->lock);
- write_unlock(&idev->lock);
+ write_unlock_bh(&idev->lock);
pr_warn("%s: regeneration time exceeded - disabled temporary address support\n",
__func__);
in6_dev_put(idev);
@@ -1071,8 +1073,8 @@ retry:
regen_advance = idev->cnf.regen_max_retry *
idev->cnf.dad_transmits *
- idev->nd_parms->retrans_time / HZ;
- write_unlock(&idev->lock);
+ NEIGH_VAR(idev->nd_parms, RETRANS_TIME) / HZ;
+ write_unlock_bh(&idev->lock);
/* A temporary address is created only if this calculated Preferred
* Lifetime is greater than REGEN_ADVANCE time units. In particular,
@@ -1099,7 +1101,7 @@ retry:
in6_dev_put(idev);
pr_info("%s: retry temporary address regeneration\n", __func__);
tmpaddr = &addr;
- write_lock(&idev->lock);
+ write_lock_bh(&idev->lock);
goto retry;
}
@@ -1407,7 +1409,7 @@ try_nextdev:
EXPORT_SYMBOL(ipv6_dev_get_saddr);
int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr,
- unsigned char banned_flags)
+ u32 banned_flags)
{
struct inet6_ifaddr *ifp;
int err = -EADDRNOTAVAIL;
@@ -1424,7 +1426,7 @@ int __ipv6_get_lladdr(struct inet6_dev *idev, struct in6_addr *addr,
}
int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr,
- unsigned char banned_flags)
+ u32 banned_flags)
{
struct inet6_dev *idev;
int err = -EADDRNOTAVAIL;
@@ -1888,7 +1890,8 @@ static void ipv6_regen_rndid(unsigned long data)
expires = jiffies +
idev->cnf.temp_prefered_lft * HZ -
- idev->cnf.regen_max_retry * idev->cnf.dad_transmits * idev->nd_parms->retrans_time -
+ idev->cnf.regen_max_retry * idev->cnf.dad_transmits *
+ NEIGH_VAR(idev->nd_parms, RETRANS_TIME) -
idev->cnf.max_desync_factor * HZ;
if (time_before(expires, jiffies)) {
pr_warn("%s: too short regeneration interval; timer disabled for %s\n",
@@ -2016,6 +2019,73 @@ static struct inet6_dev *addrconf_add_dev(struct net_device *dev)
return idev;
}
+static void manage_tempaddrs(struct inet6_dev *idev,
+ struct inet6_ifaddr *ifp,
+ __u32 valid_lft, __u32 prefered_lft,
+ bool create, unsigned long now)
+{
+ u32 flags;
+ struct inet6_ifaddr *ift;
+
+ read_lock_bh(&idev->lock);
+ /* update all temporary addresses in the list */
+ list_for_each_entry(ift, &idev->tempaddr_list, tmp_list) {
+ int age, max_valid, max_prefered;
+
+ if (ifp != ift->ifpub)
+ continue;
+
+ /* RFC 4941 section 3.3:
+ * If a received option will extend the lifetime of a public
+ * address, the lifetimes of temporary addresses should
+ * be extended, subject to the overall constraint that no
+ * temporary addresses should ever remain "valid" or "preferred"
+ * for a time longer than (TEMP_VALID_LIFETIME) or
+ * (TEMP_PREFERRED_LIFETIME - DESYNC_FACTOR), respectively.
+ */
+ age = (now - ift->cstamp) / HZ;
+ max_valid = idev->cnf.temp_valid_lft - age;
+ if (max_valid < 0)
+ max_valid = 0;
+
+ max_prefered = idev->cnf.temp_prefered_lft -
+ idev->cnf.max_desync_factor - age;
+ if (max_prefered < 0)
+ max_prefered = 0;
+
+ if (valid_lft > max_valid)
+ valid_lft = max_valid;
+
+ if (prefered_lft > max_prefered)
+ prefered_lft = max_prefered;
+
+ spin_lock(&ift->lock);
+ flags = ift->flags;
+ ift->valid_lft = valid_lft;
+ ift->prefered_lft = prefered_lft;
+ ift->tstamp = now;
+ if (prefered_lft > 0)
+ ift->flags &= ~IFA_F_DEPRECATED;
+
+ spin_unlock(&ift->lock);
+ if (!(flags&IFA_F_TENTATIVE))
+ ipv6_ifa_notify(0, ift);
+ }
+
+ if ((create || list_empty(&idev->tempaddr_list)) &&
+ idev->cnf.use_tempaddr > 0) {
+ /* When a new public address is created as described
+ * in [ADDRCONF], also create a new temporary address.
+ * Also create a temporary address if it's enabled but
+ * no temporary address currently exists.
+ */
+ read_unlock_bh(&idev->lock);
+ ipv6_create_tempaddr(ifp, NULL);
+ } else {
+ read_unlock_bh(&idev->lock);
+ }
+}
+
void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len, bool sllao)
{
struct prefix_info *pinfo;
@@ -2170,6 +2240,7 @@ ok:
return;
}
+ ifp->flags |= IFA_F_MANAGETEMPADDR;
update_lft = 0;
create = 1;
ifp->cstamp = jiffies;
@@ -2178,9 +2249,8 @@ ok:
}
if (ifp) {
- int flags;
+ u32 flags;
unsigned long now;
- struct inet6_ifaddr *ift;
u32 stored_lft;
/* update lifetime (RFC2462 5.5.3 e) */
@@ -2221,70 +2291,8 @@ ok:
} else
spin_unlock(&ifp->lock);
- read_lock_bh(&in6_dev->lock);
- /* update all temporary addresses in the list */
- list_for_each_entry(ift, &in6_dev->tempaddr_list,
- tmp_list) {
- int age, max_valid, max_prefered;
-
- if (ifp != ift->ifpub)
- continue;
-
- /*
- * RFC 4941 section 3.3:
- * If a received option will extend the lifetime
- * of a public address, the lifetimes of
- * temporary addresses should be extended,
- * subject to the overall constraint that no
- * temporary addresses should ever remain
- * "valid" or "preferred" for a time longer than
- * (TEMP_VALID_LIFETIME) or
- * (TEMP_PREFERRED_LIFETIME - DESYNC_FACTOR),
- * respectively.
- */
- age = (now - ift->cstamp) / HZ;
- max_valid = in6_dev->cnf.temp_valid_lft - age;
- if (max_valid < 0)
- max_valid = 0;
-
- max_prefered = in6_dev->cnf.temp_prefered_lft -
- in6_dev->cnf.max_desync_factor -
- age;
- if (max_prefered < 0)
- max_prefered = 0;
-
- if (valid_lft > max_valid)
- valid_lft = max_valid;
-
- if (prefered_lft > max_prefered)
- prefered_lft = max_prefered;
-
- spin_lock(&ift->lock);
- flags = ift->flags;
- ift->valid_lft = valid_lft;
- ift->prefered_lft = prefered_lft;
- ift->tstamp = now;
- if (prefered_lft > 0)
- ift->flags &= ~IFA_F_DEPRECATED;
-
- spin_unlock(&ift->lock);
- if (!(flags&IFA_F_TENTATIVE))
- ipv6_ifa_notify(0, ift);
- }
-
- if ((create || list_empty(&in6_dev->tempaddr_list)) && in6_dev->cnf.use_tempaddr > 0) {
- /*
- * When a new public address is created as
- * described in [ADDRCONF], also create a new
- * temporary address. Also create a temporary
- * address if it's enabled but no temporary
- * address currently exists.
- */
- read_unlock_bh(&in6_dev->lock);
- ipv6_create_tempaddr(ifp, NULL);
- } else {
- read_unlock_bh(&in6_dev->lock);
- }
+ manage_tempaddrs(in6_dev, ifp, valid_lft, prefered_lft,
+ create, now);
in6_ifa_put(ifp);
addrconf_verify(0);
@@ -2363,10 +2371,11 @@ err_exit:
/*
* Manual configuration of address on an interface
*/
-static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *pfx,
+static int inet6_addr_add(struct net *net, int ifindex,
+ const struct in6_addr *pfx,
const struct in6_addr *peer_pfx,
- unsigned int plen, __u8 ifa_flags, __u32 prefered_lft,
- __u32 valid_lft)
+ unsigned int plen, __u32 ifa_flags,
+ __u32 prefered_lft, __u32 valid_lft)
{
struct inet6_ifaddr *ifp;
struct inet6_dev *idev;
@@ -2385,6 +2394,9 @@ static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *p
if (!valid_lft || prefered_lft > valid_lft)
return -EINVAL;
+ if (ifa_flags & IFA_F_MANAGETEMPADDR && plen != 64)
+ return -EINVAL;
+
dev = __dev_get_by_index(net, ifindex);
if (!dev)
return -ENODEV;
@@ -2425,6 +2437,9 @@ static int inet6_addr_add(struct net *net, int ifindex, const struct in6_addr *p
* manually configured addresses
*/
addrconf_dad_start(ifp);
+ if (ifa_flags & IFA_F_MANAGETEMPADDR)
+ manage_tempaddrs(idev, ifp, valid_lft, prefered_lft,
+ true, jiffies);
in6_ifa_put(ifp);
addrconf_verify(0);
return 0;
@@ -3176,7 +3191,8 @@ static void addrconf_dad_timer(unsigned long data)
}
ifp->dad_probes--;
- addrconf_mod_dad_timer(ifp, ifp->idev->nd_parms->retrans_time);
+ addrconf_mod_dad_timer(ifp,
+ NEIGH_VAR(ifp->idev->nd_parms, RETRANS_TIME));
spin_unlock(&ifp->lock);
write_unlock(&idev->lock);
@@ -3356,7 +3372,7 @@ static int if6_seq_show(struct seq_file *seq, void *v)
ifp->idev->dev->ifindex,
ifp->prefix_len,
ifp->scope,
- ifp->flags,
+ (u8) ifp->flags,
ifp->idev->dev->name);
return 0;
}
@@ -3497,7 +3513,7 @@ restart:
!(ifp->flags&IFA_F_TENTATIVE)) {
unsigned long regen_advance = ifp->idev->cnf.regen_max_retry *
ifp->idev->cnf.dad_transmits *
- ifp->idev->nd_parms->retrans_time / HZ;
+ NEIGH_VAR(ifp->idev->nd_parms, RETRANS_TIME) / HZ;
if (age >= ifp->prefered_lft - regen_advance) {
struct inet6_ifaddr *ifpub = ifp->ifpub;
@@ -3572,6 +3588,7 @@ static const struct nla_policy ifa_ipv6_policy[IFA_MAX+1] = {
[IFA_ADDRESS] = { .len = sizeof(struct in6_addr) },
[IFA_LOCAL] = { .len = sizeof(struct in6_addr) },
[IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) },
+ [IFA_FLAGS] = { .len = sizeof(u32) },
};
static int
@@ -3595,16 +3612,21 @@ inet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh)
return inet6_addr_del(net, ifm->ifa_index, pfx, ifm->ifa_prefixlen);
}
-static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags,
+static int inet6_addr_modify(struct inet6_ifaddr *ifp, u32 ifa_flags,
u32 prefered_lft, u32 valid_lft)
{
u32 flags;
clock_t expires;
unsigned long timeout;
+ bool was_managetempaddr;
if (!valid_lft || (prefered_lft > valid_lft))
return -EINVAL;
+ if (ifa_flags & IFA_F_MANAGETEMPADDR &&
+ (ifp->flags & IFA_F_TEMPORARY || ifp->prefix_len != 64))
+ return -EINVAL;
+
timeout = addrconf_timeout_fixup(valid_lft, HZ);
if (addrconf_finite_timeout(timeout)) {
expires = jiffies_to_clock_t(timeout * HZ);
@@ -3624,7 +3646,10 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags,
}
spin_lock_bh(&ifp->lock);
- ifp->flags = (ifp->flags & ~(IFA_F_DEPRECATED | IFA_F_PERMANENT | IFA_F_NODAD | IFA_F_HOMEADDRESS)) | ifa_flags;
+ was_managetempaddr = ifp->flags & IFA_F_MANAGETEMPADDR;
+ ifp->flags &= ~(IFA_F_DEPRECATED | IFA_F_PERMANENT | IFA_F_NODAD |
+ IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR);
+ ifp->flags |= ifa_flags;
ifp->tstamp = jiffies;
ifp->valid_lft = valid_lft;
ifp->prefered_lft = prefered_lft;
@@ -3635,6 +3660,14 @@ static int inet6_addr_modify(struct inet6_ifaddr *ifp, u8 ifa_flags,
addrconf_prefix_route(&ifp->addr, ifp->prefix_len, ifp->idev->dev,
expires, flags);
+
+ if (was_managetempaddr || ifp->flags & IFA_F_MANAGETEMPADDR) {
+ if (was_managetempaddr && !(ifp->flags & IFA_F_MANAGETEMPADDR))
+ valid_lft = prefered_lft = 0;
+ manage_tempaddrs(ifp->idev, ifp, valid_lft, prefered_lft,
+ !was_managetempaddr, jiffies);
+ }
+
addrconf_verify(0);
return 0;
@@ -3650,7 +3683,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
struct inet6_ifaddr *ifa;
struct net_device *dev;
u32 valid_lft = INFINITY_LIFE_TIME, preferred_lft = INFINITY_LIFE_TIME;
- u8 ifa_flags;
+ u32 ifa_flags;
int err;
err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv6_policy);
@@ -3677,8 +3710,10 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
if (dev == NULL)
return -ENODEV;
+ ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : ifm->ifa_flags;
+
/* We ignore other flags so far. */
- ifa_flags = ifm->ifa_flags & (IFA_F_NODAD | IFA_F_HOMEADDRESS);
+ ifa_flags &= IFA_F_NODAD | IFA_F_HOMEADDRESS | IFA_F_MANAGETEMPADDR;
ifa = ipv6_get_ifaddr(net, pfx, dev, 1);
if (ifa == NULL) {
@@ -3702,7 +3737,7 @@ inet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh)
return err;
}
-static void put_ifaddrmsg(struct nlmsghdr *nlh, u8 prefixlen, u8 flags,
+static void put_ifaddrmsg(struct nlmsghdr *nlh, u8 prefixlen, u32 flags,
u8 scope, int ifindex)
{
struct ifaddrmsg *ifm;
@@ -3745,7 +3780,8 @@ static inline int inet6_ifaddr_msgsize(void)
return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
+ nla_total_size(16) /* IFA_LOCAL */
+ nla_total_size(16) /* IFA_ADDRESS */
- + nla_total_size(sizeof(struct ifa_cacheinfo));
+ + nla_total_size(sizeof(struct ifa_cacheinfo))
+ + nla_total_size(4) /* IFA_FLAGS */;
}
static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
@@ -3793,6 +3829,9 @@ static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa,
if (put_cacheinfo(skb, ifa->cstamp, ifa->tstamp, preferred, valid) < 0)
goto error;
+ if (nla_put_u32(skb, IFA_FLAGS, ifa->flags) < 0)
+ goto error;
+
return nlmsg_end(skb, nlh);
error:
@@ -4196,7 +4235,7 @@ static int inet6_fill_ifla6_attrs(struct sk_buff *skb, struct inet6_dev *idev)
ci.max_reasm_len = IPV6_MAXPLEN;
ci.tstamp = cstamp_delta(idev->tstamp);
ci.reachable_time = jiffies_to_msecs(idev->nd_parms->reachable_time);
- ci.retrans_time = jiffies_to_msecs(idev->nd_parms->retrans_time);
+ ci.retrans_time = jiffies_to_msecs(NEIGH_VAR(idev->nd_parms, RETRANS_TIME));
if (nla_put(skb, IFLA_INET6_CACHEINFO, sizeof(ci), &ci))
goto nla_put_failure;
nla = nla_reserve(skb, IFLA_INET6_CONF, DEVCONF_MAX * sizeof(s32));
@@ -4991,7 +5030,7 @@ static void __addrconf_sysctl_unregister(struct ipv6_devconf *p)
static void addrconf_sysctl_register(struct inet6_dev *idev)
{
- neigh_sysctl_register(idev->dev, idev->nd_parms, "ipv6",
+ neigh_sysctl_register(idev->dev, idev->nd_parms,
&ndisc_ifinfo_sysctl_change);
__addrconf_sysctl_register(dev_net(idev->dev), idev->dev->name,
idev, &idev->cnf);
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 4fbdb7046d28..027615ac1b4e 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -683,8 +683,7 @@ bool ipv6_opt_accepted(const struct sock *sk, const struct sk_buff *skb)
if (np->rxopt.all) {
if ((opt->hop && (np->rxopt.bits.hopopts ||
np->rxopt.bits.ohopopts)) ||
- ((IPV6_FLOWINFO_MASK &
- *(__be32 *)skb_network_header(skb)) &&
+ (ip6_flowinfo((struct ipv6hdr *) skb_network_header(skb)) &&
np->rxopt.bits.rxflow) ||
(opt->srcrt && (np->rxopt.bits.srcrt ||
np->rxopt.bits.osrcrt)) ||
diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c
index 82e1da3a40b9..81e496a2e008 100644
--- a/net/ipv6/ah6.c
+++ b/net/ipv6/ah6.c
@@ -12,8 +12,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
* Authors
*
diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c
index b8719df0366e..6eef8a7e35f2 100644
--- a/net/ipv6/esp6.c
+++ b/net/ipv6/esp6.c
@@ -12,8 +12,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
* Authors
*
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index 8acb28621f9c..e27fb78c61f2 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -61,7 +61,6 @@ static bool log_ecn_error = true;
module_param(log_ecn_error, bool, 0644);
MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN");
-#define IPV6_TCLASS_MASK (IPV6_FLOWINFO_MASK & ~IPV6_FLOWLABEL_MASK)
#define IPV6_TCLASS_SHIFT 20
#define HASH_SIZE_SHIFT 5
@@ -846,7 +845,7 @@ static inline int ip6gre_xmit_ipv6(struct sk_buff *skb, struct net_device *dev)
if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)
fl6.flowlabel |= (*(__be32 *) ipv6h & IPV6_TCLASS_MASK);
if (t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)
- fl6.flowlabel |= (*(__be32 *) ipv6h & IPV6_FLOWLABEL_MASK);
+ fl6.flowlabel |= ip6_flowlabel(ipv6h);
if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK)
fl6.flowi6_mark = skb->mark;
diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c
index 4b851692b1f6..6fb4162fa785 100644
--- a/net/ipv6/ip6_offload.c
+++ b/net/ipv6/ip6_offload.c
@@ -154,6 +154,32 @@ out:
return segs;
}
+/* Return the total length of all the extension hdrs, following the same
+ * logic in ipv6_gso_pull_exthdrs() when parsing ext-hdrs.
+ */
+static int ipv6_exthdrs_len(struct ipv6hdr *iph,
+ const struct net_offload **opps)
+{
+ struct ipv6_opt_hdr *opth = (void *)iph;
+ int len = 0, proto, optlen = sizeof(*iph);
+
+ proto = iph->nexthdr;
+ for (;;) {
+ if (proto != NEXTHDR_HOP) {
+ *opps = rcu_dereference(inet6_offloads[proto]);
+ if (unlikely(!(*opps)))
+ break;
+ if (!((*opps)->flags & INET6_PROTO_GSO_EXTHDR))
+ break;
+ }
+ opth = (void *)opth + optlen;
+ optlen = ipv6_optlen(opth);
+ len += optlen;
+ proto = opth->nexthdr;
+ }
+ return len;
+}
+
static struct sk_buff **ipv6_gro_receive(struct sk_buff **head,
struct sk_buff *skb)
{
@@ -177,6 +203,7 @@ static struct sk_buff **ipv6_gro_receive(struct sk_buff **head,
goto out;
}
+ skb_set_network_header(skb, off);
skb_gro_pull(skb, sizeof(*iph));
skb_set_transport_header(skb, skb_gro_offset(skb));
@@ -211,12 +238,16 @@ static struct sk_buff **ipv6_gro_receive(struct sk_buff **head,
if (!NAPI_GRO_CB(p)->same_flow)
continue;
- iph2 = ipv6_hdr(p);
+ iph2 = (struct ipv6hdr *)(p->data + off);
first_word = *(__be32 *)iph ^ *(__be32 *)iph2 ;
- /* All fields must match except length and Traffic Class. */
- if (nlen != skb_network_header_len(p) ||
- (first_word & htonl(0xF00FFFFF)) ||
+ /* All fields must match except length and Traffic Class.
+ * XXX skbs on the gro_list have all been parsed and pulled
+ * already so we don't need to compare nlen
+ * (nlen != (sizeof(*iph2) + ipv6_exthdrs_len(iph2, &ops)))
+ * memcmp() alone below is suffcient, right?
+ */
+ if ((first_word & htonl(0xF00FFFFF)) ||
memcmp(&iph->nexthdr, &iph2->nexthdr,
nlen - offsetof(struct ipv6hdr, nexthdr))) {
NAPI_GRO_CB(p)->same_flow = 0;
@@ -245,21 +276,21 @@ out:
return pp;
}
-static int ipv6_gro_complete(struct sk_buff *skb)
+static int ipv6_gro_complete(struct sk_buff *skb, int nhoff)
{
const struct net_offload *ops;
- struct ipv6hdr *iph = ipv6_hdr(skb);
+ struct ipv6hdr *iph = (struct ipv6hdr *)(skb->data + nhoff);
int err = -ENOSYS;
- iph->payload_len = htons(skb->len - skb_network_offset(skb) -
- sizeof(*iph));
+ iph->payload_len = htons(skb->len - nhoff - sizeof(*iph));
rcu_read_lock();
- ops = rcu_dereference(inet6_offloads[NAPI_GRO_CB(skb)->proto]);
+
+ nhoff += sizeof(*iph) + ipv6_exthdrs_len(iph, &ops);
if (WARN_ON(!ops || !ops->callbacks.gro_complete))
goto out_unlock;
- err = ops->callbacks.gro_complete(skb);
+ err = ops->callbacks.gro_complete(skb, nhoff);
out_unlock:
rcu_read_unlock();
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 4acdb63495db..9a311cc79672 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -336,7 +336,8 @@ int ip6_forward(struct sk_buff *skb)
goto drop;
if (!xfrm6_policy_check(NULL, XFRM_POLICY_FWD, skb)) {
- IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_INDISCARDS);
+ IP6_INC_STATS_BH(net, ip6_dst_idev(dst),
+ IPSTATS_MIB_INDISCARDS);
goto drop;
}
@@ -370,8 +371,8 @@ int ip6_forward(struct sk_buff *skb)
/* Force OUTPUT device used as source address */
skb->dev = dst->dev;
icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT, 0);
- IP6_INC_STATS_BH(net,
- ip6_dst_idev(dst), IPSTATS_MIB_INHDRERRORS);
+ IP6_INC_STATS_BH(net, ip6_dst_idev(dst),
+ IPSTATS_MIB_INHDRERRORS);
kfree_skb(skb);
return -ETIMEDOUT;
@@ -384,14 +385,15 @@ int ip6_forward(struct sk_buff *skb)
if (proxied > 0)
return ip6_input(skb);
else if (proxied < 0) {
- IP6_INC_STATS(net, ip6_dst_idev(dst),
- IPSTATS_MIB_INDISCARDS);
+ IP6_INC_STATS_BH(net, ip6_dst_idev(dst),
+ IPSTATS_MIB_INDISCARDS);
goto drop;
}
}
if (!xfrm6_route_forward(skb)) {
- IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_INDISCARDS);
+ IP6_INC_STATS_BH(net, ip6_dst_idev(dst),
+ IPSTATS_MIB_INDISCARDS);
goto drop;
}
dst = skb_dst(skb);
@@ -448,16 +450,17 @@ int ip6_forward(struct sk_buff *skb)
/* Again, force OUTPUT device used as source address */
skb->dev = dst->dev;
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
- IP6_INC_STATS_BH(net,
- ip6_dst_idev(dst), IPSTATS_MIB_INTOOBIGERRORS);
- IP6_INC_STATS_BH(net,
- ip6_dst_idev(dst), IPSTATS_MIB_FRAGFAILS);
+ IP6_INC_STATS_BH(net, ip6_dst_idev(dst),
+ IPSTATS_MIB_INTOOBIGERRORS);
+ IP6_INC_STATS_BH(net, ip6_dst_idev(dst),
+ IPSTATS_MIB_FRAGFAILS);
kfree_skb(skb);
return -EMSGSIZE;
}
if (skb_cow(skb, dst->dev->hard_header_len)) {
- IP6_INC_STATS(net, ip6_dst_idev(dst), IPSTATS_MIB_OUTDISCARDS);
+ IP6_INC_STATS_BH(net, ip6_dst_idev(dst),
+ IPSTATS_MIB_OUTDISCARDS);
goto drop;
}
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index d6062325db08..8d7c9867a445 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -70,7 +70,6 @@ MODULE_ALIAS_NETDEV("ip6tnl0");
#define IP6_TNL_TRACE(x...) do {;} while(0)
#endif
-#define IPV6_TCLASS_MASK (IPV6_FLOWINFO_MASK & ~IPV6_FLOWLABEL_MASK)
#define IPV6_TCLASS_SHIFT 20
#define HASH_SIZE_SHIFT 5
@@ -1131,7 +1130,7 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
if (t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)
fl6.flowlabel |= (*(__be32 *) ipv6h & IPV6_TCLASS_MASK);
if (t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)
- fl6.flowlabel |= (*(__be32 *) ipv6h & IPV6_FLOWLABEL_MASK);
+ fl6.flowlabel |= ip6_flowlabel(ipv6h);
if (t->parms.flags & IP6_TNL_F_USE_ORIG_FWMARK)
fl6.flowi6_mark = skb->mark;
diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c
index ce507d9e1c90..da9becb42e81 100644
--- a/net/ipv6/ipcomp6.c
+++ b/net/ipv6/ipcomp6.c
@@ -16,8 +16,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* [Memo]
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 1c6ce3119ff8..759fbf96515b 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -1019,7 +1019,7 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim);
}
if (np->rxopt.bits.rxtclass) {
- int tclass = np->rcv_tclass;
+ int tclass = ntohl(np->rcv_flowinfo & IPV6_TCLASS_MASK) >> 20;
put_cmsg(&msg, SOL_IPV6, IPV6_TCLASS, sizeof(tclass), &tclass);
}
if (np->rxopt.bits.rxoinfo) {
@@ -1034,6 +1034,11 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
int hlim = np->mcast_hops;
put_cmsg(&msg, SOL_IPV6, IPV6_2292HOPLIMIT, sizeof(hlim), &hlim);
}
+ if (np->rxopt.bits.rxflow) {
+ __be32 flowinfo = np->rcv_flowinfo;
+
+ put_cmsg(&msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo);
+ }
}
len -= msg.msg_controllen;
return put_user(len, optlen);
diff --git a/net/ipv6/mip6.c b/net/ipv6/mip6.c
index 9ac01dc9402e..db9b6cbc9db3 100644
--- a/net/ipv6/mip6.c
+++ b/net/ipv6/mip6.c
@@ -13,8 +13,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* Authors:
diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c
index 300865171394..09a22f4f36c9 100644
--- a/net/ipv6/ndisc.c
+++ b/net/ipv6/ndisc.c
@@ -125,17 +125,19 @@ struct neigh_table nd_tbl = {
.id = "ndisc_cache",
.parms = {
.tbl = &nd_tbl,
- .base_reachable_time = ND_REACHABLE_TIME,
- .retrans_time = ND_RETRANS_TIMER,
- .gc_staletime = 60 * HZ,
.reachable_time = ND_REACHABLE_TIME,
- .delay_probe_time = 5 * HZ,
- .queue_len_bytes = 64*1024,
- .ucast_probes = 3,
- .mcast_probes = 3,
- .anycast_delay = 1 * HZ,
- .proxy_delay = (8 * HZ) / 10,
- .proxy_qlen = 64,
+ .data = {
+ [NEIGH_VAR_MCAST_PROBES] = 3,
+ [NEIGH_VAR_UCAST_PROBES] = 3,
+ [NEIGH_VAR_RETRANS_TIME] = ND_RETRANS_TIMER,
+ [NEIGH_VAR_BASE_REACHABLE_TIME] = ND_REACHABLE_TIME,
+ [NEIGH_VAR_DELAY_PROBE_TIME] = 5 * HZ,
+ [NEIGH_VAR_GC_STALETIME] = 60 * HZ,
+ [NEIGH_VAR_QUEUE_LEN_BYTES] = 64 * 1024,
+ [NEIGH_VAR_PROXY_QLEN] = 64,
+ [NEIGH_VAR_ANYCAST_DELAY] = 1 * HZ,
+ [NEIGH_VAR_PROXY_DELAY] = (8 * HZ) / 10,
+ },
},
.gc_interval = 30 * HZ,
.gc_thresh1 = 128,
@@ -656,14 +658,14 @@ 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->parms->ucast_probes) < 0) {
+ if ((probes -= NEIGH_VAR(neigh->parms, UCAST_PROBES)) < 0) {
if (!(neigh->nud_state & NUD_VALID)) {
ND_PRINTK(1, dbg,
"%s: trying to ucast probe in NUD_INVALID: %pI6\n",
__func__, target);
}
ndisc_send_ns(dev, neigh, target, target, saddr);
- } else if ((probes -= neigh->parms->app_probes) < 0) {
+ } else if ((probes -= NEIGH_VAR(neigh->parms, APP_PROBES)) < 0) {
neigh_app_ns(neigh);
} else {
addrconf_addr_solict_mult(target, &mcaddr);
@@ -790,7 +792,7 @@ static void ndisc_recv_ns(struct sk_buff *skb)
if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) &&
skb->pkt_type != PACKET_HOST &&
inc &&
- idev->nd_parms->proxy_delay != 0) {
+ NEIGH_VAR(idev->nd_parms, PROXY_DELAY) != 0) {
/*
* for anycast or proxy,
* sender should delay its response
@@ -1210,7 +1212,7 @@ skip_defrtr:
rtime = (rtime*HZ)/1000;
if (rtime < HZ/10)
rtime = HZ/10;
- in6_dev->nd_parms->retrans_time = rtime;
+ NEIGH_VAR_SET(in6_dev->nd_parms, RETRANS_TIME, rtime);
in6_dev->tstamp = jiffies;
inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
}
@@ -1222,9 +1224,11 @@ skip_defrtr:
if (rtime < HZ/10)
rtime = HZ/10;
- if (rtime != in6_dev->nd_parms->base_reachable_time) {
- in6_dev->nd_parms->base_reachable_time = rtime;
- in6_dev->nd_parms->gc_staletime = 3 * rtime;
+ if (rtime != NEIGH_VAR(in6_dev->nd_parms, BASE_REACHABLE_TIME)) {
+ NEIGH_VAR_SET(in6_dev->nd_parms,
+ BASE_REACHABLE_TIME, rtime);
+ NEIGH_VAR_SET(in6_dev->nd_parms,
+ GC_STALETIME, 3 * rtime);
in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);
in6_dev->tstamp = jiffies;
inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);
@@ -1651,22 +1655,23 @@ int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, void __user *bu
ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default");
if (strcmp(ctl->procname, "retrans_time") == 0)
- ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
+ ret = neigh_proc_dointvec(ctl, write, buffer, lenp, ppos);
else if (strcmp(ctl->procname, "base_reachable_time") == 0)
- ret = proc_dointvec_jiffies(ctl, write,
- buffer, lenp, ppos);
+ ret = neigh_proc_dointvec_jiffies(ctl, write,
+ buffer, lenp, ppos);
else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) ||
(strcmp(ctl->procname, "base_reachable_time_ms") == 0))
- ret = proc_dointvec_ms_jiffies(ctl, write,
- buffer, lenp, ppos);
+ ret = neigh_proc_dointvec_ms_jiffies(ctl, write,
+ buffer, lenp, ppos);
else
ret = -1;
if (write && ret == 0 && dev && (idev = in6_dev_get(dev)) != NULL) {
- if (ctl->data == &idev->nd_parms->base_reachable_time)
- idev->nd_parms->reachable_time = neigh_rand_reach_time(idev->nd_parms->base_reachable_time);
+ if (ctl->data == &NEIGH_VAR(idev->nd_parms, BASE_REACHABLE_TIME))
+ idev->nd_parms->reachable_time =
+ neigh_rand_reach_time(NEIGH_VAR(idev->nd_parms, BASE_REACHABLE_TIME));
idev->tstamp = jiffies;
inet6_ifinfo_notify(RTM_NEWLINK, idev);
in6_dev_put(idev);
@@ -1725,7 +1730,7 @@ int __init ndisc_init(void)
neigh_table_init(&nd_tbl);
#ifdef CONFIG_SYSCTL
- err = neigh_sysctl_register(NULL, &nd_tbl.parms, "ipv6",
+ err = neigh_sysctl_register(NULL, &nd_tbl.parms,
&ndisc_ifinfo_sysctl_change);
if (err)
goto out_unregister_pernet;
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index a0a48ac3403f..89b2735cecf5 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -66,8 +66,9 @@
#endif
enum rt6_nud_state {
- RT6_NUD_FAIL_HARD = -2,
- RT6_NUD_FAIL_SOFT = -1,
+ RT6_NUD_FAIL_HARD = -3,
+ RT6_NUD_FAIL_PROBE = -2,
+ RT6_NUD_FAIL_DO_RR = -1,
RT6_NUD_SUCCEED = 1
};
@@ -521,7 +522,7 @@ static void rt6_probe(struct rt6_info *rt)
work = kmalloc(sizeof(*work), GFP_ATOMIC);
if (neigh && work)
- neigh->updated = jiffies;
+ __neigh_set_probe_once(neigh);
if (neigh)
write_unlock(&neigh->lock);
@@ -577,11 +578,13 @@ static inline enum rt6_nud_state rt6_check_neigh(struct rt6_info *rt)
#ifdef CONFIG_IPV6_ROUTER_PREF
else if (!(neigh->nud_state & NUD_FAILED))
ret = RT6_NUD_SUCCEED;
+ else
+ ret = RT6_NUD_FAIL_PROBE;
#endif
read_unlock(&neigh->lock);
} else {
ret = IS_ENABLED(CONFIG_IPV6_ROUTER_PREF) ?
- RT6_NUD_SUCCEED : RT6_NUD_FAIL_SOFT;
+ RT6_NUD_SUCCEED : RT6_NUD_FAIL_DO_RR;
}
rcu_read_unlock_bh();
@@ -618,16 +621,17 @@ static struct rt6_info *find_match(struct rt6_info *rt, int oif, int strict,
goto out;
m = rt6_score_route(rt, oif, strict);
- if (m == RT6_NUD_FAIL_SOFT) {
+ if (m == RT6_NUD_FAIL_DO_RR) {
match_do_rr = true;
m = 0; /* lowest valid score */
- } else if (m < 0) {
+ } else if (m == RT6_NUD_FAIL_HARD) {
goto out;
}
if (strict & RT6_LOOKUP_F_REACHABLE)
rt6_probe(rt);
+ /* note that m can be RT6_NUD_FAIL_PROBE at this point */
if (m > *mpri) {
*do_rr = match_do_rr;
*mpri = m;
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index f67033b4bb66..da046a5d7ffb 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1134,7 +1134,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
newnp->opt = NULL;
newnp->mcast_oif = inet6_iif(skb);
newnp->mcast_hops = ipv6_hdr(skb)->hop_limit;
- newnp->rcv_tclass = ipv6_get_dsfield(ipv6_hdr(skb));
+ newnp->rcv_flowinfo = ip6_flowinfo(ipv6_hdr(skb));
/*
* No need to charge this sock to the relevant IPv6 refcnt debug socks count
@@ -1214,7 +1214,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
newnp->opt = NULL;
newnp->mcast_oif = inet6_iif(skb);
newnp->mcast_hops = ipv6_hdr(skb)->hop_limit;
- newnp->rcv_tclass = ipv6_get_dsfield(ipv6_hdr(skb));
+ newnp->rcv_flowinfo = ip6_flowinfo(ipv6_hdr(skb));
/* Clone native IPv6 options from listening socket (if any)
@@ -1424,8 +1424,8 @@ ipv6_pktoptions:
np->mcast_oif = inet6_iif(opt_skb);
if (np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim)
np->mcast_hops = ipv6_hdr(opt_skb)->hop_limit;
- if (np->rxopt.bits.rxtclass)
- np->rcv_tclass = ipv6_get_dsfield(ipv6_hdr(opt_skb));
+ if (np->rxopt.bits.rxflow || np->rxopt.bits.rxtclass)
+ np->rcv_flowinfo = ip6_flowinfo(ipv6_hdr(opt_skb));
if (ipv6_opt_accepted(sk, opt_skb)) {
skb_set_owner_r(opt_skb, sk);
opt_skb = xchg(&np->pktoptions, opt_skb);
diff --git a/net/ipv6/tcpv6_offload.c b/net/ipv6/tcpv6_offload.c
index 6d18157dc32c..0d78132ff18a 100644
--- a/net/ipv6/tcpv6_offload.c
+++ b/net/ipv6/tcpv6_offload.c
@@ -66,13 +66,13 @@ skip_csum:
return tcp_gro_receive(head, skb);
}
-static int tcp6_gro_complete(struct sk_buff *skb)
+static int tcp6_gro_complete(struct sk_buff *skb, int thoff)
{
const struct ipv6hdr *iph = ipv6_hdr(skb);
struct tcphdr *th = tcp_hdr(skb);
- th->check = ~tcp_v6_check(skb->len - skb_transport_offset(skb),
- &iph->saddr, &iph->daddr, 0);
+ th->check = ~tcp_v6_check(skb->len - thoff, &iph->saddr,
+ &iph->daddr, 0);
skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
return tcp_gro_complete(skb);
diff --git a/net/ipv6/tunnel6.c b/net/ipv6/tunnel6.c
index 4b0f50d9a962..2c4e4c5c7614 100644
--- a/net/ipv6/tunnel6.c
+++ b/net/ipv6/tunnel6.c
@@ -12,8 +12,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
* Authors Mitsuru KANDA <mk@linux-ipv6.org>
* YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
diff --git a/net/ipv6/xfrm6_mode_ro.c b/net/ipv6/xfrm6_mode_ro.c
index 63d5d493098a..0e015906f9ca 100644
--- a/net/ipv6/xfrm6_mode_ro.c
+++ b/net/ipv6/xfrm6_mode_ro.c
@@ -15,8 +15,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* Authors:
diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c
index de2bcfaaf759..1c66465a42dd 100644
--- a/net/ipv6/xfrm6_tunnel.c
+++ b/net/ipv6/xfrm6_tunnel.c
@@ -12,8 +12,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
* Authors Mitsuru KANDA <mk@linux-ipv6.org>
* YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c
index de7db23049f1..73baf9b346b6 100644
--- a/net/irda/af_irda.c
+++ b/net/irda/af_irda.c
@@ -25,9 +25,7 @@
* 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., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
* Linux-IrDA now supports four different types of IrDA sockets:
*
diff --git a/net/irda/discovery.c b/net/irda/discovery.c
index b0b56a339a83..6786e7f193d2 100644
--- a/net/irda/discovery.c
+++ b/net/irda/discovery.c
@@ -24,9 +24,7 @@
* 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., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
********************************************************************/
diff --git a/net/irda/ircomm/ircomm_core.c b/net/irda/ircomm/ircomm_core.c
index b797daac063c..4490a675b1bb 100644
--- a/net/irda/ircomm/ircomm_core.c
+++ b/net/irda/ircomm/ircomm_core.c
@@ -23,9 +23,7 @@
* 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., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
********************************************************************/
diff --git a/net/irda/ircomm/ircomm_event.c b/net/irda/ircomm/ircomm_event.c
index d78554fedbac..b172c6522328 100644
--- a/net/irda/ircomm/ircomm_event.c
+++ b/net/irda/ircomm/ircomm_event.c
@@ -22,9 +22,7 @@
* 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., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
********************************************************************/
diff --git a/net/irda/ircomm/ircomm_lmp.c b/net/irda/ircomm/ircomm_lmp.c
index 3b8095c771d4..6536114adf37 100644
--- a/net/irda/ircomm/ircomm_lmp.c
+++ b/net/irda/ircomm/ircomm_lmp.c
@@ -24,9 +24,7 @@
* 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., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
********************************************************************/
diff --git a/net/irda/ircomm/ircomm_param.c b/net/irda/ircomm/ircomm_param.c
index 308939128359..f80b1a6a244b 100644
--- a/net/irda/ircomm/ircomm_param.c
+++ b/net/irda/ircomm/ircomm_param.c
@@ -22,9 +22,7 @@
* 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., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
********************************************************************/
diff --git a/net/irda/ircomm/ircomm_ttp.c b/net/irda/ircomm/ircomm_ttp.c
index 6e6509f22f60..d362d711b79c 100644
--- a/net/irda/ircomm/ircomm_ttp.c
+++ b/net/irda/ircomm/ircomm_ttp.c
@@ -23,9 +23,7 @@
* 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., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
********************************************************************/
diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c
index 41ac7938268b..2ba8b9705bb7 100644
--- a/net/irda/ircomm/ircomm_tty.c
+++ b/net/irda/ircomm/ircomm_tty.c
@@ -24,9 +24,7 @@
* 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., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
********************************************************************/
diff --git a/net/irda/ircomm/ircomm_tty_attach.c b/net/irda/ircomm/ircomm_tty_attach.c
index a2a508f5f268..2ee87bf387cc 100644
--- a/net/irda/ircomm/ircomm_tty_attach.c
+++ b/net/irda/ircomm/ircomm_tty_attach.c
@@ -23,9 +23,7 @@
* 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., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
********************************************************************/
diff --git a/net/irda/ircomm/ircomm_tty_ioctl.c b/net/irda/ircomm/ircomm_tty_ioctl.c
index b343f50dc8d7..ce943853c38d 100644
--- a/net/irda/ircomm/ircomm_tty_ioctl.c
+++ b/net/irda/ircomm/ircomm_tty_ioctl.c
@@ -22,9 +22,7 @@
* 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., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
********************************************************************/
diff --git a/net/irda/irda_device.c b/net/irda/irda_device.c
index 14653b8d664d..365b895da84b 100644
--- a/net/irda/irda_device.c
+++ b/net/irda/irda_device.c
@@ -23,9 +23,7 @@
* 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., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
********************************************************************/
diff --git a/net/irda/irlap.c b/net/irda/irlap.c
index 005b424494a0..a778df55f5d6 100644
--- a/net/irda/irlap.c
+++ b/net/irda/irlap.c
@@ -23,9 +23,7 @@
* 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., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
********************************************************************/
diff --git a/net/irda/parameters.c b/net/irda/parameters.c
index 71cd38c1a67f..6d0869716bf6 100644
--- a/net/irda/parameters.c
+++ b/net/irda/parameters.c
@@ -22,9 +22,7 @@
* 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., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
********************************************************************/
diff --git a/net/irda/qos.c b/net/irda/qos.c
index 798ffd9a705e..11a7cc0cbc28 100644
--- a/net/irda/qos.c
+++ b/net/irda/qos.c
@@ -24,9 +24,7 @@
* 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., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
********************************************************************/
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 364ce0c5962f..f80e8c4c6bcd 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -133,7 +133,9 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
struct key_params *params)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
struct sta_info *sta = NULL;
+ const struct ieee80211_cipher_scheme *cs = NULL;
struct ieee80211_key *key;
int err;
@@ -145,22 +147,28 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_TKIP:
case WLAN_CIPHER_SUITE_WEP104:
- if (IS_ERR(sdata->local->wep_tx_tfm))
+ if (IS_ERR(local->wep_tx_tfm))
return -EINVAL;
break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ case WLAN_CIPHER_SUITE_AES_CMAC:
+ case WLAN_CIPHER_SUITE_GCMP:
+ break;
default:
+ cs = ieee80211_cs_get(local, params->cipher, sdata->vif.type);
break;
}
key = ieee80211_key_alloc(params->cipher, key_idx, params->key_len,
- params->key, params->seq_len, params->seq);
+ params->key, params->seq_len, params->seq,
+ cs);
if (IS_ERR(key))
return PTR_ERR(key);
if (pairwise)
key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE;
- mutex_lock(&sdata->local->sta_mtx);
+ mutex_lock(&local->sta_mtx);
if (mac_addr) {
if (ieee80211_vif_is_mesh(&sdata->vif))
@@ -216,10 +224,13 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
break;
}
+ if (sta)
+ sta->cipher_scheme = cs;
+
err = ieee80211_key_link(key, sdata, sta);
out_unlock:
- mutex_unlock(&sdata->local->sta_mtx);
+ mutex_unlock(&local->sta_mtx);
return err;
}
@@ -244,7 +255,7 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
goto out_unlock;
if (pairwise)
- key = key_mtx_dereference(local, sta->ptk);
+ key = key_mtx_dereference(local, sta->ptk[key_idx]);
else
key = key_mtx_dereference(local, sta->gtk[key_idx]);
} else
@@ -291,7 +302,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
goto out;
if (pairwise)
- key = rcu_dereference(sta->ptk);
+ key = rcu_dereference(sta->ptk[key_idx]);
else if (key_idx < NUM_DEFAULT_KEYS)
key = rcu_dereference(sta->gtk[key_idx]);
} else
@@ -521,8 +532,8 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
STATION_INFO_PEER_PM |
STATION_INFO_NONPEER_PM;
- sinfo->llid = le16_to_cpu(sta->llid);
- sinfo->plid = le16_to_cpu(sta->plid);
+ sinfo->llid = sta->llid;
+ sinfo->plid = sta->plid;
sinfo->plink_state = sta->plink_state;
if (test_sta_flag(sta, WLAN_STA_TOFFSET_KNOWN)) {
sinfo->filled |= STATION_INFO_T_OFFSET;
@@ -846,7 +857,7 @@ static int ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
if (!resp || !resp_len)
return 1;
- old = rtnl_dereference(sdata->u.ap.probe_resp);
+ old = sdata_dereference(sdata->u.ap.probe_resp, sdata);
new = kzalloc(sizeof(struct probe_resp) + resp_len, GFP_KERNEL);
if (!new)
@@ -870,7 +881,8 @@ int ieee80211_assign_beacon(struct ieee80211_sub_if_data *sdata,
int size, err;
u32 changed = BSS_CHANGED_BEACON;
- old = rtnl_dereference(sdata->u.ap.beacon);
+ old = sdata_dereference(sdata->u.ap.beacon, sdata);
+
/* Need to have a beacon head if we don't have one yet */
if (!params->head && !old)
@@ -947,7 +959,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
BSS_CHANGED_P2P_PS;
int err;
- old = rtnl_dereference(sdata->u.ap.beacon);
+ old = sdata_dereference(sdata->u.ap.beacon, sdata);
if (old)
return -EALREADY;
@@ -968,11 +980,19 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
*/
sdata->control_port_protocol = params->crypto.control_port_ethertype;
sdata->control_port_no_encrypt = params->crypto.control_port_no_encrypt;
+ sdata->encrypt_headroom = ieee80211_cs_headroom(sdata->local,
+ &params->crypto,
+ sdata->vif.type);
+
list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
vlan->control_port_protocol =
params->crypto.control_port_ethertype;
vlan->control_port_no_encrypt =
params->crypto.control_port_no_encrypt;
+ vlan->encrypt_headroom =
+ ieee80211_cs_headroom(sdata->local,
+ &params->crypto,
+ vlan->vif.type);
}
sdata->vif.bss_conf.beacon_int = params->beacon_interval;
@@ -1001,7 +1021,8 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev,
err = drv_start_ap(sdata->local, sdata);
if (err) {
- old = rtnl_dereference(sdata->u.ap.beacon);
+ old = sdata_dereference(sdata->u.ap.beacon, sdata);
+
if (old)
kfree_rcu(old, rcu_head);
RCU_INIT_POINTER(sdata->u.ap.beacon, NULL);
@@ -1032,7 +1053,7 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev,
if (sdata->vif.csa_active)
return -EBUSY;
- old = rtnl_dereference(sdata->u.ap.beacon);
+ old = sdata_dereference(sdata->u.ap.beacon, sdata);
if (!old)
return -ENOENT;
@@ -1050,15 +1071,18 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
struct ieee80211_local *local = sdata->local;
struct beacon_data *old_beacon;
struct probe_resp *old_probe_resp;
+ struct cfg80211_chan_def chandef;
- old_beacon = rtnl_dereference(sdata->u.ap.beacon);
+ old_beacon = sdata_dereference(sdata->u.ap.beacon, sdata);
if (!old_beacon)
return -ENOENT;
- old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp);
+ old_probe_resp = sdata_dereference(sdata->u.ap.probe_resp, sdata);
/* abort any running channel switch */
sdata->vif.csa_active = false;
- cancel_work_sync(&sdata->csa_finalize_work);
+ kfree(sdata->u.ap.next_beacon);
+ sdata->u.ap.next_beacon = NULL;
+
cancel_work_sync(&sdata->u.ap.request_smps_work);
/* turn off carrier for this interface and dependent VLANs */
@@ -1091,8 +1115,10 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED);
if (sdata->wdev.cac_started) {
+ chandef = sdata->vif.bss_conf.chandef;
cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
- cfg80211_cac_event(sdata->dev, NL80211_RADAR_CAC_ABORTED,
+ cfg80211_cac_event(sdata->dev, &chandef,
+ NL80211_RADAR_CAC_ABORTED,
GFP_KERNEL);
}
@@ -1953,7 +1979,7 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
enum ieee80211_band band;
u32 changed = 0;
- if (!rtnl_dereference(sdata->u.ap.beacon))
+ if (!sdata_dereference(sdata->u.ap.beacon, sdata))
return -ENOENT;
band = ieee80211_get_sdata_band(sdata);
@@ -2963,27 +2989,33 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
struct ieee80211_local *local = sdata->local;
int err, changed = 0;
+ sdata_lock(sdata);
+ /* AP might have been stopped while waiting for the lock. */
+ if (!sdata->vif.csa_active)
+ goto unlock;
+
if (!ieee80211_sdata_running(sdata))
- return;
+ goto unlock;
sdata->radar_required = sdata->csa_radar_required;
- err = ieee80211_vif_change_channel(sdata, &local->csa_chandef,
- &changed);
+ err = ieee80211_vif_change_channel(sdata, &changed);
if (WARN_ON(err < 0))
- return;
+ goto unlock;
if (!local->use_chanctx) {
- local->_oper_chandef = local->csa_chandef;
+ local->_oper_chandef = sdata->csa_chandef;
ieee80211_hw_config(local, 0);
}
ieee80211_bss_info_change_notify(sdata, changed);
+ sdata->vif.csa_active = false;
switch (sdata->vif.type) {
case NL80211_IFTYPE_AP:
err = ieee80211_assign_beacon(sdata, sdata->u.ap.next_beacon);
if (err < 0)
- return;
+ goto unlock;
+
changed |= err;
kfree(sdata->u.ap.next_beacon);
sdata->u.ap.next_beacon = NULL;
@@ -2997,20 +3029,22 @@ void ieee80211_csa_finalize_work(struct work_struct *work)
case NL80211_IFTYPE_MESH_POINT:
err = ieee80211_mesh_finish_csa(sdata);
if (err < 0)
- return;
+ goto unlock;
break;
#endif
default:
WARN_ON(1);
- return;
+ goto unlock;
}
- sdata->vif.csa_active = false;
ieee80211_wake_queues_by_reason(&sdata->local->hw,
IEEE80211_MAX_QUEUE_MAP,
IEEE80211_QUEUE_STOP_REASON_CSA);
- cfg80211_ch_switch_notify(sdata->dev, &local->csa_chandef);
+ cfg80211_ch_switch_notify(sdata->dev, &sdata->csa_chandef);
+
+unlock:
+ sdata_unlock(sdata);
}
static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
@@ -3023,6 +3057,8 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_if_mesh __maybe_unused *ifmsh;
int err, num_chanctx;
+ lockdep_assert_held(&sdata->wdev.mtx);
+
if (!list_empty(&local->roc_list) || local->scanning)
return -EBUSY;
@@ -3143,7 +3179,7 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
IEEE80211_MAX_QUEUE_MAP,
IEEE80211_QUEUE_STOP_REASON_CSA);
- local->csa_chandef = params->chandef;
+ sdata->csa_chandef = params->chandef;
sdata->vif.csa_active = true;
ieee80211_bss_info_change_notify(sdata, err);
@@ -3153,26 +3189,25 @@ static int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
}
static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
- struct ieee80211_channel *chan, bool offchan,
- unsigned int wait, const u8 *buf, size_t len,
- bool no_cck, bool dont_wait_for_ack, u64 *cookie)
+ struct cfg80211_mgmt_tx_params *params,
+ u64 *cookie)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
struct sta_info *sta;
- const struct ieee80211_mgmt *mgmt = (void *)buf;
+ const struct ieee80211_mgmt *mgmt = (void *)params->buf;
bool need_offchan = false;
u32 flags;
int ret;
- if (dont_wait_for_ack)
+ if (params->dont_wait_for_ack)
flags = IEEE80211_TX_CTL_NO_ACK;
else
flags = IEEE80211_TX_INTFL_NL80211_FRAME_TX |
IEEE80211_TX_CTL_REQ_TX_STATUS;
- if (no_cck)
+ if (params->no_cck)
flags |= IEEE80211_TX_CTL_NO_CCK_RATE;
switch (sdata->vif.type) {
@@ -3220,7 +3255,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
/* configurations requiring offchan cannot work if no channel has been
* specified
*/
- if (need_offchan && !chan)
+ if (need_offchan && !params->chan)
return -EINVAL;
mutex_lock(&local->mtx);
@@ -3233,8 +3268,10 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
if (chanctx_conf) {
- need_offchan = chan && (chan != chanctx_conf->def.chan);
- } else if (!chan) {
+ need_offchan = params->chan &&
+ (params->chan !=
+ chanctx_conf->def.chan);
+ } else if (!params->chan) {
ret = -EINVAL;
rcu_read_unlock();
goto out_unlock;
@@ -3244,19 +3281,19 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
rcu_read_unlock();
}
- if (need_offchan && !offchan) {
+ if (need_offchan && !params->offchan) {
ret = -EBUSY;
goto out_unlock;
}
- skb = dev_alloc_skb(local->hw.extra_tx_headroom + len);
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + params->len);
if (!skb) {
ret = -ENOMEM;
goto out_unlock;
}
skb_reserve(skb, local->hw.extra_tx_headroom);
- memcpy(skb_put(skb, len), buf, len);
+ memcpy(skb_put(skb, params->len), params->buf, params->len);
IEEE80211_SKB_CB(skb)->flags = flags;
@@ -3276,8 +3313,8 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
local->hw.offchannel_tx_hw_queue;
/* This will handle all kinds of coalescing and immediate TX */
- ret = ieee80211_start_roc_work(local, sdata, chan,
- wait, cookie, skb,
+ ret = ieee80211_start_roc_work(local, sdata, params->chan,
+ params->wait, cookie, skb,
IEEE80211_ROC_TYPE_MGMT_TX);
if (ret)
kfree_skb(skb);
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
index 03ba6b5c5373..a57d5d9466bc 100644
--- a/net/mac80211/chan.c
+++ b/net/mac80211/chan.c
@@ -9,6 +9,140 @@
#include "ieee80211_i.h"
#include "driver-ops.h"
+static enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta)
+{
+ switch (sta->bandwidth) {
+ case IEEE80211_STA_RX_BW_20:
+ if (sta->ht_cap.ht_supported)
+ return NL80211_CHAN_WIDTH_20;
+ else
+ return NL80211_CHAN_WIDTH_20_NOHT;
+ case IEEE80211_STA_RX_BW_40:
+ return NL80211_CHAN_WIDTH_40;
+ case IEEE80211_STA_RX_BW_80:
+ return NL80211_CHAN_WIDTH_80;
+ case IEEE80211_STA_RX_BW_160:
+ /*
+ * This applied for both 160 and 80+80. since we use
+ * the returned value to consider degradation of
+ * ctx->conf.min_def, we have to make sure to take
+ * the bigger one (NL80211_CHAN_WIDTH_160).
+ * Otherwise we might try degrading even when not
+ * needed, as the max required sta_bw returned (80+80)
+ * might be smaller than the configured bw (160).
+ */
+ return NL80211_CHAN_WIDTH_160;
+ default:
+ WARN_ON(1);
+ return NL80211_CHAN_WIDTH_20;
+ }
+}
+
+static enum nl80211_chan_width
+ieee80211_get_max_required_bw(struct ieee80211_sub_if_data *sdata)
+{
+ enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT;
+ struct sta_info *sta;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
+ if (sdata != sta->sdata &&
+ !(sta->sdata->bss && sta->sdata->bss == sdata->bss))
+ continue;
+
+ if (!sta->uploaded)
+ continue;
+
+ max_bw = max(max_bw, ieee80211_get_sta_bw(&sta->sta));
+ }
+ rcu_read_unlock();
+
+ return max_bw;
+}
+
+static enum nl80211_chan_width
+ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
+ struct ieee80211_chanctx_conf *conf)
+{
+ struct ieee80211_sub_if_data *sdata;
+ enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ struct ieee80211_vif *vif = &sdata->vif;
+ enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20_NOHT;
+
+ if (!ieee80211_sdata_running(sdata))
+ continue;
+
+ if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
+ continue;
+
+ switch (vif->type) {
+ case NL80211_IFTYPE_AP:
+ case NL80211_IFTYPE_AP_VLAN:
+ width = ieee80211_get_max_required_bw(sdata);
+ break;
+ case NL80211_IFTYPE_P2P_DEVICE:
+ continue;
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_WDS:
+ case NL80211_IFTYPE_MESH_POINT:
+ width = vif->bss_conf.chandef.width;
+ break;
+ case NL80211_IFTYPE_UNSPECIFIED:
+ case NUM_NL80211_IFTYPES:
+ case NL80211_IFTYPE_MONITOR:
+ case NL80211_IFTYPE_P2P_CLIENT:
+ case NL80211_IFTYPE_P2P_GO:
+ WARN_ON_ONCE(1);
+ }
+ max_bw = max(max_bw, width);
+ }
+ rcu_read_unlock();
+
+ return max_bw;
+}
+
+/*
+ * recalc the min required chan width of the channel context, which is
+ * the max of min required widths of all the interfaces bound to this
+ * channel context.
+ */
+void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx)
+{
+ enum nl80211_chan_width max_bw;
+ struct cfg80211_chan_def min_def;
+
+ lockdep_assert_held(&local->chanctx_mtx);
+
+ /* don't optimize 5MHz, 10MHz, and radar_enabled confs */
+ if (ctx->conf.def.width == NL80211_CHAN_WIDTH_5 ||
+ ctx->conf.def.width == NL80211_CHAN_WIDTH_10 ||
+ ctx->conf.radar_enabled) {
+ ctx->conf.min_def = ctx->conf.def;
+ return;
+ }
+
+ max_bw = ieee80211_get_chanctx_max_required_bw(local, &ctx->conf);
+
+ /* downgrade chandef up to max_bw */
+ min_def = ctx->conf.def;
+ while (min_def.width > max_bw)
+ ieee80211_chandef_downgrade(&min_def);
+
+ if (cfg80211_chandef_identical(&ctx->conf.min_def, &min_def))
+ return;
+
+ ctx->conf.min_def = min_def;
+ if (!ctx->driver_present)
+ return;
+
+ drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_MIN_WIDTH);
+}
+
static void ieee80211_change_chanctx(struct ieee80211_local *local,
struct ieee80211_chanctx *ctx,
const struct cfg80211_chan_def *chandef)
@@ -20,6 +154,7 @@ static void ieee80211_change_chanctx(struct ieee80211_local *local,
ctx->conf.def = *chandef;
drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH);
+ ieee80211_recalc_chanctx_min_def(local, ctx);
if (!local->use_chanctx) {
local->_oper_chandef = *chandef;
@@ -93,6 +228,7 @@ ieee80211_new_chanctx(struct ieee80211_local *local,
ctx->conf.rx_chains_dynamic = 1;
ctx->mode = mode;
ctx->conf.radar_enabled = ieee80211_is_radar_required(local);
+ ieee80211_recalc_chanctx_min_def(local, ctx);
if (!local->use_chanctx)
local->hw.conf.radar_enabled = ctx->conf.radar_enabled;
@@ -179,6 +315,7 @@ static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
ctx->refcount++;
ieee80211_recalc_txpower(sdata);
+ ieee80211_recalc_chanctx_min_def(local, ctx);
sdata->vif.bss_conf.idle = false;
if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
@@ -243,6 +380,7 @@ static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
ieee80211_recalc_smps_chanctx(local, ctx);
ieee80211_recalc_radar_chanctx(local, ctx);
+ ieee80211_recalc_chanctx_min_def(local, ctx);
}
}
@@ -411,12 +549,12 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
}
int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
- const struct cfg80211_chan_def *chandef,
u32 *changed)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_chanctx_conf *conf;
struct ieee80211_chanctx *ctx;
+ const struct cfg80211_chan_def *chandef = &sdata->csa_chandef;
int ret;
u32 chanctx_changed = 0;
@@ -456,6 +594,7 @@ int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
ieee80211_recalc_chanctx_chantype(local, ctx);
ieee80211_recalc_smps_chanctx(local, ctx);
ieee80211_recalc_radar_chanctx(local, ctx);
+ ieee80211_recalc_chanctx_min_def(local, ctx);
ret = 0;
out:
diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c
index 5c090e41d9bb..fa16e54980a1 100644
--- a/net/mac80211/debugfs.c
+++ b/net/mac80211/debugfs.c
@@ -17,6 +17,172 @@
#define DEBUGFS_FORMAT_BUFFER_SIZE 100
+#define TX_LATENCY_BIN_DELIMTER_C ','
+#define TX_LATENCY_BIN_DELIMTER_S ","
+#define TX_LATENCY_BINS_DISABLED "enable(bins disabled)\n"
+#define TX_LATENCY_DISABLED "disable\n"
+
+
+/*
+ * Display if Tx latency statistics & bins are enabled/disabled
+ */
+static ssize_t sta_tx_latency_stat_read(struct file *file,
+ char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_local *local = file->private_data;
+ struct ieee80211_tx_latency_bin_ranges *tx_latency;
+ char *buf;
+ int bufsz, i, ret;
+ int pos = 0;
+
+ rcu_read_lock();
+
+ tx_latency = rcu_dereference(local->tx_latency);
+
+ if (tx_latency && tx_latency->n_ranges) {
+ bufsz = tx_latency->n_ranges * 15;
+ buf = kzalloc(bufsz, GFP_ATOMIC);
+ if (!buf)
+ goto err;
+
+ for (i = 0; i < tx_latency->n_ranges; i++)
+ pos += scnprintf(buf + pos, bufsz - pos, "%d,",
+ tx_latency->ranges[i]);
+ pos += scnprintf(buf + pos, bufsz - pos, "\n");
+ } else if (tx_latency) {
+ bufsz = sizeof(TX_LATENCY_BINS_DISABLED) + 1;
+ buf = kzalloc(bufsz, GFP_ATOMIC);
+ if (!buf)
+ goto err;
+
+ pos += scnprintf(buf + pos, bufsz - pos, "%s\n",
+ TX_LATENCY_BINS_DISABLED);
+ } else {
+ bufsz = sizeof(TX_LATENCY_DISABLED) + 1;
+ buf = kzalloc(bufsz, GFP_ATOMIC);
+ if (!buf)
+ goto err;
+
+ pos += scnprintf(buf + pos, bufsz - pos, "%s\n",
+ TX_LATENCY_DISABLED);
+ }
+
+ rcu_read_unlock();
+
+ ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+ kfree(buf);
+
+ return ret;
+err:
+ rcu_read_unlock();
+ return -ENOMEM;
+}
+
+/*
+ * Receive input from user regarding Tx latency statistics
+ * The input should indicate if Tx latency statistics and bins are
+ * enabled/disabled.
+ * If bins are enabled input should indicate the amount of different bins and
+ * their ranges. Each bin will count how many Tx frames transmitted within the
+ * appropriate latency.
+ * Legal input is:
+ * a) "enable(bins disabled)" - to enable only general statistics
+ * b) "a,b,c,d,...z" - to enable general statistics and bins, where all are
+ * numbers and a < b < c < d.. < z
+ * c) "disable" - disable all statistics
+ * NOTE: must configure Tx latency statistics bins before stations connected.
+ */
+
+static ssize_t sta_tx_latency_stat_write(struct file *file,
+ const char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct ieee80211_local *local = file->private_data;
+ char buf[128] = {};
+ char *bins = buf;
+ char *token;
+ int buf_size, i, alloc_size;
+ int prev_bin = 0;
+ int n_ranges = 0;
+ int ret = count;
+ struct ieee80211_tx_latency_bin_ranges *tx_latency;
+
+ if (sizeof(buf) <= count)
+ return -EINVAL;
+ buf_size = count;
+ if (copy_from_user(buf, userbuf, buf_size))
+ return -EFAULT;
+
+ mutex_lock(&local->sta_mtx);
+
+ /* cannot change config once we have stations */
+ if (local->num_sta)
+ goto unlock;
+
+ tx_latency =
+ rcu_dereference_protected(local->tx_latency,
+ lockdep_is_held(&local->sta_mtx));
+
+ /* disable Tx statistics */
+ if (!strcmp(buf, TX_LATENCY_DISABLED)) {
+ if (!tx_latency)
+ goto unlock;
+ rcu_assign_pointer(local->tx_latency, NULL);
+ synchronize_rcu();
+ kfree(tx_latency);
+ goto unlock;
+ }
+
+ /* Tx latency already enabled */
+ if (tx_latency)
+ goto unlock;
+
+ if (strcmp(TX_LATENCY_BINS_DISABLED, buf)) {
+ /* check how many bins and between what ranges user requested */
+ token = buf;
+ while (*token != '\0') {
+ if (*token == TX_LATENCY_BIN_DELIMTER_C)
+ n_ranges++;
+ token++;
+ }
+ n_ranges++;
+ }
+
+ alloc_size = sizeof(struct ieee80211_tx_latency_bin_ranges) +
+ n_ranges * sizeof(u32);
+ tx_latency = kzalloc(alloc_size, GFP_ATOMIC);
+ if (!tx_latency) {
+ ret = -ENOMEM;
+ goto unlock;
+ }
+ tx_latency->n_ranges = n_ranges;
+ for (i = 0; i < n_ranges; i++) { /* setting bin ranges */
+ token = strsep(&bins, TX_LATENCY_BIN_DELIMTER_S);
+ sscanf(token, "%d", &tx_latency->ranges[i]);
+ /* bins values should be in ascending order */
+ if (prev_bin >= tx_latency->ranges[i]) {
+ ret = -EINVAL;
+ kfree(tx_latency);
+ goto unlock;
+ }
+ prev_bin = tx_latency->ranges[i];
+ }
+ rcu_assign_pointer(local->tx_latency, tx_latency);
+
+unlock:
+ mutex_unlock(&local->sta_mtx);
+
+ return ret;
+}
+
+static const struct file_operations stats_tx_latency_ops = {
+ .write = sta_tx_latency_stat_write,
+ .read = sta_tx_latency_stat_read,
+ .open = simple_open,
+ .llseek = generic_file_llseek,
+};
+
int mac80211_format_buffer(char __user *userbuf, size_t count,
loff_t *ppos, char *fmt, ...)
{
@@ -315,4 +481,6 @@ void debugfs_hw_add(struct ieee80211_local *local)
DEBUGFS_DEVSTATS_ADD(dot11RTSFailureCount);
DEBUGFS_DEVSTATS_ADD(dot11FCSErrorCount);
DEBUGFS_DEVSTATS_ADD(dot11RTSSuccessCount);
+
+ DEBUGFS_DEVSTATS_ADD(tx_latency);
}
diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c
index 19c54a44ed47..80194b557a0c 100644
--- a/net/mac80211/debugfs_sta.c
+++ b/net/mac80211/debugfs_sta.c
@@ -38,6 +38,13 @@ static const struct file_operations sta_ ##name## _ops = { \
.llseek = generic_file_llseek, \
}
+#define STA_OPS_W(name) \
+static const struct file_operations sta_ ##name## _ops = { \
+ .write = sta_##name##_write, \
+ .open = simple_open, \
+ .llseek = generic_file_llseek, \
+}
+
#define STA_OPS_RW(name) \
static const struct file_operations sta_ ##name## _ops = { \
.read = sta_##name##_read, \
@@ -388,6 +395,131 @@ static ssize_t sta_last_rx_rate_read(struct file *file, char __user *userbuf,
}
STA_OPS(last_rx_rate);
+static int
+sta_tx_latency_stat_header(struct ieee80211_tx_latency_bin_ranges *tx_latency,
+ char *buf, int pos, int bufsz)
+{
+ int i;
+ int range_count = tx_latency->n_ranges;
+ u32 *bin_ranges = tx_latency->ranges;
+
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "Station\t\t\tTID\tMax\tAvg");
+ if (range_count) {
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "\t<=%d", bin_ranges[0]);
+ for (i = 0; i < range_count - 1; i++)
+ pos += scnprintf(buf + pos, bufsz - pos, "\t%d-%d",
+ bin_ranges[i], bin_ranges[i+1]);
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "\t%d<", bin_ranges[range_count - 1]);
+ }
+
+ pos += scnprintf(buf + pos, bufsz - pos, "\n");
+
+ return pos;
+}
+
+static int
+sta_tx_latency_stat_table(struct ieee80211_tx_latency_bin_ranges *tx_lat_range,
+ struct ieee80211_tx_latency_stat *tx_lat,
+ char *buf, int pos, int bufsz, int tid)
+{
+ u32 avg = 0;
+ int j;
+ int bin_count = tx_lat->bin_count;
+
+ pos += scnprintf(buf + pos, bufsz - pos, "\t\t\t%d", tid);
+ /* make sure you don't divide in 0 */
+ if (tx_lat->counter)
+ avg = tx_lat->sum / tx_lat->counter;
+
+ pos += scnprintf(buf + pos, bufsz - pos, "\t%d\t%d",
+ tx_lat->max, avg);
+
+ if (tx_lat_range->n_ranges && tx_lat->bins)
+ for (j = 0; j < bin_count; j++)
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "\t%d", tx_lat->bins[j]);
+ pos += scnprintf(buf + pos, bufsz - pos, "\n");
+
+ return pos;
+}
+
+/*
+ * Output Tx latency statistics station && restart all statistics information
+ */
+static ssize_t sta_tx_latency_stat_read(struct file *file,
+ char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct sta_info *sta = file->private_data;
+ struct ieee80211_local *local = sta->local;
+ struct ieee80211_tx_latency_bin_ranges *tx_latency;
+ char *buf;
+ int bufsz, ret, i;
+ int pos = 0;
+
+ bufsz = 20 * IEEE80211_NUM_TIDS *
+ sizeof(struct ieee80211_tx_latency_stat);
+ buf = kzalloc(bufsz, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ rcu_read_lock();
+
+ tx_latency = rcu_dereference(local->tx_latency);
+
+ if (!sta->tx_lat) {
+ pos += scnprintf(buf + pos, bufsz - pos,
+ "Tx latency statistics are not enabled\n");
+ goto unlock;
+ }
+
+ pos = sta_tx_latency_stat_header(tx_latency, buf, pos, bufsz);
+
+ pos += scnprintf(buf + pos, bufsz - pos, "%pM\n", sta->sta.addr);
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++)
+ pos = sta_tx_latency_stat_table(tx_latency, &sta->tx_lat[i],
+ buf, pos, bufsz, i);
+unlock:
+ rcu_read_unlock();
+
+ ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+ kfree(buf);
+
+ return ret;
+}
+STA_OPS(tx_latency_stat);
+
+static ssize_t sta_tx_latency_stat_reset_write(struct file *file,
+ const char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ u32 *bins;
+ int bin_count;
+ struct sta_info *sta = file->private_data;
+ int i;
+
+ if (!sta->tx_lat)
+ return -EINVAL;
+
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
+ bins = sta->tx_lat[i].bins;
+ bin_count = sta->tx_lat[i].bin_count;
+
+ sta->tx_lat[i].max = 0;
+ sta->tx_lat[i].sum = 0;
+ sta->tx_lat[i].counter = 0;
+
+ if (bin_count)
+ memset(bins, 0, bin_count * sizeof(u32));
+ }
+
+ return count;
+}
+STA_OPS_W(tx_latency_stat_reset);
+
#define DEBUGFS_ADD(name) \
debugfs_create_file(#name, 0400, \
sta->debugfs.dir, sta, &sta_ ##name## _ops);
@@ -441,6 +573,8 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
DEBUGFS_ADD(last_ack_signal);
DEBUGFS_ADD(current_tx_rate);
DEBUGFS_ADD(last_rx_rate);
+ DEBUGFS_ADD(tx_latency_stat);
+ DEBUGFS_ADD(tx_latency_stat_reset);
DEBUGFS_ADD_COUNTER(rx_packets, rx_packets);
DEBUGFS_ADD_COUNTER(tx_packets, tx_packets);
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 27a39de89679..2eda7b13124a 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -550,12 +550,12 @@ int ieee80211_ibss_finish_csa(struct ieee80211_sub_if_data *sdata)
capability);
/* XXX: should not really modify cfg80211 data */
if (cbss) {
- cbss->channel = sdata->local->csa_chandef.chan;
+ cbss->channel = sdata->csa_chandef.chan;
cfg80211_put_bss(sdata->local->hw.wiphy, cbss);
}
}
- ifibss->chandef = sdata->local->csa_chandef;
+ ifibss->chandef = sdata->csa_chandef;
/* generate the beacon */
err = ieee80211_ibss_csa_beacon(sdata, NULL);
@@ -926,7 +926,7 @@ ieee80211_ibss_process_chanswitch(struct ieee80211_sub_if_data *sdata,
IEEE80211_MAX_QUEUE_MAP,
IEEE80211_QUEUE_STOP_REASON_CSA);
- sdata->local->csa_chandef = params.chandef;
+ sdata->csa_chandef = params.chandef;
sdata->vif.csa_active = true;
ieee80211_bss_info_change_notify(sdata, err);
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 4aea4e791113..ed5bf8b4b5c2 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -728,6 +728,7 @@ struct ieee80211_sub_if_data {
u16 sequence_number;
__be16 control_port_protocol;
bool control_port_no_encrypt;
+ int encrypt_headroom;
struct ieee80211_tx_queue_params tx_conf[IEEE80211_NUM_ACS];
@@ -735,6 +736,7 @@ struct ieee80211_sub_if_data {
int csa_counter_offset_beacon;
int csa_counter_offset_presp;
bool csa_radar_required;
+ struct cfg80211_chan_def csa_chandef;
/* used to reconfigure hardware SM PS */
struct work_struct recalc_smps;
@@ -811,6 +813,9 @@ static inline void sdata_unlock(struct ieee80211_sub_if_data *sdata)
__release(&sdata->wdev.mtx);
}
+#define sdata_dereference(p, sdata) \
+ rcu_dereference_protected(p, lockdep_is_held(&sdata->wdev.mtx))
+
static inline void
sdata_assert_lock(struct ieee80211_sub_if_data *sdata)
{
@@ -896,6 +901,24 @@ struct tpt_led_trigger {
};
#endif
+/*
+ * struct ieee80211_tx_latency_bin_ranges - Tx latency statistics bins ranges
+ *
+ * Measuring Tx latency statistics. Counts how many Tx frames transmitted in a
+ * certain latency range (in Milliseconds). Each station that uses these
+ * ranges will have bins to count the amount of frames received in that range.
+ * The user can configure the ranges via debugfs.
+ * If ranges is NULL then Tx latency statistics bins are disabled for all
+ * stations.
+ *
+ * @n_ranges: number of ranges that are taken in account
+ * @ranges: the ranges that the user requested or NULL if disabled.
+ */
+struct ieee80211_tx_latency_bin_ranges {
+ int n_ranges;
+ u32 ranges[];
+};
+
/**
* mac80211 scan flags - currently active scan mode
*
@@ -1048,6 +1071,12 @@ struct ieee80211_local {
struct timer_list sta_cleanup;
int sta_generation;
+ /*
+ * Tx latency statistics parameters for all stations.
+ * Can enable via debugfs (NULL when disabled).
+ */
+ struct ieee80211_tx_latency_bin_ranges __rcu *tx_latency;
+
struct sk_buff_head pending[IEEE80211_MAX_QUEUES];
struct tasklet_struct tx_pending_tasklet;
@@ -1093,7 +1122,6 @@ struct ieee80211_local {
enum mac80211_scan_state next_scan_state;
struct delayed_work scan_work;
struct ieee80211_sub_if_data __rcu *scan_sdata;
- struct cfg80211_chan_def csa_chandef;
/* For backward compatibility only -- do not use */
struct cfg80211_chan_def _oper_chandef;
@@ -1693,6 +1721,7 @@ int __ieee80211_request_smps_mgd(struct ieee80211_sub_if_data *sdata,
int __ieee80211_request_smps_ap(struct ieee80211_sub_if_data *sdata,
enum ieee80211_smps_mode smps_mode);
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);
@@ -1731,7 +1760,6 @@ ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
/* NOTE: only use ieee80211_vif_change_channel() for channel switch */
int __must_check
ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
- const struct cfg80211_chan_def *chandef,
u32 *changed);
void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata);
void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata);
@@ -1742,6 +1770,8 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
struct ieee80211_chanctx *chanctx);
void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
struct ieee80211_chanctx *chanctx);
+void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
+ struct ieee80211_chanctx *ctx);
void ieee80211_dfs_cac_timer(unsigned long data);
void ieee80211_dfs_cac_timer_work(struct work_struct *work);
@@ -1750,6 +1780,15 @@ void ieee80211_dfs_radar_detected_work(struct work_struct *work);
int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
struct cfg80211_csa_settings *csa_settings);
+bool ieee80211_cs_valid(const struct ieee80211_cipher_scheme *cs);
+bool ieee80211_cs_list_valid(const struct ieee80211_cipher_scheme *cs, int n);
+const struct ieee80211_cipher_scheme *
+ieee80211_cs_get(struct ieee80211_local *local, u32 cipher,
+ enum nl80211_iftype iftype);
+int ieee80211_cs_headroom(struct ieee80211_local *local,
+ struct cfg80211_crypto_settings *crypto,
+ enum nl80211_iftype iftype);
+
#ifdef CONFIG_MAC80211_NOINLINE
#define debug_noinline noinline
#else
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 36c3a4cbcabf..7aa9f9dea9df 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -401,6 +401,8 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
snprintf(sdata->name, IFNAMSIZ, "%s-monitor",
wiphy_name(local->hw.wiphy));
+ sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
+
ieee80211_set_default_queues(sdata);
ret = drv_add_interface(local, sdata);
@@ -749,6 +751,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
u32 hw_reconf_flags = 0;
int i, flushed;
struct ps_data *ps;
+ struct cfg80211_chan_def chandef;
clear_bit(SDATA_STATE_RUNNING, &sdata->state);
@@ -823,11 +826,13 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
if (sdata->wdev.cac_started) {
+ chandef = sdata->vif.bss_conf.chandef;
WARN_ON(local->suspended);
mutex_lock(&local->iflist_mtx);
ieee80211_vif_release_channel(sdata);
mutex_unlock(&local->iflist_mtx);
- cfg80211_cac_event(sdata->dev, NL80211_RADAR_CAC_ABORTED,
+ cfg80211_cac_event(sdata->dev, &chandef,
+ NL80211_RADAR_CAC_ABORTED,
GFP_KERNEL);
}
@@ -1036,7 +1041,6 @@ static void ieee80211_set_multicast_list(struct net_device *dev)
*/
static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata)
{
- int flushed;
int i;
/* free extra data */
@@ -1050,9 +1054,6 @@ static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata)
if (ieee80211_vif_is_mesh(&sdata->vif))
mesh_rmc_free(sdata);
-
- flushed = sta_info_flush(sdata);
- WARN_ON(flushed);
}
static void ieee80211_uninit(struct net_device *dev)
@@ -1270,6 +1271,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
sdata->control_port_protocol = cpu_to_be16(ETH_P_PAE);
sdata->control_port_no_encrypt = false;
+ sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
sdata->noack_map = 0;
@@ -1685,6 +1687,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL;
sdata->user_power_level = local->user_power_level;
+ sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
+
/* setup type-dependent data */
ieee80211_setup_sdata(sdata, type);
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 3e51dd7d98b3..e568d98167d0 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -260,25 +260,29 @@ static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
int idx;
bool defunikey, defmultikey, defmgmtkey;
+ /* caller must provide at least one old/new */
+ if (WARN_ON(!new && !old))
+ return;
+
if (new)
list_add_tail(&new->list, &sdata->key_list);
- if (sta && pairwise) {
- rcu_assign_pointer(sta->ptk, new);
- } else if (sta) {
- if (old)
- idx = old->conf.keyidx;
- else
- idx = new->conf.keyidx;
- rcu_assign_pointer(sta->gtk[idx], new);
- } else {
- WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx);
+ WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx);
- if (old)
- idx = old->conf.keyidx;
- else
- idx = new->conf.keyidx;
+ if (old)
+ idx = old->conf.keyidx;
+ else
+ idx = new->conf.keyidx;
+ if (sta) {
+ if (pairwise) {
+ rcu_assign_pointer(sta->ptk[idx], new);
+ sta->ptk_idx = idx;
+ } else {
+ rcu_assign_pointer(sta->gtk[idx], new);
+ sta->gtk_idx = idx;
+ }
+ } else {
defunikey = old &&
old == key_mtx_dereference(sdata->local,
sdata->default_unicast_key);
@@ -312,9 +316,11 @@ static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
list_del(&old->list);
}
-struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
- const u8 *key_data,
- size_t seq_len, const u8 *seq)
+struct ieee80211_key *
+ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
+ const u8 *key_data,
+ size_t seq_len, const u8 *seq,
+ const struct ieee80211_cipher_scheme *cs)
{
struct ieee80211_key *key;
int i, j, err;
@@ -393,6 +399,18 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
return ERR_PTR(err);
}
break;
+ default:
+ if (cs) {
+ size_t len = (seq_len > MAX_PN_LEN) ?
+ MAX_PN_LEN : seq_len;
+
+ key->conf.iv_len = cs->hdr_len;
+ key->conf.icv_len = cs->mic_len;
+ for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++)
+ for (j = 0; j < len; j++)
+ key->u.gen.rx_pn[i][j] =
+ seq[len - j - 1];
+ }
}
memcpy(key->conf.key, key_data, key_len);
INIT_LIST_HEAD(&key->list);
@@ -475,7 +493,7 @@ int ieee80211_key_link(struct ieee80211_key *key,
mutex_lock(&sdata->local->key_mtx);
if (sta && pairwise)
- old_key = key_mtx_dereference(sdata->local, sta->ptk);
+ old_key = key_mtx_dereference(sdata->local, sta->ptk[idx]);
else if (sta)
old_key = key_mtx_dereference(sdata->local, sta->gtk[idx]);
else
@@ -625,8 +643,10 @@ void ieee80211_free_sta_keys(struct ieee80211_local *local,
list_add(&key->list, &keys);
}
- key = key_mtx_dereference(local, sta->ptk);
- if (key) {
+ for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
+ key = key_mtx_dereference(local, sta->ptk[i]);
+ if (!key)
+ continue;
ieee80211_key_replace(key->sdata, key->sta,
key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
key, NULL);
@@ -877,7 +897,7 @@ ieee80211_gtk_rekey_add(struct ieee80211_vif *vif,
key = ieee80211_key_alloc(keyconf->cipher, keyconf->keyidx,
keyconf->keylen, keyconf->key,
- 0, NULL);
+ 0, NULL, NULL);
if (IS_ERR(key))
return ERR_CAST(key);
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index aaae0ed37004..0aebb889caba 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -18,6 +18,7 @@
#define NUM_DEFAULT_KEYS 4
#define NUM_DEFAULT_MGMT_KEYS 2
+#define MAX_PN_LEN 16
struct ieee80211_local;
struct ieee80211_sub_if_data;
@@ -93,6 +94,10 @@ struct ieee80211_key {
u32 replays; /* dot11RSNAStatsCMACReplays */
u32 icverrors; /* dot11RSNAStatsCMACICVErrors */
} aes_cmac;
+ struct {
+ /* generic cipher scheme */
+ u8 rx_pn[IEEE80211_NUM_TIDS + 1][MAX_PN_LEN];
+ } gen;
} u;
/* number of times this key has been used */
@@ -113,9 +118,11 @@ struct ieee80211_key {
struct ieee80211_key_conf conf;
};
-struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
- const u8 *key_data,
- size_t seq_len, const u8 *seq);
+struct ieee80211_key *
+ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
+ const u8 *key_data,
+ size_t seq_len, const u8 *seq,
+ const struct ieee80211_cipher_scheme *cs);
/*
* Insert a key into data structures (sdata, sta if necessary)
* to make it used, free old key. On failure, also free the new key.
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 7d1c3ac48ed9..fa34cd2344b9 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -651,15 +651,14 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
}
EXPORT_SYMBOL(ieee80211_alloc_hw);
-int ieee80211_register_hw(struct ieee80211_hw *hw)
+static int ieee80211_init_cipher_suites(struct ieee80211_local *local)
{
- struct ieee80211_local *local = hw_to_local(hw);
- int result, i;
- enum ieee80211_band band;
- int channels, max_bitrates;
- bool supp_ht, supp_vht;
- netdev_features_t feature_whitelist;
- struct cfg80211_chan_def dflt_chandef = {};
+ bool have_wep = !(IS_ERR(local->wep_tx_tfm) ||
+ IS_ERR(local->wep_rx_tfm));
+ bool have_mfp = local->hw.flags & IEEE80211_HW_MFP_CAPABLE;
+ const struct ieee80211_cipher_scheme *cs = local->hw.cipher_schemes;
+ int n_suites = 0, r = 0, w = 0;
+ u32 *suites;
static const u32 cipher_suites[] = {
/* keep WEP first, it may be removed below */
WLAN_CIPHER_SUITE_WEP40,
@@ -671,6 +670,93 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
WLAN_CIPHER_SUITE_AES_CMAC
};
+ /* Driver specifies the ciphers, we have nothing to do... */
+ if (local->hw.wiphy->cipher_suites && have_wep)
+ return 0;
+
+ /* Set up cipher suites if driver relies on mac80211 cipher defs */
+ if (!local->hw.wiphy->cipher_suites && !cs) {
+ local->hw.wiphy->cipher_suites = cipher_suites;
+ local->hw.wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
+
+ if (!have_mfp)
+ local->hw.wiphy->n_cipher_suites--;
+
+ if (!have_wep) {
+ local->hw.wiphy->cipher_suites += 2;
+ local->hw.wiphy->n_cipher_suites -= 2;
+ }
+
+ return 0;
+ }
+
+ if (!local->hw.wiphy->cipher_suites) {
+ /*
+ * Driver specifies cipher schemes only
+ * We start counting ciphers defined by schemes, TKIP and CCMP
+ */
+ n_suites = local->hw.n_cipher_schemes + 2;
+
+ /* check if we have WEP40 and WEP104 */
+ if (have_wep)
+ n_suites += 2;
+
+ /* check if we have AES_CMAC */
+ if (have_mfp)
+ n_suites++;
+
+ suites = kmalloc(sizeof(u32) * n_suites, GFP_KERNEL);
+ if (!suites)
+ return -ENOMEM;
+
+ suites[w++] = WLAN_CIPHER_SUITE_CCMP;
+ suites[w++] = WLAN_CIPHER_SUITE_TKIP;
+
+ if (have_wep) {
+ suites[w++] = WLAN_CIPHER_SUITE_WEP40;
+ suites[w++] = WLAN_CIPHER_SUITE_WEP104;
+ }
+
+ if (have_mfp)
+ suites[w++] = WLAN_CIPHER_SUITE_AES_CMAC;
+
+ for (r = 0; r < local->hw.n_cipher_schemes; r++)
+ suites[w++] = cs[r].cipher;
+ } else {
+ /* Driver provides cipher suites, but we need to exclude WEP */
+ suites = kmemdup(local->hw.wiphy->cipher_suites,
+ sizeof(u32) * local->hw.wiphy->n_cipher_suites,
+ GFP_KERNEL);
+ if (!suites)
+ return -ENOMEM;
+
+ for (r = 0; r < local->hw.wiphy->n_cipher_suites; r++) {
+ u32 suite = local->hw.wiphy->cipher_suites[r];
+
+ if (suite == WLAN_CIPHER_SUITE_WEP40 ||
+ suite == WLAN_CIPHER_SUITE_WEP104)
+ continue;
+ suites[w++] = suite;
+ }
+ }
+
+ local->hw.wiphy->cipher_suites = suites;
+ local->hw.wiphy->n_cipher_suites = w;
+ local->wiphy_ciphers_allocated = true;
+
+ return 0;
+}
+
+int ieee80211_register_hw(struct ieee80211_hw *hw)
+{
+ struct ieee80211_local *local = hw_to_local(hw);
+ int result, i;
+ enum ieee80211_band band;
+ int channels, max_bitrates;
+ bool supp_ht, supp_vht;
+ netdev_features_t feature_whitelist;
+ struct cfg80211_chan_def dflt_chandef = {};
+
if (hw->flags & IEEE80211_HW_QUEUE_CONTROL &&
(local->hw.offchannel_tx_hw_queue == IEEE80211_INVAL_HW_QUEUE ||
local->hw.offchannel_tx_hw_queue >= local->hw.queues))
@@ -851,43 +937,12 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
if (local->hw.wiphy->max_scan_ie_len)
local->hw.wiphy->max_scan_ie_len -= local->scan_ies_len;
- /* Set up cipher suites unless driver already did */
- if (!local->hw.wiphy->cipher_suites) {
- local->hw.wiphy->cipher_suites = cipher_suites;
- local->hw.wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
- if (!(local->hw.flags & IEEE80211_HW_MFP_CAPABLE))
- local->hw.wiphy->n_cipher_suites--;
- }
- if (IS_ERR(local->wep_tx_tfm) || IS_ERR(local->wep_rx_tfm)) {
- if (local->hw.wiphy->cipher_suites == cipher_suites) {
- local->hw.wiphy->cipher_suites += 2;
- local->hw.wiphy->n_cipher_suites -= 2;
- } else {
- u32 *suites;
- int r, w = 0;
-
- /* Filter out WEP */
-
- suites = kmemdup(
- local->hw.wiphy->cipher_suites,
- sizeof(u32) * local->hw.wiphy->n_cipher_suites,
- GFP_KERNEL);
- if (!suites) {
- result = -ENOMEM;
- goto fail_wiphy_register;
- }
- for (r = 0; r < local->hw.wiphy->n_cipher_suites; r++) {
- u32 suite = local->hw.wiphy->cipher_suites[r];
- if (suite == WLAN_CIPHER_SUITE_WEP40 ||
- suite == WLAN_CIPHER_SUITE_WEP104)
- continue;
- suites[w++] = suite;
- }
- local->hw.wiphy->cipher_suites = suites;
- local->hw.wiphy->n_cipher_suites = w;
- local->wiphy_ciphers_allocated = true;
- }
- }
+ WARN_ON(!ieee80211_cs_list_valid(local->hw.cipher_schemes,
+ local->hw.n_cipher_schemes));
+
+ result = ieee80211_init_cipher_suites(local);
+ if (result < 0)
+ goto fail_wiphy_register;
if (!local->ops->remain_on_channel)
local->hw.wiphy->max_remain_on_channel_duration = 5000;
@@ -1090,6 +1145,8 @@ void ieee80211_free_hw(struct ieee80211_hw *hw)
ieee80211_free_ack_frame, NULL);
idr_destroy(&local->ack_status_frames);
+ kfree(rcu_access_pointer(local->tx_latency));
+
wiphy_free(local->hw.wiphy);
}
EXPORT_SYMBOL(ieee80211_free_hw);
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index ba105257d03f..89df62b2b689 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -674,8 +674,6 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
rcu_read_lock();
csa = rcu_dereference(ifmsh->csa);
if (csa) {
- __le16 pre_value;
-
pos = skb_put(skb, 13);
memset(pos, 0, 13);
*pos++ = WLAN_EID_CHANNEL_SWITCH;
@@ -697,8 +695,7 @@ ieee80211_mesh_build_beacon(struct ieee80211_if_mesh *ifmsh)
WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00;
put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos);
pos += 2;
- pre_value = cpu_to_le16(ifmsh->pre_value);
- memcpy(pos, &pre_value, 2);
+ put_unaligned_le16(ifmsh->pre_value, pos);
pos += 2;
}
rcu_read_unlock();
@@ -964,7 +961,7 @@ ieee80211_mesh_process_chnswitch(struct ieee80211_sub_if_data *sdata,
IEEE80211_MAX_QUEUE_MAP,
IEEE80211_QUEUE_STOP_REASON_CSA);
- sdata->local->csa_chandef = params.chandef;
+ sdata->csa_chandef = params.chandef;
sdata->vif.csa_active = true;
ieee80211_bss_info_change_notify(sdata, err);
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 2bc7fd2f787d..f39a19f9090f 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -215,8 +215,6 @@ int mesh_rmc_check(struct ieee80211_sub_if_data *sdata,
bool mesh_matches_local(struct ieee80211_sub_if_data *sdata,
struct ieee802_11_elems *ie);
void mesh_ids_set_default(struct ieee80211_if_mesh *mesh);
-void mesh_mgmt_ies_add(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb);
int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb);
int mesh_add_meshid_ie(struct ieee80211_sub_if_data *sdata,
@@ -303,8 +301,8 @@ void mesh_mpath_table_grow(void);
void mesh_mpp_table_grow(void);
/* Mesh paths */
int mesh_path_error_tx(struct ieee80211_sub_if_data *sdata,
- u8 ttl, const u8 *target, __le32 target_sn,
- __le16 target_rcode, const u8 *ra);
+ u8 ttl, const u8 *target, u32 target_sn,
+ u16 target_rcode, const u8 *ra);
void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta);
void mesh_path_flush_pending(struct mesh_path *mpath);
void mesh_path_tx_pending(struct mesh_path *mpath);
diff --git a/net/mac80211/mesh_hwmp.c b/net/mac80211/mesh_hwmp.c
index 486819cd02cd..f9514685d45a 100644
--- a/net/mac80211/mesh_hwmp.c
+++ b/net/mac80211/mesh_hwmp.c
@@ -102,12 +102,11 @@ enum mpath_frame_type {
static const u8 broadcast_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,
- const u8 *orig_addr, __le32 orig_sn,
+ const u8 *orig_addr, u32 orig_sn,
u8 target_flags, const u8 *target,
- __le32 target_sn, const u8 *da,
+ u32 target_sn, const u8 *da,
u8 hop_count, u8 ttl,
- __le32 lifetime, __le32 metric,
- __le32 preq_id,
+ u32 lifetime, u32 metric, u32 preq_id,
struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_local *local = sdata->local;
@@ -167,33 +166,33 @@ static int mesh_path_sel_frame_tx(enum mpath_frame_type action, u8 flags,
if (action == MPATH_PREP) {
memcpy(pos, target, ETH_ALEN);
pos += ETH_ALEN;
- memcpy(pos, &target_sn, 4);
+ put_unaligned_le32(target_sn, pos);
pos += 4;
} else {
if (action == MPATH_PREQ) {
- memcpy(pos, &preq_id, 4);
+ put_unaligned_le32(preq_id, pos);
pos += 4;
}
memcpy(pos, orig_addr, ETH_ALEN);
pos += ETH_ALEN;
- memcpy(pos, &orig_sn, 4);
+ put_unaligned_le32(orig_sn, pos);
pos += 4;
}
- memcpy(pos, &lifetime, 4); /* interval for RANN */
+ put_unaligned_le32(lifetime, pos); /* interval for RANN */
pos += 4;
- memcpy(pos, &metric, 4);
+ put_unaligned_le32(metric, pos);
pos += 4;
if (action == MPATH_PREQ) {
*pos++ = 1; /* destination count */
*pos++ = target_flags;
memcpy(pos, target, ETH_ALEN);
pos += ETH_ALEN;
- memcpy(pos, &target_sn, 4);
+ put_unaligned_le32(target_sn, pos);
pos += 4;
} else if (action == MPATH_PREP) {
memcpy(pos, orig_addr, ETH_ALEN);
pos += ETH_ALEN;
- memcpy(pos, &orig_sn, 4);
+ put_unaligned_le32(orig_sn, pos);
pos += 4;
}
@@ -239,8 +238,8 @@ static void prepare_frame_for_deferred_tx(struct ieee80211_sub_if_data *sdata,
* frame directly but add it to the pending queue instead.
*/
int mesh_path_error_tx(struct ieee80211_sub_if_data *sdata,
- u8 ttl, const u8 *target, __le32 target_sn,
- __le16 target_rcode, const u8 *ra)
+ u8 ttl, const u8 *target, u32 target_sn,
+ u16 target_rcode, const u8 *ra)
{
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
@@ -254,13 +253,13 @@ int mesh_path_error_tx(struct ieee80211_sub_if_data *sdata,
return -EAGAIN;
skb = dev_alloc_skb(local->tx_headroom +
- IEEE80211_ENCRYPT_HEADROOM +
+ sdata->encrypt_headroom +
IEEE80211_ENCRYPT_TAILROOM +
hdr_len +
2 + 15 /* PERR IE */);
if (!skb)
return -1;
- skb_reserve(skb, local->tx_headroom + IEEE80211_ENCRYPT_HEADROOM);
+ skb_reserve(skb, local->tx_headroom + sdata->encrypt_headroom);
mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len);
memset(mgmt, 0, hdr_len);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
@@ -293,9 +292,9 @@ int mesh_path_error_tx(struct ieee80211_sub_if_data *sdata,
pos++;
memcpy(pos, target, ETH_ALEN);
pos += ETH_ALEN;
- memcpy(pos, &target_sn, 4);
+ put_unaligned_le32(target_sn, pos);
pos += 4;
- memcpy(pos, &target_rcode, 2);
+ put_unaligned_le16(target_rcode, pos);
/* see note in function header */
prepare_frame_for_deferred_tx(sdata, skb);
@@ -592,10 +591,9 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
if (ttl != 0) {
mhwmp_dbg(sdata, "replying to the PREQ\n");
mesh_path_sel_frame_tx(MPATH_PREP, 0, orig_addr,
- cpu_to_le32(orig_sn), 0, target_addr,
- cpu_to_le32(target_sn), mgmt->sa, 0, ttl,
- cpu_to_le32(lifetime), cpu_to_le32(metric),
- 0, sdata);
+ orig_sn, 0, target_addr,
+ target_sn, mgmt->sa, 0, ttl,
+ lifetime, metric, 0, sdata);
} else {
ifmsh->mshstats.dropped_frames_ttl++;
}
@@ -625,11 +623,9 @@ static void hwmp_preq_frame_process(struct ieee80211_sub_if_data *sdata,
}
mesh_path_sel_frame_tx(MPATH_PREQ, flags, orig_addr,
- cpu_to_le32(orig_sn), target_flags, target_addr,
- cpu_to_le32(target_sn), da,
- hopcount, ttl, cpu_to_le32(lifetime),
- cpu_to_le32(metric), cpu_to_le32(preq_id),
- sdata);
+ orig_sn, target_flags, target_addr,
+ target_sn, da, hopcount, ttl, lifetime,
+ metric, preq_id, sdata);
if (!is_multicast_ether_addr(da))
ifmsh->mshstats.fwded_unicast++;
else
@@ -695,11 +691,9 @@ static void hwmp_prep_frame_process(struct ieee80211_sub_if_data *sdata,
target_sn = PREP_IE_TARGET_SN(prep_elem);
orig_sn = PREP_IE_ORIG_SN(prep_elem);
- mesh_path_sel_frame_tx(MPATH_PREP, flags, orig_addr,
- cpu_to_le32(orig_sn), 0, target_addr,
- cpu_to_le32(target_sn), next_hop, hopcount,
- ttl, cpu_to_le32(lifetime), cpu_to_le32(metric),
- 0, sdata);
+ mesh_path_sel_frame_tx(MPATH_PREP, flags, orig_addr, orig_sn, 0,
+ target_addr, target_sn, next_hop, hopcount,
+ ttl, lifetime, metric, 0, sdata);
rcu_read_unlock();
sdata->u.mesh.mshstats.fwded_unicast++;
@@ -750,8 +744,7 @@ static void hwmp_perr_frame_process(struct ieee80211_sub_if_data *sdata,
if (!ifmsh->mshcfg.dot11MeshForwarding)
goto endperr;
mesh_path_error_tx(sdata, ttl, target_addr,
- cpu_to_le32(target_sn),
- cpu_to_le16(target_rcode),
+ target_sn, target_rcode,
broadcast_addr);
} else
spin_unlock_bh(&mpath->state_lock);
@@ -847,11 +840,9 @@ static void hwmp_rann_frame_process(struct ieee80211_sub_if_data *sdata,
if (ifmsh->mshcfg.dot11MeshForwarding) {
mesh_path_sel_frame_tx(MPATH_RANN, flags, orig_addr,
- cpu_to_le32(orig_sn),
- 0, NULL, 0, broadcast_addr,
- hopcount, ttl, cpu_to_le32(interval),
- cpu_to_le32(metric + metric_txsta),
- 0, sdata);
+ orig_sn, 0, NULL, 0, broadcast_addr,
+ hopcount, ttl, interval,
+ metric + metric_txsta, 0, sdata);
}
rcu_read_unlock();
@@ -1049,11 +1040,9 @@ void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata)
spin_unlock_bh(&mpath->state_lock);
da = (mpath->is_root) ? mpath->rann_snd_addr : broadcast_addr;
- mesh_path_sel_frame_tx(MPATH_PREQ, 0, sdata->vif.addr,
- cpu_to_le32(ifmsh->sn), target_flags, mpath->dst,
- cpu_to_le32(mpath->sn), da, 0,
- ttl, cpu_to_le32(lifetime), 0,
- cpu_to_le32(ifmsh->preq_id++), sdata);
+ mesh_path_sel_frame_tx(MPATH_PREQ, 0, sdata->vif.addr, ifmsh->sn,
+ target_flags, mpath->dst, mpath->sn, da, 0,
+ ttl, lifetime, 0, ifmsh->preq_id++, sdata);
mod_timer(&mpath->timer, jiffies + mpath->discovery_timeout);
enddiscovery:
@@ -1212,10 +1201,9 @@ void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata)
switch (ifmsh->mshcfg.dot11MeshHWMPRootMode) {
case IEEE80211_PROACTIVE_RANN:
mesh_path_sel_frame_tx(MPATH_RANN, flags, sdata->vif.addr,
- cpu_to_le32(++ifmsh->sn),
- 0, NULL, 0, broadcast_addr,
- 0, ifmsh->mshcfg.element_ttl,
- cpu_to_le32(interval), 0, 0, sdata);
+ ++ifmsh->sn, 0, NULL, 0, broadcast_addr,
+ 0, ifmsh->mshcfg.element_ttl,
+ interval, 0, 0, sdata);
break;
case IEEE80211_PROACTIVE_PREQ_WITH_PREP:
flags |= IEEE80211_PREQ_PROACTIVE_PREP_FLAG;
@@ -1224,11 +1212,10 @@ void mesh_path_tx_root_frame(struct ieee80211_sub_if_data *sdata)
target_flags |= IEEE80211_PREQ_TO_FLAG |
IEEE80211_PREQ_USN_FLAG;
mesh_path_sel_frame_tx(MPATH_PREQ, flags, sdata->vif.addr,
- cpu_to_le32(++ifmsh->sn), target_flags,
- (u8 *) broadcast_addr, 0, broadcast_addr,
- 0, ifmsh->mshcfg.element_ttl,
- cpu_to_le32(interval),
- 0, cpu_to_le32(ifmsh->preq_id++), sdata);
+ ++ifmsh->sn, target_flags,
+ (u8 *) broadcast_addr, 0, broadcast_addr,
+ 0, ifmsh->mshcfg.element_ttl, interval,
+ 0, ifmsh->preq_id++, sdata);
break;
default:
mhwmp_dbg(sdata, "Proactive mechanism not supported\n");
diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c
index 89aacfd2756d..7d050ed6fe5a 100644
--- a/net/mac80211/mesh_pathtbl.c
+++ b/net/mac80211/mesh_pathtbl.c
@@ -722,7 +722,6 @@ void mesh_plink_broken(struct sta_info *sta)
struct mpath_node *node;
struct ieee80211_sub_if_data *sdata = sta->sdata;
int i;
- __le16 reason = cpu_to_le16(WLAN_REASON_MESH_PATH_DEST_UNREACHABLE);
rcu_read_lock();
tbl = rcu_dereference(mesh_paths);
@@ -736,9 +735,9 @@ void mesh_plink_broken(struct sta_info *sta)
++mpath->sn;
spin_unlock_bh(&mpath->state_lock);
mesh_path_error_tx(sdata,
- sdata->u.mesh.mshcfg.element_ttl,
- mpath->dst, cpu_to_le32(mpath->sn),
- reason, bcast);
+ sdata->u.mesh.mshcfg.element_ttl,
+ mpath->dst, mpath->sn,
+ WLAN_REASON_MESH_PATH_DEST_UNREACHABLE, bcast);
}
}
rcu_read_unlock();
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 4301aa5aa227..cf83217103f9 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -19,12 +19,6 @@
#define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \
jiffies + HZ * t / 1000))
-/* We only need a valid sta if user configured a minimum rssi_threshold. */
-#define rssi_threshold_check(sta, sdata) \
- (sdata->u.mesh.mshcfg.rssi_threshold == 0 ||\
- (sta && (s8) -ewma_read(&sta->avg_signal) > \
- sdata->u.mesh.mshcfg.rssi_threshold))
-
enum plink_event {
PLINK_UNDEFINED,
OPN_ACPT,
@@ -61,7 +55,17 @@ static const char * const mplevents[] = {
static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
enum ieee80211_self_protected_actioncode action,
- u8 *da, __le16 llid, __le16 plid, __le16 reason);
+ u8 *da, u16 llid, u16 plid, u16 reason);
+
+
+/* We only need a valid sta if user configured a minimum rssi_threshold. */
+static bool rssi_threshold_check(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta)
+{
+ s32 rssi_threshold = sdata->u.mesh.mshcfg.rssi_threshold;
+ return rssi_threshold == 0 ||
+ (sta && (s8) -ewma_read(&sta->avg_signal) > rssi_threshold);
+}
/**
* mesh_plink_fsm_restart - restart a mesh peer link finite state machine
@@ -242,7 +246,7 @@ u32 mesh_plink_deactivate(struct sta_info *sta)
spin_lock_bh(&sta->lock);
changed = __mesh_plink_deactivate(sta);
- sta->reason = cpu_to_le16(WLAN_REASON_MESH_PEER_CANCELED);
+ sta->reason = WLAN_REASON_MESH_PEER_CANCELED;
mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
sta->sta.addr, sta->llid, sta->plid,
sta->reason);
@@ -253,7 +257,7 @@ u32 mesh_plink_deactivate(struct sta_info *sta)
static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
enum ieee80211_self_protected_actioncode action,
- u8 *da, __le16 llid, __le16 plid, __le16 reason)
+ u8 *da, u16 llid, u16 plid, u16 reason)
{
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
@@ -279,7 +283,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
2 + 8 + /* peering IE */
sdata->u.mesh.ie_len);
if (!skb)
- return -1;
+ return err;
info = IEEE80211_SKB_CB(skb);
skb_reserve(skb, local->tx_headroom);
mgmt = (struct ieee80211_mgmt *) skb_put(skb, hdr_len);
@@ -301,7 +305,7 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
if (action == WLAN_SP_MESH_PEERING_CONFIRM) {
/* AID */
pos = skb_put(skb, 2);
- memcpy(pos + 2, &plid, 2);
+ put_unaligned_le16(plid, pos + 2);
}
if (ieee80211_add_srates_ie(sdata, skb, true, band) ||
ieee80211_add_ext_srates_ie(sdata, skb, true, band) ||
@@ -343,14 +347,14 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
*pos++ = ie_len;
memcpy(pos, &peering_proto, 2);
pos += 2;
- memcpy(pos, &llid, 2);
+ put_unaligned_le16(llid, pos);
pos += 2;
if (include_plid) {
- memcpy(pos, &plid, 2);
+ put_unaligned_le16(plid, pos);
pos += 2;
}
if (action == WLAN_SP_MESH_PEERING_CLOSE) {
- memcpy(pos, &reason, 2);
+ put_unaligned_le16(reason, pos);
pos += 2;
}
@@ -518,7 +522,7 @@ void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata,
sta->plink_state == NL80211_PLINK_LISTEN &&
sdata->u.mesh.accepting_plinks &&
sdata->u.mesh.mshcfg.auto_open_plinks &&
- rssi_threshold_check(sta, sdata))
+ rssi_threshold_check(sdata, sta))
changed = mesh_plink_open(sta);
ieee80211_mps_frame_release(sta, elems);
@@ -530,9 +534,10 @@ out:
static void mesh_plink_timer(unsigned long data)
{
struct sta_info *sta;
- __le16 llid, plid, reason;
+ u16 reason = 0;
struct ieee80211_sub_if_data *sdata;
struct mesh_config *mshcfg;
+ enum ieee80211_self_protected_actioncode action = 0;
/*
* This STA is valid because sta_info_destroy() will
@@ -553,9 +558,6 @@ static void mesh_plink_timer(unsigned long data)
mpl_dbg(sta->sdata,
"Mesh plink timer for %pM fired on state %s\n",
sta->sta.addr, mplstates[sta->plink_state]);
- reason = 0;
- llid = sta->llid;
- plid = sta->plid;
sdata = sta->sdata;
mshcfg = &sdata->u.mesh.mshcfg;
@@ -574,33 +576,31 @@ static void mesh_plink_timer(unsigned long data)
rand % sta->plink_timeout;
++sta->plink_retries;
mod_plink_timer(sta, sta->plink_timeout);
- spin_unlock_bh(&sta->lock);
- mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN,
- sta->sta.addr, llid, 0, 0);
+ action = WLAN_SP_MESH_PEERING_OPEN;
break;
}
- reason = cpu_to_le16(WLAN_REASON_MESH_MAX_RETRIES);
+ reason = WLAN_REASON_MESH_MAX_RETRIES;
/* fall through on else */
case NL80211_PLINK_CNF_RCVD:
/* confirm timer */
if (!reason)
- reason = cpu_to_le16(WLAN_REASON_MESH_CONFIRM_TIMEOUT);
+ reason = WLAN_REASON_MESH_CONFIRM_TIMEOUT;
sta->plink_state = NL80211_PLINK_HOLDING;
mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout);
- spin_unlock_bh(&sta->lock);
- mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
- sta->sta.addr, llid, plid, reason);
+ action = WLAN_SP_MESH_PEERING_CLOSE;
break;
case NL80211_PLINK_HOLDING:
/* holding timer */
del_timer(&sta->plink_timer);
mesh_plink_fsm_restart(sta);
- spin_unlock_bh(&sta->lock);
break;
default:
- spin_unlock_bh(&sta->lock);
break;
}
+ spin_unlock_bh(&sta->lock);
+ if (action)
+ mesh_plink_frame_tx(sdata, action, sta->sta.addr,
+ sta->llid, sta->plid, reason);
}
static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)
@@ -612,9 +612,40 @@ static inline void mesh_plink_timer_set(struct sta_info *sta, int timeout)
add_timer(&sta->plink_timer);
}
+static bool llid_in_use(struct ieee80211_sub_if_data *sdata,
+ u16 llid)
+{
+ struct ieee80211_local *local = sdata->local;
+ bool in_use = false;
+ struct sta_info *sta;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(sta, &local->sta_list, list) {
+ if (!memcmp(&sta->llid, &llid, sizeof(llid))) {
+ in_use = true;
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ return in_use;
+}
+
+static u16 mesh_get_new_llid(struct ieee80211_sub_if_data *sdata)
+{
+ u16 llid;
+
+ do {
+ get_random_bytes(&llid, sizeof(llid));
+ /* for mesh PS we still only have the AID range for TIM bits */
+ llid = (llid % IEEE80211_MAX_AID) + 1;
+ } while (llid_in_use(sdata, llid));
+
+ return llid;
+}
+
u32 mesh_plink_open(struct sta_info *sta)
{
- __le16 llid;
struct ieee80211_sub_if_data *sdata = sta->sdata;
u32 changed;
@@ -622,8 +653,7 @@ u32 mesh_plink_open(struct sta_info *sta)
return 0;
spin_lock_bh(&sta->lock);
- get_random_bytes(&llid, 2);
- sta->llid = llid;
+ sta->llid = mesh_get_new_llid(sdata);
if (sta->plink_state != NL80211_PLINK_LISTEN &&
sta->plink_state != NL80211_PLINK_BLOCKED) {
spin_unlock_bh(&sta->lock);
@@ -640,7 +670,7 @@ u32 mesh_plink_open(struct sta_info *sta)
changed = ieee80211_mps_local_status_update(sdata);
mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN,
- sta->sta.addr, llid, 0, 0);
+ sta->sta.addr, sta->llid, 0, 0);
return changed;
}
@@ -656,390 +686,147 @@ u32 mesh_plink_block(struct sta_info *sta)
return changed;
}
-
-void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt, size_t len,
- struct ieee80211_rx_status *rx_status)
+static void mesh_plink_close(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta,
+ enum plink_event event)
{
struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg;
- struct ieee802_11_elems elems;
- struct sta_info *sta;
- enum plink_event event;
- enum ieee80211_self_protected_actioncode ftype;
- size_t baselen;
- bool matches_local = true;
- u8 ie_len;
- u8 *baseaddr;
- u32 changed = 0;
- __le16 plid, llid, reason;
-
- /* need action_code, aux */
- if (len < IEEE80211_MIN_ACTION_SIZE + 3)
- return;
-
- if (sdata->u.mesh.user_mpm)
- /* userspace must register for these */
- return;
-
- if (is_multicast_ether_addr(mgmt->da)) {
- mpl_dbg(sdata,
- "Mesh plink: ignore frame from multicast address\n");
- return;
- }
-
- baseaddr = mgmt->u.action.u.self_prot.variable;
- baselen = (u8 *) mgmt->u.action.u.self_prot.variable - (u8 *) mgmt;
- if (mgmt->u.action.u.self_prot.action_code ==
- WLAN_SP_MESH_PEERING_CONFIRM) {
- baseaddr += 4;
- baselen += 4;
- }
- ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems);
-
- if (!elems.peering) {
- mpl_dbg(sdata,
- "Mesh plink: missing necessary peer link ie\n");
- return;
- }
- if (elems.rsn_len &&
- sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) {
- mpl_dbg(sdata,
- "Mesh plink: can't establish link with secure peer\n");
- return;
- }
+ u16 reason = (event == CLS_ACPT) ?
+ WLAN_REASON_MESH_CLOSE : WLAN_REASON_MESH_CONFIG;
- ftype = mgmt->u.action.u.self_prot.action_code;
- ie_len = elems.peering_len;
- if ((ftype == WLAN_SP_MESH_PEERING_OPEN && ie_len != 4) ||
- (ftype == WLAN_SP_MESH_PEERING_CONFIRM && ie_len != 6) ||
- (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len != 6
- && ie_len != 8)) {
- mpl_dbg(sdata,
- "Mesh plink: incorrect plink ie length %d %d\n",
- ftype, ie_len);
- return;
- }
-
- if (ftype != WLAN_SP_MESH_PEERING_CLOSE &&
- (!elems.mesh_id || !elems.mesh_config)) {
- mpl_dbg(sdata, "Mesh plink: missing necessary ie\n");
- return;
- }
- /* Note the lines below are correct, the llid in the frame is the plid
- * from the point of view of this host.
- */
- memcpy(&plid, PLINK_GET_LLID(elems.peering), 2);
- if (ftype == WLAN_SP_MESH_PEERING_CONFIRM ||
- (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8))
- memcpy(&llid, PLINK_GET_PLID(elems.peering), 2);
-
- /* WARNING: Only for sta pointer, is dropped & re-acquired */
- rcu_read_lock();
-
- sta = sta_info_get(sdata, mgmt->sa);
- if (!sta && ftype != WLAN_SP_MESH_PEERING_OPEN) {
- mpl_dbg(sdata, "Mesh plink: cls or cnf from unknown peer\n");
- rcu_read_unlock();
- return;
- }
-
- if (ftype == WLAN_SP_MESH_PEERING_OPEN &&
- !rssi_threshold_check(sta, sdata)) {
- mpl_dbg(sdata, "Mesh plink: %pM does not meet rssi threshold\n",
- mgmt->sa);
- rcu_read_unlock();
- return;
- }
-
- if (sta && !test_sta_flag(sta, WLAN_STA_AUTH)) {
- mpl_dbg(sdata, "Mesh plink: Action frame from non-authed peer\n");
- rcu_read_unlock();
- return;
- }
+ sta->reason = reason;
+ sta->plink_state = NL80211_PLINK_HOLDING;
+ mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout);
+}
- if (sta && sta->plink_state == NL80211_PLINK_BLOCKED) {
- rcu_read_unlock();
- return;
- }
+static u32 mesh_plink_establish(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta)
+{
+ struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg;
+ u32 changed = 0;
- /* Now we will figure out the appropriate event... */
- event = PLINK_UNDEFINED;
- if (ftype != WLAN_SP_MESH_PEERING_CLOSE &&
- !mesh_matches_local(sdata, &elems)) {
- matches_local = false;
- switch (ftype) {
- case WLAN_SP_MESH_PEERING_OPEN:
- event = OPN_RJCT;
- break;
- case WLAN_SP_MESH_PEERING_CONFIRM:
- event = CNF_RJCT;
- break;
- default:
- break;
- }
- }
+ del_timer(&sta->plink_timer);
+ sta->plink_state = NL80211_PLINK_ESTAB;
+ changed |= mesh_plink_inc_estab_count(sdata);
+ changed |= mesh_set_ht_prot_mode(sdata);
+ changed |= mesh_set_short_slot_time(sdata);
+ mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n", sta->sta.addr);
+ ieee80211_mps_sta_status_update(sta);
+ changed |= ieee80211_mps_set_sta_local_pm(sta, mshcfg->power_mode);
+ return changed;
+}
- if (!sta && !matches_local) {
- rcu_read_unlock();
- reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG);
- llid = 0;
- mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
- mgmt->sa, llid, plid, reason);
- return;
- } else if (!sta) {
- /* ftype == WLAN_SP_MESH_PEERING_OPEN */
- if (!mesh_plink_free_count(sdata)) {
- mpl_dbg(sdata, "Mesh plink error: no more free plinks\n");
- rcu_read_unlock();
- return;
- }
- event = OPN_ACPT;
- } else if (matches_local) {
- switch (ftype) {
- case WLAN_SP_MESH_PEERING_OPEN:
- if (!mesh_plink_free_count(sdata) ||
- (sta->plid && sta->plid != plid))
- event = OPN_IGNR;
- else
- event = OPN_ACPT;
- break;
- case WLAN_SP_MESH_PEERING_CONFIRM:
- if (!mesh_plink_free_count(sdata) ||
- (sta->llid != llid || sta->plid != plid))
- event = CNF_IGNR;
- else
- event = CNF_ACPT;
- break;
- case WLAN_SP_MESH_PEERING_CLOSE:
- if (sta->plink_state == NL80211_PLINK_ESTAB)
- /* Do not check for llid or plid. This does not
- * follow the standard but since multiple plinks
- * per sta are not supported, it is necessary in
- * order to avoid a livelock when MP A sees an
- * establish peer link to MP B but MP B does not
- * see it. This can be caused by a timeout in
- * B's peer link establishment or B beign
- * restarted.
- */
- event = CLS_ACPT;
- else if (sta->plid != plid)
- event = CLS_IGNR;
- else if (ie_len == 7 && sta->llid != llid)
- event = CLS_IGNR;
- else
- event = CLS_ACPT;
- break;
- default:
- mpl_dbg(sdata, "Mesh plink: unknown frame subtype\n");
- rcu_read_unlock();
- return;
- }
- }
+/**
+ * mesh_plink_fsm - step @sta MPM based on @event
+ *
+ * @sdata: interface
+ * @sta: mesh neighbor
+ * @event: peering event
+ *
+ * Return: changed MBSS flags
+ */
+static u32 mesh_plink_fsm(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta, enum plink_event event)
+{
+ struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg;
+ enum ieee80211_self_protected_actioncode action = 0;
+ u32 changed = 0;
- if (event == OPN_ACPT) {
- rcu_read_unlock();
- /* allocate sta entry if necessary and update info */
- sta = mesh_sta_info_get(sdata, mgmt->sa, &elems);
- if (!sta) {
- mpl_dbg(sdata, "Mesh plink: failed to init peer!\n");
- rcu_read_unlock();
- return;
- }
- }
+ mpl_dbg(sdata, "peer %pM in state %s got event %s\n", sta->sta.addr,
+ mplstates[sta->plink_state], mplevents[event]);
- mpl_dbg(sdata, "peer %pM in state %s got event %s\n", mgmt->sa,
- mplstates[sta->plink_state], mplevents[event]);
- reason = 0;
spin_lock_bh(&sta->lock);
switch (sta->plink_state) {
- /* spin_unlock as soon as state is updated at each case */
case NL80211_PLINK_LISTEN:
switch (event) {
case CLS_ACPT:
mesh_plink_fsm_restart(sta);
- spin_unlock_bh(&sta->lock);
break;
case OPN_ACPT:
sta->plink_state = NL80211_PLINK_OPN_RCVD;
- sta->plid = plid;
- get_random_bytes(&llid, 2);
- sta->llid = llid;
+ sta->llid = mesh_get_new_llid(sdata);
mesh_plink_timer_set(sta,
mshcfg->dot11MeshRetryTimeout);
/* set the non-peer mode to active during peering */
changed |= ieee80211_mps_local_status_update(sdata);
-
- spin_unlock_bh(&sta->lock);
- mesh_plink_frame_tx(sdata,
- WLAN_SP_MESH_PEERING_OPEN,
- sta->sta.addr, llid, 0, 0);
- mesh_plink_frame_tx(sdata,
- WLAN_SP_MESH_PEERING_CONFIRM,
- sta->sta.addr, llid, plid, 0);
+ action = WLAN_SP_MESH_PEERING_OPEN;
break;
default:
- spin_unlock_bh(&sta->lock);
break;
}
break;
-
case NL80211_PLINK_OPN_SNT:
switch (event) {
case OPN_RJCT:
case CNF_RJCT:
- reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG);
case CLS_ACPT:
- if (!reason)
- reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE);
- sta->reason = reason;
- sta->plink_state = NL80211_PLINK_HOLDING;
- if (!mod_plink_timer(sta,
- mshcfg->dot11MeshHoldingTimeout))
- sta->ignore_plink_timer = true;
-
- llid = sta->llid;
- spin_unlock_bh(&sta->lock);
- mesh_plink_frame_tx(sdata,
- WLAN_SP_MESH_PEERING_CLOSE,
- sta->sta.addr, llid, plid, reason);
+ mesh_plink_close(sdata, sta, event);
+ action = WLAN_SP_MESH_PEERING_CLOSE;
break;
case OPN_ACPT:
/* retry timer is left untouched */
sta->plink_state = NL80211_PLINK_OPN_RCVD;
- sta->plid = plid;
- llid = sta->llid;
- spin_unlock_bh(&sta->lock);
- mesh_plink_frame_tx(sdata,
- WLAN_SP_MESH_PEERING_CONFIRM,
- sta->sta.addr, llid, plid, 0);
+ action = WLAN_SP_MESH_PEERING_CONFIRM;
break;
case CNF_ACPT:
sta->plink_state = NL80211_PLINK_CNF_RCVD;
if (!mod_plink_timer(sta,
mshcfg->dot11MeshConfirmTimeout))
sta->ignore_plink_timer = true;
-
- spin_unlock_bh(&sta->lock);
break;
default:
- spin_unlock_bh(&sta->lock);
break;
}
break;
-
case NL80211_PLINK_OPN_RCVD:
switch (event) {
case OPN_RJCT:
case CNF_RJCT:
- reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG);
case CLS_ACPT:
- if (!reason)
- reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE);
- sta->reason = reason;
- sta->plink_state = NL80211_PLINK_HOLDING;
- if (!mod_plink_timer(sta,
- mshcfg->dot11MeshHoldingTimeout))
- sta->ignore_plink_timer = true;
-
- llid = sta->llid;
- spin_unlock_bh(&sta->lock);
- mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
- sta->sta.addr, llid, plid, reason);
+ mesh_plink_close(sdata, sta, event);
+ action = WLAN_SP_MESH_PEERING_CLOSE;
break;
case OPN_ACPT:
- llid = sta->llid;
- spin_unlock_bh(&sta->lock);
- mesh_plink_frame_tx(sdata,
- WLAN_SP_MESH_PEERING_CONFIRM,
- sta->sta.addr, llid, plid, 0);
+ action = WLAN_SP_MESH_PEERING_CONFIRM;
break;
case CNF_ACPT:
- del_timer(&sta->plink_timer);
- sta->plink_state = NL80211_PLINK_ESTAB;
- spin_unlock_bh(&sta->lock);
- changed |= mesh_plink_inc_estab_count(sdata);
- changed |= mesh_set_ht_prot_mode(sdata);
- changed |= mesh_set_short_slot_time(sdata);
- mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n",
- sta->sta.addr);
- ieee80211_mps_sta_status_update(sta);
- changed |= ieee80211_mps_set_sta_local_pm(sta,
- mshcfg->power_mode);
+ changed |= mesh_plink_establish(sdata, sta);
break;
default:
- spin_unlock_bh(&sta->lock);
break;
}
break;
-
case NL80211_PLINK_CNF_RCVD:
switch (event) {
case OPN_RJCT:
case CNF_RJCT:
- reason = cpu_to_le16(WLAN_REASON_MESH_CONFIG);
case CLS_ACPT:
- if (!reason)
- reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE);
- sta->reason = reason;
- sta->plink_state = NL80211_PLINK_HOLDING;
- if (!mod_plink_timer(sta,
- mshcfg->dot11MeshHoldingTimeout))
- sta->ignore_plink_timer = true;
-
- llid = sta->llid;
- spin_unlock_bh(&sta->lock);
- mesh_plink_frame_tx(sdata,
- WLAN_SP_MESH_PEERING_CLOSE,
- sta->sta.addr, llid, plid, reason);
+ mesh_plink_close(sdata, sta, event);
+ action = WLAN_SP_MESH_PEERING_CLOSE;
break;
case OPN_ACPT:
- del_timer(&sta->plink_timer);
- sta->plink_state = NL80211_PLINK_ESTAB;
- spin_unlock_bh(&sta->lock);
- changed |= mesh_plink_inc_estab_count(sdata);
- changed |= mesh_set_ht_prot_mode(sdata);
- changed |= mesh_set_short_slot_time(sdata);
- mpl_dbg(sdata, "Mesh plink with %pM ESTABLISHED\n",
- sta->sta.addr);
- mesh_plink_frame_tx(sdata,
- WLAN_SP_MESH_PEERING_CONFIRM,
- sta->sta.addr, llid, plid, 0);
- ieee80211_mps_sta_status_update(sta);
- changed |= ieee80211_mps_set_sta_local_pm(sta,
- mshcfg->power_mode);
+ changed |= mesh_plink_establish(sdata, sta);
+ action = WLAN_SP_MESH_PEERING_CONFIRM;
break;
default:
- spin_unlock_bh(&sta->lock);
break;
}
break;
-
case NL80211_PLINK_ESTAB:
switch (event) {
case CLS_ACPT:
- reason = cpu_to_le16(WLAN_REASON_MESH_CLOSE);
- sta->reason = reason;
changed |= __mesh_plink_deactivate(sta);
- sta->plink_state = NL80211_PLINK_HOLDING;
- llid = sta->llid;
- mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout);
- spin_unlock_bh(&sta->lock);
changed |= mesh_set_ht_prot_mode(sdata);
changed |= mesh_set_short_slot_time(sdata);
- mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
- sta->sta.addr, llid, plid, reason);
+ mesh_plink_close(sdata, sta, event);
+ action = WLAN_SP_MESH_PEERING_CLOSE;
break;
case OPN_ACPT:
- llid = sta->llid;
- spin_unlock_bh(&sta->lock);
- mesh_plink_frame_tx(sdata,
- WLAN_SP_MESH_PEERING_CONFIRM,
- sta->sta.addr, llid, plid, 0);
+ action = WLAN_SP_MESH_PEERING_CONFIRM;
break;
default:
- spin_unlock_bh(&sta->lock);
break;
}
break;
@@ -1049,32 +836,271 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
if (del_timer(&sta->plink_timer))
sta->ignore_plink_timer = 1;
mesh_plink_fsm_restart(sta);
- spin_unlock_bh(&sta->lock);
break;
case OPN_ACPT:
case CNF_ACPT:
case OPN_RJCT:
case CNF_RJCT:
- llid = sta->llid;
- reason = sta->reason;
- spin_unlock_bh(&sta->lock);
- mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
- sta->sta.addr, llid, plid, reason);
+ action = WLAN_SP_MESH_PEERING_CLOSE;
break;
default:
- spin_unlock_bh(&sta->lock);
+ break;
}
break;
default:
/* should not get here, PLINK_BLOCKED is dealt with at the
* beginning of the function
*/
- spin_unlock_bh(&sta->lock);
break;
}
+ spin_unlock_bh(&sta->lock);
+ if (action) {
+ mesh_plink_frame_tx(sdata, action, sta->sta.addr,
+ sta->llid, sta->plid, sta->reason);
+
+ /* also send confirm in open case */
+ if (action == WLAN_SP_MESH_PEERING_OPEN) {
+ mesh_plink_frame_tx(sdata,
+ WLAN_SP_MESH_PEERING_CONFIRM,
+ sta->sta.addr, sta->llid,
+ sta->plid, 0);
+ }
+ }
+
+ return changed;
+}
+
+/*
+ * mesh_plink_get_event - get correct MPM event
+ *
+ * @sdata: interface
+ * @sta: peer, leave NULL if processing a frame from a new suitable peer
+ * @elems: peering management IEs
+ * @ftype: frame type
+ * @llid: peer's peer link ID
+ * @plid: peer's local link ID
+ *
+ * Return: new peering event for @sta, but PLINK_UNDEFINED should be treated as
+ * an error.
+ */
+static enum plink_event
+mesh_plink_get_event(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta,
+ struct ieee802_11_elems *elems,
+ enum ieee80211_self_protected_actioncode ftype,
+ u16 llid, u16 plid)
+{
+ enum plink_event event = PLINK_UNDEFINED;
+ u8 ie_len = elems->peering_len;
+ bool matches_local;
+
+ matches_local = (ftype == WLAN_SP_MESH_PEERING_CLOSE ||
+ mesh_matches_local(sdata, elems));
+
+ /* deny open request from non-matching peer */
+ if (!matches_local && !sta) {
+ event = OPN_RJCT;
+ goto out;
+ }
+
+ if (!sta) {
+ if (ftype != WLAN_SP_MESH_PEERING_OPEN) {
+ mpl_dbg(sdata, "Mesh plink: cls or cnf from unknown peer\n");
+ goto out;
+ }
+ /* ftype == WLAN_SP_MESH_PEERING_OPEN */
+ if (!mesh_plink_free_count(sdata)) {
+ mpl_dbg(sdata, "Mesh plink error: no more free plinks\n");
+ goto out;
+ }
+ } else {
+ if (!test_sta_flag(sta, WLAN_STA_AUTH)) {
+ mpl_dbg(sdata, "Mesh plink: Action frame from non-authed peer\n");
+ goto out;
+ }
+ if (sta->plink_state == NL80211_PLINK_BLOCKED)
+ goto out;
+ }
+
+ /* new matching peer */
+ if (!sta) {
+ event = OPN_ACPT;
+ goto out;
+ }
+
+ switch (ftype) {
+ case WLAN_SP_MESH_PEERING_OPEN:
+ if (!matches_local)
+ event = OPN_RJCT;
+ if (!mesh_plink_free_count(sdata) ||
+ (sta->plid && sta->plid != plid))
+ event = OPN_IGNR;
+ else
+ event = OPN_ACPT;
+ break;
+ case WLAN_SP_MESH_PEERING_CONFIRM:
+ if (!matches_local)
+ event = CNF_RJCT;
+ if (!mesh_plink_free_count(sdata) ||
+ (sta->llid != llid || sta->plid != plid))
+ event = CNF_IGNR;
+ else
+ event = CNF_ACPT;
+ break;
+ case WLAN_SP_MESH_PEERING_CLOSE:
+ if (sta->plink_state == NL80211_PLINK_ESTAB)
+ /* Do not check for llid or plid. This does not
+ * follow the standard but since multiple plinks
+ * per sta are not supported, it is necessary in
+ * order to avoid a livelock when MP A sees an
+ * establish peer link to MP B but MP B does not
+ * see it. This can be caused by a timeout in
+ * B's peer link establishment or B beign
+ * restarted.
+ */
+ event = CLS_ACPT;
+ else if (sta->plid != plid)
+ event = CLS_IGNR;
+ else if (ie_len == 8 && sta->llid != llid)
+ event = CLS_IGNR;
+ else
+ event = CLS_ACPT;
+ break;
+ default:
+ mpl_dbg(sdata, "Mesh plink: unknown frame subtype\n");
+ break;
+ }
+
+out:
+ return event;
+}
+static void
+mesh_process_plink_frame(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt,
+ struct ieee802_11_elems *elems)
+{
+
+ struct sta_info *sta;
+ enum plink_event event;
+ enum ieee80211_self_protected_actioncode ftype;
+ u32 changed = 0;
+ u8 ie_len = elems->peering_len;
+ __le16 _plid, _llid;
+ u16 plid, llid = 0;
+
+ if (!elems->peering) {
+ mpl_dbg(sdata,
+ "Mesh plink: missing necessary peer link ie\n");
+ return;
+ }
+
+ if (elems->rsn_len &&
+ sdata->u.mesh.security == IEEE80211_MESH_SEC_NONE) {
+ mpl_dbg(sdata,
+ "Mesh plink: can't establish link with secure peer\n");
+ return;
+ }
+
+ ftype = mgmt->u.action.u.self_prot.action_code;
+ if ((ftype == WLAN_SP_MESH_PEERING_OPEN && ie_len != 4) ||
+ (ftype == WLAN_SP_MESH_PEERING_CONFIRM && ie_len != 6) ||
+ (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len != 6
+ && ie_len != 8)) {
+ mpl_dbg(sdata,
+ "Mesh plink: incorrect plink ie length %d %d\n",
+ ftype, ie_len);
+ return;
+ }
+
+ if (ftype != WLAN_SP_MESH_PEERING_CLOSE &&
+ (!elems->mesh_id || !elems->mesh_config)) {
+ mpl_dbg(sdata, "Mesh plink: missing necessary ie\n");
+ return;
+ }
+ /* Note the lines below are correct, the llid in the frame is the plid
+ * from the point of view of this host.
+ */
+ memcpy(&_plid, PLINK_GET_LLID(elems->peering), sizeof(__le16));
+ plid = le16_to_cpu(_plid);
+ if (ftype == WLAN_SP_MESH_PEERING_CONFIRM ||
+ (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8)) {
+ memcpy(&_llid, PLINK_GET_PLID(elems->peering), sizeof(__le16));
+ llid = le16_to_cpu(_llid);
+ }
+
+ /* WARNING: Only for sta pointer, is dropped & re-acquired */
+ rcu_read_lock();
+
+ sta = sta_info_get(sdata, mgmt->sa);
+
+ if (ftype == WLAN_SP_MESH_PEERING_OPEN &&
+ !rssi_threshold_check(sdata, sta)) {
+ mpl_dbg(sdata, "Mesh plink: %pM does not meet rssi threshold\n",
+ mgmt->sa);
+ goto unlock_rcu;
+ }
+
+ /* Now we will figure out the appropriate event... */
+ event = mesh_plink_get_event(sdata, sta, elems, ftype, llid, plid);
+
+ if (event == OPN_ACPT) {
+ rcu_read_unlock();
+ /* allocate sta entry if necessary and update info */
+ sta = mesh_sta_info_get(sdata, mgmt->sa, elems);
+ if (!sta) {
+ mpl_dbg(sdata, "Mesh plink: failed to init peer!\n");
+ goto unlock_rcu;
+ }
+ sta->plid = plid;
+ } else if (!sta && event == OPN_RJCT) {
+ mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE,
+ mgmt->sa, 0, plid,
+ WLAN_REASON_MESH_CONFIG);
+ goto unlock_rcu;
+ } else if (!sta || event == PLINK_UNDEFINED) {
+ /* something went wrong */
+ goto unlock_rcu;
+ }
+
+ changed |= mesh_plink_fsm(sdata, sta, event);
+
+unlock_rcu:
rcu_read_unlock();
if (changed)
ieee80211_mbss_info_change_notify(sdata, changed);
}
+
+void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt, size_t len,
+ struct ieee80211_rx_status *rx_status)
+{
+ struct ieee802_11_elems elems;
+ size_t baselen;
+ u8 *baseaddr;
+
+ /* need action_code, aux */
+ if (len < IEEE80211_MIN_ACTION_SIZE + 3)
+ return;
+
+ if (sdata->u.mesh.user_mpm)
+ /* userspace must register for these */
+ return;
+
+ if (is_multicast_ether_addr(mgmt->da)) {
+ mpl_dbg(sdata,
+ "Mesh plink: ignore frame from multicast address\n");
+ return;
+ }
+
+ baseaddr = mgmt->u.action.u.self_prot.variable;
+ baselen = (u8 *) mgmt->u.action.u.self_prot.variable - (u8 *) mgmt;
+ if (mgmt->u.action.u.self_prot.action_code ==
+ WLAN_SP_MESH_PEERING_CONFIRM) {
+ baseaddr += 4;
+ baselen += 4;
+ }
+ ieee802_11_parse_elems(baseaddr, len - baselen, true, &elems);
+ mesh_process_plink_frame(sdata, mgmt, &elems);
+}
diff --git a/net/mac80211/mesh_ps.c b/net/mac80211/mesh_ps.c
index 0f79b78b5e86..2802f9d9279d 100644
--- a/net/mac80211/mesh_ps.c
+++ b/net/mac80211/mesh_ps.c
@@ -576,10 +576,9 @@ void ieee80211_mps_frame_release(struct sta_info *sta,
int ac, buffer_local = 0;
bool has_buffered = false;
- /* TIM map only for LLID <= IEEE80211_MAX_AID */
if (sta->plink_state == NL80211_PLINK_ESTAB)
has_buffered = ieee80211_check_tim(elems->tim, elems->tim_len,
- le16_to_cpu(sta->llid) % IEEE80211_MAX_AID);
+ sta->llid);
if (has_buffered)
mps_dbg(sta->sdata, "%pM indicates buffered frames\n",
diff --git a/net/mac80211/mesh_sync.c b/net/mac80211/mesh_sync.c
index 05a256b38e24..d1cf2d553499 100644
--- a/net/mac80211/mesh_sync.c
+++ b/net/mac80211/mesh_sync.c
@@ -92,12 +92,20 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
if (stype != IEEE80211_STYPE_BEACON)
return;
- /* The current tsf is a first approximation for the timestamp
- * for the received beacon. Further down we try to get a
- * better value from the rx_status->mactime field if
- * available. Also we have to call drv_get_tsf() before
- * entering the rcu-read section.*/
- t_r = drv_get_tsf(local, sdata);
+ /*
+ * Get time when timestamp field was received. If we don't
+ * have rx timestamps, then use current tsf as an approximation.
+ * drv_get_tsf() must be called before entering the rcu-read
+ * section.
+ */
+ if (ieee80211_have_rx_timestamp(rx_status))
+ t_r = ieee80211_calculate_rx_timestamp(local, rx_status,
+ 24 + 12 +
+ elems->total_len +
+ FCS_LEN,
+ 24);
+ else
+ t_r = drv_get_tsf(local, sdata);
rcu_read_lock();
sta = sta_info_get(sdata, mgmt->sa);
@@ -117,14 +125,6 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
goto no_sync;
}
- if (ieee80211_have_rx_timestamp(rx_status))
- /* time when timestamp field was received */
- t_r = ieee80211_calculate_rx_timestamp(local, rx_status,
- 24 + 12 +
- elems->total_len +
- FCS_LEN,
- 24);
-
/* Timing offset calculation (see 13.13.2.2.2) */
t_t = le64_to_cpu(mgmt->u.beacon.timestamp);
sta->t_offset = t_t - t_r;
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index b3a3ce316656..900ead344f5b 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -330,6 +330,16 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
if (WARN_ON_ONCE(!sta))
return -EINVAL;
+ /*
+ * if bss configuration changed store the new one -
+ * this may be applicable even if channel is identical
+ */
+ ht_opmode = le16_to_cpu(ht_oper->operation_mode);
+ if (sdata->vif.bss_conf.ht_operation_mode != ht_opmode) {
+ *changed |= BSS_CHANGED_HT;
+ sdata->vif.bss_conf.ht_operation_mode = ht_opmode;
+ }
+
chan = sdata->vif.bss_conf.chandef.chan;
sband = local->hw.wiphy->bands[chan->band];
@@ -416,14 +426,6 @@ static int ieee80211_config_bw(struct ieee80211_sub_if_data *sdata,
IEEE80211_RC_BW_CHANGED);
}
- ht_opmode = le16_to_cpu(ht_oper->operation_mode);
-
- /* if bss configuration changed store the new one */
- if (sdata->vif.bss_conf.ht_operation_mode != ht_opmode) {
- *changed |= BSS_CHANGED_HT;
- sdata->vif.bss_conf.ht_operation_mode = ht_opmode;
- }
-
return 0;
}
@@ -714,7 +716,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
}
/* if present, add any custom IEs that go before HT */
- if (assoc_data->ie_len && assoc_data->ie) {
+ if (assoc_data->ie_len) {
static const u8 before_ht[] = {
WLAN_EID_SSID,
WLAN_EID_SUPP_RATES,
@@ -748,7 +750,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
&assoc_data->ap_vht_cap);
/* if present, add any custom non-vendor IEs that go after HT */
- if (assoc_data->ie_len && assoc_data->ie) {
+ if (assoc_data->ie_len) {
noffset = ieee80211_ie_split_vendor(assoc_data->ie,
assoc_data->ie_len,
offset);
@@ -779,7 +781,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
}
/* add any remaining custom (i.e. vendor specific here) IEs */
- if (assoc_data->ie_len && assoc_data->ie) {
+ if (assoc_data->ie_len) {
noffset = assoc_data->ie_len;
pos = skb_put(skb, noffset - offset);
memcpy(pos, assoc_data->ie + offset, noffset - offset);
@@ -886,8 +888,7 @@ static void ieee80211_chswitch_work(struct work_struct *work)
if (!ifmgd->associated)
goto out;
- ret = ieee80211_vif_change_channel(sdata, &local->csa_chandef,
- &changed);
+ ret = ieee80211_vif_change_channel(sdata, &changed);
if (ret) {
sdata_info(sdata,
"vif channel switch failed, disconnecting\n");
@@ -897,7 +898,7 @@ static void ieee80211_chswitch_work(struct work_struct *work)
}
if (!local->use_chanctx) {
- local->_oper_chandef = local->csa_chandef;
+ local->_oper_chandef = sdata->csa_chandef;
/* Call "hw_config" only if doing sw channel switch.
* Otherwise update the channel directly
*/
@@ -908,7 +909,7 @@ static void ieee80211_chswitch_work(struct work_struct *work)
}
/* XXX: shouldn't really modify cfg80211-owned data! */
- ifmgd->associated->channel = local->csa_chandef.chan;
+ ifmgd->associated->channel = sdata->csa_chandef.chan;
/* XXX: wait for a beacon first? */
ieee80211_wake_queues_by_reason(&local->hw,
@@ -1035,7 +1036,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
}
mutex_unlock(&local->chanctx_mtx);
- local->csa_chandef = csa_ie.chandef;
+ sdata->csa_chandef = csa_ie.chandef;
if (csa_ie.mode)
ieee80211_stop_queues_by_reason(&local->hw,
@@ -1398,10 +1399,12 @@ void ieee80211_dfs_cac_timer_work(struct work_struct *work)
struct ieee80211_sub_if_data *sdata =
container_of(delayed_work, struct ieee80211_sub_if_data,
dfs_cac_timer_work);
+ struct cfg80211_chan_def chandef = sdata->vif.bss_conf.chandef;
ieee80211_vif_release_channel(sdata);
-
- cfg80211_cac_event(sdata->dev, NL80211_RADAR_CAC_FINISHED, GFP_KERNEL);
+ cfg80211_cac_event(sdata->dev, &chandef,
+ NL80211_RADAR_CAC_FINISHED,
+ GFP_KERNEL);
}
/* MLME */
@@ -1745,6 +1748,8 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
ifmgd->flags = 0;
ieee80211_vif_release_channel(sdata);
+
+ sdata->encrypt_headroom = IEEE80211_ENCRYPT_HEADROOM;
}
void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
@@ -4191,6 +4196,8 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
sdata->control_port_protocol = req->crypto.control_port_ethertype;
sdata->control_port_no_encrypt = req->crypto.control_port_no_encrypt;
+ sdata->encrypt_headroom = ieee80211_cs_headroom(local, &req->crypto,
+ sdata->vif.type);
/* kick off associate process */
diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h
index 505bc0dea074..b95e16c07081 100644
--- a/net/mac80211/rate.h
+++ b/net/mac80211/rate.h
@@ -54,6 +54,8 @@ static inline void rate_control_rate_init(struct sta_info *sta)
struct ieee80211_supported_band *sband;
struct ieee80211_chanctx_conf *chanctx_conf;
+ ieee80211_sta_set_rx_nss(sta);
+
if (!ref)
return;
@@ -67,8 +69,6 @@ static inline void rate_control_rate_init(struct sta_info *sta)
sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band];
- ieee80211_sta_set_rx_nss(sta);
-
ref->ops->rate_init(ref->priv, sband, &chanctx_conf->def, ista,
priv_sta);
rcu_read_unlock();
diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c
index 7fa1b36e6202..d2f19f7e7091 100644
--- a/net/mac80211/rc80211_minstrel.c
+++ b/net/mac80211/rc80211_minstrel.c
@@ -422,10 +422,9 @@ init_sample_table(struct minstrel_sta_info *mi)
memset(mi->sample_table, 0xff, SAMPLE_COLUMNS * mi->n_rates);
for (col = 0; col < SAMPLE_COLUMNS; col++) {
+ prandom_bytes(rnd, sizeof(rnd));
for (i = 0; i < mi->n_rates; i++) {
- get_random_bytes(rnd, sizeof(rnd));
new_idx = (i + rnd[i & 7]) % mi->n_rates;
-
while (SAMPLE_TBL(mi, new_idx, col) != 0xff)
new_idx = (new_idx + 1) % mi->n_rates;
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index 4096ff6cc24f..d2ed18d82fe1 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -135,7 +135,7 @@ minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi);
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));
}
@@ -148,7 +148,7 @@ 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 % MCS_GROUP_RATES;
+ idx = rate->idx % 8;
} else {
group = MINSTREL_CCK_GROUP;
@@ -637,8 +637,7 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
idx = mp->cck_rates[index % ARRAY_SIZE(mp->cck_rates)];
flags = 0;
} else {
- idx = index % MCS_GROUP_RATES +
- (group->streams - 1) * MCS_GROUP_RATES;
+ idx = index % MCS_GROUP_RATES + (group->streams - 1) * 8;
flags = IEEE80211_TX_RC_MCS | group->flags;
}
@@ -702,12 +701,16 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
if (!mi->sample_tries)
return -1;
- mg = &mi->groups[mi->sample_group];
+ sample_group = mi->sample_group;
+ mg = &mi->groups[sample_group];
sample_idx = sample_table[mg->column][mg->index];
+ minstrel_next_sample_idx(mi);
+
+ if (!(mg->supported & BIT(sample_idx)))
+ return -1;
+
mr = &mg->rates[sample_idx];
- sample_group = mi->sample_group;
sample_idx += sample_group * MCS_GROUP_RATES;
- minstrel_next_sample_idx(mi);
/*
* Sampling might add some overhead (RTS, no aggregation)
@@ -818,7 +821,7 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
}
rate->idx = sample_idx % MCS_GROUP_RATES +
- (sample_group->streams - 1) * MCS_GROUP_RATES;
+ (sample_group->streams - 1) * 8;
rate->flags = IEEE80211_TX_RC_MCS | sample_group->flags;
}
@@ -1053,10 +1056,9 @@ init_sample_table(void)
memset(sample_table, 0xff, sizeof(sample_table));
for (col = 0; col < SAMPLE_COLUMNS; col++) {
+ prandom_bytes(rnd, sizeof(rnd));
for (i = 0; i < MCS_GROUP_RATES; i++) {
- get_random_bytes(rnd, sizeof(rnd));
new_idx = (i + rnd[i]) % MCS_GROUP_RATES;
-
while (sample_table[col][new_idx] != 0xff)
new_idx = (new_idx + 1) % MCS_GROUP_RATES;
diff --git a/net/mac80211/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c
index df44a5ad8270..3e7d793de0c3 100644
--- a/net/mac80211/rc80211_minstrel_ht_debugfs.c
+++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c
@@ -54,8 +54,7 @@ minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
int r = bitrates[j % 4];
p += sprintf(p, " %2u.%1uM", r / 10, r % 10);
} else {
- p += sprintf(p, " MCS%-2u", (mg->streams - 1) *
- MCS_GROUP_RATES + j);
+ p += sprintf(p, " MCS%-2u", (mg->streams - 1) * 8 + j);
}
tp = mr->cur_tp / 10;
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 2b0debb0422b..2dfa75522733 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -638,6 +638,27 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb)
return le16_to_cpu(mmie->key_id);
}
+static int iwl80211_get_cs_keyid(const struct ieee80211_cipher_scheme *cs,
+ struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ __le16 fc;
+ int hdrlen;
+ u8 keyid;
+
+ fc = hdr->frame_control;
+ hdrlen = ieee80211_hdrlen(fc);
+
+ if (skb->len < hdrlen + cs->hdr_len)
+ return -EINVAL;
+
+ skb_copy_bits(skb, hdrlen + cs->key_idx_off, &keyid, 1);
+ keyid &= cs->key_idx_mask;
+ keyid >>= cs->key_idx_shift;
+
+ return keyid;
+}
+
static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
@@ -729,9 +750,7 @@ static void ieee80211_release_reorder_frames(struct ieee80211_sub_if_data *sdata
lockdep_assert_held(&tid_agg_rx->reorder_lock);
while (ieee80211_sn_less(tid_agg_rx->head_seq_num, head_seq_num)) {
- index = ieee80211_sn_sub(tid_agg_rx->head_seq_num,
- tid_agg_rx->ssn) %
- tid_agg_rx->buf_size;
+ index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;
ieee80211_release_reorder_frame(sdata, tid_agg_rx, index,
frames);
}
@@ -757,8 +776,7 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
lockdep_assert_held(&tid_agg_rx->reorder_lock);
/* release the buffer until next missing frame */
- index = ieee80211_sn_sub(tid_agg_rx->head_seq_num,
- tid_agg_rx->ssn) % tid_agg_rx->buf_size;
+ index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;
if (!tid_agg_rx->reorder_buf[index] &&
tid_agg_rx->stored_mpdu_num) {
/*
@@ -793,15 +811,11 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata,
} else while (tid_agg_rx->reorder_buf[index]) {
ieee80211_release_reorder_frame(sdata, tid_agg_rx, index,
frames);
- index = ieee80211_sn_sub(tid_agg_rx->head_seq_num,
- tid_agg_rx->ssn) %
- tid_agg_rx->buf_size;
+ index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;
}
if (tid_agg_rx->stored_mpdu_num) {
- j = index = ieee80211_sn_sub(tid_agg_rx->head_seq_num,
- tid_agg_rx->ssn) %
- tid_agg_rx->buf_size;
+ j = index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size;
for (; j != (index - 1) % tid_agg_rx->buf_size;
j = (j + 1) % tid_agg_rx->buf_size) {
@@ -861,8 +875,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata
/* Now the new frame is always in the range of the reordering buffer */
- index = ieee80211_sn_sub(mpdu_seq_num,
- tid_agg_rx->ssn) % tid_agg_rx->buf_size;
+ index = mpdu_seq_num % tid_agg_rx->buf_size;
/* check if we already stored this frame */
if (tid_agg_rx->reorder_buf[index]) {
@@ -1369,6 +1382,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
struct ieee80211_key *sta_ptk = NULL;
int mmie_keyidx = -1;
__le16 fc;
+ const struct ieee80211_cipher_scheme *cs = NULL;
/*
* Key selection 101
@@ -1406,11 +1420,19 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
/* start without a key */
rx->key = NULL;
+ fc = hdr->frame_control;
- if (rx->sta)
- sta_ptk = rcu_dereference(rx->sta->ptk);
+ if (rx->sta) {
+ int keyid = rx->sta->ptk_idx;
- fc = hdr->frame_control;
+ if (ieee80211_has_protected(fc) && rx->sta->cipher_scheme) {
+ cs = rx->sta->cipher_scheme;
+ keyid = iwl80211_get_cs_keyid(cs, rx->skb);
+ if (unlikely(keyid < 0))
+ return RX_DROP_UNUSABLE;
+ }
+ sta_ptk = rcu_dereference(rx->sta->ptk[keyid]);
+ }
if (!ieee80211_has_protected(fc))
mmie_keyidx = ieee80211_get_mmie_keyidx(rx->skb);
@@ -1472,6 +1494,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
return RX_CONTINUE;
} else {
u8 keyid;
+
/*
* The device doesn't give us the IV so we won't be
* able to look up the key. That's ok though, we
@@ -1487,15 +1510,21 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
hdrlen = ieee80211_hdrlen(fc);
- if (rx->skb->len < 8 + hdrlen)
- return RX_DROP_UNUSABLE; /* TODO: count this? */
+ if (cs) {
+ keyidx = iwl80211_get_cs_keyid(cs, rx->skb);
- /*
- * no need to call ieee80211_wep_get_keyidx,
- * it verifies a bunch of things we've done already
- */
- skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1);
- keyidx = keyid >> 6;
+ if (unlikely(keyidx < 0))
+ return RX_DROP_UNUSABLE;
+ } else {
+ if (rx->skb->len < 8 + hdrlen)
+ return RX_DROP_UNUSABLE; /* TODO: count this? */
+ /*
+ * no need to call ieee80211_wep_get_keyidx,
+ * it verifies a bunch of things we've done already
+ */
+ skb_copy_bits(rx->skb, hdrlen + 3, &keyid, 1);
+ keyidx = keyid >> 6;
+ }
/* check per-station GTK first, if multicast packet */
if (is_multicast_ether_addr(hdr->addr1) && rx->sta)
@@ -1543,11 +1572,7 @@ ieee80211_rx_h_decrypt(struct ieee80211_rx_data *rx)
result = ieee80211_crypto_aes_cmac_decrypt(rx);
break;
default:
- /*
- * We can reach here only with HW-only algorithms
- * but why didn't it decrypt the frame?!
- */
- return RX_DROP_UNUSABLE;
+ result = ieee80211_crypto_hw_decrypt(rx);
}
/* the hdr variable is invalid after the decrypt handlers */
@@ -2057,7 +2082,6 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
struct ieee80211_sub_if_data *sdata = rx->sdata;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
- __le16 reason = cpu_to_le16(WLAN_REASON_MESH_PATH_NOFORWARD);
u16 q, hdrlen;
hdr = (struct ieee80211_hdr *) skb->data;
@@ -2165,7 +2189,9 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
} else {
/* unable to resolve next hop */
mesh_path_error_tx(sdata, ifmsh->mshcfg.element_ttl,
- fwd_hdr->addr3, 0, reason, fwd_hdr->addr2);
+ fwd_hdr->addr3, 0,
+ WLAN_REASON_MESH_PATH_NOFORWARD,
+ fwd_hdr->addr2);
IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_no_route);
kfree_skb(fwd_skb);
return RX_DROP_MONITOR;
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index bcc4833d7542..4d73c46df862 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -526,7 +526,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
ieee80211_hw_config(local, 0);
if ((req->channels[0]->flags &
- IEEE80211_CHAN_PASSIVE_SCAN) ||
+ IEEE80211_CHAN_NO_IR) ||
!local->scan_req->n_ssids) {
next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
} else {
@@ -572,7 +572,7 @@ ieee80211_scan_get_channel_time(struct ieee80211_channel *chan)
* TODO: channel switching also consumes quite some time,
* add that delay as well to get a better estimation
*/
- if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
+ if (chan->flags & IEEE80211_CHAN_NO_IR)
return IEEE80211_PASSIVE_CHANNEL_TIME;
return IEEE80211_PROBE_DELAY + IEEE80211_CHANNEL_TIME;
}
@@ -696,7 +696,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_PASSIVE_SCAN ||
+ if (chan->flags & IEEE80211_CHAN_NO_IR ||
!local->scan_req->n_ssids) {
*next_delay = IEEE80211_PASSIVE_CHANNEL_TIME;
local->next_scan_state = SCAN_DECISION;
@@ -881,7 +881,7 @@ int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
struct ieee80211_channel *tmp_ch =
&local->hw.wiphy->bands[band]->channels[i];
- if (tmp_ch->flags & (IEEE80211_CHAN_NO_IBSS |
+ if (tmp_ch->flags & (IEEE80211_CHAN_NO_IR |
IEEE80211_CHAN_DISABLED))
continue;
@@ -895,7 +895,7 @@ int ieee80211_request_ibss_scan(struct ieee80211_sub_if_data *sdata,
local->int_scan_req->n_channels = n_ch;
} else {
- if (WARN_ON_ONCE(chan->flags & (IEEE80211_CHAN_NO_IBSS |
+ if (WARN_ON_ONCE(chan->flags & (IEEE80211_CHAN_NO_IR |
IEEE80211_CHAN_DISABLED)))
goto unlock;
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 1eb66e26e49d..8ed97f76c3cf 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -266,9 +266,17 @@ struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata,
*/
void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
{
+ int i;
+
if (sta->rate_ctrl)
rate_control_free_sta(sta);
+ if (sta->tx_lat) {
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++)
+ kfree(sta->tx_lat[i].bins);
+ kfree(sta->tx_lat);
+ }
+
sta_dbg(sta->sdata, "Destroyed STA %pM\n", sta->sta.addr);
kfree(sta);
@@ -333,6 +341,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
struct timespec uptime;
+ struct ieee80211_tx_latency_bin_ranges *tx_latency;
int i;
sta = kzalloc(sizeof(*sta) + local->hw.sta_data_size, gfp);
@@ -410,6 +419,31 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
}
}
+ rcu_read_lock();
+
+ tx_latency = rcu_dereference(local->tx_latency);
+ /* init stations Tx latency statistics && TID bins */
+ if (tx_latency)
+ sta->tx_lat = kzalloc(IEEE80211_NUM_TIDS *
+ sizeof(struct ieee80211_tx_latency_stat),
+ GFP_ATOMIC);
+
+ /*
+ * if Tx latency and bins are enabled and the previous allocation
+ * succeeded
+ */
+ if (tx_latency && tx_latency->n_ranges && sta->tx_lat)
+ for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
+ /* size of bins is size of the ranges +1 */
+ sta->tx_lat[i].bin_count =
+ tx_latency->n_ranges + 1;
+ sta->tx_lat[i].bins = kcalloc(sta->tx_lat[i].bin_count,
+ sizeof(u32),
+ GFP_ATOMIC);
+ }
+
+ rcu_read_unlock();
+
sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
return sta;
@@ -507,6 +541,7 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
set_sta_flag(sta, WLAN_STA_INSERTED);
+ ieee80211_recalc_min_chandef(sdata);
ieee80211_sta_debugfs_add(sta);
rate_control_add_sta_debugfs(sta);
@@ -630,8 +665,8 @@ void sta_info_recalc_tim(struct sta_info *sta)
#ifdef CONFIG_MAC80211_MESH
} else if (ieee80211_vif_is_mesh(&sta->sdata->vif)) {
ps = &sta->sdata->u.mesh.ps;
- /* TIM map only for PLID <= IEEE80211_MAX_AID */
- id = le16_to_cpu(sta->plid) % IEEE80211_MAX_AID;
+ /* TIM map only for 1 <= PLID <= IEEE80211_MAX_AID */
+ id = sta->plid % (IEEE80211_MAX_AID + 1);
#endif
} else {
return;
@@ -869,6 +904,7 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
rate_control_remove_sta_debugfs(sta);
ieee80211_sta_debugfs_remove(sta);
+ ieee80211_recalc_min_chandef(sdata);
call_rcu(&sta->rcu_head, free_sta_rcu);
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 3ef06a26b9cb..0218caf5c14a 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -220,6 +220,25 @@ struct sta_ampdu_mlme {
u8 dialog_token_allocator;
};
+/*
+ * struct ieee80211_tx_latency_stat - Tx latency statistics
+ *
+ * Measures TX latency and jitter for a station per TID.
+ *
+ * @max: worst case latency
+ * @sum: sum of all latencies
+ * @counter: amount of Tx frames sent from interface
+ * @bins: each bin counts how many frames transmitted within a certain
+ * latency range. when disabled it is NULL.
+ * @bin_count: amount of bins.
+ */
+struct ieee80211_tx_latency_stat {
+ u32 max;
+ u32 sum;
+ u32 counter;
+ u32 *bins;
+ u32 bin_count;
+};
/**
* struct sta_info - STA information
@@ -231,8 +250,10 @@ struct sta_ampdu_mlme {
* @hnext: hash table linked list pointer
* @local: pointer to the global information
* @sdata: virtual interface this station belongs to
- * @ptk: peer key negotiated with this station, if any
+ * @ptk: peer keys negotiated with this station, if any
+ * @ptk_idx: last installed peer key index
* @gtk: group keys negotiated with this station, if any
+ * @gtk_idx: last installed group key index
* @rate_ctrl: rate control algorithm reference
* @rate_ctrl_priv: rate control private per-STA pointer
* @last_tx_rate: rate used for last transmit, to report to userspace as
@@ -274,6 +295,7 @@ struct sta_ampdu_mlme {
* @tid_seq: per-TID sequence numbers for sending to this STA
* @ampdu_mlme: A-MPDU state machine state
* @timer_to_tid: identity mapping to ID timers
+ * @tx_lat: Tx latency statistics
* @llid: Local link ID
* @plid: Peer link ID
* @reason: Cancel reason on PLINK_HOLDING state
@@ -303,6 +325,7 @@ struct sta_ampdu_mlme {
* @chain_signal_avg: signal average (per chain)
* @known_smps_mode: the smps_mode the client thinks we are in. Relevant for
* AP only.
+ * @cipher_scheme: optional cipher scheme for this station
*/
struct sta_info {
/* General information, mostly static */
@@ -312,7 +335,9 @@ struct sta_info {
struct ieee80211_local *local;
struct ieee80211_sub_if_data *sdata;
struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS];
- struct ieee80211_key __rcu *ptk;
+ struct ieee80211_key __rcu *ptk[NUM_DEFAULT_KEYS];
+ u8 gtk_idx;
+ u8 ptk_idx;
struct rate_control_ref *rate_ctrl;
void *rate_ctrl_priv;
spinlock_t lock;
@@ -380,14 +405,16 @@ struct sta_info {
struct sta_ampdu_mlme ampdu_mlme;
u8 timer_to_tid[IEEE80211_NUM_TIDS];
+ struct ieee80211_tx_latency_stat *tx_lat;
+
#ifdef CONFIG_MAC80211_MESH
/*
* Mesh peer link attributes
* TODO: move to a sub-structure that is referenced with pointer?
*/
- __le16 llid;
- __le16 plid;
- __le16 reason;
+ u16 llid;
+ u16 plid;
+ u16 reason;
u8 plink_retries;
bool ignore_plink_timer;
enum nl80211_plink_state plink_state;
@@ -414,6 +441,7 @@ struct sta_info {
unsigned int beacon_loss_count;
enum ieee80211_smps_mode known_smps_mode;
+ const struct ieee80211_cipher_scheme *cipher_scheme;
/* keep last! */
struct ieee80211_sta sta;
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 52a152b01b06..1ee85c402439 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -11,6 +11,7 @@
#include <linux/export.h>
#include <linux/etherdevice.h>
+#include <linux/time.h>
#include <net/mac80211.h>
#include <asm/unaligned.h>
#include "ieee80211_i.h"
@@ -463,6 +464,77 @@ static void ieee80211_report_used_skb(struct ieee80211_local *local,
}
/*
+ * Measure Tx frame completion and removal time for Tx latency statistics
+ * calculation. A single Tx frame latency should be measured from when it
+ * is entering the Kernel until we receive Tx complete confirmation indication
+ * and remove the skb.
+ */
+static void ieee80211_tx_latency_end_msrmnt(struct ieee80211_local *local,
+ struct sk_buff *skb,
+ struct sta_info *sta,
+ struct ieee80211_hdr *hdr)
+{
+ ktime_t skb_dprt;
+ struct timespec dprt_time;
+ u32 msrmnt;
+ u16 tid;
+ u8 *qc;
+ int i, bin_range_count, bin_count;
+ u32 *bin_ranges;
+ __le16 fc;
+ struct ieee80211_tx_latency_stat *tx_lat;
+ struct ieee80211_tx_latency_bin_ranges *tx_latency;
+ ktime_t skb_arv = skb->tstamp;
+
+ tx_latency = rcu_dereference(local->tx_latency);
+
+ /* assert Tx latency stats are enabled & frame arrived when enabled */
+ if (!tx_latency || !ktime_to_ns(skb_arv))
+ return;
+
+ fc = hdr->frame_control;
+
+ if (!ieee80211_is_data(fc)) /* make sure it is a data frame */
+ return;
+
+ /* get frame tid */
+ if (ieee80211_is_data_qos(hdr->frame_control)) {
+ qc = ieee80211_get_qos_ctl(hdr);
+ tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK;
+ } else {
+ tid = 0;
+ }
+
+ tx_lat = &sta->tx_lat[tid];
+
+ ktime_get_ts(&dprt_time); /* time stamp completion time */
+ skb_dprt = ktime_set(dprt_time.tv_sec, dprt_time.tv_nsec);
+ msrmnt = ktime_to_ms(ktime_sub(skb_dprt, skb_arv));
+
+ if (tx_lat->max < msrmnt) /* update stats */
+ tx_lat->max = msrmnt;
+ tx_lat->counter++;
+ tx_lat->sum += msrmnt;
+
+ if (!tx_lat->bins) /* bins not activated */
+ return;
+
+ /* count how many Tx frames transmitted with the appropriate latency */
+ bin_range_count = tx_latency->n_ranges;
+ bin_ranges = tx_latency->ranges;
+ bin_count = tx_lat->bin_count;
+
+ for (i = 0; i < bin_range_count; i++) {
+ if (msrmnt <= bin_ranges[i]) {
+ tx_lat->bins[i]++;
+ break;
+ }
+ }
+ if (i == bin_range_count) /* msrmnt is bigger than the biggest range */
+ tx_lat->bins[i]++;
+}
+
+/*
* Use a static threshold for now, best value to be determined
* by testing ...
* Should it depend on:
@@ -620,6 +692,12 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
if (acked)
sta->last_ack_signal = info->status.ack_signal;
+
+ /*
+ * Measure frame removal for tx latency
+ * statistics calculation
+ */
+ ieee80211_tx_latency_end_msrmnt(local, skb, sta, hdr);
}
rcu_read_unlock();
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index d4cee98533fd..e9ccf22f6dd9 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -41,14 +41,31 @@
#define CHANDEF_PR_ARG __entry->control_freq, __entry->chan_width, \
__entry->center_freq1, __entry->center_freq2
+#define MIN_CHANDEF_ENTRY \
+ __field(u32, min_control_freq) \
+ __field(u32, min_chan_width) \
+ __field(u32, min_center_freq1) \
+ __field(u32, min_center_freq2)
+
+#define MIN_CHANDEF_ASSIGN(c) \
+ __entry->min_control_freq = (c)->chan ? (c)->chan->center_freq : 0; \
+ __entry->min_chan_width = (c)->width; \
+ __entry->min_center_freq1 = (c)->center_freq1; \
+ __entry->min_center_freq2 = (c)->center_freq2;
+#define MIN_CHANDEF_PR_FMT " min_control:%d MHz min_width:%d min_center: %d/%d MHz"
+#define MIN_CHANDEF_PR_ARG __entry->min_control_freq, __entry->min_chan_width, \
+ __entry->min_center_freq1, __entry->min_center_freq2
+
#define CHANCTX_ENTRY CHANDEF_ENTRY \
+ MIN_CHANDEF_ENTRY \
__field(u8, rx_chains_static) \
__field(u8, rx_chains_dynamic)
#define CHANCTX_ASSIGN CHANDEF_ASSIGN(&ctx->conf.def) \
+ MIN_CHANDEF_ASSIGN(&ctx->conf.min_def) \
__entry->rx_chains_static = ctx->conf.rx_chains_static; \
__entry->rx_chains_dynamic = ctx->conf.rx_chains_dynamic
-#define CHANCTX_PR_FMT CHANDEF_PR_FMT " chains:%d/%d"
-#define CHANCTX_PR_ARG CHANDEF_PR_ARG, \
+#define CHANCTX_PR_FMT CHANDEF_PR_FMT MIN_CHANDEF_PR_FMT " chains:%d/%d"
+#define CHANCTX_PR_ARG CHANDEF_PR_ARG, MIN_CHANDEF_PR_ARG, \
__entry->rx_chains_static, __entry->rx_chains_dynamic
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index c558b246ef00..6d59e21cdb9f 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -19,6 +19,7 @@
#include <linux/bitmap.h>
#include <linux/rcupdate.h>
#include <linux/export.h>
+#include <linux/time.h>
#include <net/net_namespace.h>
#include <net/ieee80211_radiotap.h>
#include <net/cfg80211.h>
@@ -557,7 +558,8 @@ ieee80211_tx_h_select_key(struct ieee80211_tx_data *tx)
if (unlikely(info->flags & IEEE80211_TX_INTFL_DONT_ENCRYPT))
tx->key = NULL;
- else if (tx->sta && (key = rcu_dereference(tx->sta->ptk)))
+ else if (tx->sta &&
+ (key = rcu_dereference(tx->sta->ptk[tx->sta->ptk_idx])))
tx->key = key;
else if (ieee80211_is_mgmt(hdr->frame_control) &&
is_multicast_ether_addr(hdr->addr1) &&
@@ -840,15 +842,16 @@ static int ieee80211_fragment(struct ieee80211_tx_data *tx,
rem -= fraglen;
tmp = dev_alloc_skb(local->tx_headroom +
frag_threshold +
- IEEE80211_ENCRYPT_HEADROOM +
+ tx->sdata->encrypt_headroom +
IEEE80211_ENCRYPT_TAILROOM);
if (!tmp)
return -ENOMEM;
__skb_queue_tail(&tx->skbs, tmp);
- skb_reserve(tmp, local->tx_headroom +
- IEEE80211_ENCRYPT_HEADROOM);
+ skb_reserve(tmp,
+ local->tx_headroom + tx->sdata->encrypt_headroom);
+
/* copy control information */
memcpy(tmp->cb, skb->cb, sizeof(tmp->cb));
@@ -1485,7 +1488,7 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb,
headroom = local->tx_headroom;
if (may_encrypt)
- headroom += IEEE80211_ENCRYPT_HEADROOM;
+ headroom += sdata->encrypt_headroom;
headroom -= skb_headroom(skb);
headroom = max_t(int, 0, headroom);
@@ -1724,8 +1727,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
* radar detection by itself. We can do that later by adding a
* monitor flag interfaces used for AP support.
*/
- if ((chan->flags & (IEEE80211_CHAN_NO_IBSS | IEEE80211_CHAN_RADAR |
- IEEE80211_CHAN_PASSIVE_SCAN)))
+ if ((chan->flags & (IEEE80211_CHAN_NO_IR | IEEE80211_CHAN_RADAR)))
goto fail_rcu;
ieee80211_xmit(sdata, skb, chan->band);
@@ -1740,6 +1742,26 @@ fail:
return NETDEV_TX_OK; /* meaning, we dealt with the skb */
}
+/*
+ * Measure Tx frame arrival time for Tx latency statistics calculation
+ * A single Tx frame latency should be measured from when it is entering the
+ * Kernel until we receive Tx complete confirmation indication and the skb is
+ * freed.
+ */
+static void ieee80211_tx_latency_start_msrmnt(struct ieee80211_local *local,
+ struct sk_buff *skb)
+{
+ struct timespec skb_arv;
+ struct ieee80211_tx_latency_bin_ranges *tx_latency;
+
+ tx_latency = rcu_dereference(local->tx_latency);
+ if (!tx_latency)
+ return;
+
+ ktime_get_ts(&skb_arv);
+ skb->tstamp = ktime_set(skb_arv.tv_sec, skb_arv.tv_nsec);
+}
+
/**
* ieee80211_subif_start_xmit - netif start_xmit function for Ethernet-type
* subinterfaces (wlan#, WDS, and VLAN interfaces)
@@ -1790,6 +1812,9 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
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);
@@ -2109,7 +2134,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb,
*/
if (head_need > 0 || skb_cloned(skb)) {
- head_need += IEEE80211_ENCRYPT_HEADROOM;
+ head_need += sdata->encrypt_headroom;
head_need += local->tx_headroom;
head_need = max_t(int, 0, head_need);
if (ieee80211_skb_resize(sdata, skb, head_need, true)) {
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 9f9b9bd3fd44..875e172c001c 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -1804,6 +1804,26 @@ void ieee80211_recalc_smps(struct ieee80211_sub_if_data *sdata)
mutex_unlock(&local->chanctx_mtx);
}
+void ieee80211_recalc_min_chandef(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_local *local = sdata->local;
+ struct ieee80211_chanctx_conf *chanctx_conf;
+ struct ieee80211_chanctx *chanctx;
+
+ mutex_lock(&local->chanctx_mtx);
+
+ chanctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
+ lockdep_is_held(&local->chanctx_mtx));
+
+ if (WARN_ON_ONCE(!chanctx_conf))
+ goto unlock;
+
+ chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
+ ieee80211_recalc_chanctx_min_def(local, chanctx);
+ unlock:
+ mutex_unlock(&local->chanctx_mtx);
+}
+
static bool ieee80211_id_in_list(const u8 *ids, int n_ids, u8 id)
{
int i;
@@ -2259,14 +2279,17 @@ u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local,
void ieee80211_dfs_cac_cancel(struct ieee80211_local *local)
{
struct ieee80211_sub_if_data *sdata;
+ struct cfg80211_chan_def chandef;
mutex_lock(&local->iflist_mtx);
list_for_each_entry(sdata, &local->interfaces, list) {
cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
if (sdata->wdev.cac_started) {
+ chandef = sdata->vif.bss_conf.chandef;
ieee80211_vif_release_channel(sdata);
cfg80211_cac_event(sdata->dev,
+ &chandef,
NL80211_RADAR_CAC_ABORTED,
GFP_KERNEL);
}
@@ -2445,7 +2468,6 @@ int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
if (ieee80211_vif_is_mesh(&sdata->vif)) {
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
- __le16 pre_value;
skb_put(skb, 8);
*pos++ = WLAN_EID_CHAN_SWITCH_PARAM; /* EID */
@@ -2457,11 +2479,78 @@ int ieee80211_send_action_csa(struct ieee80211_sub_if_data *sdata,
WLAN_EID_CHAN_SWITCH_PARAM_TX_RESTRICT : 0x00;
put_unaligned_le16(WLAN_REASON_MESH_CHAN, pos); /* Reason Cd */
pos += 2;
- pre_value = cpu_to_le16(ifmsh->pre_value);
- memcpy(pos, &pre_value, 2); /* Precedence Value */
+ put_unaligned_le16(ifmsh->pre_value, pos);/* Precedence Value */
pos += 2;
}
ieee80211_tx_skb(sdata, skb);
return 0;
}
+
+bool ieee80211_cs_valid(const struct ieee80211_cipher_scheme *cs)
+{
+ return !(cs == NULL || cs->cipher == 0 ||
+ cs->hdr_len < cs->pn_len + cs->pn_off ||
+ cs->hdr_len <= cs->key_idx_off ||
+ cs->key_idx_shift > 7 ||
+ cs->key_idx_mask == 0);
+}
+
+bool ieee80211_cs_list_valid(const struct ieee80211_cipher_scheme *cs, int n)
+{
+ int i;
+
+ /* Ensure we have enough iftype bitmap space for all iftype values */
+ WARN_ON((NUM_NL80211_IFTYPES / 8 + 1) > sizeof(cs[0].iftype));
+
+ for (i = 0; i < n; i++)
+ if (!ieee80211_cs_valid(&cs[i]))
+ return false;
+
+ return true;
+}
+
+const struct ieee80211_cipher_scheme *
+ieee80211_cs_get(struct ieee80211_local *local, u32 cipher,
+ enum nl80211_iftype iftype)
+{
+ const struct ieee80211_cipher_scheme *l = local->hw.cipher_schemes;
+ int n = local->hw.n_cipher_schemes;
+ int i;
+ const struct ieee80211_cipher_scheme *cs = NULL;
+
+ for (i = 0; i < n; i++) {
+ if (l[i].cipher == cipher) {
+ cs = &l[i];
+ break;
+ }
+ }
+
+ if (!cs || !(cs->iftype & BIT(iftype)))
+ return NULL;
+
+ return cs;
+}
+
+int ieee80211_cs_headroom(struct ieee80211_local *local,
+ struct cfg80211_crypto_settings *crypto,
+ enum nl80211_iftype iftype)
+{
+ const struct ieee80211_cipher_scheme *cs;
+ int headroom = IEEE80211_ENCRYPT_HEADROOM;
+ int i;
+
+ for (i = 0; i < crypto->n_ciphers_pairwise; i++) {
+ cs = ieee80211_cs_get(local, crypto->ciphers_pairwise[i],
+ iftype);
+
+ if (cs && headroom < cs->hdr_len)
+ headroom = cs->hdr_len;
+ }
+
+ cs = ieee80211_cs_get(local, crypto->cipher_group, iftype);
+ if (cs && headroom < cs->hdr_len)
+ headroom = cs->hdr_len;
+
+ return headroom;
+}
diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c
index de0112785aae..d75f35c6e1a0 100644
--- a/net/mac80211/vht.c
+++ b/net/mac80211/vht.c
@@ -182,16 +182,15 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,
IEEE80211_VHT_CAP_SHORT_GI_160);
/* remaining ones */
- if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE) {
+ if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)
vht_cap->cap |= cap_info &
(IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE |
- IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MAX);
- }
+ IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK);
if (own_cap.cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)
vht_cap->cap |= cap_info &
(IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE |
- IEEE80211_VHT_CAP_BEAMFORMEE_STS_MAX);
+ IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK);
if (own_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE)
vht_cap->cap |= cap_info &
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index d65728220763..7313d379c0d3 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -545,6 +545,106 @@ ieee80211_crypto_ccmp_decrypt(struct ieee80211_rx_data *rx)
return RX_CONTINUE;
}
+static ieee80211_tx_result
+ieee80211_crypto_cs_encrypt(struct ieee80211_tx_data *tx,
+ struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_key *key = tx->key;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ const struct ieee80211_cipher_scheme *cs = key->sta->cipher_scheme;
+ int hdrlen;
+ u8 *pos;
+
+ if (info->control.hw_key &&
+ !(info->control.hw_key->flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE)) {
+ /* hwaccel has no need for preallocated head room */
+ return TX_CONTINUE;
+ }
+
+ if (unlikely(skb_headroom(skb) < cs->hdr_len &&
+ pskb_expand_head(skb, cs->hdr_len, 0, GFP_ATOMIC)))
+ return TX_DROP;
+
+ hdrlen = ieee80211_hdrlen(hdr->frame_control);
+
+ 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;
+}
+
+static inline int ieee80211_crypto_cs_pn_compare(u8 *pn1, u8 *pn2, int len)
+{
+ int i;
+
+ /* pn is little endian */
+ for (i = len - 1; i >= 0; i--) {
+ if (pn1[i] < pn2[i])
+ return -1;
+ else if (pn1[i] > pn2[i])
+ return 1;
+ }
+
+ return 0;
+}
+
+static ieee80211_rx_result
+ieee80211_crypto_cs_decrypt(struct ieee80211_rx_data *rx)
+{
+ struct ieee80211_key *key = rx->key;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data;
+ const struct ieee80211_cipher_scheme *cs = NULL;
+ int hdrlen = ieee80211_hdrlen(hdr->frame_control);
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
+ int data_len;
+ u8 *rx_pn;
+ u8 *skb_pn;
+ u8 qos_tid;
+
+ if (!rx->sta || !rx->sta->cipher_scheme ||
+ !(status->flag & RX_FLAG_DECRYPTED))
+ return RX_DROP_UNUSABLE;
+
+ if (!ieee80211_is_data(hdr->frame_control))
+ return RX_CONTINUE;
+
+ cs = rx->sta->cipher_scheme;
+
+ data_len = rx->skb->len - hdrlen - cs->hdr_len;
+
+ if (data_len < 0)
+ return RX_DROP_UNUSABLE;
+
+ if (ieee80211_is_data_qos(hdr->frame_control))
+ qos_tid = *ieee80211_get_qos_ctl(hdr) &
+ IEEE80211_QOS_CTL_TID_MASK;
+ else
+ qos_tid = 0;
+
+ if (skb_linearize(rx->skb))
+ return RX_DROP_UNUSABLE;
+
+ hdr = (struct ieee80211_hdr *)rx->skb->data;
+
+ rx_pn = key->u.gen.rx_pn[qos_tid];
+ skb_pn = rx->skb->data + hdrlen + cs->pn_off;
+
+ if (ieee80211_crypto_cs_pn_compare(skb_pn, rx_pn, cs->pn_len) <= 0)
+ return RX_DROP_UNUSABLE;
+
+ memcpy(rx_pn, skb_pn, cs->pn_len);
+
+ /* remove security header and MIC */
+ if (pskb_trim(rx->skb, rx->skb->len - cs->mic_len))
+ return RX_DROP_UNUSABLE;
+
+ memmove(rx->skb->data + cs->hdr_len, rx->skb->data, hdrlen);
+ skb_pull(rx->skb, cs->hdr_len);
+
+ return RX_CONTINUE;
+}
static void bip_aad(struct sk_buff *skb, u8 *aad)
{
@@ -685,6 +785,7 @@ ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx)
{
struct sk_buff *skb;
struct ieee80211_tx_info *info = NULL;
+ ieee80211_tx_result res;
skb_queue_walk(&tx->skbs, skb) {
info = IEEE80211_SKB_CB(skb);
@@ -692,9 +793,24 @@ ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx)
/* handle hw-only algorithm */
if (!info->control.hw_key)
return TX_DROP;
+
+ if (tx->key->sta->cipher_scheme) {
+ res = ieee80211_crypto_cs_encrypt(tx, skb);
+ if (res != TX_CONTINUE)
+ return res;
+ }
}
ieee80211_tx_set_protected(tx);
return TX_CONTINUE;
}
+
+ieee80211_rx_result
+ieee80211_crypto_hw_decrypt(struct ieee80211_rx_data *rx)
+{
+ if (rx->sta->cipher_scheme)
+ return ieee80211_crypto_cs_decrypt(rx);
+
+ return RX_DROP_UNUSABLE;
+}
diff --git a/net/mac80211/wpa.h b/net/mac80211/wpa.h
index 07e33f899c71..62e5a12dfe0a 100644
--- a/net/mac80211/wpa.h
+++ b/net/mac80211/wpa.h
@@ -34,5 +34,7 @@ ieee80211_rx_result
ieee80211_crypto_aes_cmac_decrypt(struct ieee80211_rx_data *rx);
ieee80211_tx_result
ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx);
+ieee80211_rx_result
+ieee80211_crypto_hw_decrypt(struct ieee80211_rx_data *rx);
#endif /* WPA_H */
diff --git a/net/netfilter/ipvs/ip_vs_nfct.c b/net/netfilter/ipvs/ip_vs_nfct.c
index c8beafd401aa..d5f41514f577 100644
--- a/net/netfilter/ipvs/ip_vs_nfct.c
+++ b/net/netfilter/ipvs/ip_vs_nfct.c
@@ -19,8 +19,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*
* Authors:
diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c
index 7278145e6a68..69f78e96fdb4 100644
--- a/net/netfilter/xt_connmark.c
+++ b/net/netfilter/xt_connmark.c
@@ -17,8 +17,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/module.h>
diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c
index 647d989a01e6..7174611bd672 100644
--- a/net/netfilter/xt_osf.c
+++ b/net/netfilter/xt_osf.c
@@ -13,8 +13,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
diff --git a/net/netlabel/netlabel_addrlist.c b/net/netlabel/netlabel_addrlist.c
index 6f1701322fb6..d0a3acfa5742 100644
--- a/net/netlabel/netlabel_addrlist.c
+++ b/net/netlabel/netlabel_addrlist.c
@@ -24,8 +24,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
diff --git a/net/netlabel/netlabel_addrlist.h b/net/netlabel/netlabel_addrlist.h
index a1287ce18130..d0f38bc9af6d 100644
--- a/net/netlabel/netlabel_addrlist.h
+++ b/net/netlabel/netlabel_addrlist.h
@@ -24,8 +24,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
diff --git a/net/netlabel/netlabel_cipso_v4.c b/net/netlabel/netlabel_cipso_v4.c
index 69345cebe3a3..c2f2a53a4879 100644
--- a/net/netlabel/netlabel_cipso_v4.c
+++ b/net/netlabel/netlabel_cipso_v4.c
@@ -23,8 +23,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
diff --git a/net/netlabel/netlabel_cipso_v4.h b/net/netlabel/netlabel_cipso_v4.h
index d24d774bfd62..875826808b00 100644
--- a/net/netlabel/netlabel_cipso_v4.h
+++ b/net/netlabel/netlabel_cipso_v4.h
@@ -23,8 +23,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
diff --git a/net/netlabel/netlabel_domainhash.c b/net/netlabel/netlabel_domainhash.c
index 85d842e6e431..f0cb92f3ddaf 100644
--- a/net/netlabel/netlabel_domainhash.c
+++ b/net/netlabel/netlabel_domainhash.c
@@ -24,8 +24,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
diff --git a/net/netlabel/netlabel_domainhash.h b/net/netlabel/netlabel_domainhash.h
index b9be0eed8980..680caf4dff56 100644
--- a/net/netlabel/netlabel_domainhash.h
+++ b/net/netlabel/netlabel_domainhash.h
@@ -24,8 +24,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c
index dce1bebf7aec..3045a964f39c 100644
--- a/net/netlabel/netlabel_kapi.c
+++ b/net/netlabel/netlabel_kapi.c
@@ -23,8 +23,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
diff --git a/net/netlabel/netlabel_mgmt.c b/net/netlabel/netlabel_mgmt.c
index 8ef83ee97c6a..e66e977ef2fa 100644
--- a/net/netlabel/netlabel_mgmt.c
+++ b/net/netlabel/netlabel_mgmt.c
@@ -23,8 +23,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
diff --git a/net/netlabel/netlabel_mgmt.h b/net/netlabel/netlabel_mgmt.h
index 5a9f31ce5799..8b6e1ab62b48 100644
--- a/net/netlabel/netlabel_mgmt.h
+++ b/net/netlabel/netlabel_mgmt.h
@@ -23,8 +23,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c
index 43817d73ccf9..78a63c18779e 100644
--- a/net/netlabel/netlabel_unlabeled.c
+++ b/net/netlabel/netlabel_unlabeled.c
@@ -23,8 +23,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
diff --git a/net/netlabel/netlabel_unlabeled.h b/net/netlabel/netlabel_unlabeled.h
index 700af49022a0..3a9e5dc9511b 100644
--- a/net/netlabel/netlabel_unlabeled.h
+++ b/net/netlabel/netlabel_unlabeled.h
@@ -23,8 +23,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
diff --git a/net/netlabel/netlabel_user.c b/net/netlabel/netlabel_user.c
index 9650c4ad5f88..1e779bb7fa43 100644
--- a/net/netlabel/netlabel_user.c
+++ b/net/netlabel/netlabel_user.c
@@ -23,8 +23,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
diff --git a/net/netlabel/netlabel_user.h b/net/netlabel/netlabel_user.h
index 81969785e279..4a397cde1a48 100644
--- a/net/netlabel/netlabel_user.h
+++ b/net/netlabel/netlabel_user.h
@@ -23,8 +23,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
diff --git a/net/nfc/af_nfc.c b/net/nfc/af_nfc.c
index 9d68441e2a5a..2277276f52bc 100644
--- a/net/nfc/af_nfc.c
+++ b/net/nfc/af_nfc.c
@@ -16,9 +16,7 @@
* 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.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/nfc.h>
diff --git a/net/nfc/core.c b/net/nfc/core.c
index 872529105abc..02ab34132157 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -16,9 +16,7 @@
* 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.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
diff --git a/net/nfc/hci/command.c b/net/nfc/hci/command.c
index 64f922be9281..a9f4d2e62d8d 100644
--- a/net/nfc/hci/command.c
+++ b/net/nfc/hci/command.c
@@ -12,9 +12,7 @@
* 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.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define pr_fmt(fmt) "hci: %s: " fmt, __func__
diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c
index d07ca4c5cf8c..3b9610031baa 100644
--- a/net/nfc/hci/core.c
+++ b/net/nfc/hci/core.c
@@ -12,9 +12,7 @@
* 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.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define pr_fmt(fmt) "hci: %s: " fmt, __func__
diff --git a/net/nfc/hci/hci.h b/net/nfc/hci/hci.h
index b274d12c18ac..c3d2e2c1394c 100644
--- a/net/nfc/hci/hci.h
+++ b/net/nfc/hci/hci.h
@@ -12,9 +12,7 @@
* 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.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __LOCAL_HCI_H
diff --git a/net/nfc/hci/hcp.c b/net/nfc/hci/hcp.c
index b6b4109f2343..e9de1514656e 100644
--- a/net/nfc/hci/hcp.c
+++ b/net/nfc/hci/hcp.c
@@ -12,9 +12,7 @@
* 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.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define pr_fmt(fmt) "hci: %s: " fmt, __func__
diff --git a/net/nfc/hci/llc.c b/net/nfc/hci/llc.c
index fe5e966e5b88..a07d2b818487 100644
--- a/net/nfc/hci/llc.c
+++ b/net/nfc/hci/llc.c
@@ -13,9 +13,7 @@
* 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.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <net/nfc/llc.h>
diff --git a/net/nfc/hci/llc.h b/net/nfc/hci/llc.h
index 7be0b7f3ceb6..5dad4c57ffb3 100644
--- a/net/nfc/hci/llc.h
+++ b/net/nfc/hci/llc.h
@@ -13,9 +13,7 @@
* 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.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __LOCAL_LLC_H_
diff --git a/net/nfc/hci/llc_nop.c b/net/nfc/hci/llc_nop.c
index 87b10291b40f..d0435d5a197b 100644
--- a/net/nfc/hci/llc_nop.c
+++ b/net/nfc/hci/llc_nop.c
@@ -13,9 +13,7 @@
* 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.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/types.h>
diff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c
index 27b313befc35..719ad0ac40de 100644
--- a/net/nfc/hci/llc_shdlc.c
+++ b/net/nfc/hci/llc_shdlc.c
@@ -13,9 +13,7 @@
* 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.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define pr_fmt(fmt) "shdlc: %s: " fmt, __func__
diff --git a/net/nfc/llcp.h b/net/nfc/llcp.h
index f4d48b57ea11..de1789e3cc82 100644
--- a/net/nfc/llcp.h
+++ b/net/nfc/llcp.h
@@ -12,9 +12,7 @@
* 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.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
enum llcp_state {
diff --git a/net/nfc/llcp_commands.c b/net/nfc/llcp_commands.c
index 1017894807c0..693cd1aad582 100644
--- a/net/nfc/llcp_commands.c
+++ b/net/nfc/llcp_commands.c
@@ -12,9 +12,7 @@
* 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.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define pr_fmt(fmt) "llcp: %s: " fmt, __func__
diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c
index 81cd3416c7d4..1349074e1ffc 100644
--- a/net/nfc/llcp_core.c
+++ b/net/nfc/llcp_core.c
@@ -12,9 +12,7 @@
* 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.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define pr_fmt(fmt) "llcp: %s: " fmt, __func__
diff --git a/net/nfc/llcp_sock.c b/net/nfc/llcp_sock.c
index 824c6056bf82..69fbc8dadba7 100644
--- a/net/nfc/llcp_sock.c
+++ b/net/nfc/llcp_sock.c
@@ -12,9 +12,7 @@
* 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.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define pr_fmt(fmt) "llcp: %s: " fmt, __func__
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index b943d46a1644..f0e955e3a385 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -20,8 +20,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
diff --git a/net/nfc/nci/data.c b/net/nfc/nci/data.c
index 2a9399dd6c68..6c3aef852876 100644
--- a/net/nfc/nci/data.c
+++ b/net/nfc/nci/data.c
@@ -16,8 +16,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
diff --git a/net/nfc/nci/lib.c b/net/nfc/nci/lib.c
index 6b7fd26c68d9..ed774a2e989a 100644
--- a/net/nfc/nci/lib.c
+++ b/net/nfc/nci/lib.c
@@ -20,8 +20,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
diff --git a/net/nfc/nci/ntf.c b/net/nfc/nci/ntf.c
index b2aa98ef0927..1e905097456b 100644
--- a/net/nfc/nci/ntf.c
+++ b/net/nfc/nci/ntf.c
@@ -20,8 +20,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
diff --git a/net/nfc/nci/rsp.c b/net/nfc/nci/rsp.c
index dd072f38ad00..041de51ccdbe 100644
--- a/net/nfc/nci/rsp.c
+++ b/net/nfc/nci/rsp.c
@@ -20,8 +20,7 @@
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*
*/
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c
index a9b2342d5253..ebbf6fb88b35 100644
--- a/net/nfc/netlink.c
+++ b/net/nfc/netlink.c
@@ -16,9 +16,7 @@
* 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.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
diff --git a/net/nfc/nfc.h b/net/nfc/nfc.h
index aaf606fc1faa..9d6e74f7e6b3 100644
--- a/net/nfc/nfc.h
+++ b/net/nfc/nfc.h
@@ -16,9 +16,7 @@
* 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.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __LOCAL_NFC_H
diff --git a/net/nfc/rawsock.c b/net/nfc/rawsock.c
index 66bcd2eb5773..c27a6e86cae4 100644
--- a/net/nfc/rawsock.c
+++ b/net/nfc/rawsock.c
@@ -16,9 +16,7 @@
* 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.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
index 65cfaa816075..716b7eebfe70 100644
--- a/net/openvswitch/actions.c
+++ b/net/openvswitch/actions.c
@@ -165,7 +165,7 @@ static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh,
}
csum_replace4(&nh->check, *addr, new_addr);
- skb->rxhash = 0;
+ skb_clear_hash(skb);
*addr = new_addr;
}
@@ -199,7 +199,7 @@ static void set_ipv6_addr(struct sk_buff *skb, u8 l4_proto,
if (recalculate_csum)
update_ipv6_checksum(skb, l4_proto, addr, new_addr);
- skb->rxhash = 0;
+ skb_clear_hash(skb);
memcpy(addr, new_addr, sizeof(__be32[4]));
}
@@ -296,7 +296,7 @@ static void set_tp_port(struct sk_buff *skb, __be16 *port,
{
inet_proto_csum_replace2(check, skb, *port, new_port, 0);
*port = new_port;
- skb->rxhash = 0;
+ skb_clear_hash(skb);
}
static void set_udp_port(struct sk_buff *skb, __be16 *port, __be16 new_port)
@@ -310,7 +310,7 @@ static void set_udp_port(struct sk_buff *skb, __be16 *port, __be16 new_port)
uh->check = CSUM_MANGLED_0;
} else {
*port = new_port;
- skb->rxhash = 0;
+ skb_clear_hash(skb);
}
}
@@ -381,7 +381,7 @@ static int set_sctp(struct sk_buff *skb,
/* Carry any checksum errors through. */
sh->checksum = old_csum ^ old_correct_csum ^ new_csum;
- skb->rxhash = 0;
+ skb_clear_hash(skb);
}
return 0;
diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c
index e42542706087..0e720c316070 100644
--- a/net/openvswitch/flow_table.c
+++ b/net/openvswitch/flow_table.c
@@ -25,7 +25,7 @@
#include <linux/if_vlan.h>
#include <net/llc_pdu.h>
#include <linux/kernel.h>
-#include <linux/jhash.h>
+#include <linux/hash.h>
#include <linux/jiffies.h>
#include <linux/llc.h>
#include <linux/module.h>
@@ -362,7 +362,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 jhash2(hash_key, hash_u32s, 0);
+ return arch_fast_hash2(hash_key, hash_u32s, 0);
}
static int flow_key_start(const struct sw_flow_key *key)
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 88cfbc189558..dd3840846ce2 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -237,6 +237,48 @@ struct packet_skb_cb {
static void __fanout_unlink(struct sock *sk, struct packet_sock *po);
static void __fanout_link(struct sock *sk, struct packet_sock *po);
+static int packet_direct_xmit(struct sk_buff *skb)
+{
+ struct net_device *dev = skb->dev;
+ const struct net_device_ops *ops = dev->netdev_ops;
+ netdev_features_t features;
+ struct netdev_queue *txq;
+ u16 queue_map;
+ int ret;
+
+ if (unlikely(!netif_running(dev) ||
+ !netif_carrier_ok(dev))) {
+ kfree_skb(skb);
+ return NET_XMIT_DROP;
+ }
+
+ features = netif_skb_features(skb);
+ if (skb_needs_linearize(skb, features) &&
+ __skb_linearize(skb)) {
+ kfree_skb(skb);
+ return NET_XMIT_DROP;
+ }
+
+ queue_map = skb_get_queue_mapping(skb);
+ txq = netdev_get_tx_queue(dev, queue_map);
+
+ __netif_tx_lock_bh(txq);
+ if (unlikely(netif_xmit_frozen_or_stopped(txq))) {
+ ret = NETDEV_TX_BUSY;
+ kfree_skb(skb);
+ goto out;
+ }
+
+ ret = ops->ndo_start_xmit(skb, dev);
+ if (likely(dev_xmit_complete(ret)))
+ txq_trans_update(txq);
+ else
+ kfree_skb(skb);
+out:
+ __netif_tx_unlock_bh(txq);
+ return ret;
+}
+
static struct net_device *packet_cached_dev_get(struct packet_sock *po)
{
struct net_device *dev;
@@ -261,6 +303,16 @@ static void packet_cached_dev_reset(struct packet_sock *po)
RCU_INIT_POINTER(po->cached_dev, NULL);
}
+static bool packet_use_direct_xmit(const struct packet_sock *po)
+{
+ return po->xmit == packet_direct_xmit;
+}
+
+static u16 packet_pick_tx_queue(struct net_device *dev)
+{
+ return (u16) raw_smp_processor_id() % dev->real_num_tx_queues;
+}
+
/* register_prot_hook must be invoked with the po->bind_lock held,
* or from a context in which asynchronous accesses to the packet
* socket is not possible (packet_create()).
@@ -458,7 +510,8 @@ static void prb_shutdown_retire_blk_timer(struct packet_sock *po,
{
struct tpacket_kbdq_core *pkc;
- pkc = tx_ring ? &po->tx_ring.prb_bdqc : &po->rx_ring.prb_bdqc;
+ pkc = tx_ring ? GET_PBDQC_FROM_RB(&po->tx_ring) :
+ GET_PBDQC_FROM_RB(&po->rx_ring);
spin_lock_bh(&rb_queue->lock);
pkc->delete_blk_timer = 1;
@@ -484,7 +537,8 @@ static void prb_setup_retire_blk_timer(struct packet_sock *po, int tx_ring)
if (tx_ring)
BUG();
- pkc = tx_ring ? &po->tx_ring.prb_bdqc : &po->rx_ring.prb_bdqc;
+ pkc = tx_ring ? GET_PBDQC_FROM_RB(&po->tx_ring) :
+ GET_PBDQC_FROM_RB(&po->rx_ring);
prb_init_blk_timer(po, pkc, prb_retire_rx_blk_timer_expired);
}
@@ -542,7 +596,7 @@ static void init_prb_bdqc(struct packet_sock *po,
struct pgv *pg_vec,
union tpacket_req_u *req_u, int tx_ring)
{
- struct tpacket_kbdq_core *p1 = &rb->prb_bdqc;
+ struct tpacket_kbdq_core *p1 = GET_PBDQC_FROM_RB(rb);
struct tpacket_block_desc *pbd;
memset(p1, 0x0, sizeof(*p1));
@@ -606,7 +660,7 @@ static void _prb_refresh_rx_retire_blk_timer(struct tpacket_kbdq_core *pkc)
static void prb_retire_rx_blk_timer_expired(unsigned long data)
{
struct packet_sock *po = (struct packet_sock *)data;
- struct tpacket_kbdq_core *pkc = &po->rx_ring.prb_bdqc;
+ struct tpacket_kbdq_core *pkc = GET_PBDQC_FROM_RB(&po->rx_ring);
unsigned int frozen;
struct tpacket_block_desc *pbd;
@@ -909,7 +963,7 @@ static void prb_clear_blk_fill_status(struct packet_ring_buffer *rb)
static void prb_fill_rxhash(struct tpacket_kbdq_core *pkc,
struct tpacket3_hdr *ppd)
{
- ppd->hv1.tp_rxhash = skb_get_rxhash(pkc->skb);
+ ppd->hv1.tp_rxhash = skb_get_hash(pkc->skb);
}
static void prb_clear_rxhash(struct tpacket_kbdq_core *pkc,
@@ -923,9 +977,11 @@ static void prb_fill_vlan_info(struct tpacket_kbdq_core *pkc,
{
if (vlan_tx_tag_present(pkc->skb)) {
ppd->hv1.tp_vlan_tci = vlan_tx_tag_get(pkc->skb);
- ppd->tp_status = TP_STATUS_VLAN_VALID;
+ ppd->hv1.tp_vlan_tpid = ntohs(pkc->skb->vlan_proto);
+ ppd->tp_status = TP_STATUS_VLAN_VALID | TP_STATUS_VLAN_TPID_VALID;
} else {
ppd->hv1.tp_vlan_tci = 0;
+ ppd->hv1.tp_vlan_tpid = 0;
ppd->tp_status = TP_STATUS_AVAILABLE;
}
}
@@ -933,6 +989,7 @@ static void prb_fill_vlan_info(struct tpacket_kbdq_core *pkc,
static void prb_run_all_ft_ops(struct tpacket_kbdq_core *pkc,
struct tpacket3_hdr *ppd)
{
+ ppd->hv1.tp_padding = 0;
prb_fill_vlan_info(pkc, ppd);
if (pkc->feature_req_word & TP_FT_REQ_FILL_RXHASH)
@@ -1241,7 +1298,7 @@ static int packet_rcv_fanout(struct sk_buff *skb, struct net_device *dev,
if (!skb)
return 0;
}
- skb_get_rxhash(skb);
+ skb_get_hash(skb);
idx = fanout_demux_hash(f, skb, num);
break;
case PACKET_FANOUT_LB:
@@ -1758,6 +1815,13 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
struct timespec ts;
__u32 ts_status;
+ /* struct tpacket{2,3}_hdr is aligned to a multiple of TPACKET_ALIGNMENT.
+ * We may add members to them until current aligned size without forcing
+ * userspace to call getsockopt(..., PACKET_HDRLEN, ...).
+ */
+ BUILD_BUG_ON(TPACKET_ALIGN(sizeof(*h.h2)) != 32);
+ BUILD_BUG_ON(TPACKET_ALIGN(sizeof(*h.h3)) != 48);
+
if (skb->pkt_type == PACKET_LOOPBACK)
goto drop;
@@ -1864,11 +1928,13 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
h.h2->tp_nsec = ts.tv_nsec;
if (vlan_tx_tag_present(skb)) {
h.h2->tp_vlan_tci = vlan_tx_tag_get(skb);
- status |= TP_STATUS_VLAN_VALID;
+ h.h2->tp_vlan_tpid = ntohs(skb->vlan_proto);
+ status |= TP_STATUS_VLAN_VALID | TP_STATUS_VLAN_TPID_VALID;
} else {
h.h2->tp_vlan_tci = 0;
+ h.h2->tp_vlan_tpid = 0;
}
- h.h2->tp_padding = 0;
+ memset(h.h2->tp_padding, 0, sizeof(h.h2->tp_padding));
hdrlen = sizeof(*h.h2);
break;
case TPACKET_V3:
@@ -1882,6 +1948,7 @@ static int tpacket_rcv(struct sk_buff *skb, struct net_device *dev,
h.h3->tp_net = netoff;
h.h3->tp_sec = ts.tv_sec;
h.h3->tp_nsec = ts.tv_nsec;
+ memset(h.h3->tp_padding, 0, sizeof(h.h3->tp_padding));
hdrlen = sizeof(*h.h3);
break;
default:
@@ -1992,9 +2059,10 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
skb_reserve(skb, hlen);
skb_reset_network_header(skb);
- skb_probe_transport_header(skb, 0);
- if (po->tp_tx_has_off) {
+ if (!packet_use_direct_xmit(po))
+ skb_probe_transport_header(skb, 0);
+ if (unlikely(po->tp_tx_has_off)) {
int off_min, off_max, off;
off_min = po->tp_hdrlen - sizeof(struct sockaddr_ll);
off_max = po->tx_ring.frame_size - tp_len;
@@ -2164,12 +2232,13 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
}
}
+ skb_set_queue_mapping(skb, packet_pick_tx_queue(dev));
skb->destructor = tpacket_destruct_skb;
__packet_set_status(po, ph, TP_STATUS_SENDING);
atomic_inc(&po->tx_ring.pending);
status = TP_STATUS_SEND_REQUEST;
- err = dev_queue_xmit(skb);
+ err = po->xmit(skb);
if (unlikely(err > 0)) {
err = net_xmit_errno(err);
if (err && __packet_get_status(po, ph) ==
@@ -2228,8 +2297,7 @@ static struct sk_buff *packet_alloc_skb(struct sock *sk, size_t prepad,
return skb;
}
-static int packet_snd(struct socket *sock,
- struct msghdr *msg, size_t len)
+static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
{
struct sock *sk = sock->sk;
struct sockaddr_ll *saddr = (struct sockaddr_ll *)msg->msg_name;
@@ -2374,6 +2442,7 @@ static int packet_snd(struct socket *sock,
skb->dev = dev;
skb->priority = sk->sk_priority;
skb->mark = sk->sk_mark;
+ skb_set_queue_mapping(skb, packet_pick_tx_queue(dev));
if (po->has_vnet_hdr) {
if (vnet_hdr.flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) {
@@ -2394,16 +2463,12 @@ static int packet_snd(struct socket *sock,
len += vnet_hdr_len;
}
- skb_probe_transport_header(skb, reserve);
-
+ if (!packet_use_direct_xmit(po))
+ skb_probe_transport_header(skb, reserve);
if (unlikely(extra_len == 4))
skb->no_fcs = 1;
- /*
- * Now send it
- */
-
- err = dev_queue_xmit(skb);
+ err = po->xmit(skb);
if (err > 0 && (err = net_xmit_errno(err)) != 0)
goto out_unlock;
@@ -2425,6 +2490,7 @@ static int packet_sendmsg(struct kiocb *iocb, struct socket *sock,
{
struct sock *sk = sock->sk;
struct packet_sock *po = pkt_sk(sk);
+
if (po->tx_ring.pg_vec)
return tpacket_snd(po, msg);
else
@@ -2639,6 +2705,7 @@ static int packet_create(struct net *net, struct socket *sock, int protocol,
po = pkt_sk(sk);
sk->sk_family = PF_PACKET;
po->num = proto;
+ po->xmit = dev_queue_xmit;
packet_cached_dev_reset(po);
@@ -2813,11 +2880,12 @@ static int packet_recvmsg(struct kiocb *iocb, struct socket *sock,
aux.tp_net = skb_network_offset(skb);
if (vlan_tx_tag_present(skb)) {
aux.tp_vlan_tci = vlan_tx_tag_get(skb);
- aux.tp_status |= TP_STATUS_VLAN_VALID;
+ aux.tp_vlan_tpid = ntohs(skb->vlan_proto);
+ aux.tp_status |= TP_STATUS_VLAN_VALID | TP_STATUS_VLAN_TPID_VALID;
} else {
aux.tp_vlan_tci = 0;
+ aux.tp_vlan_tpid = 0;
}
- aux.tp_padding = 0;
put_cmsg(msg, SOL_PACKET, PACKET_AUXDATA, sizeof(aux), &aux);
}
@@ -3218,6 +3286,18 @@ packet_setsockopt(struct socket *sock, int level, int optname, char __user *optv
po->tp_tx_has_off = !!val;
return 0;
}
+ case PACKET_QDISC_BYPASS:
+ {
+ int val;
+
+ if (optlen != sizeof(val))
+ return -EINVAL;
+ if (copy_from_user(&val, optval, sizeof(val)))
+ return -EFAULT;
+
+ po->xmit = val ? packet_direct_xmit : dev_queue_xmit;
+ return 0;
+ }
default:
return -ENOPROTOOPT;
}
@@ -3310,6 +3390,9 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
case PACKET_TX_HAS_OFF:
val = po->tp_tx_has_off;
break;
+ case PACKET_QDISC_BYPASS:
+ val = packet_use_direct_xmit(po);
+ break;
default:
return -ENOPROTOOPT;
}
diff --git a/net/packet/internal.h b/net/packet/internal.h
index 1035fa2d909c..0a87d7b36c9e 100644
--- a/net/packet/internal.h
+++ b/net/packet/internal.h
@@ -114,6 +114,7 @@ struct packet_sock {
unsigned int tp_tx_has_off:1;
unsigned int tp_tstamp;
struct net_device __rcu *cached_dev;
+ int (*xmit)(struct sk_buff *skb);
struct packet_type prot_hook ____cacheline_aligned_in_smp;
};
diff --git a/net/rfkill/core.c b/net/rfkill/core.c
index 1bacc1079942..ed7e0b4e7f90 100644
--- a/net/rfkill/core.c
+++ b/net/rfkill/core.c
@@ -14,9 +14,7 @@
* 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.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/kernel.h>
diff --git a/net/sched/act_api.c b/net/sched/act_api.c
index 69cb848e8345..8114fef308d9 100644
--- a/net/sched/act_api.c
+++ b/net/sched/act_api.c
@@ -29,25 +29,16 @@
void tcf_hash_destroy(struct tcf_common *p, struct tcf_hashinfo *hinfo)
{
- unsigned int h = tcf_hash(p->tcfc_index, hinfo->hmask);
- struct tcf_common **p1p;
-
- for (p1p = &hinfo->htab[h]; *p1p; p1p = &(*p1p)->tcfc_next) {
- if (*p1p == p) {
- write_lock_bh(hinfo->lock);
- *p1p = p->tcfc_next;
- write_unlock_bh(hinfo->lock);
- gen_kill_estimator(&p->tcfc_bstats,
- &p->tcfc_rate_est);
- /*
- * gen_estimator est_timer() might access p->tcfc_lock
- * or bstats, wait a RCU grace period before freeing p
- */
- kfree_rcu(p, tcfc_rcu);
- return;
- }
- }
- WARN_ON(1);
+ spin_lock_bh(&hinfo->lock);
+ hlist_del(&p->tcfc_head);
+ spin_unlock_bh(&hinfo->lock);
+ gen_kill_estimator(&p->tcfc_bstats,
+ &p->tcfc_rate_est);
+ /*
+ * gen_estimator est_timer() might access p->tcfc_lock
+ * or bstats, wait a RCU grace period before freeing p
+ */
+ kfree_rcu(p, tcfc_rcu);
}
EXPORT_SYMBOL(tcf_hash_destroy);
@@ -73,18 +64,19 @@ EXPORT_SYMBOL(tcf_hash_release);
static int tcf_dump_walker(struct sk_buff *skb, struct netlink_callback *cb,
struct tc_action *a, struct tcf_hashinfo *hinfo)
{
+ struct hlist_head *head;
struct tcf_common *p;
int err = 0, index = -1, i = 0, s_i = 0, n_i = 0;
struct nlattr *nest;
- read_lock_bh(hinfo->lock);
+ spin_lock_bh(&hinfo->lock);
s_i = cb->args[0];
for (i = 0; i < (hinfo->hmask + 1); i++) {
- p = hinfo->htab[tcf_hash(i, hinfo->hmask)];
+ head = &hinfo->htab[tcf_hash(i, hinfo->hmask)];
- for (; p; p = p->tcfc_next) {
+ hlist_for_each_entry_rcu(p, head, tcfc_head) {
index++;
if (index < s_i)
continue;
@@ -107,7 +99,7 @@ static int tcf_dump_walker(struct sk_buff *skb, struct netlink_callback *cb,
}
}
done:
- read_unlock_bh(hinfo->lock);
+ spin_unlock_bh(&hinfo->lock);
if (n_i)
cb->args[0] += n_i;
return n_i;
@@ -120,7 +112,9 @@ nla_put_failure:
static int tcf_del_walker(struct sk_buff *skb, struct tc_action *a,
struct tcf_hashinfo *hinfo)
{
- struct tcf_common *p, *s_p;
+ struct hlist_head *head;
+ struct hlist_node *n;
+ struct tcf_common *p;
struct nlattr *nest;
int i = 0, n_i = 0;
@@ -130,14 +124,11 @@ static int tcf_del_walker(struct sk_buff *skb, struct tc_action *a,
if (nla_put_string(skb, TCA_KIND, a->ops->kind))
goto nla_put_failure;
for (i = 0; i < (hinfo->hmask + 1); i++) {
- p = hinfo->htab[tcf_hash(i, hinfo->hmask)];
-
- while (p != NULL) {
- s_p = p->tcfc_next;
+ head = &hinfo->htab[tcf_hash(i, hinfo->hmask)];
+ hlist_for_each_entry_safe(p, n, head, tcfc_head) {
if (ACT_P_DELETED == tcf_hash_release(p, 0, hinfo))
module_put(a->ops->owner);
n_i++;
- p = s_p;
}
}
if (nla_put_u32(skb, TCA_FCNT, n_i))
@@ -168,15 +159,15 @@ EXPORT_SYMBOL(tcf_generic_walker);
struct tcf_common *tcf_hash_lookup(u32 index, struct tcf_hashinfo *hinfo)
{
- struct tcf_common *p;
+ struct tcf_common *p = NULL;
+ struct hlist_head *head;
- read_lock_bh(hinfo->lock);
- for (p = hinfo->htab[tcf_hash(index, hinfo->hmask)]; p;
- p = p->tcfc_next) {
+ spin_lock_bh(&hinfo->lock);
+ head = &hinfo->htab[tcf_hash(index, hinfo->hmask)];
+ hlist_for_each_entry_rcu(p, head, tcfc_head)
if (p->tcfc_index == index)
break;
- }
- read_unlock_bh(hinfo->lock);
+ spin_unlock_bh(&hinfo->lock);
return p;
}
@@ -191,7 +182,8 @@ u32 tcf_hash_new_index(u32 *idx_gen, struct tcf_hashinfo *hinfo)
val = 1;
} while (tcf_hash_lookup(val, hinfo));
- return (*idx_gen = val);
+ *idx_gen = val;
+ return val;
}
EXPORT_SYMBOL(tcf_hash_new_index);
@@ -235,6 +227,7 @@ struct tcf_common *tcf_hash_create(u32 index, struct nlattr *est,
p->tcfc_bindcnt = 1;
spin_lock_init(&p->tcfc_lock);
+ INIT_HLIST_NODE(&p->tcfc_head);
p->tcfc_index = index ? index : tcf_hash_new_index(idx_gen, hinfo);
p->tcfc_tm.install = jiffies;
p->tcfc_tm.lastuse = jiffies;
@@ -256,19 +249,18 @@ void tcf_hash_insert(struct tcf_common *p, struct tcf_hashinfo *hinfo)
{
unsigned int h = tcf_hash(p->tcfc_index, hinfo->hmask);
- write_lock_bh(hinfo->lock);
- p->tcfc_next = hinfo->htab[h];
- hinfo->htab[h] = p;
- write_unlock_bh(hinfo->lock);
+ spin_lock_bh(&hinfo->lock);
+ hlist_add_head(&p->tcfc_head, &hinfo->htab[h]);
+ spin_unlock_bh(&hinfo->lock);
}
EXPORT_SYMBOL(tcf_hash_insert);
-static struct tc_action_ops *act_base = NULL;
+static LIST_HEAD(act_base);
static DEFINE_RWLOCK(act_mod_lock);
int tcf_register_action(struct tc_action_ops *act)
{
- struct tc_action_ops *a, **ap;
+ struct tc_action_ops *a;
/* Must supply act, dump, cleanup and init */
if (!act->act || !act->dump || !act->cleanup || !act->init)
@@ -281,14 +273,13 @@ int tcf_register_action(struct tc_action_ops *act)
act->walk = tcf_generic_walker;
write_lock(&act_mod_lock);
- for (ap = &act_base; (a = *ap) != NULL; ap = &a->next) {
+ list_for_each_entry(a, &act_base, head) {
if (act->type == a->type || (strcmp(act->kind, a->kind) == 0)) {
write_unlock(&act_mod_lock);
return -EEXIST;
}
}
- act->next = NULL;
- *ap = act;
+ list_add_tail(&act->head, &act_base);
write_unlock(&act_mod_lock);
return 0;
}
@@ -296,16 +287,15 @@ EXPORT_SYMBOL(tcf_register_action);
int tcf_unregister_action(struct tc_action_ops *act)
{
- struct tc_action_ops *a, **ap;
+ struct tc_action_ops *a;
int err = -ENOENT;
write_lock(&act_mod_lock);
- for (ap = &act_base; (a = *ap) != NULL; ap = &a->next)
+ list_for_each_entry(a, &act_base, head)
if (a == act)
break;
if (a) {
- *ap = a->next;
- a->next = NULL;
+ list_del(&act->head);
err = 0;
}
write_unlock(&act_mod_lock);
@@ -320,7 +310,7 @@ static struct tc_action_ops *tc_lookup_action_n(char *kind)
if (kind) {
read_lock(&act_mod_lock);
- for (a = act_base; a; a = a->next) {
+ list_for_each_entry(a, &act_base, head) {
if (strcmp(kind, a->kind) == 0) {
if (!try_module_get(a->owner)) {
read_unlock(&act_mod_lock);
@@ -341,7 +331,7 @@ static struct tc_action_ops *tc_lookup_action(struct nlattr *kind)
if (kind) {
read_lock(&act_mod_lock);
- for (a = act_base; a; a = a->next) {
+ list_for_each_entry(a, &act_base, head) {
if (nla_strcmp(kind, a->kind) == 0) {
if (!try_module_get(a->owner)) {
read_unlock(&act_mod_lock);
@@ -378,7 +368,7 @@ static struct tc_action_ops *tc_lookup_action_id(u32 type)
}
#endif
-int tcf_action_exec(struct sk_buff *skb, const struct tc_action *act,
+int tcf_action_exec(struct sk_buff *skb, const struct list_head *actions,
struct tcf_result *res)
{
const struct tc_action *a;
@@ -389,7 +379,7 @@ int tcf_action_exec(struct sk_buff *skb, const struct tc_action *act,
ret = TC_ACT_OK;
goto exec_done;
}
- while ((a = act) != NULL) {
+ list_for_each_entry(a, actions, list) {
repeat:
if (a->ops) {
ret = a->ops->act(skb, a, res);
@@ -403,27 +393,26 @@ repeat:
if (ret != TC_ACT_PIPE)
goto exec_done;
}
- act = a->next;
}
exec_done:
return ret;
}
EXPORT_SYMBOL(tcf_action_exec);
-void tcf_action_destroy(struct tc_action *act, int bind)
+void tcf_action_destroy(struct list_head *actions, int bind)
{
- struct tc_action *a;
+ struct tc_action *a, *tmp;
- for (a = act; a; a = act) {
+ list_for_each_entry_safe(a, tmp, actions, list) {
if (a->ops) {
if (a->ops->cleanup(a, bind) == ACT_P_DELETED)
module_put(a->ops->owner);
- act = act->next;
+ list_del(&a->list);
kfree(a);
} else {
/*FIXME: Remove later - catch insertion bugs*/
WARN(1, "tcf_action_destroy: BUG? destroying NULL ops\n");
- act = act->next;
+ list_del(&a->list);
kfree(a);
}
}
@@ -469,14 +458,13 @@ nla_put_failure:
EXPORT_SYMBOL(tcf_action_dump_1);
int
-tcf_action_dump(struct sk_buff *skb, struct tc_action *act, int bind, int ref)
+tcf_action_dump(struct sk_buff *skb, struct list_head *actions, int bind, int ref)
{
struct tc_action *a;
int err = -EINVAL;
struct nlattr *nest;
- while ((a = act) != NULL) {
- act = a->next;
+ list_for_each_entry(a, actions, list) {
nest = nla_nest_start(skb, a->order);
if (nest == NULL)
goto nla_put_failure;
@@ -551,6 +539,7 @@ struct tc_action *tcf_action_init_1(struct net *net, struct nlattr *nla,
if (a == NULL)
goto err_mod;
+ INIT_LIST_HEAD(&a->list);
/* backward compatibility for policer */
if (name == NULL)
err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, a, ovr, bind);
@@ -577,37 +566,33 @@ err_out:
return ERR_PTR(err);
}
-struct tc_action *tcf_action_init(struct net *net, struct nlattr *nla,
+int tcf_action_init(struct net *net, struct nlattr *nla,
struct nlattr *est, char *name, int ovr,
- int bind)
+ int bind, struct list_head *actions)
{
struct nlattr *tb[TCA_ACT_MAX_PRIO + 1];
- struct tc_action *head = NULL, *act, *act_prev = NULL;
+ struct tc_action *act;
int err;
int i;
err = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL);
if (err < 0)
- return ERR_PTR(err);
+ return err;
for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) {
act = tcf_action_init_1(net, tb[i], est, name, ovr, bind);
- if (IS_ERR(act))
+ if (IS_ERR(act)) {
+ err = PTR_ERR(act);
goto err;
+ }
act->order = i;
-
- if (head == NULL)
- head = act;
- else
- act_prev->next = act;
- act_prev = act;
+ list_add_tail(&act->list, actions);
}
- return head;
+ return 0;
err:
- if (head != NULL)
- tcf_action_destroy(head, bind);
- return act;
+ tcf_action_destroy(actions, bind);
+ return err;
}
int tcf_action_copy_stats(struct sk_buff *skb, struct tc_action *a,
@@ -636,10 +621,6 @@ int tcf_action_copy_stats(struct sk_buff *skb, struct tc_action *a,
if (err < 0)
goto errout;
- if (a->ops != NULL && a->ops->get_stats != NULL)
- if (a->ops->get_stats(skb, a) < 0)
- goto errout;
-
if (gnet_stats_copy_basic(&d, &h->tcf_bstats) < 0 ||
gnet_stats_copy_rate_est(&d, &h->tcf_bstats,
&h->tcf_rate_est) < 0 ||
@@ -656,7 +637,7 @@ errout:
}
static int
-tca_get_fill(struct sk_buff *skb, struct tc_action *a, u32 portid, u32 seq,
+tca_get_fill(struct sk_buff *skb, struct list_head *actions, u32 portid, u32 seq,
u16 flags, int event, int bind, int ref)
{
struct tcamsg *t;
@@ -676,7 +657,7 @@ tca_get_fill(struct sk_buff *skb, struct tc_action *a, u32 portid, u32 seq,
if (nest == NULL)
goto out_nlmsg_trim;
- if (tcf_action_dump(skb, a, bind, ref) < 0)
+ if (tcf_action_dump(skb, actions, bind, ref) < 0)
goto out_nlmsg_trim;
nla_nest_end(skb, nest);
@@ -691,14 +672,14 @@ out_nlmsg_trim:
static int
act_get_notify(struct net *net, u32 portid, struct nlmsghdr *n,
- struct tc_action *a, int event)
+ struct list_head *actions, int event)
{
struct sk_buff *skb;
skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
if (!skb)
return -ENOBUFS;
- if (tca_get_fill(skb, a, portid, n->nlmsg_seq, 0, event, 0, 0) <= 0) {
+ if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, event, 0, 0) <= 0) {
kfree_skb(skb);
return -EINVAL;
}
@@ -729,6 +710,7 @@ tcf_action_get_1(struct nlattr *nla, struct nlmsghdr *n, u32 portid)
if (a == NULL)
goto err_out;
+ INIT_LIST_HEAD(&a->list);
err = -EINVAL;
a->ops = tc_lookup_action(tb[TCA_ACT_KIND]);
if (a->ops == NULL)
@@ -748,12 +730,12 @@ err_out:
return ERR_PTR(err);
}
-static void cleanup_a(struct tc_action *act)
+static void cleanup_a(struct list_head *actions)
{
- struct tc_action *a;
+ struct tc_action *a, *tmp;
- for (a = act; a; a = act) {
- act = a->next;
+ list_for_each_entry_safe(a, tmp, actions, list) {
+ list_del(&a->list);
kfree(a);
}
}
@@ -768,6 +750,7 @@ static struct tc_action *create_a(int i)
return NULL;
}
act->order = i;
+ INIT_LIST_HEAD(&act->list);
return act;
}
@@ -855,7 +838,8 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
{
int i, ret;
struct nlattr *tb[TCA_ACT_MAX_PRIO + 1];
- struct tc_action *head = NULL, *act, *act_prev = NULL;
+ struct tc_action *act;
+ LIST_HEAD(actions);
ret = nla_parse_nested(tb, TCA_ACT_MAX_PRIO, nla, NULL);
if (ret < 0)
@@ -875,16 +859,11 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
goto err;
}
act->order = i;
-
- if (head == NULL)
- head = act;
- else
- act_prev->next = act;
- act_prev = act;
+ list_add_tail(&act->list, &actions);
}
if (event == RTM_GETACTION)
- ret = act_get_notify(net, portid, n, head, event);
+ ret = act_get_notify(net, portid, n, &actions, event);
else { /* delete */
struct sk_buff *skb;
@@ -894,7 +873,7 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
goto err;
}
- if (tca_get_fill(skb, head, portid, n->nlmsg_seq, 0, event,
+ if (tca_get_fill(skb, &actions, portid, n->nlmsg_seq, 0, event,
0, 1) <= 0) {
kfree_skb(skb);
ret = -EINVAL;
@@ -902,7 +881,7 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
}
/* now do the delete */
- tcf_action_destroy(head, 0);
+ tcf_action_destroy(&actions, 0);
ret = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
n->nlmsg_flags & NLM_F_ECHO);
if (ret > 0)
@@ -910,11 +889,11 @@ tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
return ret;
}
err:
- cleanup_a(head);
+ cleanup_a(&actions);
return ret;
}
-static int tcf_add_notify(struct net *net, struct tc_action *a,
+static int tcf_add_notify(struct net *net, struct list_head *actions,
u32 portid, u32 seq, int event, u16 flags)
{
struct tcamsg *t;
@@ -942,7 +921,7 @@ static int tcf_add_notify(struct net *net, struct tc_action *a,
if (nest == NULL)
goto out_kfree_skb;
- if (tcf_action_dump(skb, a, 0, 0) < 0)
+ if (tcf_action_dump(skb, actions, 0, 0) < 0)
goto out_kfree_skb;
nla_nest_end(skb, nest);
@@ -966,26 +945,18 @@ tcf_action_add(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
u32 portid, int ovr)
{
int ret = 0;
- struct tc_action *act;
- struct tc_action *a;
+ LIST_HEAD(actions);
u32 seq = n->nlmsg_seq;
- act = tcf_action_init(net, nla, NULL, NULL, ovr, 0);
- if (act == NULL)
+ ret = tcf_action_init(net, nla, NULL, NULL, ovr, 0, &actions);
+ if (ret)
goto done;
- if (IS_ERR(act)) {
- ret = PTR_ERR(act);
- goto done;
- }
/* dump then free all the actions after update; inserted policy
* stays intact
*/
- ret = tcf_add_notify(net, act, portid, seq, RTM_NEWACTION, n->nlmsg_flags);
- for (a = act; a; a = act) {
- act = a->next;
- kfree(a);
- }
+ ret = tcf_add_notify(net, &actions, portid, seq, RTM_NEWACTION, n->nlmsg_flags);
+ cleanup_a(&actions);
done:
return ret;
}
diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c
index 5c5edf56adbd..5d350c57af3f 100644
--- a/net/sched/act_csum.c
+++ b/net/sched/act_csum.c
@@ -37,15 +37,8 @@
#include <net/tc_act/tc_csum.h>
#define CSUM_TAB_MASK 15
-static struct tcf_common *tcf_csum_ht[CSUM_TAB_MASK + 1];
static u32 csum_idx_gen;
-static DEFINE_RWLOCK(csum_lock);
-
-static struct tcf_hashinfo csum_hash_info = {
- .htab = tcf_csum_ht,
- .hmask = CSUM_TAB_MASK,
- .lock = &csum_lock,
-};
+static struct tcf_hashinfo csum_hash_info;
static const struct nla_policy csum_policy[TCA_CSUM_MAX + 1] = {
[TCA_CSUM_PARMS] = { .len = sizeof(struct tc_csum), },
@@ -593,6 +586,10 @@ MODULE_LICENSE("GPL");
static int __init csum_init_module(void)
{
+ int err = tcf_hashinfo_init(&csum_hash_info, CSUM_TAB_MASK+1);
+ if (err)
+ return err;
+
return tcf_register_action(&act_csum_ops);
}
diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c
index 5645a4d32abd..1e6e0e765243 100644
--- a/net/sched/act_gact.c
+++ b/net/sched/act_gact.c
@@ -24,15 +24,8 @@
#include <net/tc_act/tc_gact.h>
#define GACT_TAB_MASK 15
-static struct tcf_common *tcf_gact_ht[GACT_TAB_MASK + 1];
static u32 gact_idx_gen;
-static DEFINE_RWLOCK(gact_lock);
-
-static struct tcf_hashinfo gact_hash_info = {
- .htab = tcf_gact_ht,
- .hmask = GACT_TAB_MASK,
- .lock = &gact_lock,
-};
+static struct tcf_hashinfo gact_hash_info;
#ifdef CONFIG_GACT_PROB
static int gact_net_rand(struct tcf_gact *gact)
@@ -215,6 +208,9 @@ MODULE_LICENSE("GPL");
static int __init gact_init_module(void)
{
+ int err = tcf_hashinfo_init(&gact_hash_info, GACT_TAB_MASK+1);
+ if (err)
+ return err;
#ifdef CONFIG_GACT_PROB
pr_info("GACT probability on\n");
#else
@@ -226,6 +222,7 @@ static int __init gact_init_module(void)
static void __exit gact_cleanup_module(void)
{
tcf_unregister_action(&act_gact_ops);
+ tcf_hashinfo_destroy(&gact_hash_info);
}
module_init(gact_init_module);
diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c
index 882a89762f77..8344380ebaf1 100644
--- a/net/sched/act_ipt.c
+++ b/net/sched/act_ipt.c
@@ -29,15 +29,8 @@
#define IPT_TAB_MASK 15
-static struct tcf_common *tcf_ipt_ht[IPT_TAB_MASK + 1];
static u32 ipt_idx_gen;
-static DEFINE_RWLOCK(ipt_lock);
-
-static struct tcf_hashinfo ipt_hash_info = {
- .htab = tcf_ipt_ht,
- .hmask = IPT_TAB_MASK,
- .lock = &ipt_lock,
-};
+static struct tcf_hashinfo ipt_hash_info;
static int ipt_init_target(struct xt_entry_target *t, char *table, unsigned int hook)
{
@@ -320,7 +313,11 @@ MODULE_ALIAS("act_xt");
static int __init ipt_init_module(void)
{
- int ret1, ret2;
+ int ret1, ret2, err;
+ err = tcf_hashinfo_init(&ipt_hash_info, IPT_TAB_MASK+1);
+ if (err)
+ return err;
+
ret1 = tcf_register_action(&act_xt_ops);
if (ret1 < 0)
printk("Failed to load xt action\n");
@@ -328,9 +325,10 @@ static int __init ipt_init_module(void)
if (ret2 < 0)
printk("Failed to load ipt action\n");
- if (ret1 < 0 && ret2 < 0)
+ if (ret1 < 0 && ret2 < 0) {
+ tcf_hashinfo_destroy(&ipt_hash_info);
return ret1;
- else
+ } else
return 0;
}
@@ -338,6 +336,7 @@ static void __exit ipt_cleanup_module(void)
{
tcf_unregister_action(&act_xt_ops);
tcf_unregister_action(&act_ipt_ops);
+ tcf_hashinfo_destroy(&ipt_hash_info);
}
module_init(ipt_init_module);
diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
index 252378121ce7..199fc9838af3 100644
--- a/net/sched/act_mirred.c
+++ b/net/sched/act_mirred.c
@@ -30,16 +30,9 @@
#include <linux/if_arp.h>
#define MIRRED_TAB_MASK 7
-static struct tcf_common *tcf_mirred_ht[MIRRED_TAB_MASK + 1];
static u32 mirred_idx_gen;
-static DEFINE_RWLOCK(mirred_lock);
static LIST_HEAD(mirred_list);
-
-static struct tcf_hashinfo mirred_hash_info = {
- .htab = tcf_mirred_ht,
- .hmask = MIRRED_TAB_MASK,
- .lock = &mirred_lock,
-};
+static struct tcf_hashinfo mirred_hash_info;
static int tcf_mirred_release(struct tcf_mirred *m, int bind)
{
@@ -261,7 +254,6 @@ static struct notifier_block mirred_device_notifier = {
.notifier_call = mirred_device_event,
};
-
static struct tc_action_ops act_mirred_ops = {
.kind = "mirred",
.hinfo = &mirred_hash_info,
@@ -284,6 +276,11 @@ static int __init mirred_init_module(void)
if (err)
return err;
+ err = tcf_hashinfo_init(&mirred_hash_info, MIRRED_TAB_MASK+1);
+ if (err) {
+ unregister_netdevice_notifier(&mirred_device_notifier);
+ return err;
+ }
pr_info("Mirror/redirect action on\n");
return tcf_register_action(&act_mirred_ops);
}
@@ -291,6 +288,7 @@ static int __init mirred_init_module(void)
static void __exit mirred_cleanup_module(void)
{
unregister_netdevice_notifier(&mirred_device_notifier);
+ tcf_hashinfo_destroy(&mirred_hash_info);
tcf_unregister_action(&act_mirred_ops);
}
diff --git a/net/sched/act_nat.c b/net/sched/act_nat.c
index 6a15ace00241..409fe7181c5f 100644
--- a/net/sched/act_nat.c
+++ b/net/sched/act_nat.c
@@ -30,15 +30,9 @@
#define NAT_TAB_MASK 15
-static struct tcf_common *tcf_nat_ht[NAT_TAB_MASK + 1];
static u32 nat_idx_gen;
-static DEFINE_RWLOCK(nat_lock);
-static struct tcf_hashinfo nat_hash_info = {
- .htab = tcf_nat_ht,
- .hmask = NAT_TAB_MASK,
- .lock = &nat_lock,
-};
+static struct tcf_hashinfo nat_hash_info;
static const struct nla_policy nat_policy[TCA_NAT_MAX + 1] = {
[TCA_NAT_PARMS] = { .len = sizeof(struct tc_nat) },
@@ -316,12 +310,16 @@ MODULE_LICENSE("GPL");
static int __init nat_init_module(void)
{
+ int err = tcf_hashinfo_init(&nat_hash_info, NAT_TAB_MASK+1);
+ if (err)
+ return err;
return tcf_register_action(&act_nat_ops);
}
static void __exit nat_cleanup_module(void)
{
tcf_unregister_action(&act_nat_ops);
+ tcf_hashinfo_destroy(&nat_hash_info);
}
module_init(nat_init_module);
diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
index 03b67674169c..aa5347c1b9f1 100644
--- a/net/sched/act_pedit.c
+++ b/net/sched/act_pedit.c
@@ -24,15 +24,9 @@
#include <net/tc_act/tc_pedit.h>
#define PEDIT_TAB_MASK 15
-static struct tcf_common *tcf_pedit_ht[PEDIT_TAB_MASK + 1];
static u32 pedit_idx_gen;
-static DEFINE_RWLOCK(pedit_lock);
-static struct tcf_hashinfo pedit_hash_info = {
- .htab = tcf_pedit_ht,
- .hmask = PEDIT_TAB_MASK,
- .lock = &pedit_lock,
-};
+static struct tcf_hashinfo pedit_hash_info;
static const struct nla_policy pedit_policy[TCA_PEDIT_MAX + 1] = {
[TCA_PEDIT_PARMS] = { .len = sizeof(struct tc_pedit) },
@@ -252,11 +246,15 @@ MODULE_LICENSE("GPL");
static int __init pedit_init_module(void)
{
+ int err = tcf_hashinfo_init(&pedit_hash_info, PEDIT_TAB_MASK+1);
+ if (err)
+ return err;
return tcf_register_action(&act_pedit_ops);
}
static void __exit pedit_cleanup_module(void)
{
+ tcf_hashinfo_destroy(&pedit_hash_info);
tcf_unregister_action(&act_pedit_ops);
}
diff --git a/net/sched/act_police.c b/net/sched/act_police.c
index 16a62c36928a..0cc305e7e469 100644
--- a/net/sched/act_police.c
+++ b/net/sched/act_police.c
@@ -41,15 +41,8 @@ struct tcf_police {
container_of(pc, struct tcf_police, common)
#define POL_TAB_MASK 15
-static struct tcf_common *tcf_police_ht[POL_TAB_MASK + 1];
static u32 police_idx_gen;
-static DEFINE_RWLOCK(police_lock);
-
-static struct tcf_hashinfo police_hash_info = {
- .htab = tcf_police_ht,
- .hmask = POL_TAB_MASK,
- .lock = &police_lock,
-};
+static struct tcf_hashinfo police_hash_info;
/* old policer structure from before tc actions */
struct tc_police_compat {
@@ -67,18 +60,19 @@ struct tc_police_compat {
static int tcf_act_police_walker(struct sk_buff *skb, struct netlink_callback *cb,
int type, struct tc_action *a)
{
+ struct hlist_head *head;
struct tcf_common *p;
int err = 0, index = -1, i = 0, s_i = 0, n_i = 0;
struct nlattr *nest;
- read_lock_bh(&police_lock);
+ spin_lock_bh(&police_hash_info.lock);
s_i = cb->args[0];
for (i = 0; i < (POL_TAB_MASK + 1); i++) {
- p = tcf_police_ht[tcf_hash(i, POL_TAB_MASK)];
+ head = &police_hash_info.htab[tcf_hash(i, POL_TAB_MASK)];
- for (; p; p = p->tcfc_next) {
+ hlist_for_each_entry_rcu(p, head, tcfc_head) {
index++;
if (index < s_i)
continue;
@@ -101,7 +95,7 @@ static int tcf_act_police_walker(struct sk_buff *skb, struct netlink_callback *c
}
}
done:
- read_unlock_bh(&police_lock);
+ spin_unlock_bh(&police_hash_info.lock);
if (n_i)
cb->args[0] += n_i;
return n_i;
@@ -113,25 +107,16 @@ nla_put_failure:
static void tcf_police_destroy(struct tcf_police *p)
{
- unsigned int h = tcf_hash(p->tcf_index, POL_TAB_MASK);
- struct tcf_common **p1p;
-
- for (p1p = &tcf_police_ht[h]; *p1p; p1p = &(*p1p)->tcfc_next) {
- if (*p1p == &p->common) {
- write_lock_bh(&police_lock);
- *p1p = p->tcf_next;
- write_unlock_bh(&police_lock);
- gen_kill_estimator(&p->tcf_bstats,
- &p->tcf_rate_est);
- /*
- * gen_estimator est_timer() might access p->tcf_lock
- * or bstats, wait a RCU grace period before freeing p
- */
- kfree_rcu(p, tcf_rcu);
- return;
- }
- }
- WARN_ON(1);
+ spin_lock_bh(&police_hash_info.lock);
+ hlist_del(&p->tcf_head);
+ spin_unlock_bh(&police_hash_info.lock);
+ gen_kill_estimator(&p->tcf_bstats,
+ &p->tcf_rate_est);
+ /*
+ * gen_estimator est_timer() might access p->tcf_lock
+ * or bstats, wait a RCU grace period before freeing p
+ */
+ kfree_rcu(p, tcf_rcu);
}
static const struct nla_policy police_policy[TCA_POLICE_MAX + 1] = {
@@ -266,10 +251,9 @@ override:
police->tcf_index = parm->index ? parm->index :
tcf_hash_new_index(&police_idx_gen, &police_hash_info);
h = tcf_hash(police->tcf_index, POL_TAB_MASK);
- write_lock_bh(&police_lock);
- police->tcf_next = tcf_police_ht[h];
- tcf_police_ht[h] = &police->common;
- write_unlock_bh(&police_lock);
+ spin_lock_bh(&police_hash_info.lock);
+ hlist_add_head(&police->tcf_head, &police_hash_info.htab[h]);
+ spin_unlock_bh(&police_hash_info.lock);
a->priv = police;
return ret;
@@ -414,12 +398,19 @@ static struct tc_action_ops act_police_ops = {
static int __init
police_init_module(void)
{
- return tcf_register_action(&act_police_ops);
+ int err = tcf_hashinfo_init(&police_hash_info, POL_TAB_MASK+1);
+ if (err)
+ return err;
+ err = tcf_register_action(&act_police_ops);
+ if (err)
+ tcf_hashinfo_destroy(&police_hash_info);
+ return err;
}
static void __exit
police_cleanup_module(void)
{
+ tcf_hashinfo_destroy(&police_hash_info);
tcf_unregister_action(&act_police_ops);
}
diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c
index 31157d3e729c..2d7a0eb11c69 100644
--- a/net/sched/act_simple.c
+++ b/net/sched/act_simple.c
@@ -25,15 +25,8 @@
#include <net/tc_act/tc_defact.h>
#define SIMP_TAB_MASK 7
-static struct tcf_common *tcf_simp_ht[SIMP_TAB_MASK + 1];
static u32 simp_idx_gen;
-static DEFINE_RWLOCK(simp_lock);
-
-static struct tcf_hashinfo simp_hash_info = {
- .htab = tcf_simp_ht,
- .hmask = SIMP_TAB_MASK,
- .lock = &simp_lock,
-};
+static struct tcf_hashinfo simp_hash_info;
#define SIMP_MAX_DATA 32
static int tcf_simp(struct sk_buff *skb, const struct tc_action *a,
@@ -209,14 +202,23 @@ MODULE_LICENSE("GPL");
static int __init simp_init_module(void)
{
- int ret = tcf_register_action(&act_simp_ops);
+ int err, ret;
+ err = tcf_hashinfo_init(&simp_hash_info, SIMP_TAB_MASK+1);
+ if (err)
+ return err;
+
+ ret = tcf_register_action(&act_simp_ops);
if (!ret)
pr_info("Simple TC action Loaded\n");
+ else
+ tcf_hashinfo_destroy(&simp_hash_info);
+
return ret;
}
static void __exit simp_cleanup_module(void)
{
+ tcf_hashinfo_destroy(&simp_hash_info);
tcf_unregister_action(&act_simp_ops);
}
diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c
index 35ea643b4325..90ed04a83cf3 100644
--- a/net/sched/act_skbedit.c
+++ b/net/sched/act_skbedit.c
@@ -11,8 +11,7 @@
* 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., 59 Temple
- * Place - Suite 330, Boston, MA 02111-1307 USA.
+ * this program; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Duyck <alexander.h.duyck@intel.com>
*/
@@ -29,15 +28,8 @@
#include <net/tc_act/tc_skbedit.h>
#define SKBEDIT_TAB_MASK 15
-static struct tcf_common *tcf_skbedit_ht[SKBEDIT_TAB_MASK + 1];
static u32 skbedit_idx_gen;
-static DEFINE_RWLOCK(skbedit_lock);
-
-static struct tcf_hashinfo skbedit_hash_info = {
- .htab = tcf_skbedit_ht,
- .hmask = SKBEDIT_TAB_MASK,
- .lock = &skbedit_lock,
-};
+static struct tcf_hashinfo skbedit_hash_info;
static int tcf_skbedit(struct sk_buff *skb, const struct tc_action *a,
struct tcf_result *res)
@@ -211,11 +203,15 @@ MODULE_LICENSE("GPL");
static int __init skbedit_init_module(void)
{
+ int err = tcf_hashinfo_init(&skbedit_hash_info, SKBEDIT_TAB_MASK+1);
+ if (err)
+ return err;
return tcf_register_action(&act_skbedit_ops);
}
static void __exit skbedit_cleanup_module(void)
{
+ tcf_hashinfo_destroy(&skbedit_hash_info);
tcf_unregister_action(&act_skbedit_ops);
}
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index 8e118af90973..6b085cf27a65 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -31,8 +31,7 @@
#include <net/pkt_cls.h>
/* The list of all installed classifier types */
-
-static struct tcf_proto_ops *tcf_proto_base __read_mostly;
+static LIST_HEAD(tcf_proto_base);
/* Protects list of registered TC modules. It is pure SMP lock. */
static DEFINE_RWLOCK(cls_mod_lock);
@@ -45,7 +44,7 @@ static const struct tcf_proto_ops *tcf_proto_lookup_ops(struct nlattr *kind)
if (kind) {
read_lock(&cls_mod_lock);
- for (t = tcf_proto_base; t; t = t->next) {
+ list_for_each_entry(t, &tcf_proto_base, head) {
if (nla_strcmp(kind, t->kind) == 0) {
if (!try_module_get(t->owner))
t = NULL;
@@ -61,16 +60,15 @@ static const struct tcf_proto_ops *tcf_proto_lookup_ops(struct nlattr *kind)
int register_tcf_proto_ops(struct tcf_proto_ops *ops)
{
- struct tcf_proto_ops *t, **tp;
+ struct tcf_proto_ops *t;
int rc = -EEXIST;
write_lock(&cls_mod_lock);
- for (tp = &tcf_proto_base; (t = *tp) != NULL; tp = &t->next)
+ list_for_each_entry(t, &tcf_proto_base, head)
if (!strcmp(ops->kind, t->kind))
goto out;
- ops->next = NULL;
- *tp = ops;
+ list_add_tail(&ops->head, &tcf_proto_base);
rc = 0;
out:
write_unlock(&cls_mod_lock);
@@ -80,17 +78,17 @@ EXPORT_SYMBOL(register_tcf_proto_ops);
int unregister_tcf_proto_ops(struct tcf_proto_ops *ops)
{
- struct tcf_proto_ops *t, **tp;
+ struct tcf_proto_ops *t;
int rc = -ENOENT;
write_lock(&cls_mod_lock);
- for (tp = &tcf_proto_base; (t = *tp) != NULL; tp = &t->next)
+ list_for_each_entry(t, &tcf_proto_base, head)
if (t == ops)
break;
if (!t)
goto out;
- *tp = t->next;
+ list_del(&t->head);
rc = 0;
out:
write_unlock(&cls_mod_lock);
@@ -500,46 +498,41 @@ out:
void tcf_exts_destroy(struct tcf_proto *tp, struct tcf_exts *exts)
{
#ifdef CONFIG_NET_CLS_ACT
- if (exts->action) {
- tcf_action_destroy(exts->action, TCA_ACT_UNBIND);
- exts->action = NULL;
- }
+ tcf_action_destroy(&exts->actions, TCA_ACT_UNBIND);
+ INIT_LIST_HEAD(&exts->actions);
#endif
}
EXPORT_SYMBOL(tcf_exts_destroy);
int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
- struct nlattr *rate_tlv, struct tcf_exts *exts,
- const struct tcf_ext_map *map)
+ struct nlattr *rate_tlv, struct tcf_exts *exts)
{
- memset(exts, 0, sizeof(*exts));
-
#ifdef CONFIG_NET_CLS_ACT
{
struct tc_action *act;
- if (map->police && tb[map->police]) {
- act = tcf_action_init_1(net, tb[map->police], rate_tlv,
+ INIT_LIST_HEAD(&exts->actions);
+ if (exts->police && tb[exts->police]) {
+ act = tcf_action_init_1(net, tb[exts->police], rate_tlv,
"police", TCA_ACT_NOREPLACE,
TCA_ACT_BIND);
if (IS_ERR(act))
return PTR_ERR(act);
- act->type = TCA_OLD_COMPAT;
- exts->action = act;
- } else if (map->action && tb[map->action]) {
- act = tcf_action_init(net, tb[map->action], rate_tlv,
+ act->type = exts->type = TCA_OLD_COMPAT;
+ list_add(&act->list, &exts->actions);
+ } else if (exts->action && tb[exts->action]) {
+ int err;
+ err = tcf_action_init(net, tb[exts->action], rate_tlv,
NULL, TCA_ACT_NOREPLACE,
- TCA_ACT_BIND);
- if (IS_ERR(act))
- return PTR_ERR(act);
-
- exts->action = act;
+ TCA_ACT_BIND, &exts->actions);
+ if (err)
+ return err;
}
}
#else
- if ((map->action && tb[map->action]) ||
- (map->police && tb[map->police]))
+ if ((exts->action && tb[exts->action]) ||
+ (exts->police && tb[exts->police]))
return -EOPNOTSUPP;
#endif
@@ -551,43 +544,44 @@ void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst,
struct tcf_exts *src)
{
#ifdef CONFIG_NET_CLS_ACT
- if (src->action) {
- struct tc_action *act;
+ if (!list_empty(&src->actions)) {
+ LIST_HEAD(tmp);
tcf_tree_lock(tp);
- act = dst->action;
- dst->action = src->action;
+ list_splice_init(&dst->actions, &tmp);
+ list_splice(&src->actions, &dst->actions);
tcf_tree_unlock(tp);
- if (act)
- tcf_action_destroy(act, TCA_ACT_UNBIND);
+ tcf_action_destroy(&tmp, TCA_ACT_UNBIND);
}
#endif
}
EXPORT_SYMBOL(tcf_exts_change);
-int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts,
- const struct tcf_ext_map *map)
+#define tcf_exts_first_act(ext) \
+ list_first_entry(&(exts)->actions, struct tc_action, list)
+
+int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts)
{
#ifdef CONFIG_NET_CLS_ACT
- if (map->action && exts->action) {
+ if (exts->action && !list_empty(&exts->actions)) {
/*
* again for backward compatible mode - we want
* to work with both old and new modes of entering
* tc data even if iproute2 was newer - jhs
*/
struct nlattr *nest;
-
- if (exts->action->type != TCA_OLD_COMPAT) {
- nest = nla_nest_start(skb, map->action);
+ if (exts->type != TCA_OLD_COMPAT) {
+ nest = nla_nest_start(skb, exts->action);
if (nest == NULL)
goto nla_put_failure;
- if (tcf_action_dump(skb, exts->action, 0, 0) < 0)
+ if (tcf_action_dump(skb, &exts->actions, 0, 0) < 0)
goto nla_put_failure;
nla_nest_end(skb, nest);
- } else if (map->police) {
- nest = nla_nest_start(skb, map->police);
+ } else if (exts->police) {
+ struct tc_action *act = tcf_exts_first_act(exts);
+ nest = nla_nest_start(skb, exts->police);
if (nest == NULL)
goto nla_put_failure;
- if (tcf_action_dump_old(skb, exts->action, 0, 0) < 0)
+ if (tcf_action_dump_old(skb, act, 0, 0) < 0)
goto nla_put_failure;
nla_nest_end(skb, nest);
}
@@ -600,17 +594,14 @@ nla_put_failure: __attribute__ ((unused))
EXPORT_SYMBOL(tcf_exts_dump);
-int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts,
- const struct tcf_ext_map *map)
+int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts)
{
#ifdef CONFIG_NET_CLS_ACT
- if (exts->action)
- if (tcf_action_copy_stats(skb, exts->action, 1) < 0)
- goto nla_put_failure;
+ struct tc_action *a = tcf_exts_first_act(exts);
+ if (tcf_action_copy_stats(skb, a, 1) < 0)
+ return -1;
#endif
return 0;
-nla_put_failure: __attribute__ ((unused))
- return -1;
}
EXPORT_SYMBOL(tcf_exts_dump_stats);
diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c
index 636d9131d870..b6552035d1f4 100644
--- a/net/sched/cls_basic.c
+++ b/net/sched/cls_basic.c
@@ -34,11 +34,6 @@ struct basic_filter {
struct list_head link;
};
-static const struct tcf_ext_map basic_ext_map = {
- .action = TCA_BASIC_ACT,
- .police = TCA_BASIC_POLICE
-};
-
static int basic_classify(struct sk_buff *skb, const struct tcf_proto *tp,
struct tcf_result *res)
{
@@ -141,7 +136,8 @@ static int basic_set_parms(struct net *net, struct tcf_proto *tp,
struct tcf_exts e;
struct tcf_ematch_tree t;
- err = tcf_exts_validate(net, tp, tb, est, &e, &basic_ext_map);
+ tcf_exts_init(&e, TCA_BASIC_ACT, TCA_BASIC_POLICE);
+ err = tcf_exts_validate(net, tp, tb, est, &e);
if (err < 0)
return err;
@@ -191,6 +187,7 @@ static int basic_change(struct net *net, struct sk_buff *in_skb,
if (f == NULL)
goto errout;
+ tcf_exts_init(&f->exts, TCA_BASIC_ACT, TCA_BASIC_POLICE);
err = -EINVAL;
if (handle)
f->handle = handle;
@@ -263,13 +260,13 @@ static int basic_dump(struct tcf_proto *tp, unsigned long fh,
nla_put_u32(skb, TCA_BASIC_CLASSID, f->res.classid))
goto nla_put_failure;
- if (tcf_exts_dump(skb, &f->exts, &basic_ext_map) < 0 ||
+ if (tcf_exts_dump(skb, &f->exts) < 0 ||
tcf_em_tree_dump(skb, &f->ematches, TCA_BASIC_EMATCHES) < 0)
goto nla_put_failure;
nla_nest_end(skb, nest);
- if (tcf_exts_dump_stats(skb, &f->exts, &basic_ext_map) < 0)
+ if (tcf_exts_dump_stats(skb, &f->exts) < 0)
goto nla_put_failure;
return skb->len;
diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c
index 1002a8226281..00a5a585e5f1 100644
--- a/net/sched/cls_bpf.c
+++ b/net/sched/cls_bpf.c
@@ -46,11 +46,6 @@ static const struct nla_policy bpf_policy[TCA_BPF_MAX + 1] = {
.len = sizeof(struct sock_filter) * BPF_MAXINSNS },
};
-static const struct tcf_ext_map bpf_ext_map = {
- .action = TCA_BPF_ACT,
- .police = TCA_BPF_POLICE,
-};
-
static int cls_bpf_classify(struct sk_buff *skb, const struct tcf_proto *tp,
struct tcf_result *res)
{
@@ -174,7 +169,8 @@ static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp,
if (!tb[TCA_BPF_OPS_LEN] || !tb[TCA_BPF_OPS] || !tb[TCA_BPF_CLASSID])
return -EINVAL;
- ret = tcf_exts_validate(net, tp, tb, est, &exts, &bpf_ext_map);
+ tcf_exts_init(&exts, TCA_BPF_ACT, TCA_BPF_POLICE);
+ ret = tcf_exts_validate(net, tp, tb, est, &exts);
if (ret < 0)
return ret;
@@ -271,6 +267,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb,
if (prog == NULL)
return -ENOBUFS;
+ tcf_exts_init(&prog->exts, TCA_BPF_ACT, TCA_BPF_POLICE);
if (handle == 0)
prog->handle = cls_bpf_grab_new_handle(tp, head);
else
@@ -323,14 +320,14 @@ static int cls_bpf_dump(struct tcf_proto *tp, unsigned long fh,
if (nla == NULL)
goto nla_put_failure;
- memcpy(nla_data(nla), prog->bpf_ops, nla_len(nla));
+ memcpy(nla_data(nla), prog->bpf_ops, nla_len(nla));
- if (tcf_exts_dump(skb, &prog->exts, &bpf_ext_map) < 0)
+ if (tcf_exts_dump(skb, &prog->exts) < 0)
goto nla_put_failure;
nla_nest_end(skb, nest);
- if (tcf_exts_dump_stats(skb, &prog->exts, &bpf_ext_map) < 0)
+ if (tcf_exts_dump_stats(skb, &prog->exts) < 0)
goto nla_put_failure;
return skb->len;
diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c
index 16006c92c3fd..f9d212583ea2 100644
--- a/net/sched/cls_cgroup.c
+++ b/net/sched/cls_cgroup.c
@@ -172,11 +172,6 @@ static int cls_cgroup_init(struct tcf_proto *tp)
return 0;
}
-static const struct tcf_ext_map cgroup_ext_map = {
- .action = TCA_CGROUP_ACT,
- .police = TCA_CGROUP_POLICE,
-};
-
static const struct nla_policy cgroup_policy[TCA_CGROUP_MAX + 1] = {
[TCA_CGROUP_EMATCHES] = { .type = NLA_NESTED },
};
@@ -203,6 +198,7 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb,
if (head == NULL)
return -ENOBUFS;
+ tcf_exts_init(&head->exts, TCA_CGROUP_ACT, TCA_CGROUP_POLICE);
head->handle = handle;
tcf_tree_lock(tp);
@@ -218,8 +214,8 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb,
if (err < 0)
return err;
- err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e,
- &cgroup_ext_map);
+ tcf_exts_init(&e, TCA_CGROUP_ACT, TCA_CGROUP_POLICE);
+ err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e);
if (err < 0)
return err;
@@ -277,13 +273,13 @@ static int cls_cgroup_dump(struct tcf_proto *tp, unsigned long fh,
if (nest == NULL)
goto nla_put_failure;
- if (tcf_exts_dump(skb, &head->exts, &cgroup_ext_map) < 0 ||
+ if (tcf_exts_dump(skb, &head->exts) < 0 ||
tcf_em_tree_dump(skb, &head->ematches, TCA_CGROUP_EMATCHES) < 0)
goto nla_put_failure;
nla_nest_end(skb, nest);
- if (tcf_exts_dump_stats(skb, &head->exts, &cgroup_ext_map) < 0)
+ if (tcf_exts_dump_stats(skb, &head->exts) < 0)
goto nla_put_failure;
return skb->len;
diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c
index 7881e2fccbc2..dfd18a5c3e81 100644
--- a/net/sched/cls_flow.c
+++ b/net/sched/cls_flow.c
@@ -56,11 +56,6 @@ struct flow_filter {
u32 hashrnd;
};
-static const struct tcf_ext_map flow_ext_map = {
- .action = TCA_FLOW_ACT,
- .police = TCA_FLOW_POLICE,
-};
-
static inline u32 addr_fold(void *addr)
{
unsigned long a = (unsigned long)addr;
@@ -220,7 +215,7 @@ static u32 flow_get_vlan_tag(const struct sk_buff *skb)
static u32 flow_get_rxhash(struct sk_buff *skb)
{
- return skb_get_rxhash(skb);
+ return skb_get_hash(skb);
}
static u32 flow_key_get(struct sk_buff *skb, int key, struct flow_keys *flow)
@@ -397,7 +392,8 @@ static int flow_change(struct net *net, struct sk_buff *in_skb,
return -EOPNOTSUPP;
}
- err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, &flow_ext_map);
+ tcf_exts_init(&e, TCA_FLOW_ACT, TCA_FLOW_POLICE);
+ err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e);
if (err < 0)
return err;
@@ -455,6 +451,7 @@ static int flow_change(struct net *net, struct sk_buff *in_skb,
f->handle = handle;
f->mask = ~0U;
+ tcf_exts_init(&f->exts, TCA_FLOW_ACT, TCA_FLOW_POLICE);
get_random_bytes(&f->hashrnd, 4);
f->perturb_timer.function = flow_perturbation;
@@ -608,7 +605,7 @@ static int flow_dump(struct tcf_proto *tp, unsigned long fh,
nla_put_u32(skb, TCA_FLOW_PERTURB, f->perturb_period / HZ))
goto nla_put_failure;
- if (tcf_exts_dump(skb, &f->exts, &flow_ext_map) < 0)
+ if (tcf_exts_dump(skb, &f->exts) < 0)
goto nla_put_failure;
#ifdef CONFIG_NET_EMATCH
if (f->ematches.hdr.nmatches &&
@@ -617,7 +614,7 @@ static int flow_dump(struct tcf_proto *tp, unsigned long fh,
#endif
nla_nest_end(skb, nest);
- if (tcf_exts_dump_stats(skb, &f->exts, &flow_ext_map) < 0)
+ if (tcf_exts_dump_stats(skb, &f->exts) < 0)
goto nla_put_failure;
return skb->len;
diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c
index 9b97172db84a..3f9cece13807 100644
--- a/net/sched/cls_fw.c
+++ b/net/sched/cls_fw.c
@@ -46,11 +46,6 @@ struct fw_filter {
struct tcf_exts exts;
};
-static const struct tcf_ext_map fw_ext_map = {
- .action = TCA_FW_ACT,
- .police = TCA_FW_POLICE
-};
-
static inline int fw_hash(u32 handle)
{
if (HTSIZE == 4096)
@@ -200,7 +195,8 @@ fw_change_attrs(struct net *net, struct tcf_proto *tp, struct fw_filter *f,
u32 mask;
int err;
- err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, &fw_ext_map);
+ tcf_exts_init(&e, TCA_FW_ACT, TCA_FW_POLICE);
+ err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e);
if (err < 0)
return err;
@@ -280,6 +276,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb,
if (f == NULL)
return -ENOBUFS;
+ tcf_exts_init(&f->exts, TCA_FW_ACT, TCA_FW_POLICE);
f->id = handle;
err = fw_change_attrs(net, tp, f, tb, tca, base);
@@ -359,12 +356,12 @@ static int fw_dump(struct tcf_proto *tp, unsigned long fh,
nla_put_u32(skb, TCA_FW_MASK, head->mask))
goto nla_put_failure;
- if (tcf_exts_dump(skb, &f->exts, &fw_ext_map) < 0)
+ if (tcf_exts_dump(skb, &f->exts) < 0)
goto nla_put_failure;
nla_nest_end(skb, nest);
- if (tcf_exts_dump_stats(skb, &f->exts, &fw_ext_map) < 0)
+ if (tcf_exts_dump_stats(skb, &f->exts) < 0)
goto nla_put_failure;
return skb->len;
diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c
index 37da567d833e..2473953a5948 100644
--- a/net/sched/cls_route.c
+++ b/net/sched/cls_route.c
@@ -59,11 +59,6 @@ struct route4_filter {
#define ROUTE4_FAILURE ((struct route4_filter *)(-1L))
-static const struct tcf_ext_map route_ext_map = {
- .police = TCA_ROUTE4_POLICE,
- .action = TCA_ROUTE4_ACT
-};
-
static inline int route4_fastmap_hash(u32 id, int iif)
{
return id & 0xF;
@@ -347,7 +342,8 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp,
struct route4_bucket *b;
struct tcf_exts e;
- err = tcf_exts_validate(net, tp, tb, est, &e, &route_ext_map);
+ tcf_exts_init(&e, TCA_ROUTE4_ACT, TCA_ROUTE4_POLICE);
+ err = tcf_exts_validate(net, tp, tb, est, &e);
if (err < 0)
return err;
@@ -481,6 +477,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb,
if (f == NULL)
goto errout;
+ tcf_exts_init(&f->exts, TCA_ROUTE4_ACT, TCA_ROUTE4_POLICE);
err = route4_set_parms(net, tp, base, f, handle, head, tb,
tca[TCA_RATE], 1);
if (err < 0)
@@ -589,12 +586,12 @@ static int route4_dump(struct tcf_proto *tp, unsigned long fh,
nla_put_u32(skb, TCA_ROUTE4_CLASSID, f->res.classid))
goto nla_put_failure;
- if (tcf_exts_dump(skb, &f->exts, &route_ext_map) < 0)
+ if (tcf_exts_dump(skb, &f->exts) < 0)
goto nla_put_failure;
nla_nest_end(skb, nest);
- if (tcf_exts_dump_stats(skb, &f->exts, &route_ext_map) < 0)
+ if (tcf_exts_dump_stats(skb, &f->exts) < 0)
goto nla_put_failure;
return skb->len;
diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h
index 252d8b05872e..4f25c2ac825b 100644
--- a/net/sched/cls_rsvp.h
+++ b/net/sched/cls_rsvp.h
@@ -116,11 +116,6 @@ static inline unsigned int hash_src(__be32 *src)
return h & 0xF;
}
-static struct tcf_ext_map rsvp_ext_map = {
- .police = TCA_RSVP_POLICE,
- .action = TCA_RSVP_ACT
-};
-
#define RSVP_APPLY_RESULT() \
{ \
int r = tcf_exts_exec(skb, &f->exts, res); \
@@ -440,7 +435,8 @@ static int rsvp_change(struct net *net, struct sk_buff *in_skb,
if (err < 0)
return err;
- err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, &rsvp_ext_map);
+ tcf_exts_init(&e, TCA_RSVP_ACT, TCA_RSVP_POLICE);
+ err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e);
if (err < 0)
return err;
@@ -471,6 +467,7 @@ static int rsvp_change(struct net *net, struct sk_buff *in_skb,
if (f == NULL)
goto errout2;
+ tcf_exts_init(&f->exts, TCA_RSVP_ACT, TCA_RSVP_POLICE);
h2 = 16;
if (tb[TCA_RSVP_SRC]) {
memcpy(f->src, nla_data(tb[TCA_RSVP_SRC]), sizeof(f->src));
@@ -633,12 +630,12 @@ static int rsvp_dump(struct tcf_proto *tp, unsigned long fh,
nla_put(skb, TCA_RSVP_SRC, sizeof(f->src), f->src))
goto nla_put_failure;
- if (tcf_exts_dump(skb, &f->exts, &rsvp_ext_map) < 0)
+ if (tcf_exts_dump(skb, &f->exts) < 0)
goto nla_put_failure;
nla_nest_end(skb, nest);
- if (tcf_exts_dump_stats(skb, &f->exts, &rsvp_ext_map) < 0)
+ if (tcf_exts_dump_stats(skb, &f->exts) < 0)
goto nla_put_failure;
return skb->len;
diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c
index b86535a40169..ffad18791c93 100644
--- a/net/sched/cls_tcindex.c
+++ b/net/sched/cls_tcindex.c
@@ -50,11 +50,6 @@ struct tcindex_data {
int fall_through; /* 0: only classify if explicit match */
};
-static const struct tcf_ext_map tcindex_ext_map = {
- .police = TCA_TCINDEX_POLICE,
- .action = TCA_TCINDEX_ACT
-};
-
static inline int
tcindex_filter_is_set(struct tcindex_filter_result *r)
{
@@ -209,17 +204,21 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base,
struct tcindex_filter *f = NULL; /* make gcc behave */
struct tcf_exts e;
- err = tcf_exts_validate(net, tp, tb, est, &e, &tcindex_ext_map);
+ tcf_exts_init(&e, TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE);
+ err = tcf_exts_validate(net, tp, tb, est, &e);
if (err < 0)
return err;
memcpy(&cp, p, sizeof(cp));
memset(&new_filter_result, 0, sizeof(new_filter_result));
+ tcf_exts_init(&new_filter_result.exts, TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE);
if (old_r)
memcpy(&cr, r, sizeof(cr));
- else
+ else {
memset(&cr, 0, sizeof(cr));
+ tcf_exts_init(&cr.exts, TCA_TCINDEX_ACT, TCA_TCINDEX_POLICE);
+ }
if (tb[TCA_TCINDEX_HASH])
cp.hash = nla_get_u32(tb[TCA_TCINDEX_HASH]);
@@ -468,11 +467,11 @@ static int tcindex_dump(struct tcf_proto *tp, unsigned long fh,
nla_put_u32(skb, TCA_TCINDEX_CLASSID, r->res.classid))
goto nla_put_failure;
- if (tcf_exts_dump(skb, &r->exts, &tcindex_ext_map) < 0)
+ if (tcf_exts_dump(skb, &r->exts) < 0)
goto nla_put_failure;
nla_nest_end(skb, nest);
- if (tcf_exts_dump_stats(skb, &r->exts, &tcindex_ext_map) < 0)
+ if (tcf_exts_dump_stats(skb, &r->exts) < 0)
goto nla_put_failure;
}
diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c
index eb07a1e536e6..20f2fb79c747 100644
--- a/net/sched/cls_u32.c
+++ b/net/sched/cls_u32.c
@@ -79,11 +79,6 @@ struct tc_u_common {
u32 hgenerator;
};
-static const struct tcf_ext_map u32_ext_map = {
- .action = TCA_U32_ACT,
- .police = TCA_U32_POLICE
-};
-
static inline unsigned int u32_hash_fold(__be32 key,
const struct tc_u32_sel *sel,
u8 fshift)
@@ -352,7 +347,7 @@ static int u32_destroy_key(struct tcf_proto *tp, struct tc_u_knode *n)
return 0;
}
-static int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode* key)
+static int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode *key)
{
struct tc_u_knode **kp;
struct tc_u_hnode *ht = key->ht_up;
@@ -496,7 +491,8 @@ static int u32_set_parms(struct net *net, struct tcf_proto *tp,
int err;
struct tcf_exts e;
- err = tcf_exts_validate(net, tp, tb, est, &e, &u32_ext_map);
+ tcf_exts_init(&e, TCA_U32_ACT, TCA_U32_POLICE);
+ err = tcf_exts_validate(net, tp, tb, est, &e);
if (err < 0)
return err;
@@ -646,6 +642,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb,
n->ht_up = ht;
n->handle = handle;
n->fshift = s->hmask ? ffs(ntohl(s->hmask)) - 1 : 0;
+ tcf_exts_init(&n->exts, TCA_U32_ACT, TCA_U32_POLICE);
#ifdef CONFIG_CLS_U32_MARK
if (tb[TCA_U32_MARK]) {
@@ -759,7 +756,7 @@ static int u32_dump(struct tcf_proto *tp, unsigned long fh,
goto nla_put_failure;
#endif
- if (tcf_exts_dump(skb, &n->exts, &u32_ext_map) < 0)
+ if (tcf_exts_dump(skb, &n->exts) < 0)
goto nla_put_failure;
#ifdef CONFIG_NET_CLS_IND
@@ -778,7 +775,7 @@ static int u32_dump(struct tcf_proto *tp, unsigned long fh,
nla_nest_end(skb, nest);
if (TC_U32_KEY(n->handle))
- if (tcf_exts_dump_stats(skb, &n->exts, &u32_ext_map) < 0)
+ if (tcf_exts_dump_stats(skb, &n->exts) < 0)
goto nla_put_failure;
return skb->len;
diff --git a/net/sched/em_meta.c b/net/sched/em_meta.c
index e5cef9567225..9b8c0b0e60d7 100644
--- a/net/sched/em_meta.c
+++ b/net/sched/em_meta.c
@@ -222,7 +222,7 @@ META_COLLECTOR(int_maclen)
META_COLLECTOR(int_rxhash)
{
- dst->value = skb_get_rxhash(skb);
+ dst->value = skb_get_hash(skb);
}
/**************************************************************************
@@ -271,40 +271,52 @@ META_COLLECTOR(int_rtiif)
* Socket Attributes
**************************************************************************/
-#define SKIP_NONLOCAL(skb) \
- if (unlikely(skb->sk == NULL)) { \
- *err = -1; \
- return; \
- }
+#define skip_nonlocal(skb) \
+ (unlikely(skb->sk == NULL))
META_COLLECTOR(int_sk_family)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = skb->sk->sk_family;
}
META_COLLECTOR(int_sk_state)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = skb->sk->sk_state;
}
META_COLLECTOR(int_sk_reuse)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = skb->sk->sk_reuse;
}
META_COLLECTOR(int_sk_bound_if)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
/* No error if bound_dev_if is 0, legal userspace check */
dst->value = skb->sk->sk_bound_dev_if;
}
META_COLLECTOR(var_sk_bound_if)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
if (skb->sk->sk_bound_dev_if == 0) {
dst->value = (unsigned long) "any";
@@ -322,151 +334,226 @@ META_COLLECTOR(var_sk_bound_if)
META_COLLECTOR(int_sk_refcnt)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = atomic_read(&skb->sk->sk_refcnt);
}
META_COLLECTOR(int_sk_rcvbuf)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = skb->sk->sk_rcvbuf;
}
META_COLLECTOR(int_sk_shutdown)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = skb->sk->sk_shutdown;
}
META_COLLECTOR(int_sk_proto)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = skb->sk->sk_protocol;
}
META_COLLECTOR(int_sk_type)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = skb->sk->sk_type;
}
META_COLLECTOR(int_sk_rmem_alloc)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = sk_rmem_alloc_get(skb->sk);
}
META_COLLECTOR(int_sk_wmem_alloc)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = sk_wmem_alloc_get(skb->sk);
}
META_COLLECTOR(int_sk_omem_alloc)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = atomic_read(&skb->sk->sk_omem_alloc);
}
META_COLLECTOR(int_sk_rcv_qlen)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = skb->sk->sk_receive_queue.qlen;
}
META_COLLECTOR(int_sk_snd_qlen)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = skb->sk->sk_write_queue.qlen;
}
META_COLLECTOR(int_sk_wmem_queued)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = skb->sk->sk_wmem_queued;
}
META_COLLECTOR(int_sk_fwd_alloc)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = skb->sk->sk_forward_alloc;
}
META_COLLECTOR(int_sk_sndbuf)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = skb->sk->sk_sndbuf;
}
META_COLLECTOR(int_sk_alloc)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = (__force int) skb->sk->sk_allocation;
}
META_COLLECTOR(int_sk_hash)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = skb->sk->sk_hash;
}
META_COLLECTOR(int_sk_lingertime)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = skb->sk->sk_lingertime / HZ;
}
META_COLLECTOR(int_sk_err_qlen)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = skb->sk->sk_error_queue.qlen;
}
META_COLLECTOR(int_sk_ack_bl)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = skb->sk->sk_ack_backlog;
}
META_COLLECTOR(int_sk_max_ack_bl)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = skb->sk->sk_max_ack_backlog;
}
META_COLLECTOR(int_sk_prio)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = skb->sk->sk_priority;
}
META_COLLECTOR(int_sk_rcvlowat)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = skb->sk->sk_rcvlowat;
}
META_COLLECTOR(int_sk_rcvtimeo)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = skb->sk->sk_rcvtimeo / HZ;
}
META_COLLECTOR(int_sk_sndtimeo)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = skb->sk->sk_sndtimeo / HZ;
}
META_COLLECTOR(int_sk_sendmsg_off)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = skb->sk->sk_frag.offset;
}
META_COLLECTOR(int_sk_write_pend)
{
- SKIP_NONLOCAL(skb);
+ if (skip_nonlocal(skb)) {
+ *err = -1;
+ return;
+ }
dst->value = skb->sk->sk_write_pending;
}
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index cd81505662b8..c31190e29b90 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -271,11 +271,15 @@ static struct Qdisc *qdisc_match_from_root(struct Qdisc *root, u32 handle)
return NULL;
}
-static void qdisc_list_add(struct Qdisc *q)
+void qdisc_list_add(struct Qdisc *q)
{
+ struct Qdisc *root = qdisc_dev(q)->qdisc;
+
+ WARN_ON_ONCE(root == &noop_qdisc);
if ((q->parent != TC_H_ROOT) && !(q->flags & TCQ_F_INGRESS))
- list_add_tail(&q->list, &qdisc_dev(q)->qdisc->list);
+ list_add_tail(&q->list, &root->list);
}
+EXPORT_SYMBOL(qdisc_list_add);
void qdisc_list_del(struct Qdisc *q)
{
diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c
index 7a42c81a19eb..d5a8a4b2454f 100644
--- a/net/sched/sch_cbq.c
+++ b/net/sched/sch_cbq.c
@@ -1058,7 +1058,8 @@ static void cbq_normalize_quanta(struct cbq_sched_data *q, int prio)
cl->quantum = (cl->weight*cl->allot*q->nclasses[prio])/
q->quanta[prio];
}
- if (cl->quantum <= 0 || cl->quantum>32*qdisc_dev(cl->qdisc)->mtu) {
+ if (cl->quantum <= 0 ||
+ cl->quantum > 32*qdisc_dev(cl->qdisc)->mtu) {
pr_warning("CBQ: class %08x has bad quantum==%ld, repaired.\n",
cl->common.classid, cl->quantum);
cl->quantum = qdisc_dev(cl->qdisc)->mtu/2 + 1;
diff --git a/net/sched/sch_dsmark.c b/net/sched/sch_dsmark.c
index 3886365cc207..0952fd2684e4 100644
--- a/net/sched/sch_dsmark.c
+++ b/net/sched/sch_dsmark.c
@@ -47,7 +47,7 @@ struct dsmark_qdisc_data {
static inline int dsmark_valid_index(struct dsmark_qdisc_data *p, u16 index)
{
- return (index <= p->indices && index > 0);
+ return index <= p->indices && index > 0;
}
/* ------------------------- Class/flow operations ------------------------- */
diff --git a/net/sched/sch_fq.c b/net/sched/sch_fq.c
index 95d843961907..08ef7a42c0e4 100644
--- a/net/sched/sch_fq.c
+++ b/net/sched/sch_fq.c
@@ -47,6 +47,7 @@
#include <linux/rbtree.h>
#include <linux/hash.h>
#include <linux/prefetch.h>
+#include <linux/vmalloc.h>
#include <net/netlink.h>
#include <net/pkt_sched.h>
#include <net/sock.h>
@@ -225,7 +226,7 @@ static struct fq_flow *fq_classify(struct sk_buff *skb, struct fq_sched_data *q)
/* By forcing low order bit to 1, we make sure to not
* collide with a local flow (socket pointers are word aligned)
*/
- sk = (struct sock *)(skb_get_rxhash(skb) | 1L);
+ sk = (struct sock *)(skb_get_hash(skb) | 1L);
}
root = &q->fq_root[hash_32((u32)(long)sk, q->fq_trees_log)];
@@ -578,15 +579,36 @@ static void fq_rehash(struct fq_sched_data *q,
q->stat_gc_flows += fcnt;
}
-static int fq_resize(struct fq_sched_data *q, u32 log)
+static void *fq_alloc_node(size_t sz, int node)
{
+ void *ptr;
+
+ ptr = kmalloc_node(sz, GFP_KERNEL | __GFP_REPEAT | __GFP_NOWARN, node);
+ if (!ptr)
+ ptr = vmalloc_node(sz, node);
+ return ptr;
+}
+
+static void fq_free(void *addr)
+{
+ if (addr && is_vmalloc_addr(addr))
+ vfree(addr);
+ else
+ kfree(addr);
+}
+
+static int fq_resize(struct Qdisc *sch, u32 log)
+{
+ struct fq_sched_data *q = qdisc_priv(sch);
struct rb_root *array;
u32 idx;
if (q->fq_root && log == q->fq_trees_log)
return 0;
- array = kmalloc(sizeof(struct rb_root) << log, GFP_KERNEL);
+ /* If XPS was setup, we can allocate memory on right NUMA node */
+ array = fq_alloc_node(sizeof(struct rb_root) << log,
+ netdev_queue_numa_node_read(sch->dev_queue));
if (!array)
return -ENOMEM;
@@ -595,7 +617,7 @@ static int fq_resize(struct fq_sched_data *q, u32 log)
if (q->fq_root) {
fq_rehash(q, q->fq_root, q->fq_trees_log, array, log);
- kfree(q->fq_root);
+ fq_free(q->fq_root);
}
q->fq_root = array;
q->fq_trees_log = log;
@@ -676,7 +698,7 @@ static int fq_change(struct Qdisc *sch, struct nlattr *opt)
}
if (!err)
- err = fq_resize(q, fq_log);
+ err = fq_resize(sch, fq_log);
while (sch->q.qlen > sch->limit) {
struct sk_buff *skb = fq_dequeue(sch);
@@ -697,7 +719,7 @@ static void fq_destroy(struct Qdisc *sch)
struct fq_sched_data *q = qdisc_priv(sch);
fq_reset(sch);
- kfree(q->fq_root);
+ fq_free(q->fq_root);
qdisc_watchdog_cancel(&q->watchdog);
}
@@ -723,7 +745,7 @@ static int fq_init(struct Qdisc *sch, struct nlattr *opt)
if (opt)
err = fq_change(sch, opt);
else
- err = fq_resize(q, q->fq_trees_log);
+ err = fq_resize(sch, q->fq_trees_log);
return err;
}
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index 922a09406ba7..32bb942d2faa 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -338,13 +338,13 @@ EXPORT_SYMBOL(netif_carrier_off);
cheaper.
*/
-static int noop_enqueue(struct sk_buff *skb, struct Qdisc * qdisc)
+static int noop_enqueue(struct sk_buff *skb, struct Qdisc *qdisc)
{
kfree_skb(skb);
return NET_XMIT_CN;
}
-static struct sk_buff *noop_dequeue(struct Qdisc * qdisc)
+static struct sk_buff *noop_dequeue(struct Qdisc *qdisc)
{
return NULL;
}
@@ -718,8 +718,8 @@ static void attach_default_qdiscs(struct net_device *dev)
} else {
qdisc = qdisc_create_dflt(txq, &mq_qdisc_ops, TC_H_ROOT);
if (qdisc) {
- qdisc->ops->attach(qdisc);
dev->qdisc = qdisc;
+ qdisc->ops->attach(qdisc);
}
}
}
diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c
index 717b2108f852..6b0e854b0115 100644
--- a/net/sched/sch_htb.c
+++ b/net/sched/sch_htb.c
@@ -1337,7 +1337,6 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
struct htb_sched *q = qdisc_priv(sch);
struct htb_class *cl = (struct htb_class *)*arg, *parent;
struct nlattr *opt = tca[TCA_OPTIONS];
- struct qdisc_rate_table *rtab = NULL, *ctab = NULL;
struct nlattr *tb[TCA_HTB_MAX + 1];
struct tc_htb_opt *hopt;
u64 rate64, ceil64;
@@ -1361,16 +1360,11 @@ static int htb_change_class(struct Qdisc *sch, u32 classid,
goto failure;
/* Keeping backward compatible with rate_table based iproute2 tc */
- if (hopt->rate.linklayer == TC_LINKLAYER_UNAWARE) {
- rtab = qdisc_get_rtab(&hopt->rate, tb[TCA_HTB_RTAB]);
- if (rtab)
- qdisc_put_rtab(rtab);
- }
- if (hopt->ceil.linklayer == TC_LINKLAYER_UNAWARE) {
- ctab = qdisc_get_rtab(&hopt->ceil, tb[TCA_HTB_CTAB]);
- if (ctab)
- qdisc_put_rtab(ctab);
- }
+ if (hopt->rate.linklayer == TC_LINKLAYER_UNAWARE)
+ qdisc_put_rtab(qdisc_get_rtab(&hopt->rate, tb[TCA_HTB_RTAB]));
+
+ if (hopt->ceil.linklayer == TC_LINKLAYER_UNAWARE)
+ qdisc_put_rtab(qdisc_get_rtab(&hopt->ceil, tb[TCA_HTB_CTAB]));
if (!cl) { /* new class */
struct Qdisc *new_q;
diff --git a/net/sched/sch_mq.c b/net/sched/sch_mq.c
index 2e56185736d6..a8b2864a696b 100644
--- a/net/sched/sch_mq.c
+++ b/net/sched/sch_mq.c
@@ -78,14 +78,19 @@ static void mq_attach(struct Qdisc *sch)
{
struct net_device *dev = qdisc_dev(sch);
struct mq_sched *priv = qdisc_priv(sch);
- struct Qdisc *qdisc;
+ struct Qdisc *qdisc, *old;
unsigned int ntx;
for (ntx = 0; ntx < dev->num_tx_queues; ntx++) {
qdisc = priv->qdiscs[ntx];
- qdisc = dev_graft_qdisc(qdisc->dev_queue, qdisc);
- if (qdisc)
- qdisc_destroy(qdisc);
+ old = dev_graft_qdisc(qdisc->dev_queue, qdisc);
+ if (old)
+ qdisc_destroy(old);
+#ifdef CONFIG_NET_SCHED
+ if (ntx < dev->real_num_tx_queues)
+ qdisc_list_add(qdisc);
+#endif
+
}
kfree(priv->qdiscs);
priv->qdiscs = NULL;
diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c
index d44c868cb537..6749e2f540d0 100644
--- a/net/sched/sch_mqprio.c
+++ b/net/sched/sch_mqprio.c
@@ -167,15 +167,17 @@ static void mqprio_attach(struct Qdisc *sch)
{
struct net_device *dev = qdisc_dev(sch);
struct mqprio_sched *priv = qdisc_priv(sch);
- struct Qdisc *qdisc;
+ struct Qdisc *qdisc, *old;
unsigned int ntx;
/* Attach underlying qdisc */
for (ntx = 0; ntx < dev->num_tx_queues; ntx++) {
qdisc = priv->qdiscs[ntx];
- qdisc = dev_graft_qdisc(qdisc->dev_queue, qdisc);
- if (qdisc)
- qdisc_destroy(qdisc);
+ old = dev_graft_qdisc(qdisc->dev_queue, qdisc);
+ if (old)
+ qdisc_destroy(old);
+ if (ntx < dev->real_num_tx_queues)
+ qdisc_list_add(qdisc);
}
kfree(priv->qdiscs);
priv->qdiscs = NULL;
diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c
index 2a2b096d9a66..afb050a735fa 100644
--- a/net/sched/sch_multiq.c
+++ b/net/sched/sch_multiq.c
@@ -11,8 +11,7 @@
* 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., 59 Temple
- * Place - Suite 330, Boston, MA 02111-1307 USA.
+ * this program; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Alexander Duyck <alexander.h.duyck@intel.com>
*/
diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c
index bccd52b36e97..f3befd6b4781 100644
--- a/net/sched/sch_netem.c
+++ b/net/sched/sch_netem.c
@@ -729,7 +729,7 @@ static int get_loss_clg(struct Qdisc *sch, const struct nlattr *attr)
nla_for_each_nested(la, attr, rem) {
u16 type = nla_type(la);
- switch(type) {
+ switch (type) {
case NETEM_LOSS_GI: {
const struct tc_netem_gimodel *gi = nla_data(la);
diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c
index d3a1bc26dbfc..76f01e0258df 100644
--- a/net/sched/sch_sfq.c
+++ b/net/sched/sch_sfq.c
@@ -237,10 +237,12 @@ static inline void sfq_link(struct sfq_sched_data *q, sfq_index x)
}
#define sfq_unlink(q, x, n, p) \
- n = q->slots[x].dep.next; \
- p = q->slots[x].dep.prev; \
- sfq_dep_head(q, p)->next = n; \
- sfq_dep_head(q, n)->prev = p
+ do { \
+ n = q->slots[x].dep.next; \
+ p = q->slots[x].dep.prev; \
+ sfq_dep_head(q, p)->next = n; \
+ sfq_dep_head(q, n)->prev = p; \
+ } while (0)
static inline void sfq_dec(struct sfq_sched_data *q, sfq_index x)
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index 31ed008c8e13..5ae609200674 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -22,9 +22,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
@@ -90,14 +89,12 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
/* Initialize the object handling fields. */
atomic_set(&asoc->base.refcnt, 1);
- asoc->base.dead = false;
/* Initialize the bind addr area. */
sctp_bind_addr_init(&asoc->base.bind_addr, ep->base.bind_addr.port);
asoc->state = SCTP_STATE_CLOSED;
asoc->cookie_life = ms_to_ktime(sp->assocparams.sasoc_cookie_life);
- asoc->frag_point = 0;
asoc->user_frag = sp->user_frag;
/* Set the association max_retrans and RTO values from the
@@ -110,8 +107,6 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
asoc->rto_max = msecs_to_jiffies(sp->rtoinfo.srto_max);
asoc->rto_min = msecs_to_jiffies(sp->rtoinfo.srto_min);
- asoc->overall_error_count = 0;
-
/* Initialize the association's heartbeat interval based on the
* sock configured value.
*/
@@ -132,18 +127,15 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
*/
asoc->param_flags = sp->param_flags;
- /* Initialize the maximum mumber of new data packets that can be sent
+ /* Initialize the maximum number of new data packets that can be sent
* in a burst.
*/
asoc->max_burst = sp->max_burst;
/* initialize association timers */
- asoc->timeouts[SCTP_EVENT_TIMEOUT_NONE] = 0;
asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_COOKIE] = asoc->rto_initial;
asoc->timeouts[SCTP_EVENT_TIMEOUT_T1_INIT] = asoc->rto_initial;
asoc->timeouts[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN] = asoc->rto_initial;
- asoc->timeouts[SCTP_EVENT_TIMEOUT_T3_RTX] = 0;
- asoc->timeouts[SCTP_EVENT_TIMEOUT_T4_RTO] = 0;
/* sctpimpguide Section 2.12.2
* If the 'T5-shutdown-guard' timer is used, it SHOULD be set to the
@@ -152,7 +144,6 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
asoc->timeouts[SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD]
= 5 * asoc->rto_max;
- asoc->timeouts[SCTP_EVENT_TIMEOUT_HEARTBEAT] = 0;
asoc->timeouts[SCTP_EVENT_TIMEOUT_SACK] = asoc->sackdelay;
asoc->timeouts[SCTP_EVENT_TIMEOUT_AUTOCLOSE] = sp->autoclose * HZ;
@@ -172,11 +163,6 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
asoc->max_init_timeo =
msecs_to_jiffies(sp->initmsg.sinit_max_init_timeo);
- /* Allocate storage for the ssnmap after the inbound and outbound
- * streams have been negotiated during Init.
- */
- asoc->ssnmap = NULL;
-
/* Set the local window size for receive.
* This is also the rcvbuf space per association.
* RFC 6 - A SCTP receiver MUST be able to receive a minimum of
@@ -189,25 +175,15 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
asoc->a_rwnd = asoc->rwnd;
- asoc->rwnd_over = 0;
- asoc->rwnd_press = 0;
-
/* Use my own max window until I learn something better. */
asoc->peer.rwnd = SCTP_DEFAULT_MAXWINDOW;
- /* Set the sndbuf size for transmit. */
- asoc->sndbuf_used = 0;
-
/* Initialize the receive memory counter */
atomic_set(&asoc->rmem_alloc, 0);
init_waitqueue_head(&asoc->wait);
asoc->c.my_vtag = sctp_generate_tag(ep);
- asoc->peer.i.init_tag = 0; /* INIT needs a vtag of 0. */
- asoc->c.peer_vtag = 0;
- asoc->c.my_ttag = 0;
- asoc->c.peer_ttag = 0;
asoc->c.my_port = ep->base.bind_addr.port;
asoc->c.initial_tsn = sctp_generate_tsn(ep);
@@ -218,7 +194,6 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
asoc->adv_peer_ack_point = asoc->ctsn_ack_point;
asoc->highest_sacked = asoc->ctsn_ack_point;
asoc->last_cwr_tsn = asoc->ctsn_ack_point;
- asoc->unack_data = 0;
/* ADDIP Section 4.1 Asconf Chunk Procedures
*
@@ -237,7 +212,6 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
/* Make an empty list of remote transport addresses. */
INIT_LIST_HEAD(&asoc->peer.transport_addr_list);
- asoc->peer.transport_count = 0;
/* RFC 2960 5.1 Normal Establishment of an Association
*
@@ -251,20 +225,15 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
* already received one packet.]
*/
asoc->peer.sack_needed = 1;
- asoc->peer.sack_cnt = 0;
asoc->peer.sack_generation = 1;
/* Assume that the peer will tell us if he recognizes ASCONF
* as part of INIT exchange.
- * The sctp_addip_noauth option is there for backward compatibilty
+ * The sctp_addip_noauth option is there for backward compatibility
* and will revert old behavior.
*/
- asoc->peer.asconf_capable = 0;
if (net->sctp.addip_noauth)
asoc->peer.asconf_capable = 1;
- asoc->asconf_addr_del_pending = NULL;
- asoc->src_out_of_asoc_ok = 0;
- asoc->new_transport = NULL;
/* Create an input queue. */
sctp_inq_init(&asoc->base.inqueue);
@@ -276,12 +245,6 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
if (!sctp_ulpq_init(&asoc->ulpq, asoc))
goto fail_init;
- memset(&asoc->peer.tsn_map, 0, sizeof(struct sctp_tsnmap));
-
- asoc->need_ecne = 0;
-
- asoc->assoc_id = 0;
-
/* Assume that peer would support both address types unless we are
* told otherwise.
*/
@@ -297,9 +260,6 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
asoc->default_timetolive = sp->default_timetolive;
asoc->default_rcv_context = sp->default_rcv_context;
- /* SCTP_GET_ASSOC_STATS COUNTERS */
- memset(&asoc->stats, 0, sizeof(struct sctp_priv_assoc_stats));
-
/* AUTH related initializations */
INIT_LIST_HEAD(&asoc->endpoint_shared_keys);
err = sctp_auth_asoc_copy_shkeys(ep, asoc, gfp);
@@ -307,9 +267,7 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
goto fail_init;
asoc->active_key_id = ep->active_key_id;
- asoc->asoc_shared_key = NULL;
- asoc->default_hmac_id = 0;
/* Save the hmacs and chunks list into this association */
if (ep->auth_hmacs_list)
memcpy(asoc->c.auth_hmacs, ep->auth_hmacs_list,
@@ -994,17 +952,13 @@ int sctp_cmp_addr_exact(const union sctp_addr *ss1,
*/
struct sctp_chunk *sctp_get_ecne_prepend(struct sctp_association *asoc)
{
- struct sctp_chunk *chunk;
+ if (!asoc->need_ecne)
+ return NULL;
/* Send ECNE if needed.
* Not being able to allocate a chunk here is not deadly.
*/
- if (asoc->need_ecne)
- chunk = sctp_make_ecne(asoc, asoc->last_ecne_tsn);
- else
- chunk = NULL;
-
- return chunk;
+ return sctp_make_ecne(asoc, asoc->last_ecne_tsn);
}
/*
@@ -1265,7 +1219,7 @@ void sctp_assoc_update(struct sctp_association *asoc,
}
}
- /* SCTP-AUTH: Save the peer parameters from the new assocaitions
+ /* SCTP-AUTH: Save the peer parameters from the new associations
* and also move the association shared keys over
*/
kfree(asoc->peer.peer_random);
@@ -1393,7 +1347,7 @@ void sctp_assoc_sync_pmtu(struct sock *sk, struct sctp_association *asoc)
}
/* Should we send a SACK to update our peer? */
-static inline int sctp_peer_needs_update(struct sctp_association *asoc)
+static inline bool sctp_peer_needs_update(struct sctp_association *asoc)
{
struct net *net = sock_net(asoc->base.sk);
switch (asoc->state) {
@@ -1405,12 +1359,12 @@ static inline int sctp_peer_needs_update(struct sctp_association *asoc)
((asoc->rwnd - asoc->a_rwnd) >= max_t(__u32,
(asoc->base.sk->sk_rcvbuf >> net->sctp.rwnd_upd_shift),
asoc->pathmtu)))
- return 1;
+ return true;
break;
default:
break;
}
- return 0;
+ return false;
}
/* Increase asoc's rwnd by len and send any window update SACK if needed. */
@@ -1490,7 +1444,7 @@ void sctp_assoc_rwnd_decrease(struct sctp_association *asoc, unsigned int len)
/* If we've reached or overflowed our receive buffer, announce
* a 0 rwnd if rwnd would still be positive. Store the
- * the pottential pressure overflow so that the window can be restored
+ * the potential pressure overflow so that the window can be restored
* back to original value.
*/
if (rx_count >= asoc->base.sk->sk_rcvbuf)
diff --git a/net/sctp/auth.c b/net/sctp/auth.c
index 46b5977978a1..5c9f64c1c906 100644
--- a/net/sctp/auth.c
+++ b/net/sctp/auth.c
@@ -16,9 +16,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c
index 077bb070052b..871cdf9567e6 100644
--- a/net/sctp/bind_addr.c
+++ b/net/sctp/bind_addr.c
@@ -21,9 +21,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c
index f2044fcb9dd1..5573e425b0c0 100644
--- a/net/sctp/chunk.c
+++ b/net/sctp/chunk.c
@@ -18,9 +18,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
diff --git a/net/sctp/command.c b/net/sctp/command.c
index 3d9a9ff69c03..dd7375851618 100644
--- a/net/sctp/command.c
+++ b/net/sctp/command.c
@@ -19,9 +19,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
diff --git a/net/sctp/debug.c b/net/sctp/debug.c
index e89015d8935a..95d7b15dad21 100644
--- a/net/sctp/debug.c
+++ b/net/sctp/debug.c
@@ -22,9 +22,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c
index 09b8daac87c8..6ffb6c1b13b7 100644
--- a/net/sctp/endpointola.c
+++ b/net/sctp/endpointola.c
@@ -23,9 +23,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
diff --git a/net/sctp/input.c b/net/sctp/input.c
index 98b69bbecdd9..2a192a7c5d81 100644
--- a/net/sctp/input.c
+++ b/net/sctp/input.c
@@ -23,9 +23,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
@@ -537,8 +536,7 @@ struct sock *sctp_err_lookup(struct net *net, int family, struct sk_buff *skb,
return sk;
out:
- if (asoc)
- sctp_association_put(asoc);
+ sctp_association_put(asoc);
return NULL;
}
@@ -546,8 +544,7 @@ out:
void sctp_err_finish(struct sock *sk, struct sctp_association *asoc)
{
sctp_bh_unlock_sock(sk);
- if (asoc)
- sctp_association_put(asoc);
+ sctp_association_put(asoc);
}
/*
diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c
index 5856932fdc38..4de12afa13d4 100644
--- a/net/sctp/inqueue.c
+++ b/net/sctp/inqueue.c
@@ -24,9 +24,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 7567e6f1a920..32db816ffbaa 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -21,9 +21,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
diff --git a/net/sctp/objcnt.c b/net/sctp/objcnt.c
index 647396baa56f..0c28e8a55324 100644
--- a/net/sctp/objcnt.c
+++ b/net/sctp/objcnt.c
@@ -20,9 +20,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
diff --git a/net/sctp/output.c b/net/sctp/output.c
index 0fb140f8f088..3f55823279d9 100644
--- a/net/sctp/output.c
+++ b/net/sctp/output.c
@@ -20,9 +20,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
index f51ba985a36e..b6b09f3f1a81 100644
--- a/net/sctp/outqueue.c
+++ b/net/sctp/outqueue.c
@@ -22,9 +22,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
diff --git a/net/sctp/primitive.c b/net/sctp/primitive.c
index ce1ffd811775..ab8d9f96a177 100644
--- a/net/sctp/primitive.c
+++ b/net/sctp/primitive.c
@@ -23,9 +23,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
diff --git a/net/sctp/proc.c b/net/sctp/proc.c
index 0c0642156842..de32e14f7398 100644
--- a/net/sctp/proc.c
+++ b/net/sctp/proc.c
@@ -16,9 +16,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 5e17092f4ada..19bd4c5bdae9 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -23,9 +23,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index fe690320b1e4..d9aaf9641aaa 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -23,9 +23,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index 1a6eef39ab2f..02b7ad1ff467 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -22,9 +22,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index a26065be7289..ee02771d8b9c 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -22,9 +22,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c
index c5999b2dde7d..a4f17437fb22 100644
--- a/net/sctp/sm_statetable.c
+++ b/net/sctp/sm_statetable.c
@@ -22,9 +22,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 42b709c95cf3..d39fd0c2c4cf 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -28,9 +28,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
diff --git a/net/sctp/ssnmap.c b/net/sctp/ssnmap.c
index 6007124aefa0..b9c8521c1a98 100644
--- a/net/sctp/ssnmap.c
+++ b/net/sctp/ssnmap.c
@@ -18,9 +18,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c
index b0565afb61c7..9dd5ac084663 100644
--- a/net/sctp/sysctl.c
+++ b/net/sctp/sysctl.c
@@ -19,9 +19,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index efc46ffed1fd..d0810dc5f079 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -24,9 +24,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
diff --git a/net/sctp/tsnmap.c b/net/sctp/tsnmap.c
index fbda20028285..7635f9f2311d 100644
--- a/net/sctp/tsnmap.c
+++ b/net/sctp/tsnmap.c
@@ -21,9 +21,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c
index 81089ed65456..85c64658bd0b 100644
--- a/net/sctp/ulpevent.c
+++ b/net/sctp/ulpevent.c
@@ -22,9 +22,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c
index 1c1484ed605d..a67470083be8 100644
--- a/net/sctp/ulpqueue.c
+++ b/net/sctp/ulpqueue.c
@@ -21,9 +21,8 @@
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with GNU CC; see the file COPYING. If not, write to
- * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * along with GNU CC; see the file COPYING. If not, see
+ * <http://www.gnu.org/licenses/>.
*
* Please send any bug reports or fixes you make to the
* email address(es):
diff --git a/net/socket.c b/net/socket.c
index e83c416708af..879933aaed4c 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1445,48 +1445,61 @@ SYSCALL_DEFINE4(socketpair, int, family, int, type, int, protocol,
err = fd1;
goto out_release_both;
}
+
fd2 = get_unused_fd_flags(flags);
if (unlikely(fd2 < 0)) {
err = fd2;
- put_unused_fd(fd1);
- goto out_release_both;
+ goto out_put_unused_1;
}
newfile1 = sock_alloc_file(sock1, flags, NULL);
if (unlikely(IS_ERR(newfile1))) {
err = PTR_ERR(newfile1);
- put_unused_fd(fd1);
- put_unused_fd(fd2);
- goto out_release_both;
+ goto out_put_unused_both;
}
newfile2 = sock_alloc_file(sock2, flags, NULL);
if (IS_ERR(newfile2)) {
err = PTR_ERR(newfile2);
- fput(newfile1);
- put_unused_fd(fd1);
- put_unused_fd(fd2);
- sock_release(sock2);
- goto out;
+ goto out_fput_1;
}
+ err = put_user(fd1, &usockvec[0]);
+ if (err)
+ goto out_fput_both;
+
+ err = put_user(fd2, &usockvec[1]);
+ if (err)
+ goto out_fput_both;
+
audit_fd_pair(fd1, fd2);
+
fd_install(fd1, newfile1);
fd_install(fd2, newfile2);
/* fd1 and fd2 may be already another descriptors.
* Not kernel problem.
*/
- err = put_user(fd1, &usockvec[0]);
- if (!err)
- err = put_user(fd2, &usockvec[1]);
- if (!err)
- return 0;
+ return 0;
- sys_close(fd2);
- sys_close(fd1);
- return err;
+out_fput_both:
+ fput(newfile2);
+ fput(newfile1);
+ put_unused_fd(fd2);
+ put_unused_fd(fd1);
+ goto out;
+
+out_fput_1:
+ fput(newfile1);
+ put_unused_fd(fd2);
+ put_unused_fd(fd1);
+ sock_release(sock2);
+ goto out;
+out_put_unused_both:
+ put_unused_fd(fd2);
+out_put_unused_1:
+ put_unused_fd(fd1);
out_release_both:
sock_release(sock2);
out_release_1:
@@ -2968,11 +2981,8 @@ static int bond_ioctl(struct net *net, unsigned int cmd,
struct compat_ifreq __user *ifr32)
{
struct ifreq kifr;
- struct ifreq __user *uifr;
mm_segment_t old_fs;
int err;
- u32 data;
- void __user *datap;
switch (cmd) {
case SIOCBONDENSLAVE:
@@ -2989,26 +2999,13 @@ static int bond_ioctl(struct net *net, unsigned int cmd,
set_fs(old_fs);
return err;
- case SIOCBONDSLAVEINFOQUERY:
- case SIOCBONDINFOQUERY:
- uifr = compat_alloc_user_space(sizeof(*uifr));
- if (copy_in_user(&uifr->ifr_name, &ifr32->ifr_name, IFNAMSIZ))
- return -EFAULT;
-
- if (get_user(data, &ifr32->ifr_ifru.ifru_data))
- return -EFAULT;
-
- datap = compat_ptr(data);
- if (put_user(datap, &uifr->ifr_ifru.ifru_data))
- return -EFAULT;
-
- return dev_ioctl(net, cmd, uifr);
default:
return -ENOIOCTLCMD;
}
}
-static int siocdevprivate_ioctl(struct net *net, unsigned int cmd,
+/* Handle ioctls that use ifreq::ifr_data and just need struct ifreq converted */
+static int compat_ifr_data_ioctl(struct net *net, unsigned int cmd,
struct compat_ifreq __user *u_ifreq32)
{
struct ifreq __user *u_ifreq64;
@@ -3019,19 +3016,16 @@ static int siocdevprivate_ioctl(struct net *net, unsigned int cmd,
if (copy_from_user(&tmp_buf[0], &(u_ifreq32->ifr_ifrn.ifrn_name[0]),
IFNAMSIZ))
return -EFAULT;
- if (__get_user(data32, &u_ifreq32->ifr_ifru.ifru_data))
+ if (get_user(data32, &u_ifreq32->ifr_ifru.ifru_data))
return -EFAULT;
data64 = compat_ptr(data32);
u_ifreq64 = compat_alloc_user_space(sizeof(*u_ifreq64));
- /* Don't check these user accesses, just let that get trapped
- * in the ioctl handler instead.
- */
if (copy_to_user(&u_ifreq64->ifr_ifrn.ifrn_name[0], &tmp_buf[0],
IFNAMSIZ))
return -EFAULT;
- if (__put_user(data64, &u_ifreq64->ifr_ifru.ifru_data))
+ if (put_user(data64, &u_ifreq64->ifr_ifru.ifru_data))
return -EFAULT;
return dev_ioctl(net, cmd, u_ifreq64);
@@ -3111,27 +3105,6 @@ static int compat_sioc_ifmap(struct net *net, unsigned int cmd,
return err;
}
-static int compat_siocshwtstamp(struct net *net, struct compat_ifreq __user *uifr32)
-{
- void __user *uptr;
- compat_uptr_t uptr32;
- struct ifreq __user *uifr;
-
- uifr = compat_alloc_user_space(sizeof(*uifr));
- if (copy_in_user(uifr, uifr32, sizeof(struct compat_ifreq)))
- return -EFAULT;
-
- if (get_user(uptr32, &uifr32->ifr_data))
- return -EFAULT;
-
- uptr = compat_ptr(uptr32);
-
- if (put_user(uptr, &uifr->ifr_data))
- return -EFAULT;
-
- return dev_ioctl(net, SIOCSHWTSTAMP, uifr);
-}
-
struct rtentry32 {
u32 rt_pad1;
struct sockaddr rt_dst; /* target address */
@@ -3243,7 +3216,7 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
struct net *net = sock_net(sk);
if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15))
- return siocdevprivate_ioctl(net, cmd, argp);
+ return compat_ifr_data_ioctl(net, cmd, argp);
switch (cmd) {
case SIOCSIFBR:
@@ -3263,8 +3236,6 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
case SIOCBONDENSLAVE:
case SIOCBONDRELEASE:
case SIOCBONDSETHWADDR:
- case SIOCBONDSLAVEINFOQUERY:
- case SIOCBONDINFOQUERY:
case SIOCBONDCHANGEACTIVE:
return bond_ioctl(net, cmd, argp);
case SIOCADDRT:
@@ -3274,8 +3245,11 @@ static int compat_sock_ioctl_trans(struct file *file, struct socket *sock,
return do_siocgstamp(net, sock, cmd, argp);
case SIOCGSTAMPNS:
return do_siocgstampns(net, sock, cmd, argp);
+ case SIOCBONDSLAVEINFOQUERY:
+ case SIOCBONDINFOQUERY:
case SIOCSHWTSTAMP:
- return compat_siocshwtstamp(net, argp);
+ case SIOCGHWTSTAMP:
+ return compat_ifr_data_ioctl(net, cmd, argp);
case FIOSETOWN:
case SIOCSPGRP:
diff --git a/net/tipc/bcast.c b/net/tipc/bcast.c
index 0d4402587fdf..4c2a80b3c01e 100644
--- a/net/tipc/bcast.c
+++ b/net/tipc/bcast.c
@@ -621,12 +621,6 @@ static int tipc_bcbearer_send(struct sk_buff *buf, struct tipc_bearer *unused1,
if (!p)
break; /* No more bearers to try */
- if (tipc_bearer_blocked(p)) {
- if (!s || tipc_bearer_blocked(s))
- continue; /* Can't use either bearer */
- b = s;
- }
-
tipc_nmap_diff(&bcbearer->remains, &b->nodes,
&bcbearer->remains_new);
if (bcbearer->remains_new.count == bcbearer->remains.count)
diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c
index 3f9707a16d06..3bb5f266b0eb 100644
--- a/net/tipc/bearer.c
+++ b/net/tipc/bearer.c
@@ -1,8 +1,8 @@
/*
* net/tipc/bearer.c: TIPC bearer code
*
- * Copyright (c) 1996-2006, Ericsson AB
- * Copyright (c) 2004-2006, 2010-2011, Wind River Systems
+ * Copyright (c) 1996-2006, 2013, Ericsson AB
+ * Copyright (c) 2004-2006, 2010-2013, Wind River Systems
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,8 +41,13 @@
#define MAX_ADDR_STR 60
-static struct tipc_media *media_list[MAX_MEDIA];
-static u32 media_count;
+static struct tipc_media * const media_info_array[] = {
+ &eth_media_info,
+#ifdef CONFIG_TIPC_MEDIA_IB
+ &ib_media_info,
+#endif
+ NULL
+};
struct tipc_bearer tipc_bearers[MAX_BEARERS];
@@ -55,11 +60,11 @@ struct tipc_media *tipc_media_find(const char *name)
{
u32 i;
- for (i = 0; i < media_count; i++) {
- if (!strcmp(media_list[i]->name, name))
- return media_list[i];
+ for (i = 0; media_info_array[i] != NULL; i++) {
+ if (!strcmp(media_info_array[i]->name, name))
+ break;
}
- return NULL;
+ return media_info_array[i];
}
/**
@@ -69,44 +74,11 @@ static struct tipc_media *media_find_id(u8 type)
{
u32 i;
- for (i = 0; i < media_count; i++) {
- if (media_list[i]->type_id == type)
- return media_list[i];
+ for (i = 0; media_info_array[i] != NULL; i++) {
+ if (media_info_array[i]->type_id == type)
+ break;
}
- return NULL;
-}
-
-/**
- * tipc_register_media - register a media type
- *
- * Bearers for this media type must be activated separately at a later stage.
- */
-int tipc_register_media(struct tipc_media *m_ptr)
-{
- int res = -EINVAL;
-
- write_lock_bh(&tipc_net_lock);
-
- if ((strlen(m_ptr->name) + 1) > TIPC_MAX_MEDIA_NAME)
- goto exit;
- if (m_ptr->priority > TIPC_MAX_LINK_PRI)
- goto exit;
- if ((m_ptr->tolerance < TIPC_MIN_LINK_TOL) ||
- (m_ptr->tolerance > TIPC_MAX_LINK_TOL))
- goto exit;
- if (media_count >= MAX_MEDIA)
- goto exit;
- if (tipc_media_find(m_ptr->name) || media_find_id(m_ptr->type_id))
- goto exit;
-
- media_list[media_count] = m_ptr;
- media_count++;
- res = 0;
-exit:
- write_unlock_bh(&tipc_net_lock);
- if (res)
- pr_warn("Media <%s> registration error\n", m_ptr->name);
- return res;
+ return media_info_array[i];
}
/**
@@ -144,13 +116,11 @@ struct sk_buff *tipc_media_get_names(void)
if (!buf)
return NULL;
- read_lock_bh(&tipc_net_lock);
- for (i = 0; i < media_count; i++) {
+ for (i = 0; media_info_array[i] != NULL; i++) {
tipc_cfg_append_tlv(buf, TIPC_TLV_MEDIA_NAME,
- media_list[i]->name,
- strlen(media_list[i]->name) + 1);
+ media_info_array[i]->name,
+ strlen(media_info_array[i]->name) + 1);
}
- read_unlock_bh(&tipc_net_lock);
return buf;
}
@@ -239,7 +209,7 @@ struct tipc_bearer *tipc_bearer_find_interface(const char *if_name)
struct sk_buff *tipc_bearer_get_names(void)
{
struct sk_buff *buf;
- struct tipc_bearer *b_ptr;
+ struct tipc_bearer *b;
int i, j;
buf = tipc_cfg_reply_alloc(MAX_BEARERS * TLV_SPACE(TIPC_MAX_BEARER_NAME));
@@ -247,13 +217,13 @@ struct sk_buff *tipc_bearer_get_names(void)
return NULL;
read_lock_bh(&tipc_net_lock);
- for (i = 0; i < media_count; i++) {
+ for (i = 0; media_info_array[i] != NULL; i++) {
for (j = 0; j < MAX_BEARERS; j++) {
- b_ptr = &tipc_bearers[j];
- if (b_ptr->active && (b_ptr->media == media_list[i])) {
+ b = &tipc_bearers[j];
+ if (b->active && (b->media == media_info_array[i])) {
tipc_cfg_append_tlv(buf, TIPC_TLV_BEARER_NAME,
- b_ptr->name,
- strlen(b_ptr->name) + 1);
+ b->name,
+ strlen(b->name) + 1);
}
}
}
@@ -275,31 +245,6 @@ void tipc_bearer_remove_dest(struct tipc_bearer *b_ptr, u32 dest)
tipc_disc_remove_dest(b_ptr->link_req);
}
-/*
- * Interrupt enabling new requests after bearer blocking:
- * See bearer_send().
- */
-void tipc_continue(struct tipc_bearer *b)
-{
- spin_lock_bh(&b->lock);
- b->blocked = 0;
- spin_unlock_bh(&b->lock);
-}
-
-/*
- * tipc_bearer_blocked - determines if bearer is currently blocked
- */
-int tipc_bearer_blocked(struct tipc_bearer *b)
-{
- int res;
-
- spin_lock_bh(&b->lock);
- res = b->blocked;
- spin_unlock_bh(&b->lock);
-
- return res;
-}
-
/**
* tipc_enable_bearer - enable bearer with the given name
*/
@@ -387,6 +332,7 @@ restart:
b_ptr = &tipc_bearers[bearer_id];
strcpy(b_ptr->name, name);
+ b_ptr->media = m_ptr;
res = m_ptr->enable_media(b_ptr);
if (res) {
pr_warn("Bearer <%s> rejected, enable failure (%d)\n",
@@ -395,7 +341,6 @@ restart:
}
b_ptr->identity = bearer_id;
- b_ptr->media = m_ptr;
b_ptr->tolerance = m_ptr->tolerance;
b_ptr->window = m_ptr->window;
b_ptr->net_plane = bearer_id + 'A';
@@ -420,17 +365,16 @@ exit:
}
/**
- * tipc_block_bearer - Block the bearer, and reset all its links
+ * tipc_reset_bearer - Reset all links established over this bearer
*/
-int tipc_block_bearer(struct tipc_bearer *b_ptr)
+static int tipc_reset_bearer(struct tipc_bearer *b_ptr)
{
struct tipc_link *l_ptr;
struct tipc_link *temp_l_ptr;
read_lock_bh(&tipc_net_lock);
- pr_info("Blocking bearer <%s>\n", b_ptr->name);
+ pr_info("Resetting bearer <%s>\n", b_ptr->name);
spin_lock_bh(&b_ptr->lock);
- b_ptr->blocked = 1;
list_for_each_entry_safe(l_ptr, temp_l_ptr, &b_ptr->links, link_list) {
struct tipc_node *n_ptr = l_ptr->owner;
@@ -456,7 +400,6 @@ static void bearer_disable(struct tipc_bearer *b_ptr)
pr_info("Disabling bearer <%s>\n", b_ptr->name);
spin_lock_bh(&b_ptr->lock);
- b_ptr->blocked = 1;
b_ptr->media->disable_media(b_ptr);
list_for_each_entry_safe(l_ptr, temp_l_ptr, &b_ptr->links, link_list) {
tipc_link_delete(l_ptr);
@@ -490,6 +433,211 @@ int tipc_disable_bearer(const char *name)
}
+/* tipc_l2_media_addr_set - initialize Ethernet media address structure
+ *
+ * Media-dependent "value" field stores MAC address in first 6 bytes
+ * and zeroes out the remaining bytes.
+ */
+void tipc_l2_media_addr_set(const struct tipc_bearer *b,
+ struct tipc_media_addr *a, char *mac)
+{
+ int len = b->media->hwaddr_len;
+
+ if (unlikely(sizeof(a->value) < len)) {
+ WARN_ONCE(1, "Media length invalid\n");
+ return;
+ }
+
+ memcpy(a->value, mac, len);
+ memset(a->value + len, 0, sizeof(a->value) - len);
+ a->media_id = b->media->type_id;
+ a->broadcast = !memcmp(mac, b->bcast_addr.value, len);
+}
+
+int tipc_enable_l2_media(struct tipc_bearer *b)
+{
+ struct net_device *dev;
+ char *driver_name = strchr((const char *)b->name, ':') + 1;
+
+ /* Find device with specified name */
+ dev = dev_get_by_name(&init_net, driver_name);
+ if (!dev)
+ return -ENODEV;
+
+ /* Associate TIPC bearer with Ethernet bearer */
+ b->media_ptr = dev;
+ memset(b->bcast_addr.value, 0, sizeof(b->bcast_addr.value));
+ memcpy(b->bcast_addr.value, dev->broadcast, b->media->hwaddr_len);
+ b->bcast_addr.media_id = b->media->type_id;
+ b->bcast_addr.broadcast = 1;
+ b->mtu = dev->mtu;
+ tipc_l2_media_addr_set(b, &b->addr, (char *)dev->dev_addr);
+ rcu_assign_pointer(dev->tipc_ptr, b);
+ return 0;
+}
+
+/* tipc_disable_l2_media - detach TIPC bearer from an Ethernet interface
+ *
+ * Mark Ethernet bearer as inactive so that incoming buffers are thrown away,
+ * then get worker thread to complete bearer cleanup. (Can't do cleanup
+ * here because cleanup code needs to sleep and caller holds spinlocks.)
+ */
+void tipc_disable_l2_media(struct tipc_bearer *b)
+{
+ struct net_device *dev = (struct net_device *)b->media_ptr;
+ RCU_INIT_POINTER(dev->tipc_ptr, NULL);
+ dev_put(dev);
+}
+
+/**
+ * tipc_l2_send_msg - send a TIPC packet out over an Ethernet interface
+ * @buf: the packet to be sent
+ * @b_ptr: the bearer throught which the packet is to be sent
+ * @dest: peer destination address
+ */
+int tipc_l2_send_msg(struct sk_buff *buf, struct tipc_bearer *b,
+ struct tipc_media_addr *dest)
+{
+ struct sk_buff *clone;
+ int delta;
+ struct net_device *dev = (struct net_device *)b->media_ptr;
+
+ clone = skb_clone(buf, GFP_ATOMIC);
+ if (!clone)
+ return 0;
+
+ delta = dev->hard_header_len - skb_headroom(buf);
+ if ((delta > 0) &&
+ pskb_expand_head(clone, SKB_DATA_ALIGN(delta), 0, GFP_ATOMIC)) {
+ kfree_skb(clone);
+ return 0;
+ }
+
+ skb_reset_network_header(clone);
+ clone->dev = dev;
+ clone->protocol = htons(ETH_P_TIPC);
+ dev_hard_header(clone, dev, ETH_P_TIPC, dest->value,
+ dev->dev_addr, clone->len);
+ dev_queue_xmit(clone);
+ return 0;
+}
+
+/* tipc_bearer_send- sends buffer to destination over bearer
+ *
+ * IMPORTANT:
+ * The media send routine must not alter the buffer being passed in
+ * as it may be needed for later retransmission!
+ */
+void tipc_bearer_send(struct tipc_bearer *b, struct sk_buff *buf,
+ struct tipc_media_addr *dest)
+{
+ b->media->send_msg(buf, b, dest);
+}
+
+/**
+ * tipc_l2_rcv_msg - handle incoming TIPC message from an interface
+ * @buf: the received packet
+ * @dev: the net device that the packet was received on
+ * @pt: the packet_type structure which was used to register this handler
+ * @orig_dev: the original receive net device in case the device is a bond
+ *
+ * Accept only packets explicitly sent to this node, or broadcast packets;
+ * ignores packets sent using interface multicast, and traffic sent to other
+ * nodes (which can happen if interface is running in promiscuous mode).
+ */
+static int tipc_l2_rcv_msg(struct sk_buff *buf, struct net_device *dev,
+ struct packet_type *pt, struct net_device *orig_dev)
+{
+ struct tipc_bearer *b_ptr;
+
+ if (!net_eq(dev_net(dev), &init_net)) {
+ kfree_skb(buf);
+ return NET_RX_DROP;
+ }
+
+ rcu_read_lock();
+ b_ptr = rcu_dereference(dev->tipc_ptr);
+ if (likely(b_ptr)) {
+ if (likely(buf->pkt_type <= PACKET_BROADCAST)) {
+ buf->next = NULL;
+ tipc_recv_msg(buf, b_ptr);
+ rcu_read_unlock();
+ return NET_RX_SUCCESS;
+ }
+ }
+ rcu_read_unlock();
+
+ kfree_skb(buf);
+ return NET_RX_DROP;
+}
+
+/**
+ * tipc_l2_device_event - handle device events from network device
+ * @nb: the context of the notification
+ * @evt: the type of event
+ * @ptr: the net device that the event was on
+ *
+ * This function is called by the Ethernet driver in case of link
+ * change event.
+ */
+static int tipc_l2_device_event(struct notifier_block *nb, unsigned long evt,
+ void *ptr)
+{
+ struct tipc_bearer *b_ptr;
+ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+
+ if (!net_eq(dev_net(dev), &init_net))
+ return NOTIFY_DONE;
+
+ rcu_read_lock();
+ b_ptr = rcu_dereference(dev->tipc_ptr);
+ if (!b_ptr) {
+ rcu_read_unlock();
+ return NOTIFY_DONE;
+ }
+
+ b_ptr->mtu = dev->mtu;
+
+ switch (evt) {
+ case NETDEV_CHANGE:
+ if (netif_carrier_ok(dev))
+ break;
+ case NETDEV_DOWN:
+ case NETDEV_CHANGEMTU:
+ case NETDEV_CHANGEADDR:
+ tipc_reset_bearer(b_ptr);
+ break;
+ case NETDEV_UNREGISTER:
+ case NETDEV_CHANGENAME:
+ tipc_disable_bearer(b_ptr->name);
+ break;
+ }
+ rcu_read_unlock();
+
+ return NOTIFY_OK;
+}
+
+static struct packet_type tipc_packet_type __read_mostly = {
+ .type = __constant_htons(ETH_P_TIPC),
+ .func = tipc_l2_rcv_msg,
+};
+
+static struct notifier_block notifier = {
+ .notifier_call = tipc_l2_device_event,
+ .priority = 0,
+};
+
+int tipc_bearer_setup(void)
+{
+ dev_add_pack(&tipc_packet_type);
+ return register_netdevice_notifier(&notifier);
+}
+
+void tipc_bearer_cleanup(void)
+{
+ unregister_netdevice_notifier(&notifier);
+ dev_remove_pack(&tipc_packet_type);
+}
void tipc_bearer_stop(void)
{
@@ -499,5 +647,4 @@ void tipc_bearer_stop(void)
if (tipc_bearers[i].active)
bearer_disable(&tipc_bearers[i]);
}
- media_count = 0;
}
diff --git a/net/tipc/bearer.h b/net/tipc/bearer.h
index e5e04be6fffa..fa95c34ca926 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, Ericsson AB
+ * Copyright (c) 1996-2006, 2013, Ericsson AB
* Copyright (c) 2005, 2010-2011, Wind River Systems
* All rights reserved.
*
@@ -73,18 +73,18 @@ struct tipc_media_addr {
struct tipc_bearer;
/**
- * struct tipc_media - TIPC media information available to internal users
+ * struct tipc_media - Media specific info exposed to generic bearer layer
* @send_msg: routine which handles buffer transmission
* @enable_media: routine which enables a media
* @disable_media: routine which disables a media
* @addr2str: routine which converts media address to string
* @addr2msg: routine which converts media address to protocol message area
* @msg2addr: routine which converts media address from protocol message area
- * @bcast_addr: media address used in broadcasting
* @priority: default link (and bearer) priority
* @tolerance: default time (in ms) before declaring link failure
* @window: default window (in packets) before declaring link congestion
* @type_id: TIPC media identifier
+ * @hwaddr_len: TIPC media address len
* @name: media name
*/
struct tipc_media {
@@ -101,18 +101,20 @@ struct tipc_media {
u32 tolerance;
u32 window;
u32 type_id;
+ u32 hwaddr_len;
char name[TIPC_MAX_MEDIA_NAME];
};
/**
- * struct tipc_bearer - TIPC bearer structure
+ * struct tipc_bearer - Generic TIPC bearer structure
+ * @dev: ptr to associated network device
* @usr_handle: pointer to additional media-specific information about bearer
* @mtu: max packet size bearer can support
- * @blocked: non-zero if bearer is blocked
* @lock: spinlock for controlling access to bearer
* @addr: media-specific address associated with bearer
* @name: bearer name (format = media:interface)
* @media: ptr to media structure associated with bearer
+ * @bcast_addr: media address used in broadcasting
* @priority: default link priority for bearer
* @window: default window size for bearer
* @tolerance: default link tolerance for bearer
@@ -128,9 +130,8 @@ struct tipc_media {
* care of initializing all other fields.
*/
struct tipc_bearer {
- void *usr_handle; /* initalized by media */
+ void *media_ptr; /* initalized by media */
u32 mtu; /* initalized by media */
- int blocked; /* initalized by media */
struct tipc_media_addr addr; /* initalized by media */
char name[TIPC_MAX_BEARER_NAME];
spinlock_t lock;
@@ -159,34 +160,31 @@ extern struct tipc_bearer tipc_bearers[];
/*
* TIPC routines available to supported media types
*/
-int tipc_register_media(struct tipc_media *m_ptr);
void tipc_recv_msg(struct sk_buff *buf, struct tipc_bearer *tb_ptr);
-int tipc_block_bearer(struct tipc_bearer *b_ptr);
-void tipc_continue(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);
/*
* Routines made available to TIPC by supported media types
*/
-int tipc_eth_media_start(void);
-void tipc_eth_media_stop(void);
+extern struct tipc_media eth_media_info;
#ifdef CONFIG_TIPC_MEDIA_IB
-int tipc_ib_media_start(void);
-void tipc_ib_media_stop(void);
-#else
-static inline int tipc_ib_media_start(void) { return 0; }
-static inline void tipc_ib_media_stop(void) { return; }
+extern struct tipc_media ib_media_info;
#endif
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);
struct sk_buff *tipc_media_get_names(void);
+void tipc_l2_media_addr_set(const struct tipc_bearer *b,
+ struct tipc_media_addr *a, char *mac);
+int tipc_enable_l2_media(struct tipc_bearer *b);
+void tipc_disable_l2_media(struct tipc_bearer *b);
+int tipc_l2_send_msg(struct sk_buff *buf, struct tipc_bearer *b,
+ struct tipc_media_addr *dest);
struct sk_buff *tipc_bearer_get_names(void);
void tipc_bearer_add_dest(struct tipc_bearer *b_ptr, u32 dest);
@@ -194,20 +192,10 @@ void tipc_bearer_remove_dest(struct tipc_bearer *b_ptr, u32 dest);
struct tipc_bearer *tipc_bearer_find(const char *name);
struct tipc_bearer *tipc_bearer_find_interface(const char *if_name);
struct tipc_media *tipc_media_find(const char *name);
-int tipc_bearer_blocked(struct tipc_bearer *b_ptr);
+int tipc_bearer_setup(void);
+void tipc_bearer_cleanup(void);
void tipc_bearer_stop(void);
-
-/**
- * tipc_bearer_send- sends buffer to destination over bearer
- *
- * IMPORTANT:
- * The media send routine must not alter the buffer being passed in
- * as it may be needed for later retransmission!
- */
-static inline void tipc_bearer_send(struct tipc_bearer *b, struct sk_buff *buf,
- struct tipc_media_addr *dest)
-{
- b->media->send_msg(buf, b, dest);
-}
+void tipc_bearer_send(struct tipc_bearer *b, struct sk_buff *buf,
+ struct tipc_media_addr *dest);
#endif /* _TIPC_BEARER_H */
diff --git a/net/tipc/core.c b/net/tipc/core.c
index c6d3f75a9e1b..f9e88d8b04ca 100644
--- a/net/tipc/core.c
+++ b/net/tipc/core.c
@@ -82,8 +82,7 @@ struct sk_buff *tipc_buf_acquire(u32 size)
static void tipc_core_stop_net(void)
{
tipc_net_stop();
- tipc_eth_media_stop();
- tipc_ib_media_stop();
+ tipc_bearer_cleanup();
}
/**
@@ -94,10 +93,7 @@ int tipc_core_start_net(unsigned long addr)
int res;
tipc_net_start(addr);
- res = tipc_eth_media_start();
- if (res < 0)
- goto err;
- res = tipc_ib_media_start();
+ res = tipc_bearer_setup();
if (res < 0)
goto err;
return res;
diff --git a/net/tipc/core.h b/net/tipc/core.h
index 94895d4e86ab..1ff477b0450d 100644
--- a/net/tipc/core.h
+++ b/net/tipc/core.h
@@ -47,7 +47,7 @@
#include <linux/mm.h>
#include <linux/timer.h>
#include <linux/string.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <linux/atomic.h>
#include <asm/hardirq.h>
diff --git a/net/tipc/discover.c b/net/tipc/discover.c
index ecc758c6eacf..bc849f1efa16 100644
--- a/net/tipc/discover.c
+++ b/net/tipc/discover.c
@@ -239,7 +239,7 @@ void tipc_disc_recv_msg(struct sk_buff *buf, struct tipc_bearer *b_ptr)
/* Accept discovery message & send response, if necessary */
link_fully_up = link_working_working(link);
- if ((type == DSC_REQ_MSG) && !link_fully_up && !b_ptr->blocked) {
+ if ((type == DSC_REQ_MSG) && !link_fully_up) {
rbuf = tipc_disc_init_msg(DSC_RESP_MSG, orig, b_ptr);
if (rbuf) {
tipc_bearer_send(b_ptr, rbuf, &media_addr);
@@ -288,16 +288,6 @@ void tipc_disc_remove_dest(struct tipc_link_req *req)
}
/**
- * disc_send_msg - send link setup request message
- * @req: ptr to link request structure
- */
-static void disc_send_msg(struct tipc_link_req *req)
-{
- if (!req->bearer->blocked)
- tipc_bearer_send(req->bearer, req->buf, &req->dest);
-}
-
-/**
* disc_timeout - send a periodic link setup request
* @req: ptr to link request structure
*
@@ -322,7 +312,8 @@ static void disc_timeout(struct tipc_link_req *req)
* hold at fast polling rate if don't have any associated nodes,
* otherwise hold at slow polling rate
*/
- disc_send_msg(req);
+ tipc_bearer_send(req->bearer, req->buf, &req->dest);
+
req->timer_intv *= 2;
if (req->num_nodes)
@@ -368,7 +359,7 @@ int tipc_disc_create(struct tipc_bearer *b_ptr, struct tipc_media_addr *dest,
k_init_timer(&req->timer, (Handler)disc_timeout, (unsigned long)req);
k_start_timer(&req->timer, req->timer_intv);
b_ptr->link_req = req;
- disc_send_msg(req);
+ tipc_bearer_send(req->bearer, req->buf, &req->dest);
return 0;
}
diff --git a/net/tipc/eth_media.c b/net/tipc/eth_media.c
index f80d59f5a161..67cf3f935dba 100644
--- a/net/tipc/eth_media.c
+++ b/net/tipc/eth_media.c
@@ -1,7 +1,7 @@
/*
* net/tipc/eth_media.c: Ethernet bearer support for TIPC
*
- * Copyright (c) 2001-2007, Ericsson AB
+ * Copyright (c) 2001-2007, 2013, Ericsson AB
* Copyright (c) 2005-2008, 2011-2013, Wind River Systems
* All rights reserved.
*
@@ -37,259 +37,11 @@
#include "core.h"
#include "bearer.h"
-#define MAX_ETH_MEDIA MAX_BEARERS
-
#define ETH_ADDR_OFFSET 4 /* message header offset of MAC address */
-/**
- * struct eth_media - Ethernet bearer data structure
- * @bearer: ptr to associated "generic" bearer structure
- * @dev: ptr to associated Ethernet network device
- * @tipc_packet_type: used in binding TIPC to Ethernet driver
- * @setup: work item used when enabling bearer
- * @cleanup: work item used when disabling bearer
- */
-struct eth_media {
- struct tipc_bearer *bearer;
- struct net_device *dev;
- struct packet_type tipc_packet_type;
- struct work_struct setup;
- struct work_struct cleanup;
-};
-
-static struct tipc_media eth_media_info;
-static struct eth_media eth_media_array[MAX_ETH_MEDIA];
-static int eth_started;
-
-static int recv_notification(struct notifier_block *nb, unsigned long evt,
- void *dv);
-/*
- * Network device notifier info
- */
-static struct notifier_block notifier = {
- .notifier_call = recv_notification,
- .priority = 0
-};
-
-/**
- * eth_media_addr_set - initialize Ethernet media address structure
- *
- * Media-dependent "value" field stores MAC address in first 6 bytes
- * and zeroes out the remaining bytes.
- */
-static void eth_media_addr_set(const struct tipc_bearer *tb_ptr,
- struct tipc_media_addr *a, char *mac)
-{
- memcpy(a->value, mac, ETH_ALEN);
- memset(a->value + ETH_ALEN, 0, sizeof(a->value) - ETH_ALEN);
- a->media_id = TIPC_MEDIA_TYPE_ETH;
- a->broadcast = !memcmp(mac, tb_ptr->bcast_addr.value, ETH_ALEN);
-}
-
-/**
- * send_msg - send a TIPC message out over an Ethernet interface
- */
-static int send_msg(struct sk_buff *buf, struct tipc_bearer *tb_ptr,
- struct tipc_media_addr *dest)
-{
- struct sk_buff *clone;
- struct net_device *dev;
- int delta;
-
- clone = skb_clone(buf, GFP_ATOMIC);
- if (!clone)
- return 0;
-
- dev = ((struct eth_media *)(tb_ptr->usr_handle))->dev;
- delta = dev->hard_header_len - skb_headroom(buf);
-
- if ((delta > 0) &&
- pskb_expand_head(clone, SKB_DATA_ALIGN(delta), 0, GFP_ATOMIC)) {
- kfree_skb(clone);
- return 0;
- }
-
- skb_reset_network_header(clone);
- clone->dev = dev;
- clone->protocol = htons(ETH_P_TIPC);
- dev_hard_header(clone, dev, ETH_P_TIPC, dest->value,
- dev->dev_addr, clone->len);
- dev_queue_xmit(clone);
- return 0;
-}
-
-/**
- * recv_msg - handle incoming TIPC message from an Ethernet interface
- *
- * Accept only packets explicitly sent to this node, or broadcast packets;
- * ignores packets sent using Ethernet multicast, and traffic sent to other
- * nodes (which can happen if interface is running in promiscuous mode).
- */
-static int recv_msg(struct sk_buff *buf, struct net_device *dev,
- struct packet_type *pt, struct net_device *orig_dev)
-{
- struct eth_media *eb_ptr = (struct eth_media *)pt->af_packet_priv;
-
- if (!net_eq(dev_net(dev), &init_net)) {
- kfree_skb(buf);
- return NET_RX_DROP;
- }
-
- if (likely(eb_ptr->bearer)) {
- if (likely(buf->pkt_type <= PACKET_BROADCAST)) {
- buf->next = NULL;
- tipc_recv_msg(buf, eb_ptr->bearer);
- return NET_RX_SUCCESS;
- }
- }
- kfree_skb(buf);
- return NET_RX_DROP;
-}
-
-/**
- * setup_media - setup association between Ethernet bearer and interface
- */
-static void setup_media(struct work_struct *work)
-{
- struct eth_media *eb_ptr =
- container_of(work, struct eth_media, setup);
-
- dev_add_pack(&eb_ptr->tipc_packet_type);
-}
-
-/**
- * enable_media - attach TIPC bearer to an Ethernet interface
- */
-static int enable_media(struct tipc_bearer *tb_ptr)
-{
- struct net_device *dev;
- struct eth_media *eb_ptr = &eth_media_array[0];
- struct eth_media *stop = &eth_media_array[MAX_ETH_MEDIA];
- char *driver_name = strchr((const char *)tb_ptr->name, ':') + 1;
- int pending_dev = 0;
-
- /* Find unused Ethernet bearer structure */
- while (eb_ptr->dev) {
- if (!eb_ptr->bearer)
- pending_dev++;
- if (++eb_ptr == stop)
- return pending_dev ? -EAGAIN : -EDQUOT;
- }
-
- /* Find device with specified name */
- dev = dev_get_by_name(&init_net, driver_name);
- if (!dev)
- return -ENODEV;
-
- /* Create Ethernet bearer for device */
- eb_ptr->dev = dev;
- eb_ptr->tipc_packet_type.type = htons(ETH_P_TIPC);
- eb_ptr->tipc_packet_type.dev = dev;
- eb_ptr->tipc_packet_type.func = recv_msg;
- eb_ptr->tipc_packet_type.af_packet_priv = eb_ptr;
- INIT_LIST_HEAD(&(eb_ptr->tipc_packet_type.list));
- INIT_WORK(&eb_ptr->setup, setup_media);
- schedule_work(&eb_ptr->setup);
-
- /* Associate TIPC bearer with Ethernet bearer */
- eb_ptr->bearer = tb_ptr;
- tb_ptr->usr_handle = (void *)eb_ptr;
- memset(tb_ptr->bcast_addr.value, 0, sizeof(tb_ptr->bcast_addr.value));
- memcpy(tb_ptr->bcast_addr.value, dev->broadcast, ETH_ALEN);
- tb_ptr->bcast_addr.media_id = TIPC_MEDIA_TYPE_ETH;
- tb_ptr->bcast_addr.broadcast = 1;
- tb_ptr->mtu = dev->mtu;
- tb_ptr->blocked = 0;
- eth_media_addr_set(tb_ptr, &tb_ptr->addr, (char *)dev->dev_addr);
- return 0;
-}
-
-/**
- * cleanup_media - break association between Ethernet bearer and interface
- *
- * This routine must be invoked from a work queue because it can sleep.
- */
-static void cleanup_media(struct work_struct *work)
-{
- struct eth_media *eb_ptr =
- container_of(work, struct eth_media, cleanup);
-
- dev_remove_pack(&eb_ptr->tipc_packet_type);
- dev_put(eb_ptr->dev);
- eb_ptr->dev = NULL;
-}
-
-/**
- * disable_media - detach TIPC bearer from an Ethernet interface
- *
- * Mark Ethernet bearer as inactive so that incoming buffers are thrown away,
- * then get worker thread to complete bearer cleanup. (Can't do cleanup
- * here because cleanup code needs to sleep and caller holds spinlocks.)
- */
-static void disable_media(struct tipc_bearer *tb_ptr)
-{
- struct eth_media *eb_ptr = (struct eth_media *)tb_ptr->usr_handle;
-
- eb_ptr->bearer = NULL;
- INIT_WORK(&eb_ptr->cleanup, cleanup_media);
- schedule_work(&eb_ptr->cleanup);
-}
-
-/**
- * recv_notification - handle device updates from OS
- *
- * Change the state of the Ethernet bearer (if any) associated with the
- * specified device.
- */
-static int recv_notification(struct notifier_block *nb, unsigned long evt,
- void *ptr)
-{
- struct net_device *dev = netdev_notifier_info_to_dev(ptr);
- struct eth_media *eb_ptr = &eth_media_array[0];
- struct eth_media *stop = &eth_media_array[MAX_ETH_MEDIA];
-
- if (!net_eq(dev_net(dev), &init_net))
- return NOTIFY_DONE;
-
- while ((eb_ptr->dev != dev)) {
- if (++eb_ptr == stop)
- return NOTIFY_DONE; /* couldn't find device */
- }
- if (!eb_ptr->bearer)
- return NOTIFY_DONE; /* bearer had been disabled */
-
- eb_ptr->bearer->mtu = dev->mtu;
-
- switch (evt) {
- case NETDEV_CHANGE:
- if (netif_carrier_ok(dev))
- tipc_continue(eb_ptr->bearer);
- else
- tipc_block_bearer(eb_ptr->bearer);
- break;
- case NETDEV_UP:
- tipc_continue(eb_ptr->bearer);
- break;
- case NETDEV_DOWN:
- tipc_block_bearer(eb_ptr->bearer);
- break;
- case NETDEV_CHANGEMTU:
- case NETDEV_CHANGEADDR:
- tipc_block_bearer(eb_ptr->bearer);
- tipc_continue(eb_ptr->bearer);
- break;
- case NETDEV_UNREGISTER:
- case NETDEV_CHANGENAME:
- tipc_disable_bearer(eb_ptr->bearer->name);
- break;
- }
- return NOTIFY_OK;
-}
-
-/**
- * eth_addr2str - convert Ethernet address to string
- */
-static int eth_addr2str(struct tipc_media_addr *a, char *str_buf, int str_size)
+/* convert Ethernet address to string */
+static int tipc_eth_addr2str(struct tipc_media_addr *a, char *str_buf,
+ int str_size)
{
if (str_size < 18) /* 18 = strlen("aa:bb:cc:dd:ee:ff\0") */
return 1;
@@ -298,10 +50,8 @@ static int eth_addr2str(struct tipc_media_addr *a, char *str_buf, int str_size)
return 0;
}
-/**
- * eth_str2addr - convert Ethernet address format to message header format
- */
-static int eth_addr2msg(struct tipc_media_addr *a, char *msg_area)
+/* convert Ethernet address format to message header format */
+static int tipc_eth_addr2msg(struct tipc_media_addr *a, char *msg_area)
{
memset(msg_area, 0, TIPC_MEDIA_ADDR_SIZE);
msg_area[TIPC_MEDIA_TYPE_OFFSET] = TIPC_MEDIA_TYPE_ETH;
@@ -309,68 +59,30 @@ static int eth_addr2msg(struct tipc_media_addr *a, char *msg_area)
return 0;
}
-/**
- * eth_str2addr - convert message header address format to Ethernet format
- */
-static int eth_msg2addr(const struct tipc_bearer *tb_ptr,
- struct tipc_media_addr *a, char *msg_area)
+/* convert message header address format to Ethernet format */
+static int tipc_eth_msg2addr(const struct tipc_bearer *tb_ptr,
+ struct tipc_media_addr *a, char *msg_area)
{
if (msg_area[TIPC_MEDIA_TYPE_OFFSET] != TIPC_MEDIA_TYPE_ETH)
return 1;
- eth_media_addr_set(tb_ptr, a, msg_area + ETH_ADDR_OFFSET);
+ tipc_l2_media_addr_set(tb_ptr, a, msg_area + ETH_ADDR_OFFSET);
return 0;
}
-/*
- * Ethernet media registration info
- */
-static struct tipc_media eth_media_info = {
- .send_msg = send_msg,
- .enable_media = enable_media,
- .disable_media = disable_media,
- .addr2str = eth_addr2str,
- .addr2msg = eth_addr2msg,
- .msg2addr = eth_msg2addr,
+/* Ethernet media registration info */
+struct tipc_media eth_media_info = {
+ .send_msg = tipc_l2_send_msg,
+ .enable_media = tipc_enable_l2_media,
+ .disable_media = tipc_disable_l2_media,
+ .addr2str = tipc_eth_addr2str,
+ .addr2msg = tipc_eth_addr2msg,
+ .msg2addr = tipc_eth_msg2addr,
.priority = TIPC_DEF_LINK_PRI,
.tolerance = TIPC_DEF_LINK_TOL,
.window = TIPC_DEF_LINK_WIN,
.type_id = TIPC_MEDIA_TYPE_ETH,
+ .hwaddr_len = ETH_ALEN,
.name = "eth"
};
-/**
- * tipc_eth_media_start - activate Ethernet bearer support
- *
- * Register Ethernet media type with TIPC bearer code. Also register
- * with OS for notifications about device state changes.
- */
-int tipc_eth_media_start(void)
-{
- int res;
-
- if (eth_started)
- return -EINVAL;
-
- res = tipc_register_media(&eth_media_info);
- if (res)
- return res;
-
- res = register_netdevice_notifier(&notifier);
- if (!res)
- eth_started = 1;
- return res;
-}
-
-/**
- * tipc_eth_media_stop - deactivate Ethernet bearer support
- */
-void tipc_eth_media_stop(void)
-{
- if (!eth_started)
- return;
-
- flush_scheduled_work();
- unregister_netdevice_notifier(&notifier);
- eth_started = 0;
-}
diff --git a/net/tipc/ib_media.c b/net/tipc/ib_media.c
index c13989297464..844a77e25828 100644
--- a/net/tipc/ib_media.c
+++ b/net/tipc/ib_media.c
@@ -42,252 +42,9 @@
#include "core.h"
#include "bearer.h"
-#define MAX_IB_MEDIA MAX_BEARERS
-
-/**
- * struct ib_media - Infiniband media data structure
- * @bearer: ptr to associated "generic" bearer structure
- * @dev: ptr to associated Infiniband network device
- * @tipc_packet_type: used in binding TIPC to Infiniband driver
- * @cleanup: work item used when disabling bearer
- */
-
-struct ib_media {
- struct tipc_bearer *bearer;
- struct net_device *dev;
- struct packet_type tipc_packet_type;
- struct work_struct setup;
- struct work_struct cleanup;
-};
-
-static struct tipc_media ib_media_info;
-static struct ib_media ib_media_array[MAX_IB_MEDIA];
-static int ib_started;
-
-/**
- * ib_media_addr_set - initialize Infiniband media address structure
- *
- * Media-dependent "value" field stores MAC address in first 6 bytes
- * and zeroes out the remaining bytes.
- */
-static void ib_media_addr_set(const struct tipc_bearer *tb_ptr,
- struct tipc_media_addr *a, char *mac)
-{
- BUILD_BUG_ON(sizeof(a->value) < INFINIBAND_ALEN);
- memcpy(a->value, mac, INFINIBAND_ALEN);
- a->media_id = TIPC_MEDIA_TYPE_IB;
- a->broadcast = !memcmp(mac, tb_ptr->bcast_addr.value, INFINIBAND_ALEN);
-}
-
-/**
- * send_msg - send a TIPC message out over an InfiniBand interface
- */
-static int send_msg(struct sk_buff *buf, struct tipc_bearer *tb_ptr,
- struct tipc_media_addr *dest)
-{
- struct sk_buff *clone;
- struct net_device *dev;
- int delta;
-
- clone = skb_clone(buf, GFP_ATOMIC);
- if (!clone)
- return 0;
-
- dev = ((struct ib_media *)(tb_ptr->usr_handle))->dev;
- delta = dev->hard_header_len - skb_headroom(buf);
-
- if ((delta > 0) &&
- pskb_expand_head(clone, SKB_DATA_ALIGN(delta), 0, GFP_ATOMIC)) {
- kfree_skb(clone);
- return 0;
- }
-
- skb_reset_network_header(clone);
- clone->dev = dev;
- clone->protocol = htons(ETH_P_TIPC);
- dev_hard_header(clone, dev, ETH_P_TIPC, dest->value,
- dev->dev_addr, clone->len);
- dev_queue_xmit(clone);
- return 0;
-}
-
-/**
- * recv_msg - handle incoming TIPC message from an InfiniBand interface
- *
- * Accept only packets explicitly sent to this node, or broadcast packets;
- * ignores packets sent using InfiniBand multicast, and traffic sent to other
- * nodes (which can happen if interface is running in promiscuous mode).
- */
-static int recv_msg(struct sk_buff *buf, struct net_device *dev,
- struct packet_type *pt, struct net_device *orig_dev)
-{
- struct ib_media *ib_ptr = (struct ib_media *)pt->af_packet_priv;
-
- if (!net_eq(dev_net(dev), &init_net)) {
- kfree_skb(buf);
- return NET_RX_DROP;
- }
-
- if (likely(ib_ptr->bearer)) {
- if (likely(buf->pkt_type <= PACKET_BROADCAST)) {
- buf->next = NULL;
- tipc_recv_msg(buf, ib_ptr->bearer);
- return NET_RX_SUCCESS;
- }
- }
- kfree_skb(buf);
- return NET_RX_DROP;
-}
-
-/**
- * setup_bearer - setup association between InfiniBand bearer and interface
- */
-static void setup_media(struct work_struct *work)
-{
- struct ib_media *ib_ptr =
- container_of(work, struct ib_media, setup);
-
- dev_add_pack(&ib_ptr->tipc_packet_type);
-}
-
-/**
- * enable_media - attach TIPC bearer to an InfiniBand interface
- */
-static int enable_media(struct tipc_bearer *tb_ptr)
-{
- struct net_device *dev;
- struct ib_media *ib_ptr = &ib_media_array[0];
- struct ib_media *stop = &ib_media_array[MAX_IB_MEDIA];
- char *driver_name = strchr((const char *)tb_ptr->name, ':') + 1;
- int pending_dev = 0;
-
- /* Find unused InfiniBand bearer structure */
- while (ib_ptr->dev) {
- if (!ib_ptr->bearer)
- pending_dev++;
- if (++ib_ptr == stop)
- return pending_dev ? -EAGAIN : -EDQUOT;
- }
-
- /* Find device with specified name */
- dev = dev_get_by_name(&init_net, driver_name);
- if (!dev)
- return -ENODEV;
-
- /* Create InfiniBand bearer for device */
- ib_ptr->dev = dev;
- ib_ptr->tipc_packet_type.type = htons(ETH_P_TIPC);
- ib_ptr->tipc_packet_type.dev = dev;
- ib_ptr->tipc_packet_type.func = recv_msg;
- ib_ptr->tipc_packet_type.af_packet_priv = ib_ptr;
- INIT_LIST_HEAD(&(ib_ptr->tipc_packet_type.list));
- INIT_WORK(&ib_ptr->setup, setup_media);
- schedule_work(&ib_ptr->setup);
-
- /* Associate TIPC bearer with InfiniBand bearer */
- ib_ptr->bearer = tb_ptr;
- tb_ptr->usr_handle = (void *)ib_ptr;
- memset(tb_ptr->bcast_addr.value, 0, sizeof(tb_ptr->bcast_addr.value));
- memcpy(tb_ptr->bcast_addr.value, dev->broadcast, INFINIBAND_ALEN);
- tb_ptr->bcast_addr.media_id = TIPC_MEDIA_TYPE_IB;
- tb_ptr->bcast_addr.broadcast = 1;
- tb_ptr->mtu = dev->mtu;
- tb_ptr->blocked = 0;
- ib_media_addr_set(tb_ptr, &tb_ptr->addr, (char *)dev->dev_addr);
- return 0;
-}
-
-/**
- * cleanup_bearer - break association between InfiniBand bearer and interface
- *
- * This routine must be invoked from a work queue because it can sleep.
- */
-static void cleanup_bearer(struct work_struct *work)
-{
- struct ib_media *ib_ptr =
- container_of(work, struct ib_media, cleanup);
-
- dev_remove_pack(&ib_ptr->tipc_packet_type);
- dev_put(ib_ptr->dev);
- ib_ptr->dev = NULL;
-}
-
-/**
- * disable_media - detach TIPC bearer from an InfiniBand interface
- *
- * Mark InfiniBand bearer as inactive so that incoming buffers are thrown away,
- * then get worker thread to complete bearer cleanup. (Can't do cleanup
- * here because cleanup code needs to sleep and caller holds spinlocks.)
- */
-static void disable_media(struct tipc_bearer *tb_ptr)
-{
- struct ib_media *ib_ptr = (struct ib_media *)tb_ptr->usr_handle;
-
- ib_ptr->bearer = NULL;
- INIT_WORK(&ib_ptr->cleanup, cleanup_bearer);
- schedule_work(&ib_ptr->cleanup);
-}
-
-/**
- * recv_notification - handle device updates from OS
- *
- * Change the state of the InfiniBand bearer (if any) associated with the
- * specified device.
- */
-static int recv_notification(struct notifier_block *nb, unsigned long evt,
- void *ptr)
-{
- struct net_device *dev = netdev_notifier_info_to_dev(ptr);
- struct ib_media *ib_ptr = &ib_media_array[0];
- struct ib_media *stop = &ib_media_array[MAX_IB_MEDIA];
-
- if (!net_eq(dev_net(dev), &init_net))
- return NOTIFY_DONE;
-
- while ((ib_ptr->dev != dev)) {
- if (++ib_ptr == stop)
- return NOTIFY_DONE; /* couldn't find device */
- }
- if (!ib_ptr->bearer)
- return NOTIFY_DONE; /* bearer had been disabled */
-
- ib_ptr->bearer->mtu = dev->mtu;
-
- switch (evt) {
- case NETDEV_CHANGE:
- if (netif_carrier_ok(dev))
- tipc_continue(ib_ptr->bearer);
- else
- tipc_block_bearer(ib_ptr->bearer);
- break;
- case NETDEV_UP:
- tipc_continue(ib_ptr->bearer);
- break;
- case NETDEV_DOWN:
- tipc_block_bearer(ib_ptr->bearer);
- break;
- case NETDEV_CHANGEMTU:
- case NETDEV_CHANGEADDR:
- tipc_block_bearer(ib_ptr->bearer);
- tipc_continue(ib_ptr->bearer);
- break;
- case NETDEV_UNREGISTER:
- case NETDEV_CHANGENAME:
- tipc_disable_bearer(ib_ptr->bearer->name);
- break;
- }
- return NOTIFY_OK;
-}
-
-static struct notifier_block notifier = {
- .notifier_call = recv_notification,
- .priority = 0,
-};
-
-/**
- * ib_addr2str - convert InfiniBand address to string
- */
-static int ib_addr2str(struct tipc_media_addr *a, char *str_buf, int str_size)
+/* convert InfiniBand address to string */
+static int tipc_ib_addr2str(struct tipc_media_addr *a, char *str_buf,
+ int str_size)
{
if (str_size < 60) /* 60 = 19 * strlen("xx:") + strlen("xx\0") */
return 1;
@@ -297,10 +54,8 @@ static int ib_addr2str(struct tipc_media_addr *a, char *str_buf, int str_size)
return 0;
}
-/**
- * ib_addr2msg - convert InfiniBand address format to message header format
- */
-static int ib_addr2msg(struct tipc_media_addr *a, char *msg_area)
+/* convert InfiniBand address format to message header format */
+static int tipc_ib_addr2msg(struct tipc_media_addr *a, char *msg_area)
{
memset(msg_area, 0, TIPC_MEDIA_ADDR_SIZE);
msg_area[TIPC_MEDIA_TYPE_OFFSET] = TIPC_MEDIA_TYPE_IB;
@@ -308,65 +63,27 @@ static int ib_addr2msg(struct tipc_media_addr *a, char *msg_area)
return 0;
}
-/**
- * ib_msg2addr - convert message header address format to InfiniBand format
- */
-static int ib_msg2addr(const struct tipc_bearer *tb_ptr,
- struct tipc_media_addr *a, char *msg_area)
+/* convert message header address format to InfiniBand format */
+static int tipc_ib_msg2addr(const struct tipc_bearer *tb_ptr,
+ struct tipc_media_addr *a, char *msg_area)
{
- ib_media_addr_set(tb_ptr, a, msg_area);
+ tipc_l2_media_addr_set(tb_ptr, a, msg_area);
return 0;
}
-/*
- * InfiniBand media registration info
- */
-static struct tipc_media ib_media_info = {
- .send_msg = send_msg,
- .enable_media = enable_media,
- .disable_media = disable_media,
- .addr2str = ib_addr2str,
- .addr2msg = ib_addr2msg,
- .msg2addr = ib_msg2addr,
+/* InfiniBand media registration info */
+struct tipc_media ib_media_info = {
+ .send_msg = tipc_l2_send_msg,
+ .enable_media = tipc_enable_l2_media,
+ .disable_media = tipc_disable_l2_media,
+ .addr2str = tipc_ib_addr2str,
+ .addr2msg = tipc_ib_addr2msg,
+ .msg2addr = tipc_ib_msg2addr,
.priority = TIPC_DEF_LINK_PRI,
.tolerance = TIPC_DEF_LINK_TOL,
.window = TIPC_DEF_LINK_WIN,
.type_id = TIPC_MEDIA_TYPE_IB,
+ .hwaddr_len = INFINIBAND_ALEN,
.name = "ib"
};
-/**
- * tipc_ib_media_start - activate InfiniBand bearer support
- *
- * Register InfiniBand media type with TIPC bearer code. Also register
- * with OS for notifications about device state changes.
- */
-int tipc_ib_media_start(void)
-{
- int res;
-
- if (ib_started)
- return -EINVAL;
-
- res = tipc_register_media(&ib_media_info);
- if (res)
- return res;
-
- res = register_netdevice_notifier(&notifier);
- if (!res)
- ib_started = 1;
- return res;
-}
-
-/**
- * tipc_ib_media_stop - deactivate InfiniBand bearer support
- */
-void tipc_ib_media_stop(void)
-{
- if (!ib_started)
- return;
-
- flush_scheduled_work();
- unregister_netdevice_notifier(&notifier);
- ib_started = 0;
-}
diff --git a/net/tipc/link.c b/net/tipc/link.c
index 69cd9bf3f561..3d73144a1ccc 100644
--- a/net/tipc/link.c
+++ b/net/tipc/link.c
@@ -386,14 +386,7 @@ exit:
*/
static void link_release_outqueue(struct tipc_link *l_ptr)
{
- struct sk_buff *buf = l_ptr->first_out;
- struct sk_buff *next;
-
- while (buf) {
- next = buf->next;
- kfree_skb(buf);
- buf = next;
- }
+ kfree_skb_list(l_ptr->first_out);
l_ptr->first_out = NULL;
l_ptr->out_queue_size = 0;
}
@@ -415,32 +408,15 @@ void tipc_link_reset_fragments(struct tipc_link *l_ptr)
*/
void tipc_link_stop(struct tipc_link *l_ptr)
{
- struct sk_buff *buf;
- struct sk_buff *next;
-
- buf = l_ptr->oldest_deferred_in;
- while (buf) {
- next = buf->next;
- kfree_skb(buf);
- buf = next;
- }
-
- buf = l_ptr->first_out;
- while (buf) {
- next = buf->next;
- kfree_skb(buf);
- buf = next;
- }
-
+ kfree_skb_list(l_ptr->oldest_deferred_in);
+ kfree_skb_list(l_ptr->first_out);
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)
{
- struct sk_buff *buf;
u32 prev_state = l_ptr->state;
u32 checkpoint = l_ptr->next_in_no;
int was_active_link = tipc_link_is_active(l_ptr);
@@ -471,12 +447,7 @@ void tipc_link_reset(struct tipc_link *l_ptr)
link_release_outqueue(l_ptr);
kfree_skb(l_ptr->proto_msg_queue);
l_ptr->proto_msg_queue = NULL;
- buf = l_ptr->oldest_deferred_in;
- while (buf) {
- struct sk_buff *next = buf->next;
- kfree_skb(buf);
- buf = next;
- }
+ kfree_skb_list(l_ptr->oldest_deferred_in);
if (!list_empty(&l_ptr->waiting_ports))
tipc_link_wakeup_ports(l_ptr, 1);
@@ -517,10 +488,11 @@ static void link_state_event(struct tipc_link *l_ptr, unsigned int event)
if (!l_ptr->started && (event != STARTING_EVT))
return; /* Not yet. */
- if (link_blocked(l_ptr)) {
+ /* Check whether changeover is going on */
+ if (l_ptr->exp_msg_count) {
if (event == TIMEOUT_EVT)
link_set_timer(l_ptr, cont_intv);
- return; /* Changeover going on */
+ return;
}
switch (l_ptr->state) {
@@ -790,8 +762,7 @@ int tipc_link_send_buf(struct tipc_link *l_ptr, struct sk_buff *buf)
return link_send_long_buf(l_ptr, buf);
/* Packet can be queued or sent. */
- if (likely(!tipc_bearer_blocked(l_ptr->b_ptr) &&
- !link_congested(l_ptr))) {
+ if (likely(!link_congested(l_ptr))) {
link_add_to_outqueue(l_ptr, buf, msg);
tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr);
@@ -957,14 +928,13 @@ static int link_send_buf_fast(struct tipc_link *l_ptr, struct sk_buff *buf,
if (likely(!link_congested(l_ptr))) {
if (likely(msg_size(msg) <= l_ptr->max_pkt)) {
- if (likely(!tipc_bearer_blocked(l_ptr->b_ptr))) {
- link_add_to_outqueue(l_ptr, buf, msg);
- tipc_bearer_send(l_ptr->b_ptr, buf,
- &l_ptr->media_addr);
- l_ptr->unacked_window = 0;
- return res;
- }
- } else
+ link_add_to_outqueue(l_ptr, buf, msg);
+ tipc_bearer_send(l_ptr->b_ptr, buf,
+ &l_ptr->media_addr);
+ l_ptr->unacked_window = 0;
+ return res;
+ }
+ else
*used_max_pkt = l_ptr->max_pkt;
}
return tipc_link_send_buf(l_ptr, buf); /* All other cases */
@@ -1013,8 +983,7 @@ exit:
}
/* Exit if link (or bearer) is congested */
- if (link_congested(l_ptr) ||
- tipc_bearer_blocked(l_ptr->b_ptr)) {
+ if (link_congested(l_ptr)) {
res = link_schedule_port(l_ptr,
sender->ref, res);
goto exit;
@@ -1127,10 +1096,7 @@ again:
if (copy_from_user(buf->data + fragm_crs, sect_crs, sz)) {
res = -EFAULT;
error:
- for (; buf_chain; buf_chain = buf) {
- buf = buf_chain->next;
- kfree_skb(buf_chain);
- }
+ kfree_skb_list(buf_chain);
return res;
}
sect_crs += sz;
@@ -1180,18 +1146,12 @@ error:
if (l_ptr->max_pkt < max_pkt) {
sender->max_pkt = l_ptr->max_pkt;
tipc_node_unlock(node);
- for (; buf_chain; buf_chain = buf) {
- buf = buf_chain->next;
- kfree_skb(buf_chain);
- }
+ kfree_skb_list(buf_chain);
goto again;
}
} else {
reject:
- for (; buf_chain; buf_chain = buf) {
- buf = buf_chain->next;
- kfree_skb(buf_chain);
- }
+ kfree_skb_list(buf_chain);
return tipc_port_reject_sections(sender, hdr, msg_sect,
len, TIPC_ERR_NO_NODE);
}
@@ -1281,9 +1241,6 @@ void tipc_link_push_queue(struct tipc_link *l_ptr)
{
u32 res;
- if (tipc_bearer_blocked(l_ptr->b_ptr))
- return;
-
do {
res = tipc_link_push_packet(l_ptr);
} while (!res);
@@ -1370,26 +1327,15 @@ void tipc_link_retransmit(struct tipc_link *l_ptr, struct sk_buff *buf,
msg = buf_msg(buf);
- if (tipc_bearer_blocked(l_ptr->b_ptr)) {
- if (l_ptr->retransm_queue_size == 0) {
- l_ptr->retransm_queue_head = msg_seqno(msg);
- l_ptr->retransm_queue_size = retransmits;
- } else {
- pr_err("Unexpected retransmit on link %s (qsize=%d)\n",
- l_ptr->name, l_ptr->retransm_queue_size);
+ /* Detect repeated retransmit failures */
+ if (l_ptr->last_retransmitted == msg_seqno(msg)) {
+ if (++l_ptr->stale_count > 100) {
+ link_retransmit_failure(l_ptr, buf);
+ return;
}
- return;
} else {
- /* Detect repeated retransmit failures on unblocked bearer */
- if (l_ptr->last_retransmitted == msg_seqno(msg)) {
- if (++l_ptr->stale_count > 100) {
- link_retransmit_failure(l_ptr, buf);
- return;
- }
- } else {
- l_ptr->last_retransmitted = msg_seqno(msg);
- l_ptr->stale_count = 1;
- }
+ l_ptr->last_retransmitted = msg_seqno(msg);
+ l_ptr->stale_count = 1;
}
while (retransmits && (buf != l_ptr->next_out) && buf) {
@@ -1786,7 +1732,8 @@ void tipc_link_send_proto_msg(struct tipc_link *l_ptr, u32 msg_typ,
l_ptr->proto_msg_queue = NULL;
}
- if (link_blocked(l_ptr))
+ /* Don't send protocol message during link changeover */
+ if (l_ptr->exp_msg_count)
return;
/* Abort non-RESET send if communication with node is prohibited */
@@ -1861,12 +1808,6 @@ void tipc_link_send_proto_msg(struct tipc_link *l_ptr, u32 msg_typ,
skb_copy_to_linear_data(buf, msg, sizeof(l_ptr->proto_msg));
buf->priority = TC_PRIO_CONTROL;
- /* Defer message if bearer is already blocked */
- if (tipc_bearer_blocked(l_ptr->b_ptr)) {
- l_ptr->proto_msg_queue = buf;
- return;
- }
-
tipc_bearer_send(l_ptr->b_ptr, buf, &l_ptr->media_addr);
l_ptr->unacked_window = 0;
kfree_skb(buf);
@@ -1885,7 +1826,8 @@ static void link_recv_proto_msg(struct tipc_link *l_ptr, struct sk_buff *buf)
u32 msg_tol;
struct tipc_msg *msg = buf_msg(buf);
- if (link_blocked(l_ptr))
+ /* Discard protocol message during link changeover */
+ if (l_ptr->exp_msg_count)
goto exit;
/* record unnumbered packet arrival (force mismatch on next timeout) */
@@ -2306,11 +2248,7 @@ static int link_send_long_buf(struct tipc_link *l_ptr, struct sk_buff *buf)
fragm = tipc_buf_acquire(fragm_sz + INT_H_SIZE);
if (fragm == NULL) {
kfree_skb(buf);
- while (buf_chain) {
- buf = buf_chain;
- buf_chain = buf_chain->next;
- kfree_skb(buf);
- }
+ kfree_skb_list(buf_chain);
return -ENOMEM;
}
msg_set_size(&fragm_hdr, fragm_sz + INT_H_SIZE);
diff --git a/net/tipc/link.h b/net/tipc/link.h
index 8a6c1026644d..424b1dfe436b 100644
--- a/net/tipc/link.h
+++ b/net/tipc/link.h
@@ -112,7 +112,6 @@ struct tipc_stats {
* @continuity_interval: link continuity testing interval [in ms]
* @abort_limit: # of unacknowledged continuity probes needed to reset link
* @state: current state of link FSM
- * @blocked: indicates if link has been administratively blocked
* @fsm_msg_cnt: # of protocol messages link FSM has sent in current state
* @proto_msg: template for control messages generated by link
* @pmsg: convenience pointer to "proto_msg" field
@@ -162,7 +161,6 @@ struct tipc_link {
u32 continuity_interval;
u32 abort_limit;
int state;
- int blocked;
u32 fsm_msg_cnt;
struct {
unchar hdr[INT_H_SIZE];
@@ -312,11 +310,6 @@ static inline int link_reset_reset(struct tipc_link *l_ptr)
return l_ptr->state == RESET_RESET;
}
-static inline int link_blocked(struct tipc_link *l_ptr)
-{
- return l_ptr->exp_msg_count || l_ptr->blocked;
-}
-
static inline int link_congested(struct tipc_link *l_ptr)
{
return l_ptr->out_queue_size >= l_ptr->queue_limit[0];
diff --git a/net/tipc/name_table.c b/net/tipc/name_table.c
index 09dcd54b04e1..92a1533af4e0 100644
--- a/net/tipc/name_table.c
+++ b/net/tipc/name_table.c
@@ -148,8 +148,7 @@ static struct publication *publ_create(u32 type, u32 lower, u32 upper,
*/
static struct sub_seq *tipc_subseq_alloc(u32 cnt)
{
- struct sub_seq *sseq = kcalloc(cnt, sizeof(struct sub_seq), GFP_ATOMIC);
- return sseq;
+ return kcalloc(cnt, sizeof(struct sub_seq), GFP_ATOMIC);
}
/**
diff --git a/net/tipc/node.c b/net/tipc/node.c
index 25100c0a6fe8..bf1ac89b4806 100644
--- a/net/tipc/node.c
+++ b/net/tipc/node.c
@@ -291,11 +291,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) {
- while (n_ptr->bclink.deferred_head) {
- struct sk_buff *buf = n_ptr->bclink.deferred_head;
- n_ptr->bclink.deferred_head = buf->next;
- kfree_skb(buf);
- }
+ kfree_skb_list(n_ptr->bclink.deferred_head);
n_ptr->bclink.deferred_size = 0;
if (n_ptr->bclink.reasm_head) {
diff --git a/net/tipc/port.c b/net/tipc/port.c
index c081a7632302..5fd4c8cec08e 100644
--- a/net/tipc/port.c
+++ b/net/tipc/port.c
@@ -832,17 +832,14 @@ exit:
*/
int __tipc_disconnect(struct tipc_port *tp_ptr)
{
- int res;
-
if (tp_ptr->connected) {
tp_ptr->connected = 0;
/* let timer expire on it's own to avoid deadlock! */
tipc_nodesub_unsubscribe(&tp_ptr->subscription);
- res = 0;
- } else {
- res = -ENOTCONN;
+ return 0;
}
- return res;
+
+ return -ENOTCONN;
}
/*
diff --git a/net/tipc/socket.c b/net/tipc/socket.c
index 3b61851bb927..83f466e57fea 100644
--- a/net/tipc/socket.c
+++ b/net/tipc/socket.c
@@ -239,7 +239,6 @@ static int tipc_sk_create(struct net *net, struct socket *sock, int protocol,
int tipc_sock_create_local(int type, struct socket **res)
{
int rc;
- struct sock *sk;
rc = sock_create_lite(AF_TIPC, type, 0, res);
if (rc < 0) {
@@ -248,8 +247,6 @@ int tipc_sock_create_local(int type, struct socket **res)
}
tipc_sk_create(&init_net, *res, 0, 1);
- sk = (*res)->sk;
-
return 0;
}
@@ -754,16 +751,14 @@ static int send_stream(struct kiocb *iocb, struct socket *sock,
/* Handle special cases where there is no connection */
if (unlikely(sock->state != SS_CONNECTED)) {
- if (sock->state == SS_UNCONNECTED) {
+ res = -ENOTCONN;
+
+ if (sock->state == SS_UNCONNECTED)
res = send_packet(NULL, sock, m, total_len);
- goto exit;
- } else if (sock->state == SS_DISCONNECTING) {
+ else if (sock->state == SS_DISCONNECTING)
res = -EPIPE;
- goto exit;
- } else {
- res = -ENOTCONN;
- goto exit;
- }
+
+ goto exit;
}
if (unlikely(m->msg_name)) {
@@ -1311,14 +1306,12 @@ static u32 filter_connect(struct tipc_sock *tsock, struct sk_buff **buf)
static unsigned int rcvbuf_limit(struct sock *sk, struct sk_buff *buf)
{
struct tipc_msg *msg = buf_msg(buf);
- unsigned int limit;
if (msg_connected(msg))
- limit = sysctl_tipc_rmem[2];
- else
- limit = sk->sk_rcvbuf >> TIPC_CRITICAL_IMPORTANCE <<
- msg_importance(msg);
- return limit;
+ return sysctl_tipc_rmem[2];
+
+ return sk->sk_rcvbuf >> TIPC_CRITICAL_IMPORTANCE <<
+ msg_importance(msg);
}
/**
@@ -1514,14 +1507,12 @@ static int connect(struct socket *sock, struct sockaddr *dest, int destlen,
sock->state != SS_CONNECTING,
timeout ? (long)msecs_to_jiffies(timeout)
: MAX_SCHEDULE_TIMEOUT);
- lock_sock(sk);
if (res <= 0) {
if (res == 0)
res = -ETIMEDOUT;
- else
- ; /* leave "res" unchanged */
- goto exit;
+ return res;
}
+ lock_sock(sk);
}
if (unlikely(sock->state == SS_DISCONNECTING))
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index a427623ee574..800ca61758ff 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -80,6 +80,8 @@
* with BSD names.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/signal.h>
@@ -366,7 +368,7 @@ static void unix_sock_destructor(struct sock *sk)
WARN_ON(!sk_unhashed(sk));
WARN_ON(sk->sk_socket);
if (!sock_flag(sk, SOCK_DEAD)) {
- printk(KERN_INFO "Attempt to release alive unix socket: %p\n", sk);
+ pr_info("Attempt to release alive unix socket: %p\n", sk);
return;
}
@@ -378,7 +380,7 @@ static void unix_sock_destructor(struct sock *sk)
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
local_bh_enable();
#ifdef UNIX_REFCNT_DEBUG
- printk(KERN_DEBUG "UNIX %p is destroyed, %ld are still alive.\n", sk,
+ pr_debug("UNIX %p is destroyed, %ld are still alive.\n", sk,
atomic_long_read(&unix_nr_socks));
#endif
}
@@ -2441,8 +2443,7 @@ static int __init af_unix_init(void)
rc = proto_register(&unix_proto, 1);
if (rc != 0) {
- printk(KERN_CRIT "%s: Cannot create unix_sock SLAB cache!\n",
- __func__);
+ pr_crit("%s: Cannot create unix_sock SLAB cache!\n", __func__);
goto out;
}
diff --git a/net/wireless/chan.c b/net/wireless/chan.c
index 9b8cc877eb19..78559b5bbd1f 100644
--- a/net/wireless/chan.c
+++ b/net/wireless/chan.c
@@ -277,6 +277,32 @@ void cfg80211_set_dfs_state(struct wiphy *wiphy,
width, dfs_state);
}
+static u32 cfg80211_get_start_freq(u32 center_freq,
+ u32 bandwidth)
+{
+ u32 start_freq;
+
+ if (bandwidth <= 20)
+ start_freq = center_freq;
+ else
+ start_freq = center_freq - bandwidth/2 + 10;
+
+ return start_freq;
+}
+
+static u32 cfg80211_get_end_freq(u32 center_freq,
+ u32 bandwidth)
+{
+ u32 end_freq;
+
+ if (bandwidth <= 20)
+ end_freq = center_freq;
+ else
+ end_freq = center_freq + bandwidth/2 - 10;
+
+ return end_freq;
+}
+
static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
u32 center_freq,
u32 bandwidth)
@@ -284,13 +310,8 @@ static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
struct ieee80211_channel *c;
u32 freq, start_freq, end_freq;
- if (bandwidth <= 20) {
- start_freq = center_freq;
- end_freq = center_freq;
- } else {
- start_freq = center_freq - bandwidth/2 + 10;
- end_freq = center_freq + bandwidth/2 - 10;
- }
+ start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
+ end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
for (freq = start_freq; freq <= end_freq; freq += 20) {
c = ieee80211_get_channel(wiphy, freq);
@@ -330,33 +351,159 @@ int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
}
EXPORT_SYMBOL(cfg80211_chandef_dfs_required);
-static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
- u32 center_freq, u32 bandwidth,
- u32 prohibited_flags)
+static int cfg80211_get_chans_dfs_usable(struct wiphy *wiphy,
+ u32 center_freq,
+ u32 bandwidth)
{
struct ieee80211_channel *c;
u32 freq, start_freq, end_freq;
+ int count = 0;
- if (bandwidth <= 20) {
- start_freq = center_freq;
- end_freq = center_freq;
- } else {
- start_freq = center_freq - bandwidth/2 + 10;
- end_freq = center_freq + bandwidth/2 - 10;
+ start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
+ end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
+
+ /*
+ * Check entire range of channels for the bandwidth.
+ * Check all channels are DFS channels (DFS_USABLE or
+ * DFS_AVAILABLE). Return number of usable channels
+ * (require CAC). Allow DFS and non-DFS channel mix.
+ */
+ for (freq = start_freq; freq <= end_freq; freq += 20) {
+ c = ieee80211_get_channel(wiphy, freq);
+ if (!c)
+ return -EINVAL;
+
+ if (c->flags & IEEE80211_CHAN_DISABLED)
+ return -EINVAL;
+
+ if (c->flags & IEEE80211_CHAN_RADAR) {
+ if (c->dfs_state == NL80211_DFS_UNAVAILABLE)
+ return -EINVAL;
+
+ if (c->dfs_state == NL80211_DFS_USABLE)
+ count++;
+ }
}
+ return count;
+}
+
+bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef)
+{
+ int width;
+ int r1, r2 = 0;
+
+ if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+ return false;
+
+ width = cfg80211_chandef_get_width(chandef);
+ if (width < 0)
+ return false;
+
+ r1 = cfg80211_get_chans_dfs_usable(wiphy, chandef->center_freq1,
+ width);
+
+ if (r1 < 0)
+ return false;
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_80P80:
+ WARN_ON(!chandef->center_freq2);
+ r2 = cfg80211_get_chans_dfs_usable(wiphy,
+ chandef->center_freq2,
+ width);
+ if (r2 < 0)
+ return false;
+ break;
+ default:
+ WARN_ON(chandef->center_freq2);
+ break;
+ }
+
+ return (r1 + r2 > 0);
+}
+
+
+static bool cfg80211_get_chans_dfs_available(struct wiphy *wiphy,
+ u32 center_freq,
+ u32 bandwidth)
+{
+ struct ieee80211_channel *c;
+ u32 freq, start_freq, end_freq;
+
+ start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
+ end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
+
+ /*
+ * Check entire range of channels for the bandwidth.
+ * If any channel in between is disabled or has not
+ * had gone through CAC return false
+ */
for (freq = start_freq; freq <= end_freq; freq += 20) {
c = ieee80211_get_channel(wiphy, freq);
if (!c)
return false;
- /* check for radar flags */
- if ((prohibited_flags & c->flags & IEEE80211_CHAN_RADAR) &&
+ if (c->flags & IEEE80211_CHAN_DISABLED)
+ return false;
+
+ if ((c->flags & IEEE80211_CHAN_RADAR) &&
(c->dfs_state != NL80211_DFS_AVAILABLE))
return false;
+ }
+
+ return true;
+}
+
+static bool cfg80211_chandef_dfs_available(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef)
+{
+ int width;
+ int r;
+
+ if (WARN_ON(!cfg80211_chandef_valid(chandef)))
+ return false;
+
+ width = cfg80211_chandef_get_width(chandef);
+ if (width < 0)
+ return false;
+
+ r = cfg80211_get_chans_dfs_available(wiphy, chandef->center_freq1,
+ width);
+
+ /* If any of channels unavailable for cf1 just return */
+ if (!r)
+ return r;
+
+ switch (chandef->width) {
+ case NL80211_CHAN_WIDTH_80P80:
+ WARN_ON(!chandef->center_freq2);
+ r = cfg80211_get_chans_dfs_available(wiphy,
+ chandef->center_freq2,
+ width);
+ default:
+ WARN_ON(chandef->center_freq2);
+ break;
+ }
+
+ return r;
+}
- /* check for the other flags */
- if (c->flags & prohibited_flags & ~IEEE80211_CHAN_RADAR)
+
+static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
+ u32 center_freq, u32 bandwidth,
+ u32 prohibited_flags)
+{
+ struct ieee80211_channel *c;
+ u32 freq, start_freq, end_freq;
+
+ start_freq = cfg80211_get_start_freq(center_freq, bandwidth);
+ end_freq = cfg80211_get_end_freq(center_freq, bandwidth);
+
+ for (freq = start_freq; freq <= end_freq; freq += 20) {
+ c = ieee80211_get_channel(wiphy, freq);
+ if (!c || c->flags & prohibited_flags)
return false;
}
@@ -462,14 +609,19 @@ bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
struct cfg80211_chan_def *chandef)
{
bool res;
+ u32 prohibited_flags = IEEE80211_CHAN_DISABLED |
+ IEEE80211_CHAN_NO_IR |
+ IEEE80211_CHAN_RADAR;
trace_cfg80211_reg_can_beacon(wiphy, chandef);
- res = cfg80211_chandef_usable(wiphy, chandef,
- IEEE80211_CHAN_DISABLED |
- IEEE80211_CHAN_PASSIVE_SCAN |
- IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_RADAR);
+ if (cfg80211_chandef_dfs_required(wiphy, chandef) > 0 &&
+ cfg80211_chandef_dfs_available(wiphy, chandef)) {
+ /* We can skip IEEE80211_CHAN_NO_IR if chandef dfs available */
+ prohibited_flags = IEEE80211_CHAN_DISABLED;
+ }
+
+ res = cfg80211_chandef_usable(wiphy, chandef, prohibited_flags);
trace_cfg80211_return_bool(res);
return res;
@@ -510,6 +662,7 @@ cfg80211_get_chan_state(struct wireless_dev *wdev,
: CHAN_MODE_EXCLUSIVE;
return;
}
+ break;
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
if (wdev->current_bss) {
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 52b865fb7351..06db6eb5258a 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -357,8 +357,6 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
rdev->wiphy.rts_threshold = (u32) -1;
rdev->wiphy.coverage_class = 0;
- rdev->wiphy.features = NL80211_FEATURE_SCAN_FLUSH;
-
return &rdev->wiphy;
}
EXPORT_SYMBOL(wiphy_new);
@@ -575,6 +573,8 @@ int wiphy_register(struct wiphy *wiphy)
/* check and set up bitrates */
ieee80211_set_bitrate_flags(wiphy);
+ rdev->wiphy.features |= NL80211_FEATURE_SCAN_FLUSH;
+
rtnl_lock();
res = device_add(&rdev->wiphy.dev);
if (res) {
@@ -595,7 +595,7 @@ int wiphy_register(struct wiphy *wiphy)
if (IS_ERR(rdev->wiphy.debugfsdir))
rdev->wiphy.debugfsdir = NULL;
- if (wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) {
+ if (wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) {
struct regulatory_request request;
request.wiphy_idx = get_wiphy_idx(wiphy);
diff --git a/net/wireless/core.h b/net/wireless/core.h
index af10e59af2d8..0a277c33bb02 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -317,9 +317,8 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid);
void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev);
int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
- struct ieee80211_channel *chan, bool offchan,
- unsigned int wait, const u8 *buf, size_t len,
- bool no_cck, bool dont_wait_for_ack, u64 *cookie);
+ struct cfg80211_mgmt_tx_params *params,
+ u64 *cookie);
void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa,
const struct ieee80211_ht_cap *ht_capa_mask);
void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa,
@@ -382,6 +381,19 @@ int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
enum cfg80211_chan_mode chanmode,
u8 radar_detect);
+/**
+ * cfg80211_chandef_dfs_usable - checks if chandef is DFS usable
+ * @wiphy: the wiphy to validate against
+ * @chandef: the channel definition to check
+ *
+ * Checks if chandef is usable and we can/need start CAC on such channel.
+ *
+ * Return: Return true if all channels available and at least
+ * one channel require CAC (NL80211_DFS_USABLE)
+ */
+bool cfg80211_chandef_dfs_usable(struct wiphy *wiphy,
+ const struct cfg80211_chan_def *chandef);
+
void cfg80211_set_dfs_state(struct wiphy *wiphy,
const struct cfg80211_chan_def *chandef,
enum nl80211_dfs_state dfs_state);
diff --git a/net/wireless/genregdb.awk b/net/wireless/genregdb.awk
index 42ed274e81f4..9a8217d2a908 100644
--- a/net/wireless/genregdb.awk
+++ b/net/wireless/genregdb.awk
@@ -33,15 +33,7 @@ BEGIN {
regdb = "const struct ieee80211_regdomain *reg_regdb[] = {\n"
}
-/^[ \t]*#/ {
- # Ignore
-}
-
-!active && /^[ \t]*$/ {
- # Ignore
-}
-
-!active && /country/ {
+function parse_country_head() {
country=$2
sub(/:/, "", country)
printf "static const struct ieee80211_regdomain regdom_%s = {\n", country
@@ -57,7 +49,8 @@ BEGIN {
regdb = regdb "\t&regdom_" country ",\n"
}
-active && /^[ \t]*\(/ {
+function parse_reg_rule()
+{
start = $1
sub(/\(/, "", start)
end = $3
@@ -107,17 +100,21 @@ active && /^[ \t]*\(/ {
} else if (flagarray[arg] == "PTMP-ONLY") {
flags = flags "\n\t\t\tNL80211_RRF_PTMP_ONLY | "
} else if (flagarray[arg] == "PASSIVE-SCAN") {
- flags = flags "\n\t\t\tNL80211_RRF_PASSIVE_SCAN | "
+ flags = flags "\n\t\t\tNL80211_RRF_NO_IR | "
} else if (flagarray[arg] == "NO-IBSS") {
- flags = flags "\n\t\t\tNL80211_RRF_NO_IBSS | "
+ flags = flags "\n\t\t\tNL80211_RRF_NO_IR | "
+ } else if (flagarray[arg] == "NO-IR") {
+ flags = flags "\n\t\t\tNL80211_RRF_NO_IR | "
}
+
}
flags = flags "0"
printf "\t\tREG_RULE(%d, %d, %d, %d, %d, %s),\n", start, end, bw, gain, power, flags
rules++
}
-active && /^[ \t]*$/ {
+function print_tail_country()
+{
active = 0
printf "\t},\n"
printf "\t.n_reg_rules = %d\n", rules
@@ -125,7 +122,29 @@ active && /^[ \t]*$/ {
rules = 0;
}
+/^[ \t]*#/ {
+ # Ignore
+}
+
+!active && /^[ \t]*$/ {
+ # Ignore
+}
+
+!active && /country/ {
+ parse_country_head()
+}
+
+active && /^[ \t]*\(/ {
+ parse_reg_rule()
+}
+
+active && /^[ \t]*$/ {
+ print_tail_country()
+}
+
END {
+ if (active)
+ print_tail_country()
print regdb "};"
print ""
print "int reg_regdb_size = ARRAY_SIZE(reg_regdb);"
diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c
index 89737ee2669a..730147ed8e65 100644
--- a/net/wireless/ibss.c
+++ b/net/wireless/ibss.c
@@ -274,7 +274,7 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
for (i = 0; i < sband->n_channels; i++) {
chan = &sband->channels[i];
- if (chan->flags & IEEE80211_CHAN_NO_IBSS)
+ if (chan->flags & IEEE80211_CHAN_NO_IR)
continue;
if (chan->flags & IEEE80211_CHAN_DISABLED)
continue;
@@ -346,7 +346,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev,
chan = ieee80211_get_channel(wdev->wiphy, freq);
if (!chan)
return -EINVAL;
- if (chan->flags & IEEE80211_CHAN_NO_IBSS ||
+ if (chan->flags & IEEE80211_CHAN_NO_IR ||
chan->flags & IEEE80211_CHAN_DISABLED)
return -EINVAL;
}
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c
index 0553fd4d85ae..b0e1869de7de 100644
--- a/net/wireless/mesh.c
+++ b/net/wireless/mesh.c
@@ -141,8 +141,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
for (i = 0; i < sband->n_channels; i++) {
chan = &sband->channels[i];
- if (chan->flags & (IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_PASSIVE_SCAN |
+ if (chan->flags & (IEEE80211_CHAN_NO_IR |
IEEE80211_CHAN_DISABLED |
IEEE80211_CHAN_RADAR))
continue;
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 6a6b1c8e907d..52cca05044a8 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -520,9 +520,7 @@ void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev)
int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
- struct ieee80211_channel *chan, bool offchan,
- unsigned int wait, const u8 *buf, size_t len,
- bool no_cck, bool dont_wait_for_ack, u64 *cookie)
+ struct cfg80211_mgmt_tx_params *params, u64 *cookie)
{
const struct ieee80211_mgmt *mgmt;
u16 stype;
@@ -533,10 +531,10 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
if (!rdev->ops->mgmt_tx)
return -EOPNOTSUPP;
- if (len < 24 + 1)
+ if (params->len < 24 + 1)
return -EINVAL;
- mgmt = (const struct ieee80211_mgmt *) buf;
+ mgmt = (const struct ieee80211_mgmt *)params->buf;
if (!ieee80211_is_mgmt(mgmt->frame_control))
return -EINVAL;
@@ -615,9 +613,7 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev,
return -EINVAL;
/* Transmit the Action frame as requested by user space */
- return rdev_mgmt_tx(rdev, wdev, chan, offchan,
- wait, buf, len, no_cck, dont_wait_for_ack,
- cookie);
+ return rdev_mgmt_tx(rdev, wdev, params, cookie);
}
bool cfg80211_rx_mgmt(struct wireless_dev *wdev, int freq, int sig_mbm,
@@ -763,12 +759,12 @@ void cfg80211_radar_event(struct wiphy *wiphy,
EXPORT_SYMBOL(cfg80211_radar_event);
void cfg80211_cac_event(struct net_device *netdev,
+ const struct cfg80211_chan_def *chandef,
enum nl80211_radar_event event, gfp_t gfp)
{
struct wireless_dev *wdev = netdev->ieee80211_ptr;
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
- struct cfg80211_chan_def chandef;
unsigned long timeout;
trace_cfg80211_cac_event(netdev, event);
@@ -779,14 +775,12 @@ void cfg80211_cac_event(struct net_device *netdev,
if (WARN_ON(!wdev->channel))
return;
- cfg80211_chandef_create(&chandef, wdev->channel, NL80211_CHAN_NO_HT);
-
switch (event) {
case NL80211_RADAR_CAC_FINISHED:
timeout = wdev->cac_start_time +
msecs_to_jiffies(IEEE80211_DFS_MIN_CAC_TIME_MS);
WARN_ON(!time_after_eq(jiffies, timeout));
- cfg80211_set_dfs_state(wiphy, &chandef, NL80211_DFS_AVAILABLE);
+ cfg80211_set_dfs_state(wiphy, chandef, NL80211_DFS_AVAILABLE);
break;
case NL80211_RADAR_CAC_ABORTED:
break;
@@ -796,6 +790,6 @@ void cfg80211_cac_event(struct net_device *netdev,
}
wdev->cac_started = false;
- nl80211_radar_notify(rdev, &chandef, event, netdev, gfp);
+ nl80211_radar_notify(rdev, chandef, event, netdev, gfp);
}
EXPORT_SYMBOL(cfg80211_cac_event);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 138dc3bb8b67..a693f86e5970 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -564,12 +564,12 @@ static int nl80211_msg_put_channel(struct sk_buff *msg,
if ((chan->flags & IEEE80211_CHAN_DISABLED) &&
nla_put_flag(msg, NL80211_FREQUENCY_ATTR_DISABLED))
goto nla_put_failure;
- if ((chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
- nla_put_flag(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN))
- goto nla_put_failure;
- if ((chan->flags & IEEE80211_CHAN_NO_IBSS) &&
- nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS))
- goto nla_put_failure;
+ if (chan->flags & IEEE80211_CHAN_NO_IR) {
+ if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IR))
+ goto nla_put_failure;
+ if (nla_put_flag(msg, __NL80211_FREQUENCY_ATTR_NO_IBSS))
+ goto nla_put_failure;
+ }
if (chan->flags & IEEE80211_CHAN_RADAR) {
if (nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
goto nla_put_failure;
@@ -1247,10 +1247,6 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
goto nla_put_failure;
- if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) &&
- nla_put_flag(msg, WIPHY_FLAG_SUPPORTS_5_10_MHZ))
- goto nla_put_failure;
-
state->split_start++;
if (state->split)
break;
@@ -1579,6 +1575,11 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
if (nl80211_send_coalesce(msg, dev))
goto nla_put_failure;
+ if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) &&
+ (nla_put_flag(msg, NL80211_ATTR_SUPPORT_5_MHZ) ||
+ nla_put_flag(msg, NL80211_ATTR_SUPPORT_10_MHZ)))
+ goto nla_put_failure;
+
/* done */
state->split_start = 0;
break;
@@ -2187,7 +2188,7 @@ static inline u64 wdev_id(struct wireless_dev *wdev)
}
static int nl80211_send_chandef(struct sk_buff *msg,
- struct cfg80211_chan_def *chandef)
+ const struct cfg80211_chan_def *chandef)
{
WARN_ON(!cfg80211_chandef_valid(chandef));
@@ -3236,6 +3237,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
return PTR_ERR(params.acl);
}
+ wdev_lock(wdev);
err = rdev_start_ap(rdev, dev, &params);
if (!err) {
wdev->preset_chandef = params.chandef;
@@ -3244,6 +3246,7 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
wdev->ssid_len = params.ssid_len;
memcpy(wdev->ssid, params.ssid, wdev->ssid_len);
}
+ wdev_unlock(wdev);
kfree(params.acl);
@@ -3272,7 +3275,11 @@ static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
if (err)
return err;
- return rdev_change_beacon(rdev, dev, &params);
+ wdev_lock(wdev);
+ err = rdev_change_beacon(rdev, dev, &params);
+ wdev_unlock(wdev);
+
+ return err;
}
static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info)
@@ -4478,7 +4485,9 @@ static int nl80211_set_bss(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 bss_parameters params;
+ int err;
memset(&params, 0, sizeof(params));
/* default to not changing parameters */
@@ -4544,7 +4553,11 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
return -EOPNOTSUPP;
- return rdev_change_bss(rdev, dev, &params);
+ wdev_lock(wdev);
+ err = rdev_change_bss(rdev, dev, &params);
+ wdev_unlock(wdev);
+
+ return err;
}
static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
@@ -5098,7 +5111,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
char *alpha2 = NULL;
int rem_reg_rules = 0, r = 0;
u32 num_rules = 0, rule_idx = 0, size_of_regd;
- u8 dfs_region = 0;
+ enum nl80211_dfs_regions dfs_region = NL80211_DFS_UNSET;
struct ieee80211_regdomain *rd = NULL;
if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
@@ -5119,6 +5132,9 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
}
+ if (!reg_is_valid_request(alpha2))
+ return -EINVAL;
+
size_of_regd = sizeof(struct ieee80211_regdomain) +
num_rules * sizeof(struct ieee80211_reg_rule);
@@ -5365,10 +5381,8 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
request->flags = nla_get_u32(
info->attrs[NL80211_ATTR_SCAN_FLAGS]);
- if (((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
- !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) ||
- ((request->flags & NL80211_SCAN_FLAG_FLUSH) &&
- !(wiphy->features & NL80211_FEATURE_SCAN_FLUSH))) {
+ if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
+ !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) {
err = -EOPNOTSUPP;
goto out_free;
}
@@ -5608,10 +5622,8 @@ static int nl80211_start_sched_scan(struct sk_buff *skb,
if (info->attrs[NL80211_ATTR_SCAN_FLAGS]) {
request->flags = nla_get_u32(
info->attrs[NL80211_ATTR_SCAN_FLAGS]);
- if (((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
- !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) ||
- ((request->flags & NL80211_SCAN_FLAG_FLUSH) &&
- !(wiphy->features & NL80211_FEATURE_SCAN_FLUSH))) {
+ if ((request->flags & NL80211_SCAN_FLAG_LOW_PRIORITY) &&
+ !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) {
err = -EOPNOTSUPP;
goto out_free;
}
@@ -5674,7 +5686,7 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
if (err == 0)
return -EINVAL;
- if (chandef.chan->dfs_state != NL80211_DFS_USABLE)
+ if (!cfg80211_chandef_dfs_usable(wdev->wiphy, &chandef))
return -EINVAL;
if (!rdev->ops->start_radar_detection)
@@ -5814,7 +5826,11 @@ skip_beacons:
if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX])
params.block_tx = true;
- return rdev_channel_switch(rdev, dev, &params);
+ wdev_lock(wdev);
+ err = rdev_channel_switch(rdev, dev, &params);
+ wdev_unlock(wdev);
+
+ return err;
}
static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
@@ -7447,10 +7463,10 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
void *hdr = NULL;
u64 cookie;
struct sk_buff *msg = NULL;
- unsigned int wait = 0;
- bool offchan, no_cck, dont_wait_for_ack;
-
- dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK];
+ struct cfg80211_mgmt_tx_params params = {
+ .dont_wait_for_ack =
+ info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK],
+ };
if (!info->attrs[NL80211_ATTR_FRAME])
return -EINVAL;
@@ -7477,24 +7493,24 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_DURATION]) {
if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
return -EINVAL;
- wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
+ params.wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
/*
* We should wait on the channel for at least a minimum amount
* of time (10ms) but no longer than the driver supports.
*/
- if (wait < NL80211_MIN_REMAIN_ON_CHANNEL_TIME ||
- wait > rdev->wiphy.max_remain_on_channel_duration)
+ if (params.wait < NL80211_MIN_REMAIN_ON_CHANNEL_TIME ||
+ params.wait > rdev->wiphy.max_remain_on_channel_duration)
return -EINVAL;
}
- offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
+ params.offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
- if (offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
+ if (params.offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
return -EINVAL;
- no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
+ params.no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
/* get the channel if any has been specified, otherwise pass NULL to
* the driver. The latter will use the current one
@@ -7506,10 +7522,10 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
return err;
}
- if (!chandef.chan && offchan)
+ if (!chandef.chan && params.offchan)
return -EINVAL;
- if (!dont_wait_for_ack) {
+ if (!params.dont_wait_for_ack) {
msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!msg)
return -ENOMEM;
@@ -7522,10 +7538,10 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
}
}
- err = cfg80211_mlme_mgmt_tx(rdev, wdev, chandef.chan, offchan, wait,
- nla_data(info->attrs[NL80211_ATTR_FRAME]),
- nla_len(info->attrs[NL80211_ATTR_FRAME]),
- no_cck, dont_wait_for_ack, &cookie);
+ params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]);
+ params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]);
+ params.chan = chandef.chan;
+ err = cfg80211_mlme_mgmt_tx(rdev, wdev, &params, &cookie);
if (err)
goto free_msg;
@@ -10810,21 +10826,18 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
struct wiphy *wiphy = wdev->wiphy;
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
- trace_cfg80211_ch_switch_notify(dev, chandef);
+ ASSERT_WDEV_LOCK(wdev);
- wdev_lock(wdev);
+ 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))
- goto out;
+ return;
wdev->channel = chandef->chan;
nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL);
-out:
- wdev_unlock(wdev);
- return;
}
EXPORT_SYMBOL(cfg80211_ch_switch_notify);
@@ -10883,7 +10896,7 @@ EXPORT_SYMBOL(cfg80211_cqm_txe_notify);
void
nl80211_radar_notify(struct cfg80211_registered_device *rdev,
- struct cfg80211_chan_def *chandef,
+ const struct cfg80211_chan_def *chandef,
enum nl80211_radar_event event,
struct net_device *netdev, gfp_t gfp)
{
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index 2c0f2b3c07cb..b1b231324e10 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -70,7 +70,7 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
void
nl80211_radar_notify(struct cfg80211_registered_device *rdev,
- struct cfg80211_chan_def *chandef,
+ const struct cfg80211_chan_def *chandef,
enum nl80211_radar_event event,
struct net_device *netdev, gfp_t gfp);
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 37ce9fdfe934..a6c03ab14a0d 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -624,16 +624,12 @@ rdev_cancel_remain_on_channel(struct cfg80211_registered_device *rdev,
static inline int rdev_mgmt_tx(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
- struct ieee80211_channel *chan, bool offchan,
- unsigned int wait, const u8 *buf, size_t len,
- bool no_cck, bool dont_wait_for_ack, u64 *cookie)
+ struct cfg80211_mgmt_tx_params *params,
+ u64 *cookie)
{
int ret;
- trace_rdev_mgmt_tx(&rdev->wiphy, wdev, chan, offchan,
- wait, no_cck, dont_wait_for_ack);
- ret = rdev->ops->mgmt_tx(&rdev->wiphy, wdev, chan, offchan,
- wait, buf, len, no_cck,
- dont_wait_for_ack, cookie);
+ trace_rdev_mgmt_tx(&rdev->wiphy, wdev, params);
+ ret = rdev->ops->mgmt_tx(&rdev->wiphy, wdev, params, cookie);
trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie);
return ret;
}
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 7da67fd0b418..ec54e1aac8e2 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -120,6 +120,21 @@ static const struct ieee80211_regdomain *get_wiphy_regdom(struct wiphy *wiphy)
return rtnl_dereference(wiphy->regd);
}
+static const char *reg_dfs_region_str(enum nl80211_dfs_regions dfs_region)
+{
+ switch (dfs_region) {
+ case NL80211_DFS_UNSET:
+ return "unset";
+ case NL80211_DFS_FCC:
+ return "FCC";
+ case NL80211_DFS_ETSI:
+ return "ETSI";
+ case NL80211_DFS_JP:
+ return "JP";
+ }
+ return "Unknown";
+}
+
static void rcu_free_regdom(const struct ieee80211_regdomain *r)
{
if (!r)
@@ -163,35 +178,29 @@ static const struct ieee80211_regdomain world_regdom = {
REG_RULE(2412-10, 2462+10, 40, 6, 20, 0),
/* IEEE 802.11b/g, channels 12..13. */
REG_RULE(2467-10, 2472+10, 40, 6, 20,
- NL80211_RRF_PASSIVE_SCAN |
- NL80211_RRF_NO_IBSS),
+ NL80211_RRF_NO_IR),
/* IEEE 802.11 channel 14 - Only JP enables
* this and for 802.11b only */
REG_RULE(2484-10, 2484+10, 20, 6, 20,
- NL80211_RRF_PASSIVE_SCAN |
- NL80211_RRF_NO_IBSS |
+ NL80211_RRF_NO_IR |
NL80211_RRF_NO_OFDM),
/* IEEE 802.11a, channel 36..48 */
REG_RULE(5180-10, 5240+10, 160, 6, 20,
- NL80211_RRF_PASSIVE_SCAN |
- NL80211_RRF_NO_IBSS),
+ NL80211_RRF_NO_IR),
/* IEEE 802.11a, channel 52..64 - DFS required */
REG_RULE(5260-10, 5320+10, 160, 6, 20,
- NL80211_RRF_PASSIVE_SCAN |
- NL80211_RRF_NO_IBSS |
+ NL80211_RRF_NO_IR |
NL80211_RRF_DFS),
/* IEEE 802.11a, channel 100..144 - DFS required */
REG_RULE(5500-10, 5720+10, 160, 6, 20,
- NL80211_RRF_PASSIVE_SCAN |
- NL80211_RRF_NO_IBSS |
+ NL80211_RRF_NO_IR |
NL80211_RRF_DFS),
/* IEEE 802.11a, channel 149..165 */
REG_RULE(5745-10, 5825+10, 80, 6, 20,
- NL80211_RRF_PASSIVE_SCAN |
- NL80211_RRF_NO_IBSS),
+ NL80211_RRF_NO_IR),
/* IEEE 802.11ad (60gHz), channels 1..3 */
REG_RULE(56160+2160*1-1080, 56160+2160*3+1080, 2160, 0, 0, 0),
@@ -208,11 +217,26 @@ static char user_alpha2[2];
module_param(ieee80211_regdom, charp, 0444);
MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
+static void reg_kfree_last_request(void)
+{
+ struct regulatory_request *lr;
+
+ lr = get_last_request();
+
+ if (lr != &core_request_world && lr)
+ kfree_rcu(lr, rcu_head);
+}
+
+static void reg_update_last_request(struct regulatory_request *request)
+{
+ reg_kfree_last_request();
+ rcu_assign_pointer(last_request, request);
+}
+
static void reset_regdomains(bool full_reset,
const struct ieee80211_regdomain *new_regdom)
{
const struct ieee80211_regdomain *r;
- struct regulatory_request *lr;
ASSERT_RTNL();
@@ -235,10 +259,7 @@ static void reset_regdomains(bool full_reset,
if (!full_reset)
return;
- lr = get_last_request();
- if (lr != &core_request_world && lr)
- kfree_rcu(lr, rcu_head);
- rcu_assign_pointer(last_request, &core_request_world);
+ reg_update_last_request(&core_request_world);
}
/*
@@ -456,7 +477,15 @@ static int call_crda(const char *alpha2)
return kobject_uevent(&reg_pdev->dev.kobj, KOBJ_CHANGE);
}
-static bool reg_is_valid_request(const char *alpha2)
+static enum reg_request_treatment
+reg_call_crda(struct regulatory_request *request)
+{
+ if (call_crda(request->alpha2))
+ return REG_REQ_IGNORE;
+ return REG_REQ_OK;
+}
+
+bool reg_is_valid_request(const char *alpha2)
{
struct regulatory_request *lr = get_last_request();
@@ -557,6 +586,20 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
}
/*
+ * Later on we can perhaps use the more restrictive DFS
+ * region but we don't have information for that yet so
+ * for now simply disallow conflicts.
+ */
+static enum nl80211_dfs_regions
+reg_intersect_dfs_region(const enum nl80211_dfs_regions dfs_region1,
+ const enum nl80211_dfs_regions dfs_region2)
+{
+ if (dfs_region1 != dfs_region2)
+ return NL80211_DFS_UNSET;
+ return dfs_region1;
+}
+
+/*
* Helper for regdom_intersect(), this does the real
* mathematical intersection fun
*/
@@ -687,6 +730,8 @@ regdom_intersect(const struct ieee80211_regdomain *rd1,
rd->n_reg_rules = num_rules;
rd->alpha2[0] = '9';
rd->alpha2[1] = '8';
+ rd->dfs_region = reg_intersect_dfs_region(rd1->dfs_region,
+ rd2->dfs_region);
return rd;
}
@@ -698,10 +743,8 @@ regdom_intersect(const struct ieee80211_regdomain *rd1,
static u32 map_regdom_flags(u32 rd_flags)
{
u32 channel_flags = 0;
- if (rd_flags & NL80211_RRF_PASSIVE_SCAN)
- channel_flags |= IEEE80211_CHAN_PASSIVE_SCAN;
- if (rd_flags & NL80211_RRF_NO_IBSS)
- channel_flags |= IEEE80211_CHAN_NO_IBSS;
+ if (rd_flags & NL80211_RRF_NO_IR_ALL)
+ channel_flags |= IEEE80211_CHAN_NO_IR;
if (rd_flags & NL80211_RRF_DFS)
channel_flags |= IEEE80211_CHAN_RADAR;
if (rd_flags & NL80211_RRF_NO_OFDM)
@@ -854,8 +897,18 @@ static void handle_channel(struct wiphy *wiphy,
PTR_ERR(reg_rule) == -ERANGE)
return;
- REG_DBG_PRINT("Disabling freq %d MHz\n", chan->center_freq);
- chan->flags |= IEEE80211_CHAN_DISABLED;
+ if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
+ request_wiphy && request_wiphy == wiphy &&
+ request_wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
+ REG_DBG_PRINT("Disabling freq %d MHz for good\n",
+ chan->center_freq);
+ chan->orig_flags |= IEEE80211_CHAN_DISABLED;
+ chan->flags = chan->orig_flags;
+ } else {
+ REG_DBG_PRINT("Disabling freq %d MHz\n",
+ chan->center_freq);
+ chan->flags |= IEEE80211_CHAN_DISABLED;
+ }
return;
}
@@ -873,7 +926,7 @@ static void handle_channel(struct wiphy *wiphy,
if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
request_wiphy && request_wiphy == wiphy &&
- request_wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) {
+ request_wiphy->regulatory_flags & REGULATORY_STRICT_REG) {
/*
* This guarantees the driver's requested regulatory domain
* will always be used as a base for further regulatory
@@ -899,13 +952,11 @@ static void handle_channel(struct wiphy *wiphy,
chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp);
if (chan->orig_mpwr) {
/*
- * Devices that have their own custom regulatory domain
- * but also use WIPHY_FLAG_STRICT_REGULATORY will follow the
- * passed country IE power settings.
+ * Devices that use REGULATORY_COUNTRY_IE_FOLLOW_POWER
+ * will always follow the passed country IE power settings.
*/
if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
- wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY &&
- wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY)
+ wiphy->regulatory_flags & REGULATORY_COUNTRY_IE_FOLLOW_POWER)
chan->max_power = chan->max_reg_power;
else
chan->max_power = min(chan->orig_mpwr,
@@ -975,8 +1026,8 @@ static bool reg_dev_ignore_cell_hint(struct wiphy *wiphy)
static bool wiphy_strict_alpha2_regd(struct wiphy *wiphy)
{
- if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY &&
- !(wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY))
+ if (wiphy->regulatory_flags & REGULATORY_STRICT_REG &&
+ !(wiphy->regulatory_flags & REGULATORY_CUSTOM_REG))
return true;
return false;
}
@@ -994,7 +1045,7 @@ static bool ignore_reg_update(struct wiphy *wiphy,
}
if (initiator == NL80211_REGDOM_SET_BY_CORE &&
- wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) {
+ wiphy->regulatory_flags & REGULATORY_CUSTOM_REG) {
REG_DBG_PRINT("Ignoring regulatory request set by %s "
"since the driver uses its own custom "
"regulatory domain\n",
@@ -1032,7 +1083,7 @@ static bool reg_is_world_roaming(struct wiphy *wiphy)
return true;
if (lr && lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE &&
- wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY)
+ wiphy->regulatory_flags & REGULATORY_CUSTOM_REG)
return true;
return false;
@@ -1060,19 +1111,14 @@ static void handle_reg_beacon(struct wiphy *wiphy, unsigned int chan_idx,
if (!reg_is_world_roaming(wiphy))
return;
- if (wiphy->flags & WIPHY_FLAG_DISABLE_BEACON_HINTS)
+ if (wiphy->regulatory_flags & REGULATORY_DISABLE_BEACON_HINTS)
return;
chan_before.center_freq = chan->center_freq;
chan_before.flags = chan->flags;
- if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) {
- chan->flags &= ~IEEE80211_CHAN_PASSIVE_SCAN;
- channel_changed = true;
- }
-
- if (chan->flags & IEEE80211_CHAN_NO_IBSS) {
- chan->flags &= ~IEEE80211_CHAN_NO_IBSS;
+ if (chan->flags & IEEE80211_CHAN_NO_IR) {
+ chan->flags &= ~IEEE80211_CHAN_NO_IR;
channel_changed = true;
}
@@ -1205,14 +1251,30 @@ static void reg_process_ht_flags(struct wiphy *wiphy)
reg_process_ht_flags_band(wiphy, wiphy->bands[band]);
}
+static void reg_call_notifier(struct wiphy *wiphy,
+ struct regulatory_request *request)
+{
+ if (wiphy->reg_notifier)
+ wiphy->reg_notifier(wiphy, request);
+}
+
static void wiphy_update_regulatory(struct wiphy *wiphy,
enum nl80211_reg_initiator initiator)
{
enum ieee80211_band band;
struct regulatory_request *lr = get_last_request();
- if (ignore_reg_update(wiphy, initiator))
+ if (ignore_reg_update(wiphy, initiator)) {
+ /*
+ * Regulatory updates set by CORE are ignored for custom
+ * regulatory cards. Let us notify the changes to the driver,
+ * as some drivers used this to restore its orig_* reg domain.
+ */
+ if (initiator == NL80211_REGDOM_SET_BY_CORE &&
+ wiphy->regulatory_flags & REGULATORY_CUSTOM_REG)
+ reg_call_notifier(wiphy, lr);
return;
+ }
lr->dfs_region = get_cfg80211_regdom()->dfs_region;
@@ -1221,9 +1283,7 @@ static void wiphy_update_regulatory(struct wiphy *wiphy,
reg_process_beacons(wiphy);
reg_process_ht_flags(wiphy);
-
- if (wiphy->reg_notifier)
- wiphy->reg_notifier(wiphy, lr);
+ reg_call_notifier(wiphy, lr);
}
static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
@@ -1236,15 +1296,6 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
wiphy = &rdev->wiphy;
wiphy_update_regulatory(wiphy, initiator);
- /*
- * Regulatory updates set by CORE are ignored for custom
- * regulatory cards. Let us notify the changes to the driver,
- * as some drivers used this to restore its orig_* reg domain.
- */
- if (initiator == NL80211_REGDOM_SET_BY_CORE &&
- wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY &&
- wiphy->reg_notifier)
- wiphy->reg_notifier(wiphy, get_last_request());
}
}
@@ -1263,7 +1314,8 @@ static void handle_channel_custom(struct wiphy *wiphy,
if (IS_ERR(reg_rule)) {
REG_DBG_PRINT("Disabling freq %d MHz as custom regd has no rule that fits it\n",
chan->center_freq);
- chan->flags = IEEE80211_CHAN_DISABLED;
+ chan->orig_flags |= IEEE80211_CHAN_DISABLED;
+ chan->flags = chan->orig_flags;
return;
}
@@ -1305,6 +1357,10 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
enum ieee80211_band band;
unsigned int bands_set = 0;
+ WARN(!(wiphy->regulatory_flags & REGULATORY_CUSTOM_REG),
+ "wiphy should have REGULATORY_CUSTOM_REG\n");
+ wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
+
for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
if (!wiphy->bands[band])
continue;
@@ -1320,225 +1376,285 @@ void wiphy_apply_custom_regulatory(struct wiphy *wiphy,
}
EXPORT_SYMBOL(wiphy_apply_custom_regulatory);
-/* This has the logic which determines when a new request
- * should be ignored. */
-static enum reg_request_treatment
-get_reg_request_treatment(struct wiphy *wiphy,
- struct regulatory_request *pending_request)
+static void reg_set_request_processed(void)
{
- struct wiphy *last_wiphy = NULL;
+ bool need_more_processing = false;
struct regulatory_request *lr = get_last_request();
- /* All initial requests are respected */
- if (!lr)
- return REG_REQ_OK;
+ lr->processed = true;
- switch (pending_request->initiator) {
- case NL80211_REGDOM_SET_BY_CORE:
- return REG_REQ_OK;
- case NL80211_REGDOM_SET_BY_COUNTRY_IE:
- if (reg_request_cell_base(lr)) {
- /* Trust a Cell base station over the AP's country IE */
- if (regdom_changes(pending_request->alpha2))
- return REG_REQ_IGNORE;
- return REG_REQ_ALREADY_SET;
- }
+ spin_lock(&reg_requests_lock);
+ if (!list_empty(&reg_requests_list))
+ need_more_processing = true;
+ spin_unlock(&reg_requests_lock);
- last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
+ if (lr->initiator == NL80211_REGDOM_SET_BY_USER)
+ cancel_delayed_work(&reg_timeout);
- if (unlikely(!is_an_alpha2(pending_request->alpha2)))
- return -EINVAL;
- if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) {
- if (last_wiphy != wiphy) {
- /*
- * Two cards with two APs claiming different
- * Country IE alpha2s. We could
- * intersect them, but that seems unlikely
- * to be correct. Reject second one for now.
- */
- if (regdom_changes(pending_request->alpha2))
- return REG_REQ_IGNORE;
- return REG_REQ_ALREADY_SET;
- }
- /*
- * Two consecutive Country IE hints on the same wiphy.
- * This should be picked up early by the driver/stack
- */
- if (WARN_ON(regdom_changes(pending_request->alpha2)))
- return REG_REQ_OK;
- return REG_REQ_ALREADY_SET;
- }
- return REG_REQ_OK;
- case NL80211_REGDOM_SET_BY_DRIVER:
- if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) {
- if (regdom_changes(pending_request->alpha2))
- return REG_REQ_OK;
- return REG_REQ_ALREADY_SET;
- }
+ if (need_more_processing)
+ schedule_work(&reg_work);
+}
- /*
- * This would happen if you unplug and plug your card
- * back in or if you add a new device for which the previously
- * loaded card also agrees on the regulatory domain.
- */
- if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
- !regdom_changes(pending_request->alpha2))
- return REG_REQ_ALREADY_SET;
+/**
+ * reg_process_hint_core - process core regulatory requests
+ * @pending_request: a pending core regulatory request
+ *
+ * The wireless subsystem can use this function to process
+ * a regulatory request issued by the regulatory core.
+ *
+ * Returns one of the different reg request treatment values.
+ */
+static enum reg_request_treatment
+reg_process_hint_core(struct regulatory_request *core_request)
+{
+
+ core_request->intersect = false;
+ core_request->processed = false;
+
+ reg_update_last_request(core_request);
+ return reg_call_crda(core_request);
+}
+
+static enum reg_request_treatment
+__reg_process_hint_user(struct regulatory_request *user_request)
+{
+ struct regulatory_request *lr = get_last_request();
+
+ if (reg_request_cell_base(user_request))
+ return reg_ignore_cell_hint(user_request);
+
+ if (reg_request_cell_base(lr))
+ return REG_REQ_IGNORE;
+
+ if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)
return REG_REQ_INTERSECT;
- case NL80211_REGDOM_SET_BY_USER:
- if (reg_request_cell_base(pending_request))
- return reg_ignore_cell_hint(pending_request);
+ /*
+ * If the user knows better the user should set the regdom
+ * to their country before the IE is picked up
+ */
+ if (lr->initiator == NL80211_REGDOM_SET_BY_USER &&
+ lr->intersect)
+ return REG_REQ_IGNORE;
+ /*
+ * Process user requests only after previous user/driver/core
+ * requests have been processed
+ */
+ if ((lr->initiator == NL80211_REGDOM_SET_BY_CORE ||
+ lr->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
+ lr->initiator == NL80211_REGDOM_SET_BY_USER) &&
+ regdom_changes(lr->alpha2))
+ return REG_REQ_IGNORE;
- if (reg_request_cell_base(lr))
- return REG_REQ_IGNORE;
+ if (!regdom_changes(user_request->alpha2))
+ return REG_REQ_ALREADY_SET;
- if (lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)
- return REG_REQ_INTERSECT;
- /*
- * If the user knows better the user should set the regdom
- * to their country before the IE is picked up
- */
- if (lr->initiator == NL80211_REGDOM_SET_BY_USER &&
- lr->intersect)
- return REG_REQ_IGNORE;
- /*
- * Process user requests only after previous user/driver/core
- * requests have been processed
- */
- if ((lr->initiator == NL80211_REGDOM_SET_BY_CORE ||
- lr->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
- lr->initiator == NL80211_REGDOM_SET_BY_USER) &&
- regdom_changes(lr->alpha2))
- return REG_REQ_IGNORE;
+ return REG_REQ_OK;
+}
- if (!regdom_changes(pending_request->alpha2))
- return REG_REQ_ALREADY_SET;
+/**
+ * reg_process_hint_user - process user regulatory requests
+ * @user_request: a pending user regulatory request
+ *
+ * The wireless subsystem can use this function to process
+ * a regulatory request initiated by userspace.
+ *
+ * Returns one of the different reg request treatment values.
+ */
+static enum reg_request_treatment
+reg_process_hint_user(struct regulatory_request *user_request)
+{
+ enum reg_request_treatment treatment;
- return REG_REQ_OK;
+ treatment = __reg_process_hint_user(user_request);
+ if (treatment == REG_REQ_IGNORE ||
+ treatment == REG_REQ_ALREADY_SET) {
+ kfree(user_request);
+ return treatment;
}
- return REG_REQ_IGNORE;
+ user_request->intersect = treatment == REG_REQ_INTERSECT;
+ user_request->processed = false;
+
+ reg_update_last_request(user_request);
+
+ user_alpha2[0] = user_request->alpha2[0];
+ user_alpha2[1] = user_request->alpha2[1];
+
+ return reg_call_crda(user_request);
}
-static void reg_set_request_processed(void)
+static enum reg_request_treatment
+__reg_process_hint_driver(struct regulatory_request *driver_request)
{
- bool need_more_processing = false;
struct regulatory_request *lr = get_last_request();
- lr->processed = true;
-
- spin_lock(&reg_requests_lock);
- if (!list_empty(&reg_requests_list))
- need_more_processing = true;
- spin_unlock(&reg_requests_lock);
+ if (lr->initiator == NL80211_REGDOM_SET_BY_CORE) {
+ if (regdom_changes(driver_request->alpha2))
+ return REG_REQ_OK;
+ return REG_REQ_ALREADY_SET;
+ }
- if (lr->initiator == NL80211_REGDOM_SET_BY_USER)
- cancel_delayed_work(&reg_timeout);
+ /*
+ * This would happen if you unplug and plug your card
+ * back in or if you add a new device for which the previously
+ * loaded card also agrees on the regulatory domain.
+ */
+ if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER &&
+ !regdom_changes(driver_request->alpha2))
+ return REG_REQ_ALREADY_SET;
- if (need_more_processing)
- schedule_work(&reg_work);
+ return REG_REQ_INTERSECT;
}
/**
- * __regulatory_hint - hint to the wireless core a regulatory domain
- * @wiphy: if the hint comes from country information from an AP, this
- * is required to be set to the wiphy that received the information
- * @pending_request: the regulatory request currently being processed
+ * reg_process_hint_driver - process driver regulatory requests
+ * @driver_request: a pending driver regulatory request
*
- * The Wireless subsystem can use this function to hint to the wireless core
- * what it believes should be the current regulatory domain.
+ * The wireless subsystem can use this function to process
+ * a regulatory request issued by an 802.11 driver.
*
* Returns one of the different reg request treatment values.
*/
static enum reg_request_treatment
-__regulatory_hint(struct wiphy *wiphy,
- struct regulatory_request *pending_request)
+reg_process_hint_driver(struct wiphy *wiphy,
+ struct regulatory_request *driver_request)
{
const struct ieee80211_regdomain *regd;
- bool intersect = false;
enum reg_request_treatment treatment;
- struct regulatory_request *lr;
- treatment = get_reg_request_treatment(wiphy, pending_request);
+ treatment = __reg_process_hint_driver(driver_request);
switch (treatment) {
- case REG_REQ_INTERSECT:
- if (pending_request->initiator ==
- NL80211_REGDOM_SET_BY_DRIVER) {
- regd = reg_copy_regd(get_cfg80211_regdom());
- if (IS_ERR(regd)) {
- kfree(pending_request);
- return PTR_ERR(regd);
- }
- rcu_assign_pointer(wiphy->regd, regd);
- }
- intersect = true;
- break;
case REG_REQ_OK:
break;
- default:
- /*
- * If the regulatory domain being requested by the
- * driver has already been set just copy it to the
- * wiphy
- */
- if (treatment == REG_REQ_ALREADY_SET &&
- pending_request->initiator == NL80211_REGDOM_SET_BY_DRIVER) {
- regd = reg_copy_regd(get_cfg80211_regdom());
- if (IS_ERR(regd)) {
- kfree(pending_request);
- return REG_REQ_IGNORE;
- }
- treatment = REG_REQ_ALREADY_SET;
- rcu_assign_pointer(wiphy->regd, regd);
- goto new_request;
- }
- kfree(pending_request);
+ case REG_REQ_IGNORE:
+ kfree(driver_request);
return treatment;
+ case REG_REQ_INTERSECT:
+ /* fall through */
+ case REG_REQ_ALREADY_SET:
+ regd = reg_copy_regd(get_cfg80211_regdom());
+ if (IS_ERR(regd)) {
+ kfree(driver_request);
+ return REG_REQ_IGNORE;
+ }
+ rcu_assign_pointer(wiphy->regd, regd);
}
-new_request:
- lr = get_last_request();
- if (lr != &core_request_world && lr)
- kfree_rcu(lr, rcu_head);
- pending_request->intersect = intersect;
- pending_request->processed = false;
- rcu_assign_pointer(last_request, pending_request);
- lr = pending_request;
+ driver_request->intersect = treatment == REG_REQ_INTERSECT;
+ driver_request->processed = false;
- pending_request = NULL;
+ reg_update_last_request(driver_request);
- if (lr->initiator == NL80211_REGDOM_SET_BY_USER) {
- user_alpha2[0] = lr->alpha2[0];
- user_alpha2[1] = lr->alpha2[1];
+ /*
+ * Since CRDA will not be called in this case as we already
+ * have applied the requested regulatory domain before we just
+ * inform userspace we have processed the request
+ */
+ if (treatment == REG_REQ_ALREADY_SET) {
+ nl80211_send_reg_change_event(driver_request);
+ reg_set_request_processed();
+ return treatment;
}
- /* When r == REG_REQ_INTERSECT we do need to call CRDA */
- if (treatment != REG_REQ_OK && treatment != REG_REQ_INTERSECT) {
+ return reg_call_crda(driver_request);
+}
+
+static enum reg_request_treatment
+__reg_process_hint_country_ie(struct wiphy *wiphy,
+ struct regulatory_request *country_ie_request)
+{
+ struct wiphy *last_wiphy = NULL;
+ struct regulatory_request *lr = get_last_request();
+
+ if (reg_request_cell_base(lr)) {
+ /* Trust a Cell base station over the AP's country IE */
+ if (regdom_changes(country_ie_request->alpha2))
+ return REG_REQ_IGNORE;
+ return REG_REQ_ALREADY_SET;
+ } else {
+ if (wiphy->regulatory_flags & REGULATORY_COUNTRY_IE_IGNORE)
+ return REG_REQ_IGNORE;
+ }
+
+ if (unlikely(!is_an_alpha2(country_ie_request->alpha2)))
+ return -EINVAL;
+
+ if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE)
+ return REG_REQ_OK;
+
+ last_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
+
+ if (last_wiphy != wiphy) {
/*
- * Since CRDA will not be called in this case as we already
- * have applied the requested regulatory domain before we just
- * inform userspace we have processed the request
+ * Two cards with two APs claiming different
+ * Country IE alpha2s. We could
+ * intersect them, but that seems unlikely
+ * to be correct. Reject second one for now.
*/
- if (treatment == REG_REQ_ALREADY_SET) {
- nl80211_send_reg_change_event(lr);
- reg_set_request_processed();
- }
- return treatment;
+ if (regdom_changes(country_ie_request->alpha2))
+ return REG_REQ_IGNORE;
+ return REG_REQ_ALREADY_SET;
}
+ /*
+ * Two consecutive Country IE hints on the same wiphy.
+ * This should be picked up early by the driver/stack
+ */
+ if (WARN_ON(regdom_changes(country_ie_request->alpha2)))
+ return REG_REQ_OK;
+ return REG_REQ_ALREADY_SET;
+}
+
+/**
+ * reg_process_hint_country_ie - process regulatory requests from country IEs
+ * @country_ie_request: a regulatory request from a country IE
+ *
+ * The wireless subsystem can use this function to process
+ * a regulatory request issued by a country Information Element.
+ *
+ * Returns one of the different reg request treatment values.
+ */
+static enum reg_request_treatment
+reg_process_hint_country_ie(struct wiphy *wiphy,
+ struct regulatory_request *country_ie_request)
+{
+ enum reg_request_treatment treatment;
+
+ treatment = __reg_process_hint_country_ie(wiphy, country_ie_request);
- if (call_crda(lr->alpha2))
+ switch (treatment) {
+ case REG_REQ_OK:
+ break;
+ case REG_REQ_IGNORE:
+ /* fall through */
+ case REG_REQ_ALREADY_SET:
+ kfree(country_ie_request);
+ return treatment;
+ case REG_REQ_INTERSECT:
+ kfree(country_ie_request);
+ /*
+ * This doesn't happen yet, not sure we
+ * ever want to support it for this case.
+ */
+ WARN_ONCE(1, "Unexpected intersection for country IEs");
return REG_REQ_IGNORE;
- return REG_REQ_OK;
+ }
+
+ country_ie_request->intersect = false;
+ country_ie_request->processed = false;
+
+ reg_update_last_request(country_ie_request);
+
+ return reg_call_crda(country_ie_request);
}
/* This processes *all* regulatory hints */
-static void reg_process_hint(struct regulatory_request *reg_request,
- enum nl80211_reg_initiator reg_initiator)
+static void reg_process_hint(struct regulatory_request *reg_request)
{
struct wiphy *wiphy = NULL;
+ enum reg_request_treatment treatment;
if (WARN_ON(!reg_request->alpha2))
return;
@@ -1546,23 +1662,37 @@ static void reg_process_hint(struct regulatory_request *reg_request,
if (reg_request->wiphy_idx != WIPHY_IDX_INVALID)
wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx);
- if (reg_initiator == NL80211_REGDOM_SET_BY_DRIVER && !wiphy) {
+ if (reg_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && !wiphy) {
kfree(reg_request);
return;
}
- switch (__regulatory_hint(wiphy, reg_request)) {
- case REG_REQ_ALREADY_SET:
- /* This is required so that the orig_* parameters are saved */
- if (wiphy && wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY)
- wiphy_update_regulatory(wiphy, reg_initiator);
+ switch (reg_request->initiator) {
+ case NL80211_REGDOM_SET_BY_CORE:
+ reg_process_hint_core(reg_request);
+ return;
+ case NL80211_REGDOM_SET_BY_USER:
+ treatment = reg_process_hint_user(reg_request);
+ if (treatment == REG_REQ_OK ||
+ treatment == REG_REQ_ALREADY_SET)
+ return;
+ schedule_delayed_work(&reg_timeout, msecs_to_jiffies(3142));
+ return;
+ case NL80211_REGDOM_SET_BY_DRIVER:
+ treatment = reg_process_hint_driver(wiphy, reg_request);
break;
- default:
- if (reg_initiator == NL80211_REGDOM_SET_BY_USER)
- schedule_delayed_work(&reg_timeout,
- msecs_to_jiffies(3142));
+ case NL80211_REGDOM_SET_BY_COUNTRY_IE:
+ treatment = reg_process_hint_country_ie(wiphy, reg_request);
break;
+ default:
+ WARN(1, "invalid initiator %d\n", reg_request->initiator);
+ return;
}
+
+ /* This is required so that the orig_* parameters are saved */
+ if (treatment == REG_REQ_ALREADY_SET && wiphy &&
+ wiphy->regulatory_flags & REGULATORY_STRICT_REG)
+ wiphy_update_regulatory(wiphy, reg_request->initiator);
}
/*
@@ -1596,7 +1726,7 @@ static void reg_process_pending_hints(void)
spin_unlock(&reg_requests_lock);
- reg_process_hint(reg_request, reg_request->initiator);
+ reg_process_hint(reg_request);
}
/* Processes beacon hints -- this has nothing to do with country IEs */
@@ -1888,7 +2018,7 @@ static void restore_regulatory_settings(bool reset_user)
world_alpha2[1] = cfg80211_world_regdom->alpha2[1];
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
- if (rdev->wiphy.flags & WIPHY_FLAG_CUSTOM_REGULATORY)
+ if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG)
restore_custom_reg_settings(&rdev->wiphy);
}
@@ -2016,7 +2146,7 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd)
}
}
-bool reg_supported_dfs_region(u8 dfs_region)
+bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region)
{
switch (dfs_region) {
case NL80211_DFS_UNSET:
@@ -2031,27 +2161,6 @@ bool reg_supported_dfs_region(u8 dfs_region)
}
}
-static void print_dfs_region(u8 dfs_region)
-{
- if (!dfs_region)
- return;
-
- switch (dfs_region) {
- case NL80211_DFS_FCC:
- pr_info(" DFS Master region FCC");
- break;
- case NL80211_DFS_ETSI:
- pr_info(" DFS Master region ETSI");
- break;
- case NL80211_DFS_JP:
- pr_info(" DFS Master region JP");
- break;
- default:
- pr_info(" DFS Master region Unknown");
- break;
- }
-}
-
static void print_regdomain(const struct ieee80211_regdomain *rd)
{
struct regulatory_request *lr = get_last_request();
@@ -2083,7 +2192,7 @@ static void print_regdomain(const struct ieee80211_regdomain *rd)
}
}
- print_dfs_region(rd->dfs_region);
+ pr_info(" DFS Master region: %s", reg_dfs_region_str(rd->dfs_region));
print_rd_rules(rd);
}
@@ -2093,48 +2202,60 @@ static void print_regdomain_info(const struct ieee80211_regdomain *rd)
print_rd_rules(rd);
}
-/* Takes ownership of rd only if it doesn't fail */
-static int __set_regdom(const struct ieee80211_regdomain *rd)
+static int reg_set_rd_core(const struct ieee80211_regdomain *rd)
+{
+ if (!is_world_regdom(rd->alpha2))
+ return -EINVAL;
+ update_world_regdomain(rd);
+ return 0;
+}
+
+static int reg_set_rd_user(const struct ieee80211_regdomain *rd,
+ struct regulatory_request *user_request)
{
- const struct ieee80211_regdomain *regd;
const struct ieee80211_regdomain *intersected_rd = NULL;
- struct wiphy *request_wiphy;
- struct regulatory_request *lr = get_last_request();
- /* Some basic sanity checks first */
+ if (is_world_regdom(rd->alpha2))
+ return -EINVAL;
+
+ if (!regdom_changes(rd->alpha2))
+ return -EALREADY;
- if (!reg_is_valid_request(rd->alpha2))
+ if (!is_valid_rd(rd)) {
+ pr_err("Invalid regulatory domain detected:\n");
+ print_regdomain_info(rd);
return -EINVAL;
+ }
- if (is_world_regdom(rd->alpha2)) {
- update_world_regdomain(rd);
+ if (!user_request->intersect) {
+ reset_regdomains(false, rd);
return 0;
}
- if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) &&
- !is_unknown_alpha2(rd->alpha2))
+ intersected_rd = regdom_intersect(rd, get_cfg80211_regdom());
+ if (!intersected_rd)
return -EINVAL;
- /*
- * Lets only bother proceeding on the same alpha2 if the current
- * rd is non static (it means CRDA was present and was used last)
- * and the pending request came in from a country IE
- */
- if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) {
- /*
- * If someone else asked us to change the rd lets only bother
- * checking if the alpha2 changes if CRDA was already called
- */
- if (!regdom_changes(rd->alpha2))
- return -EALREADY;
- }
+ kfree(rd);
+ rd = NULL;
+ reset_regdomains(false, intersected_rd);
- /*
- * Now lets set the regulatory domain, update all driver channels
- * and finally inform them of what we have done, in case they want
- * to review or adjust their own settings based on their own
- * internal EEPROM data
- */
+ return 0;
+}
+
+static int reg_set_rd_driver(const struct ieee80211_regdomain *rd,
+ struct regulatory_request *driver_request)
+{
+ const struct ieee80211_regdomain *regd;
+ const struct ieee80211_regdomain *intersected_rd = NULL;
+ const struct ieee80211_regdomain *tmp;
+ struct wiphy *request_wiphy;
+
+ if (is_world_regdom(rd->alpha2))
+ return -EINVAL;
+
+ if (!regdom_changes(rd->alpha2))
+ return -EALREADY;
if (!is_valid_rd(rd)) {
pr_err("Invalid regulatory domain detected:\n");
@@ -2142,29 +2263,13 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
return -EINVAL;
}
- request_wiphy = wiphy_idx_to_wiphy(lr->wiphy_idx);
- if (!request_wiphy &&
- (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER ||
- lr->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE)) {
+ request_wiphy = wiphy_idx_to_wiphy(driver_request->wiphy_idx);
+ if (!request_wiphy) {
schedule_delayed_work(&reg_timeout, 0);
return -ENODEV;
}
- if (!lr->intersect) {
- if (lr->initiator != NL80211_REGDOM_SET_BY_DRIVER) {
- reset_regdomains(false, rd);
- return 0;
- }
-
- /*
- * For a driver hint, lets copy the regulatory domain the
- * driver wanted to the wiphy to deal with conflicts
- */
-
- /*
- * Userspace could have sent two replies with only
- * one kernel request.
- */
+ if (!driver_request->intersect) {
if (request_wiphy->regd)
return -EALREADY;
@@ -2177,38 +2282,59 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
return 0;
}
- /* Intersection requires a bit more work */
+ intersected_rd = regdom_intersect(rd, get_cfg80211_regdom());
+ if (!intersected_rd)
+ return -EINVAL;
- if (lr->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) {
- intersected_rd = regdom_intersect(rd, get_cfg80211_regdom());
- if (!intersected_rd)
- return -EINVAL;
+ /*
+ * We can trash what CRDA provided now.
+ * However if a driver requested this specific regulatory
+ * domain we keep it for its private use
+ */
+ tmp = get_wiphy_regdom(request_wiphy);
+ rcu_assign_pointer(request_wiphy->regd, rd);
+ rcu_free_regdom(tmp);
- /*
- * We can trash what CRDA provided now.
- * However if a driver requested this specific regulatory
- * domain we keep it for its private use
- */
- if (lr->initiator == NL80211_REGDOM_SET_BY_DRIVER) {
- const struct ieee80211_regdomain *tmp;
+ rd = NULL;
- tmp = get_wiphy_regdom(request_wiphy);
- rcu_assign_pointer(request_wiphy->regd, rd);
- rcu_free_regdom(tmp);
- } else {
- kfree(rd);
- }
+ reset_regdomains(false, intersected_rd);
- rd = NULL;
+ return 0;
+}
- reset_regdomains(false, intersected_rd);
+static int reg_set_rd_country_ie(const struct ieee80211_regdomain *rd,
+ struct regulatory_request *country_ie_request)
+{
+ struct wiphy *request_wiphy;
- return 0;
+ if (!is_alpha2_set(rd->alpha2) && !is_an_alpha2(rd->alpha2) &&
+ !is_unknown_alpha2(rd->alpha2))
+ return -EINVAL;
+
+ /*
+ * Lets only bother proceeding on the same alpha2 if the current
+ * rd is non static (it means CRDA was present and was used last)
+ * and the pending request came in from a country IE
+ */
+
+ if (!is_valid_rd(rd)) {
+ pr_err("Invalid regulatory domain detected:\n");
+ print_regdomain_info(rd);
+ return -EINVAL;
}
- return -EINVAL;
-}
+ request_wiphy = wiphy_idx_to_wiphy(country_ie_request->wiphy_idx);
+ if (!request_wiphy) {
+ schedule_delayed_work(&reg_timeout, 0);
+ return -ENODEV;
+ }
+
+ if (country_ie_request->intersect)
+ return -EINVAL;
+ reset_regdomains(false, rd);
+ return 0;
+}
/*
* Use this call to set the current regulatory domain. Conflicts with
@@ -2220,10 +2346,32 @@ int set_regdom(const struct ieee80211_regdomain *rd)
struct regulatory_request *lr;
int r;
+ if (!reg_is_valid_request(rd->alpha2)) {
+ kfree(rd);
+ return -EINVAL;
+ }
+
lr = get_last_request();
/* Note that this doesn't update the wiphys, this is done below */
- r = __set_regdom(rd);
+ switch (lr->initiator) {
+ case NL80211_REGDOM_SET_BY_CORE:
+ r = reg_set_rd_core(rd);
+ break;
+ case NL80211_REGDOM_SET_BY_USER:
+ r = reg_set_rd_user(rd, lr);
+ break;
+ case NL80211_REGDOM_SET_BY_DRIVER:
+ r = reg_set_rd_driver(rd, lr);
+ break;
+ case NL80211_REGDOM_SET_BY_COUNTRY_IE:
+ r = reg_set_rd_country_ie(rd, lr);
+ break;
+ default:
+ WARN(1, "invalid initiator %d\n", lr->initiator);
+ return -EINVAL;
+ }
+
if (r) {
if (r == -EALREADY)
reg_set_request_processed();
diff --git a/net/wireless/reg.h b/net/wireless/reg.h
index 9677e3c13da9..cc4c2c0a6723 100644
--- a/net/wireless/reg.h
+++ b/net/wireless/reg.h
@@ -18,8 +18,9 @@
extern const struct ieee80211_regdomain __rcu *cfg80211_regdomain;
+bool reg_is_valid_request(const char *alpha2);
bool is_world_regdom(const char *alpha2);
-bool reg_supported_dfs_region(u8 dfs_region);
+bool reg_supported_dfs_region(enum nl80211_dfs_regions dfs_region);
int regulatory_hint_user(const char *alpha2,
enum nl80211_user_reg_hint_type user_reg_hint_type);
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index ba5f0d6614d5..f7aa7a72d9bc 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -1653,9 +1653,8 @@ TRACE_EVENT(rdev_cancel_remain_on_channel,
TRACE_EVENT(rdev_mgmt_tx,
TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev,
- struct ieee80211_channel *chan, bool offchan,
- unsigned int wait, bool no_cck, bool dont_wait_for_ack),
- TP_ARGS(wiphy, wdev, chan, offchan, wait, no_cck, dont_wait_for_ack),
+ struct cfg80211_mgmt_tx_params *params),
+ TP_ARGS(wiphy, wdev, params),
TP_STRUCT__entry(
WIPHY_ENTRY
WDEV_ENTRY
@@ -1668,11 +1667,11 @@ TRACE_EVENT(rdev_mgmt_tx,
TP_fast_assign(
WIPHY_ASSIGN;
WDEV_ASSIGN;
- CHAN_ASSIGN(chan);
- __entry->offchan = offchan;
- __entry->wait = wait;
- __entry->no_cck = no_cck;
- __entry->dont_wait_for_ack = dont_wait_for_ack;
+ CHAN_ASSIGN(params->chan);
+ __entry->offchan = params->offchan;
+ __entry->wait = params->wait;
+ __entry->no_cck = params->no_cck;
+ __entry->dont_wait_for_ack = params->dont_wait_for_ack;
),
TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", " CHAN_PR_FMT ", offchan: %s,"
" wait: %u, no cck: %s, dont wait for ack: %s",
diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c
index 7622789d3750..c8a8297cd4b8 100644
--- a/net/x25/af_x25.c
+++ b/net/x25/af_x25.c
@@ -35,6 +35,8 @@
* response
*/
+#define pr_fmt(fmt) "X25: " fmt
+
#include <linux/module.h>
#include <linux/capability.h>
#include <linux/errno.h>
@@ -1809,7 +1811,7 @@ static int __init x25_init(void)
if (rc != 0)
goto out_sock;
- printk(KERN_INFO "X.25 for Linux Version 0.2\n");
+ pr_info("Linux Version 0.2\n");
x25_register_sysctl();
rc = x25_proc_init();
diff --git a/net/x25/x25_dev.c b/net/x25/x25_dev.c
index a8a236338e61..39231237e1c3 100644
--- a/net/x25/x25_dev.c
+++ b/net/x25/x25_dev.c
@@ -17,6 +17,8 @@
* 2000-09-04 Henner Eisen Prevent freeing a dangling skb.
*/
+#define pr_fmt(fmt) "X25: " fmt
+
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
@@ -89,7 +91,7 @@ static int x25_receive_data(struct sk_buff *skb, struct x25_neigh *nb)
*/
if (frametype != X25_CLEAR_CONFIRMATION)
- printk(KERN_DEBUG "x25_receive_data(): unknown frame type %2x\n",frametype);
+ pr_debug("x25_receive_data(): unknown frame type %2x\n",frametype);
return 0;
}
@@ -114,7 +116,7 @@ int x25_lapb_receive_frame(struct sk_buff *skb, struct net_device *dev,
*/
nb = x25_get_neigh(dev);
if (!nb) {
- printk(KERN_DEBUG "X.25: unknown neighbour - %s\n", dev->name);
+ pr_debug("unknown neighbour - %s\n", dev->name);
goto drop;
}
@@ -154,7 +156,7 @@ void x25_establish_link(struct x25_neigh *nb)
switch (nb->dev->type) {
case ARPHRD_X25:
if ((skb = alloc_skb(1, GFP_ATOMIC)) == NULL) {
- printk(KERN_ERR "x25_dev: out of memory\n");
+ pr_err("x25_dev: out of memory\n");
return;
}
ptr = skb_put(skb, 1);
@@ -189,7 +191,7 @@ void x25_terminate_link(struct x25_neigh *nb)
skb = alloc_skb(1, GFP_ATOMIC);
if (!skb) {
- printk(KERN_ERR "x25_dev: out of memory\n");
+ pr_err("x25_dev: out of memory\n");
return;
}
diff --git a/net/x25/x25_facilities.c b/net/x25/x25_facilities.c
index b8253250d723..7ecd04c21360 100644
--- a/net/x25/x25_facilities.c
+++ b/net/x25/x25_facilities.c
@@ -21,6 +21,8 @@
* on response.
*/
+#define pr_fmt(fmt) "X25: " fmt
+
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/skbuff.h>
@@ -109,7 +111,7 @@ int x25_parse_facilities(struct sk_buff *skb, struct x25_facilities *facilities,
case X25_MARKER:
break;
default:
- printk(KERN_DEBUG "X.25: unknown facility "
+ pr_debug("unknown facility "
"%02X, value %02X\n",
p[0], p[1]);
break;
@@ -132,7 +134,7 @@ int x25_parse_facilities(struct sk_buff *skb, struct x25_facilities *facilities,
*vc_fac_mask |= X25_MASK_WINDOW_SIZE;
break;
default:
- printk(KERN_DEBUG "X.25: unknown facility "
+ pr_debug("unknown facility "
"%02X, values %02X, %02X\n",
p[0], p[1], p[2]);
break;
@@ -143,7 +145,7 @@ int x25_parse_facilities(struct sk_buff *skb, struct x25_facilities *facilities,
case X25_FAC_CLASS_C:
if (len < 4)
return -1;
- printk(KERN_DEBUG "X.25: unknown facility %02X, "
+ pr_debug("unknown facility %02X, "
"values %02X, %02X, %02X\n",
p[0], p[1], p[2], p[3]);
p += 4;
@@ -172,7 +174,7 @@ int x25_parse_facilities(struct sk_buff *skb, struct x25_facilities *facilities,
*vc_fac_mask |= X25_MASK_CALLED_AE;
break;
default:
- printk(KERN_DEBUG "X.25: unknown facility %02X,"
+ pr_debug("unknown facility %02X,"
"length %d\n", p[0], p[1]);
break;
}
@@ -341,12 +343,12 @@ void x25_limit_facilities(struct x25_facilities *facilities,
if (!nb->extended) {
if (facilities->winsize_in > 7) {
- printk(KERN_DEBUG "X.25: incoming winsize limited to 7\n");
+ pr_debug("incoming winsize limited to 7\n");
facilities->winsize_in = 7;
}
if (facilities->winsize_out > 7) {
facilities->winsize_out = 7;
- printk( KERN_DEBUG "X.25: outgoing winsize limited to 7\n");
+ pr_debug("outgoing winsize limited to 7\n");
}
}
}
diff --git a/net/x25/x25_forward.c b/net/x25/x25_forward.c
index c541b622ae16..cf561f1613e1 100644
--- a/net/x25/x25_forward.c
+++ b/net/x25/x25_forward.c
@@ -8,6 +8,9 @@
* History
* 03-01-2007 Added forwarding for x.25 Andrew Hendry
*/
+
+#define pr_fmt(fmt) "X25: " fmt
+
#include <linux/if_arp.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -51,7 +54,7 @@ int x25_forward_call(struct x25_address *dest_addr, struct x25_neigh *from,
list_for_each(entry, &x25_forward_list) {
x25_frwd = list_entry(entry, struct x25_forward, node);
if (x25_frwd->lci == lci) {
- printk(KERN_WARNING "X.25: call request for lci which is already registered!, transmitting but not registering new pair\n");
+ pr_warn("call request for lci which is already registered!, transmitting but not registering new pair\n");
same_lci = 1;
}
}
diff --git a/net/x25/x25_in.c b/net/x25/x25_in.c
index a49cd4ec551a..d1b0dc79bb6f 100644
--- a/net/x25/x25_in.c
+++ b/net/x25/x25_in.c
@@ -23,6 +23,8 @@
* i-frames.
*/
+#define pr_fmt(fmt) "X25: " fmt
+
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/kernel.h>
@@ -317,7 +319,7 @@ static int x25_state3_machine(struct sock *sk, struct sk_buff *skb, int frametyp
break;
default:
- printk(KERN_WARNING "x25: unknown %02X in state 3\n", frametype);
+ pr_warn("unknown %02X in state 3\n", frametype);
break;
}
diff --git a/net/x25/x25_link.c b/net/x25/x25_link.c
index 4acacf3c6617..fd5ffb25873f 100644
--- a/net/x25/x25_link.c
+++ b/net/x25/x25_link.c
@@ -21,6 +21,8 @@
* 2000-09-04 Henner Eisen dev_hold() / dev_put() for x25_neigh.
*/
+#define pr_fmt(fmt) "X25: " fmt
+
#include <linux/kernel.h>
#include <linux/jiffies.h>
#include <linux/timer.h>
@@ -93,13 +95,13 @@ void x25_link_control(struct sk_buff *skb, struct x25_neigh *nb,
if (!pskb_may_pull(skb, X25_STD_MIN_LEN + 4))
break;
- printk(KERN_WARNING "x25: diagnostic #%d - %02X %02X %02X\n",
+ pr_warn("diagnostic #%d - %02X %02X %02X\n",
skb->data[3], skb->data[4],
skb->data[5], skb->data[6]);
break;
default:
- printk(KERN_WARNING "x25: received unknown %02X with LCI 000\n",
+ pr_warn("received unknown %02X with LCI 000\n",
frametype);
break;
}
diff --git a/net/x25/x25_subr.c b/net/x25/x25_subr.c
index 5170d52bfd96..6b5af65f491f 100644
--- a/net/x25/x25_subr.c
+++ b/net/x25/x25_subr.c
@@ -23,6 +23,8 @@
* restriction on response.
*/
+#define pr_fmt(fmt) "X25: " fmt
+
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/string.h>
@@ -148,7 +150,7 @@ void x25_write_internal(struct sock *sk, int frametype)
case X25_RESET_CONFIRMATION:
break;
default:
- printk(KERN_ERR "X.25: invalid frame type %02X\n", frametype);
+ pr_err("invalid frame type %02X\n", frametype);
return;
}
@@ -338,7 +340,7 @@ int x25_decode(struct sock *sk, struct sk_buff *skb, int *ns, int *nr, int *q,
}
}
- printk(KERN_DEBUG "X.25: invalid PLP frame %02X %02X %02X\n",
+ pr_debug("invalid PLP frame %02X %02X %02X\n",
frame[0], frame[1], frame[2]);
return X25_ILLEGAL;