summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2014-11-18 09:32:36 +0100
committerJohannes Berg <johannes.berg@intel.com>2014-11-18 09:32:44 +0100
commit760a52e80fdf018a9f83a499427923e18e3bd582 (patch)
treeb37743abb28d9109ce6278b8a896712e713bb25a /net
parent0395442ad25853f50d515f4dc00e3475b0df920d (diff)
parent47b6308b643302e642ca2a5cb6470926f7e1c428 (diff)
downloadop-kernel-dev-760a52e80fdf018a9f83a499427923e18e3bd582.zip
op-kernel-dev-760a52e80fdf018a9f83a499427923e18e3bd582.tar.gz
Merge remote-tracking branch 'wireless-next/master' into mac80211-next
This brings in some mwifiex changes that further patches will need to work on top to not cause merge conflicts. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net')
-rw-r--r--net/6lowpan/iphc.c100
-rw-r--r--net/9p/trans_virtio.c2
-rw-r--r--net/Kconfig1
-rw-r--r--net/batman-adv/gateway_common.c8
-rw-r--r--net/bluetooth/6lowpan.c297
-rw-r--r--net/bluetooth/Kconfig20
-rw-r--r--net/bluetooth/bnep/Kconfig2
-rw-r--r--net/bluetooth/cmtp/Kconfig2
-rw-r--r--net/bluetooth/hci_conn.c14
-rw-r--r--net/bluetooth/hci_core.c88
-rw-r--r--net/bluetooth/hci_event.c111
-rw-r--r--net/bluetooth/hci_sock.c2
-rw-r--r--net/bluetooth/hidp/Kconfig2
-rw-r--r--net/bluetooth/l2cap_core.c7
-rw-r--r--net/bluetooth/mgmt.c139
-rw-r--r--net/bluetooth/rfcomm/Kconfig2
-rw-r--r--net/bluetooth/rfcomm/core.c12
-rw-r--r--net/bluetooth/smp.c34
-rw-r--r--net/caif/caif_usb.c7
-rw-r--r--net/caif/cfmuxl.c4
-rw-r--r--net/ceph/Kconfig1
-rw-r--r--net/ceph/ceph_common.c15
-rw-r--r--net/ceph/ceph_strings.c75
-rw-r--r--net/ceph/crypto.c1
-rw-r--r--net/ceph/debugfs.c3
-rw-r--r--net/ceph/messenger.c28
-rw-r--r--net/ceph/mon_client.c8
-rw-r--r--net/ceph/osd_client.c192
-rw-r--r--net/ceph/osdmap.c52
-rw-r--r--net/ceph/pagelist.c7
-rw-r--r--net/core/dev.c16
-rw-r--r--net/core/drop_monitor.c2
-rw-r--r--net/core/filter.c9
-rw-r--r--net/core/flow_dissector.c36
-rw-r--r--net/core/skbuff.c37
-rw-r--r--net/core/sock.c2
-rw-r--r--net/dccp/ipv6.c3
-rw-r--r--net/dccp/proto.c2
-rw-r--r--net/dns_resolver/dns_key.c18
-rw-r--r--net/dsa/slave.c3
-rw-r--r--net/ieee802154/6lowpan_rtnl.c146
-rw-r--r--net/ieee802154/Makefile4
-rw-r--r--net/ieee802154/af802154.h4
-rw-r--r--net/ieee802154/af_ieee802154.c4
-rw-r--r--net/ieee802154/core.c163
-rw-r--r--net/ieee802154/core.h29
-rw-r--r--net/ieee802154/dgram.c6
-rw-r--r--net/ieee802154/header_ops.c3
-rw-r--r--net/ieee802154/ieee802154.h4
-rw-r--r--net/ieee802154/netlink.c4
-rw-r--r--net/ieee802154/nl-mac.c230
-rw-r--r--net/ieee802154/nl-phy.c29
-rw-r--r--net/ieee802154/nl_policy.c4
-rw-r--r--net/ieee802154/raw.c4
-rw-r--r--net/ieee802154/rdev-ops.h23
-rw-r--r--net/ieee802154/reassembly.c8
-rw-r--r--net/ieee802154/reassembly.h4
-rw-r--r--net/ieee802154/sysfs.c104
-rw-r--r--net/ieee802154/sysfs.h9
-rw-r--r--net/ieee802154/wpan-class.c230
-rw-r--r--net/ipv4/fib_semantics.c2
-rw-r--r--net/ipv4/fou.c3
-rw-r--r--net/ipv4/gre_offload.c6
-rw-r--r--net/ipv4/ip_output.c12
-rw-r--r--net/ipv4/ip_tunnel_core.c3
-rw-r--r--net/ipv4/route.c4
-rw-r--r--net/ipv4/syncookies.c18
-rw-r--r--net/ipv4/tcp.c6
-rw-r--r--net/ipv4/tcp_input.c36
-rw-r--r--net/ipv4/tcp_ipv4.c22
-rw-r--r--net/ipv4/tcp_memcontrol.c2
-rw-r--r--net/ipv4/tcp_output.c36
-rw-r--r--net/ipv6/anycast.c1
-rw-r--r--net/ipv6/ip6_offload.c1
-rw-r--r--net/ipv6/sit.c6
-rw-r--r--net/ipv6/syncookies.c4
-rw-r--r--net/ipv6/tcp_ipv6.c26
-rw-r--r--net/mac802154/Kconfig2
-rw-r--r--net/mac802154/Makefile4
-rw-r--r--net/mac802154/cfg.c47
-rw-r--r--net/mac802154/cfg.h9
-rw-r--r--net/mac802154/driver-ops.h226
-rw-r--r--net/mac802154/ieee802154_dev.c415
-rw-r--r--net/mac802154/ieee802154_i.h (renamed from net/mac802154/mac802154.h)102
-rw-r--r--net/mac802154/iface.c521
-rw-r--r--net/mac802154/llsec.c4
-rw-r--r--net/mac802154/mac_cmd.c64
-rw-r--r--net/mac802154/main.c191
-rw-r--r--net/mac802154/mib.c265
-rw-r--r--net/mac802154/monitor.c117
-rw-r--r--net/mac802154/rx.c311
-rw-r--r--net/mac802154/tx.c154
-rw-r--r--net/mac802154/util.c55
-rw-r--r--net/mac802154/wpan.c599
-rw-r--r--net/netfilter/ipvs/ip_vs_ftp.c6
-rw-r--r--net/netfilter/nf_conntrack_ftp.c4
-rw-r--r--net/netfilter/nf_conntrack_sip.c22
-rw-r--r--net/netfilter/nf_log.c2
-rw-r--r--net/netfilter/nf_nat_sip.c2
-rw-r--r--net/netfilter/nft_reject.c10
-rw-r--r--net/netlabel/netlabel_kapi.c1
-rw-r--r--net/netlink/af_netlink.c2
-rw-r--r--net/netrom/af_netrom.c2
-rw-r--r--net/netrom/nr_dev.c2
-rw-r--r--net/netrom/nr_in.c2
-rw-r--r--net/netrom/nr_out.c2
-rw-r--r--net/netrom/nr_route.c2
-rw-r--r--net/netrom/nr_subr.c2
-rw-r--r--net/netrom/nr_timer.c2
-rw-r--r--net/openvswitch/flow.c9
-rw-r--r--net/openvswitch/flow_netlink.c93
-rw-r--r--net/openvswitch/vport-geneve.c2
-rw-r--r--net/openvswitch/vport.c4
-rw-r--r--net/rds/ib_rdma.c2
-rw-r--r--net/rds/rdma.c7
-rw-r--r--net/rxrpc/ar-key.c2
-rw-r--r--net/sched/sch_generic.c20
-rw-r--r--net/sctp/associola.c2
-rw-r--r--net/sctp/inqueue.c33
-rw-r--r--net/sctp/protocol.c2
-rw-r--r--net/sctp/sm_make_chunk.c99
-rw-r--r--net/sctp/sm_statefuns.c21
-rw-r--r--net/socket.c3
-rw-r--r--net/tipc/link.c7
-rw-r--r--net/wireless/lib80211.c32
125 files changed, 3054 insertions, 2997 deletions
diff --git a/net/6lowpan/iphc.c b/net/6lowpan/iphc.c
index 142eef5..aced97d 100644
--- a/net/6lowpan/iphc.c
+++ b/net/6lowpan/iphc.c
@@ -171,37 +171,6 @@ static int uncompress_context_based_src_addr(struct sk_buff *skb,
return 0;
}
-static int skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr,
- struct net_device *dev, skb_delivery_cb deliver_skb)
-{
- struct sk_buff *new;
- int stat;
-
- new = skb_copy_expand(skb, sizeof(struct ipv6hdr), skb_tailroom(skb),
- GFP_ATOMIC);
- kfree_skb(skb);
-
- if (!new)
- return -ENOMEM;
-
- skb_push(new, sizeof(struct ipv6hdr));
- skb_reset_network_header(new);
- skb_copy_to_linear_data(new, hdr, sizeof(struct ipv6hdr));
-
- new->protocol = htons(ETH_P_IPV6);
- new->pkt_type = PACKET_HOST;
- new->dev = dev;
-
- raw_dump_table(__func__, "raw skb data dump before receiving",
- new->data, new->len);
-
- stat = deliver_skb(new, dev);
-
- kfree_skb(new);
-
- return stat;
-}
-
/* Uncompress function for multicast destination address,
* when M bit is set.
*/
@@ -332,10 +301,12 @@ err:
/* TTL uncompression values */
static const u8 lowpan_ttl_values[] = { 0, 1, 64, 255 };
-int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
- const u8 *saddr, const u8 saddr_type, const u8 saddr_len,
- const u8 *daddr, const u8 daddr_type, const u8 daddr_len,
- u8 iphc0, u8 iphc1, skb_delivery_cb deliver_skb)
+int
+lowpan_header_decompress(struct sk_buff *skb, struct net_device *dev,
+ const u8 *saddr, const u8 saddr_type,
+ const u8 saddr_len, const u8 *daddr,
+ const u8 daddr_type, const u8 daddr_len,
+ u8 iphc0, u8 iphc1)
{
struct ipv6hdr hdr = {};
u8 tmp, num_context = 0;
@@ -348,7 +319,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
if (iphc1 & LOWPAN_IPHC_CID) {
pr_debug("CID flag is set, increase header with one\n");
if (lowpan_fetch_skb(skb, &num_context, sizeof(num_context)))
- goto drop;
+ return -EINVAL;
}
hdr.version = 6;
@@ -360,7 +331,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
*/
case 0: /* 00b */
if (lowpan_fetch_skb(skb, &tmp, sizeof(tmp)))
- goto drop;
+ return -EINVAL;
memcpy(&hdr.flow_lbl, &skb->data[0], 3);
skb_pull(skb, 3);
@@ -373,7 +344,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
*/
case 2: /* 10b */
if (lowpan_fetch_skb(skb, &tmp, sizeof(tmp)))
- goto drop;
+ return -EINVAL;
hdr.priority = ((tmp >> 2) & 0x0f);
hdr.flow_lbl[0] = ((tmp << 6) & 0xC0) | ((tmp >> 2) & 0x30);
@@ -383,7 +354,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
*/
case 1: /* 01b */
if (lowpan_fetch_skb(skb, &tmp, sizeof(tmp)))
- goto drop;
+ return -EINVAL;
hdr.flow_lbl[0] = (skb->data[0] & 0x0F) | ((tmp >> 2) & 0x30);
memcpy(&hdr.flow_lbl[1], &skb->data[0], 2);
@@ -400,7 +371,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) {
/* Next header is carried inline */
if (lowpan_fetch_skb(skb, &hdr.nexthdr, sizeof(hdr.nexthdr)))
- goto drop;
+ return -EINVAL;
pr_debug("NH flag is set, next header carried inline: %02x\n",
hdr.nexthdr);
@@ -412,7 +383,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
} else {
if (lowpan_fetch_skb(skb, &hdr.hop_limit,
sizeof(hdr.hop_limit)))
- goto drop;
+ return -EINVAL;
}
/* Extract SAM to the tmp variable */
@@ -431,7 +402,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
/* Check on error of previous branch */
if (err)
- goto drop;
+ return -EINVAL;
/* Extract DAM to the tmp variable */
tmp = ((iphc1 & LOWPAN_IPHC_DAM_11) >> LOWPAN_IPHC_DAM_BIT) & 0x03;
@@ -446,7 +417,7 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
tmp);
if (err)
- goto drop;
+ return -EINVAL;
}
} else {
err = uncompress_addr(skb, &hdr.daddr, tmp, daddr,
@@ -454,28 +425,23 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
pr_debug("dest: stateless compression mode %d dest %pI6c\n",
tmp, &hdr.daddr);
if (err)
- goto drop;
+ return -EINVAL;
}
/* UDP data uncompression */
if (iphc0 & LOWPAN_IPHC_NH_C) {
struct udphdr uh;
- struct sk_buff *new;
+ const int needed = sizeof(struct udphdr) + sizeof(hdr);
if (uncompress_udp_header(skb, &uh))
- goto drop;
+ return -EINVAL;
/* replace the compressed UDP head by the uncompressed UDP
* header
*/
- new = skb_copy_expand(skb, sizeof(struct udphdr),
- skb_tailroom(skb), GFP_ATOMIC);
- kfree_skb(skb);
-
- if (!new)
- return -ENOMEM;
-
- skb = new;
+ err = skb_cow(skb, needed);
+ if (unlikely(err))
+ return err;
skb_push(skb, sizeof(struct udphdr));
skb_reset_transport_header(skb);
@@ -485,6 +451,10 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
(u8 *)&uh, sizeof(uh));
hdr.nexthdr = UIP_PROTO_UDP;
+ } else {
+ err = skb_cow(skb, sizeof(hdr));
+ if (unlikely(err))
+ return err;
}
hdr.payload_len = htons(skb->len);
@@ -497,15 +467,15 @@ int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
hdr.version, ntohs(hdr.payload_len), hdr.nexthdr,
hdr.hop_limit, &hdr.daddr);
- raw_dump_table(__func__, "raw header dump", (u8 *)&hdr, sizeof(hdr));
+ skb_push(skb, sizeof(hdr));
+ skb_reset_network_header(skb);
+ skb_copy_to_linear_data(skb, &hdr, sizeof(hdr));
- return skb_deliver(skb, &hdr, dev, deliver_skb);
+ raw_dump_table(__func__, "raw header dump", (u8 *)&hdr, sizeof(hdr));
-drop:
- kfree_skb(skb);
- return -EINVAL;
+ return 0;
}
-EXPORT_SYMBOL_GPL(lowpan_process_data);
+EXPORT_SYMBOL_GPL(lowpan_header_decompress);
static u8 lowpan_compress_addr_64(u8 **hc_ptr, u8 shift,
const struct in6_addr *ipaddr,
@@ -535,9 +505,17 @@ static u8 lowpan_compress_addr_64(u8 **hc_ptr, u8 shift,
static void compress_udp_header(u8 **hc_ptr, struct sk_buff *skb)
{
- struct udphdr *uh = udp_hdr(skb);
+ struct udphdr *uh;
u8 tmp;
+ /* In the case of RAW sockets the transport header is not set by
+ * the ip6 stack so we must set it ourselves
+ */
+ if (skb->transport_header == skb->network_header)
+ skb_set_transport_header(skb, sizeof(struct ipv6hdr));
+
+ uh = udp_hdr(skb);
+
if (((ntohs(uh->source) & LOWPAN_NHC_UDP_4BIT_MASK) ==
LOWPAN_NHC_UDP_4BIT_PORT) &&
((ntohs(uh->dest) & LOWPAN_NHC_UDP_4BIT_MASK) ==
diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c
index 6940d8f..daa749c 100644
--- a/net/9p/trans_virtio.c
+++ b/net/9p/trans_virtio.c
@@ -575,6 +575,8 @@ static int p9_virtio_probe(struct virtio_device *vdev)
/* Ceiling limit to avoid denial of service attacks */
chan->p9_max_pages = nr_free_buffer_pages()/4;
+ virtio_device_ready(vdev);
+
mutex_lock(&virtio_9p_lock);
list_add_tail(&chan->chan_list, &virtio_chan_list);
mutex_unlock(&virtio_9p_lock);
diff --git a/net/Kconfig b/net/Kconfig
index d6b138e..6272420 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -6,6 +6,7 @@ menuconfig NET
bool "Networking support"
select NLATTR
select GENERIC_NET_UTILS
+ select ANON_INODES
---help---
Unless you really know what you are doing, you should say Y here.
The reason is that some programs need kernel networking support even
diff --git a/net/batman-adv/gateway_common.c b/net/batman-adv/gateway_common.c
index 6f5e621..88a1bc3 100644
--- a/net/batman-adv/gateway_common.c
+++ b/net/batman-adv/gateway_common.c
@@ -44,10 +44,10 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
if (strlen(buff) > 4) {
tmp_ptr = buff + strlen(buff) - 4;
- if (strnicmp(tmp_ptr, "mbit", 4) == 0)
+ if (strncasecmp(tmp_ptr, "mbit", 4) == 0)
bw_unit_type = BATADV_BW_UNIT_MBIT;
- if ((strnicmp(tmp_ptr, "kbit", 4) == 0) ||
+ if ((strncasecmp(tmp_ptr, "kbit", 4) == 0) ||
(bw_unit_type == BATADV_BW_UNIT_MBIT))
*tmp_ptr = '\0';
}
@@ -77,10 +77,10 @@ static bool batadv_parse_gw_bandwidth(struct net_device *net_dev, char *buff,
if (strlen(slash_ptr + 1) > 4) {
tmp_ptr = slash_ptr + 1 - 4 + strlen(slash_ptr + 1);
- if (strnicmp(tmp_ptr, "mbit", 4) == 0)
+ if (strncasecmp(tmp_ptr, "mbit", 4) == 0)
bw_unit_type = BATADV_BW_UNIT_MBIT;
- if ((strnicmp(tmp_ptr, "kbit", 4) == 0) ||
+ if ((strncasecmp(tmp_ptr, "kbit", 4) == 0) ||
(bw_unit_type == BATADV_BW_UNIT_MBIT))
*tmp_ptr = '\0';
}
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index c2e0d14..dc23c55 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -53,7 +53,7 @@ struct skb_cb {
* The list contains struct lowpan_dev elements.
*/
static LIST_HEAD(bt_6lowpan_devices);
-static DEFINE_RWLOCK(devices_lock);
+static DEFINE_SPINLOCK(devices_lock);
/* If psm is set to 0 (default value), then 6lowpan is disabled.
* Other values are used to indicate a Protocol Service Multiplexer
@@ -67,6 +67,7 @@ static struct l2cap_chan *listen_chan;
struct lowpan_peer {
struct list_head list;
+ struct rcu_head rcu;
struct l2cap_chan *chan;
/* peer addresses in various formats */
@@ -86,6 +87,13 @@ struct lowpan_dev {
struct delayed_work notify_peers;
};
+static inline void peer_free(struct rcu_head *head)
+{
+ struct lowpan_peer *e = container_of(head, struct lowpan_peer, rcu);
+
+ kfree(e);
+}
+
static inline struct lowpan_dev *lowpan_dev(const struct net_device *netdev)
{
return netdev_priv(netdev);
@@ -93,13 +101,14 @@ static inline struct lowpan_dev *lowpan_dev(const struct net_device *netdev)
static inline void peer_add(struct lowpan_dev *dev, struct lowpan_peer *peer)
{
- list_add(&peer->list, &dev->peers);
+ list_add_rcu(&peer->list, &dev->peers);
atomic_inc(&dev->peer_count);
}
static inline bool peer_del(struct lowpan_dev *dev, struct lowpan_peer *peer)
{
- list_del(&peer->list);
+ list_del_rcu(&peer->list);
+ call_rcu(&peer->rcu, peer_free);
module_put(THIS_MODULE);
@@ -114,31 +123,37 @@ static inline bool peer_del(struct lowpan_dev *dev, struct lowpan_peer *peer)
static inline struct lowpan_peer *peer_lookup_ba(struct lowpan_dev *dev,
bdaddr_t *ba, __u8 type)
{
- struct lowpan_peer *peer, *tmp;
+ struct lowpan_peer *peer;
BT_DBG("peers %d addr %pMR type %d", atomic_read(&dev->peer_count),
ba, type);
- list_for_each_entry_safe(peer, tmp, &dev->peers, list) {
+ rcu_read_lock();
+
+ list_for_each_entry_rcu(peer, &dev->peers, list) {
BT_DBG("dst addr %pMR dst type %d",
&peer->chan->dst, peer->chan->dst_type);
if (bacmp(&peer->chan->dst, ba))
continue;
- if (type == peer->chan->dst_type)
+ if (type == peer->chan->dst_type) {
+ rcu_read_unlock();
return peer;
+ }
}
+ rcu_read_unlock();
+
return NULL;
}
-static inline struct lowpan_peer *peer_lookup_chan(struct lowpan_dev *dev,
- struct l2cap_chan *chan)
+static inline struct lowpan_peer *__peer_lookup_chan(struct lowpan_dev *dev,
+ struct l2cap_chan *chan)
{
- struct lowpan_peer *peer, *tmp;
+ struct lowpan_peer *peer;
- list_for_each_entry_safe(peer, tmp, &dev->peers, list) {
+ list_for_each_entry_rcu(peer, &dev->peers, list) {
if (peer->chan == chan)
return peer;
}
@@ -146,12 +161,12 @@ static inline struct lowpan_peer *peer_lookup_chan(struct lowpan_dev *dev,
return NULL;
}
-static inline struct lowpan_peer *peer_lookup_conn(struct lowpan_dev *dev,
- struct l2cap_conn *conn)
+static inline struct lowpan_peer *__peer_lookup_conn(struct lowpan_dev *dev,
+ struct l2cap_conn *conn)
{
- struct lowpan_peer *peer, *tmp;
+ struct lowpan_peer *peer;
- list_for_each_entry_safe(peer, tmp, &dev->peers, list) {
+ list_for_each_entry_rcu(peer, &dev->peers, list) {
if (peer->chan->conn == conn)
return peer;
}
@@ -163,7 +178,7 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_dev *dev,
struct in6_addr *daddr,
struct sk_buff *skb)
{
- struct lowpan_peer *peer, *tmp;
+ struct lowpan_peer *peer;
struct in6_addr *nexthop;
struct rt6_info *rt = (struct rt6_info *)skb_dst(skb);
int count = atomic_read(&dev->peer_count);
@@ -174,9 +189,13 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_dev *dev,
* send the packet. If only one peer exists, then we can send the
* packet right away.
*/
- if (count == 1)
- return list_first_entry(&dev->peers, struct lowpan_peer,
- list);
+ if (count == 1) {
+ rcu_read_lock();
+ peer = list_first_or_null_rcu(&dev->peers, struct lowpan_peer,
+ list);
+ rcu_read_unlock();
+ return peer;
+ }
if (!rt) {
nexthop = &lowpan_cb(skb)->gw;
@@ -195,53 +214,57 @@ static inline struct lowpan_peer *peer_lookup_dst(struct lowpan_dev *dev,
BT_DBG("gw %pI6c", nexthop);
- list_for_each_entry_safe(peer, tmp, &dev->peers, list) {
+ rcu_read_lock();
+
+ list_for_each_entry_rcu(peer, &dev->peers, list) {
BT_DBG("dst addr %pMR dst type %d ip %pI6c",
&peer->chan->dst, peer->chan->dst_type,
&peer->peer_addr);
- if (!ipv6_addr_cmp(&peer->peer_addr, nexthop))
+ if (!ipv6_addr_cmp(&peer->peer_addr, nexthop)) {
+ rcu_read_unlock();
return peer;
+ }
}
+ rcu_read_unlock();
+
return NULL;
}
static struct lowpan_peer *lookup_peer(struct l2cap_conn *conn)
{
- struct lowpan_dev *entry, *tmp;
+ struct lowpan_dev *entry;
struct lowpan_peer *peer = NULL;
- unsigned long flags;
- read_lock_irqsave(&devices_lock, flags);
+ rcu_read_lock();
- list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
- peer = peer_lookup_conn(entry, conn);
+ list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
+ peer = __peer_lookup_conn(entry, conn);
if (peer)
break;
}
- read_unlock_irqrestore(&devices_lock, flags);
+ rcu_read_unlock();
return peer;
}
static struct lowpan_dev *lookup_dev(struct l2cap_conn *conn)
{
- struct lowpan_dev *entry, *tmp;
+ struct lowpan_dev *entry;
struct lowpan_dev *dev = NULL;
- unsigned long flags;
- read_lock_irqsave(&devices_lock, flags);
+ rcu_read_lock();
- list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+ list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
if (conn->hcon->hdev == entry->hdev) {
dev = entry;
break;
}
}
- read_unlock_irqrestore(&devices_lock, flags);
+ rcu_read_unlock();
return dev;
}
@@ -249,59 +272,49 @@ static struct lowpan_dev *lookup_dev(struct l2cap_conn *conn)
static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev)
{
struct sk_buff *skb_cp;
- int ret;
skb_cp = skb_copy(skb, GFP_ATOMIC);
if (!skb_cp)
- return -ENOMEM;
-
- ret = netif_rx(skb_cp);
- if (ret < 0) {
- BT_DBG("receive skb %d", ret);
return NET_RX_DROP;
- }
- return ret;
+ return netif_rx(skb_cp);
}
-static int process_data(struct sk_buff *skb, struct net_device *netdev,
- struct l2cap_chan *chan)
+static int iphc_decompress(struct sk_buff *skb, struct net_device *netdev,
+ struct l2cap_chan *chan)
{
const u8 *saddr, *daddr;
u8 iphc0, iphc1;
struct lowpan_dev *dev;
struct lowpan_peer *peer;
- unsigned long flags;
dev = lowpan_dev(netdev);
- read_lock_irqsave(&devices_lock, flags);
- peer = peer_lookup_chan(dev, chan);
- read_unlock_irqrestore(&devices_lock, flags);
+ rcu_read_lock();
+ peer = __peer_lookup_chan(dev, chan);
+ rcu_read_unlock();
if (!peer)
- goto drop;
+ return -EINVAL;
saddr = peer->eui64_addr;
daddr = dev->netdev->dev_addr;
/* at least two bytes will be used for the encoding */
if (skb->len < 2)
- goto drop;
+ return -EINVAL;
if (lowpan_fetch_skb_u8(skb, &iphc0))
- goto drop;
+ return -EINVAL;
if (lowpan_fetch_skb_u8(skb, &iphc1))
- goto drop;
+ return -EINVAL;
- return lowpan_process_data(skb, netdev,
- saddr, IEEE802154_ADDR_LONG, EUI64_ADDR_LEN,
- daddr, IEEE802154_ADDR_LONG, EUI64_ADDR_LEN,
- iphc0, iphc1, give_skb_to_upper);
+ return lowpan_header_decompress(skb, netdev,
+ saddr, IEEE802154_ADDR_LONG,
+ EUI64_ADDR_LEN, daddr,
+ IEEE802154_ADDR_LONG, EUI64_ADDR_LEN,
+ iphc0, iphc1);
-drop:
- kfree_skb(skb);
- return -EINVAL;
}
static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
@@ -316,6 +329,10 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
if (dev->type != ARPHRD_6LOWPAN)
goto drop;
+ skb = skb_share_check(skb, GFP_ATOMIC);
+ if (!skb)
+ goto drop;
+
/* check that it's our buffer */
if (skb->data[0] == LOWPAN_DISPATCH_IPV6) {
/* Copy the packet so that the IPv6 header is
@@ -340,8 +357,8 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
dev->stats.rx_bytes += skb->len;
dev->stats.rx_packets++;
- kfree_skb(local_skb);
- kfree_skb(skb);
+ consume_skb(local_skb);
+ consume_skb(skb);
} else {
switch (skb->data[0] & 0xe0) {
case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */
@@ -349,14 +366,27 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
if (!local_skb)
goto drop;
- ret = process_data(local_skb, dev, chan);
- if (ret != NET_RX_SUCCESS)
+ ret = iphc_decompress(local_skb, dev, chan);
+ if (ret < 0) {
+ kfree_skb(local_skb);
goto drop;
+ }
+
+ local_skb->protocol = htons(ETH_P_IPV6);
+ local_skb->pkt_type = PACKET_HOST;
+ local_skb->dev = dev;
+
+ if (give_skb_to_upper(local_skb, dev)
+ != NET_RX_SUCCESS) {
+ kfree_skb(local_skb);
+ goto drop;
+ }
dev->stats.rx_bytes += skb->len;
dev->stats.rx_packets++;
- kfree_skb(skb);
+ consume_skb(local_skb);
+ consume_skb(skb);
break;
default:
break;
@@ -443,7 +473,6 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev,
if (ipv6_addr_is_multicast(&ipv6_daddr)) {
lowpan_cb(skb)->chan = NULL;
} else {
- unsigned long flags;
u8 addr_type;
/* Get destination BT device from skb.
@@ -454,19 +483,14 @@ static int setup_header(struct sk_buff *skb, struct net_device *netdev,
BT_DBG("dest addr %pMR type %d IP %pI6c", &addr,
addr_type, &ipv6_daddr);
- read_lock_irqsave(&devices_lock, flags);
peer = peer_lookup_ba(dev, &addr, addr_type);
- read_unlock_irqrestore(&devices_lock, flags);
-
if (!peer) {
/* The packet might be sent to 6lowpan interface
* because of routing (either via default route
* or user set route) so get peer according to
* the destination address.
*/
- read_lock_irqsave(&devices_lock, flags);
peer = peer_lookup_dst(dev, &ipv6_daddr, skb);
- read_unlock_irqrestore(&devices_lock, flags);
if (!peer) {
BT_DBG("no such peer %pMR found", &addr);
return -ENOENT;
@@ -549,14 +573,13 @@ static int send_pkt(struct l2cap_chan *chan, struct sk_buff *skb,
static int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
{
struct sk_buff *local_skb;
- struct lowpan_dev *entry, *tmp;
- unsigned long flags;
+ struct lowpan_dev *entry;
int err = 0;
- read_lock_irqsave(&devices_lock, flags);
+ rcu_read_lock();
- list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
- struct lowpan_peer *pentry, *ptmp;
+ list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
+ struct lowpan_peer *pentry;
struct lowpan_dev *dev;
if (entry->netdev != netdev)
@@ -564,7 +587,7 @@ static int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
dev = lowpan_dev(entry->netdev);
- list_for_each_entry_safe(pentry, ptmp, &dev->peers, list) {
+ list_for_each_entry_rcu(pentry, &dev->peers, list) {
int ret;
local_skb = skb_clone(skb, GFP_ATOMIC);
@@ -581,7 +604,7 @@ static int send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev)
}
}
- read_unlock_irqrestore(&devices_lock, flags);
+ rcu_read_unlock();
return err;
}
@@ -638,7 +661,26 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev)
return err < 0 ? NET_XMIT_DROP : err;
}
+static struct lock_class_key bt_tx_busylock;
+static struct lock_class_key bt_netdev_xmit_lock_key;
+
+static void bt_set_lockdep_class_one(struct net_device *dev,
+ struct netdev_queue *txq,
+ void *_unused)
+{
+ lockdep_set_class(&txq->_xmit_lock, &bt_netdev_xmit_lock_key);
+}
+
+static int bt_dev_init(struct net_device *dev)
+{
+ netdev_for_each_tx_queue(dev, bt_set_lockdep_class_one, NULL);
+ dev->qdisc_tx_busylock = &bt_tx_busylock;
+
+ return 0;
+}
+
static const struct net_device_ops netdev_ops = {
+ .ndo_init = bt_dev_init,
.ndo_start_xmit = bt_xmit,
};
@@ -783,7 +825,6 @@ static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan,
struct lowpan_dev *dev)
{
struct lowpan_peer *peer;
- unsigned long flags;
peer = kzalloc(sizeof(*peer), GFP_ATOMIC);
if (!peer)
@@ -806,10 +847,10 @@ static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan,
*/
set_ip_addr_bits(chan->dst_type, (u8 *)&peer->peer_addr.s6_addr + 8);
- write_lock_irqsave(&devices_lock, flags);
+ spin_lock(&devices_lock);
INIT_LIST_HEAD(&peer->list);
peer_add(dev, peer);
- write_unlock_irqrestore(&devices_lock, flags);
+ spin_unlock(&devices_lock);
/* Notifying peers about us needs to be done without locks held */
INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers);
@@ -822,7 +863,6 @@ static int setup_netdev(struct l2cap_chan *chan, struct lowpan_dev **dev)
{
struct net_device *netdev;
int err = 0;
- unsigned long flags;
netdev = alloc_netdev(sizeof(struct lowpan_dev), IFACE_NAME_TEMPLATE,
NET_NAME_UNKNOWN, netdev_setup);
@@ -852,10 +892,10 @@ static int setup_netdev(struct l2cap_chan *chan, struct lowpan_dev **dev)
(*dev)->hdev = chan->conn->hcon->hdev;
INIT_LIST_HEAD(&(*dev)->peers);
- write_lock_irqsave(&devices_lock, flags);
+ spin_lock(&devices_lock);
INIT_LIST_HEAD(&(*dev)->list);
- list_add(&(*dev)->list, &bt_6lowpan_devices);
- write_unlock_irqrestore(&devices_lock, flags);
+ list_add_rcu(&(*dev)->list, &bt_6lowpan_devices);
+ spin_unlock(&devices_lock);
return 0;
@@ -909,11 +949,10 @@ static void delete_netdev(struct work_struct *work)
static void chan_close_cb(struct l2cap_chan *chan)
{
- struct lowpan_dev *entry, *tmp;
+ struct lowpan_dev *entry;
struct lowpan_dev *dev = NULL;
struct lowpan_peer *peer;
int err = -ENOENT;
- unsigned long flags;
bool last = false, removed = true;
BT_DBG("chan %p conn %p", chan, chan->conn);
@@ -928,11 +967,11 @@ static void chan_close_cb(struct l2cap_chan *chan)
removed = false;
}
- write_lock_irqsave(&devices_lock, flags);
+ spin_lock(&devices_lock);
- list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+ list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
dev = lowpan_dev(entry->netdev);
- peer = peer_lookup_chan(dev, chan);
+ peer = __peer_lookup_chan(dev, chan);
if (peer) {
last = peer_del(dev, peer);
err = 0;
@@ -943,13 +982,12 @@ static void chan_close_cb(struct l2cap_chan *chan)
atomic_read(&chan->kref.refcount));
l2cap_chan_put(chan);
- kfree(peer);
break;
}
}
if (!err && last && dev && !atomic_read(&dev->peer_count)) {
- write_unlock_irqrestore(&devices_lock, flags);
+ spin_unlock(&devices_lock);
cancel_delayed_work_sync(&dev->notify_peers);
@@ -960,7 +998,7 @@ static void chan_close_cb(struct l2cap_chan *chan)
schedule_work(&entry->delete_netdev);
}
} else {
- write_unlock_irqrestore(&devices_lock, flags);
+ spin_unlock(&devices_lock);
}
return;
@@ -1152,10 +1190,9 @@ static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
static void disconnect_all_peers(void)
{
- struct lowpan_dev *entry, *tmp_dev;
+ struct lowpan_dev *entry;
struct lowpan_peer *peer, *tmp_peer, *new_peer;
struct list_head peers;
- unsigned long flags;
INIT_LIST_HEAD(&peers);
@@ -1164,10 +1201,10 @@ static void disconnect_all_peers(void)
* with the same list at the same time.
*/
- read_lock_irqsave(&devices_lock, flags);
+ rcu_read_lock();
- list_for_each_entry_safe(entry, tmp_dev, &bt_6lowpan_devices, list) {
- list_for_each_entry_safe(peer, tmp_peer, &entry->peers, list) {
+ list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
+ list_for_each_entry_rcu(peer, &entry->peers, list) {
new_peer = kmalloc(sizeof(*new_peer), GFP_ATOMIC);
if (!new_peer)
break;
@@ -1179,26 +1216,36 @@ static void disconnect_all_peers(void)
}
}
- read_unlock_irqrestore(&devices_lock, flags);
+ rcu_read_unlock();
+ spin_lock(&devices_lock);
list_for_each_entry_safe(peer, tmp_peer, &peers, list) {
l2cap_chan_close(peer->chan, ENOENT);
- kfree(peer);
+
+ list_del_rcu(&peer->list);
+ call_rcu(&peer->rcu, peer_free);
+
+ module_put(THIS_MODULE);
}
+ spin_unlock(&devices_lock);
}
-static int lowpan_psm_set(void *data, u64 val)
-{
+struct set_psm {
+ struct work_struct work;
u16 psm;
+};
- psm = val;
- if (psm == 0 || psm_6lowpan != psm)
+static void do_psm_set(struct work_struct *work)
+{
+ struct set_psm *set_psm = container_of(work, struct set_psm, work);
+
+ if (set_psm->psm == 0 || psm_6lowpan != set_psm->psm)
/* Disconnect existing connections if 6lowpan is
* disabled (psm = 0), or if psm changes.
*/
disconnect_all_peers();
- psm_6lowpan = psm;
+ psm_6lowpan = set_psm->psm;
if (listen_chan) {
l2cap_chan_close(listen_chan, 0);
@@ -1207,6 +1254,22 @@ static int lowpan_psm_set(void *data, u64 val)
listen_chan = bt_6lowpan_listen();
+ kfree(set_psm);
+}
+
+static int lowpan_psm_set(void *data, u64 val)
+{
+ struct set_psm *set_psm;
+
+ set_psm = kzalloc(sizeof(*set_psm), GFP_KERNEL);
+ if (!set_psm)
+ return -ENOMEM;
+
+ set_psm->psm = val;
+ INIT_WORK(&set_psm->work, do_psm_set);
+
+ schedule_work(&set_psm->work);
+
return 0;
}
@@ -1288,19 +1351,18 @@ static ssize_t lowpan_control_write(struct file *fp,
static int lowpan_control_show(struct seq_file *f, void *ptr)
{
- struct lowpan_dev *entry, *tmp_dev;
- struct lowpan_peer *peer, *tmp_peer;
- unsigned long flags;
+ struct lowpan_dev *entry;
+ struct lowpan_peer *peer;
- read_lock_irqsave(&devices_lock, flags);
+ spin_lock(&devices_lock);
- list_for_each_entry_safe(entry, tmp_dev, &bt_6lowpan_devices, list) {
- list_for_each_entry_safe(peer, tmp_peer, &entry->peers, list)
+ list_for_each_entry(entry, &bt_6lowpan_devices, list) {
+ list_for_each_entry(peer, &entry->peers, list)
seq_printf(f, "%pMR (type %u)\n",
&peer->chan->dst, peer->chan->dst_type);
}
- read_unlock_irqrestore(&devices_lock, flags);
+ spin_unlock(&devices_lock);
return 0;
}
@@ -1322,7 +1384,6 @@ static void disconnect_devices(void)
{
struct lowpan_dev *entry, *tmp, *new_dev;
struct list_head devices;
- unsigned long flags;
INIT_LIST_HEAD(&devices);
@@ -1331,9 +1392,9 @@ static void disconnect_devices(void)
* devices list.
*/
- read_lock_irqsave(&devices_lock, flags);
+ rcu_read_lock();
- list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+ list_for_each_entry_rcu(entry, &bt_6lowpan_devices, list) {
new_dev = kmalloc(sizeof(*new_dev), GFP_ATOMIC);
if (!new_dev)
break;
@@ -1341,10 +1402,10 @@ static void disconnect_devices(void)
new_dev->netdev = entry->netdev;
INIT_LIST_HEAD(&new_dev->list);
- list_add(&new_dev->list, &devices);
+ list_add_rcu(&new_dev->list, &devices);
}
- read_unlock_irqrestore(&devices_lock, flags);
+ rcu_read_unlock();
list_for_each_entry_safe(entry, tmp, &devices, list) {
ifdown(entry->netdev);
@@ -1359,17 +1420,15 @@ static int device_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
- struct lowpan_dev *entry, *tmp;
- unsigned long flags;
+ struct lowpan_dev *entry;
if (netdev->type != ARPHRD_6LOWPAN)
return NOTIFY_DONE;
switch (event) {
case NETDEV_UNREGISTER:
- write_lock_irqsave(&devices_lock, flags);
- list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices,
- list) {
+ spin_lock(&devices_lock);
+ list_for_each_entry(entry, &bt_6lowpan_devices, list) {
if (entry->netdev == netdev) {
BT_DBG("Unregistered netdev %s %p",
netdev->name, netdev);
@@ -1378,7 +1437,7 @@ static int device_event(struct notifier_block *unused,
break;
}
}
- write_unlock_irqrestore(&devices_lock, flags);
+ spin_unlock(&devices_lock);
break;
}
diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig
index 600fb29..5e97a8f 100644
--- a/net/bluetooth/Kconfig
+++ b/net/bluetooth/Kconfig
@@ -39,11 +39,10 @@ menuconfig BT
to Bluetooth kernel modules are provided in the BlueZ packages. For
more information, see <http://www.bluez.org/>.
-config BT_6LOWPAN
- tristate "Bluetooth 6LoWPAN support"
- depends on BT && 6LOWPAN
- help
- IPv6 compression over Bluetooth Low Energy.
+config BT_BREDR
+ bool "Bluetooth Classic (BR/EDR) features"
+ depends on BT
+ default y
source "net/bluetooth/rfcomm/Kconfig"
@@ -53,4 +52,15 @@ source "net/bluetooth/cmtp/Kconfig"
source "net/bluetooth/hidp/Kconfig"
+config BT_LE
+ bool "Bluetooth Low Energy (LE) features"
+ depends on BT
+ default y
+
+config BT_6LOWPAN
+ tristate "Bluetooth 6LoWPAN support"
+ depends on BT_LE && 6LOWPAN
+ help
+ IPv6 compression over Bluetooth Low Energy.
+
source "drivers/bluetooth/Kconfig"
diff --git a/net/bluetooth/bnep/Kconfig b/net/bluetooth/bnep/Kconfig
index 71791fc..9b70317 100644
--- a/net/bluetooth/bnep/Kconfig
+++ b/net/bluetooth/bnep/Kconfig
@@ -1,6 +1,6 @@
config BT_BNEP
tristate "BNEP protocol support"
- depends on BT
+ depends on BT_BREDR
select CRC32
help
BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet
diff --git a/net/bluetooth/cmtp/Kconfig b/net/bluetooth/cmtp/Kconfig
index 94cbf42..939da0f 100644
--- a/net/bluetooth/cmtp/Kconfig
+++ b/net/bluetooth/cmtp/Kconfig
@@ -1,6 +1,6 @@
config BT_CMTP
tristate "CMTP protocol support"
- depends on BT && ISDN_CAPI
+ depends on BT_BREDR && ISDN_CAPI
help
CMTP (CAPI Message Transport Protocol) is a transport layer
for CAPI messages. CMTP is required for the Bluetooth Common
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index b9517bd..96887ae 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -141,10 +141,11 @@ int hci_disconnect(struct hci_conn *conn, __u8 reason)
*/
if (conn->type == ACL_LINK && conn->role == HCI_ROLE_MASTER) {
struct hci_dev *hdev = conn->hdev;
- struct hci_cp_read_clock_offset cp;
+ struct hci_cp_read_clock_offset clkoff_cp;
- cp.handle = cpu_to_le16(conn->handle);
- hci_send_cmd(hdev, HCI_OP_READ_CLOCK_OFFSET, sizeof(cp), &cp);
+ clkoff_cp.handle = cpu_to_le16(conn->handle);
+ hci_send_cmd(hdev, HCI_OP_READ_CLOCK_OFFSET, sizeof(clkoff_cp),
+ &clkoff_cp);
}
conn->state = BT_DISCONN;
@@ -415,7 +416,7 @@ static void le_conn_timeout(struct work_struct *work)
* happen with broken hardware or if low duty cycle was used
* (which doesn't have a timeout of its own).
*/
- if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) {
+ if (conn->role == HCI_ROLE_SLAVE) {
u8 enable = 0x00;
hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable),
&enable);
@@ -517,7 +518,7 @@ int hci_conn_del(struct hci_conn *conn)
/* Unacked frames */
hdev->acl_cnt += conn->sent;
} else if (conn->type == LE_LINK) {
- cancel_delayed_work_sync(&conn->le_conn_timeout);
+ cancel_delayed_work(&conn->le_conn_timeout);
if (hdev->le_pkts)
hdev->le_cnt += conn->sent;
@@ -544,6 +545,9 @@ int hci_conn_del(struct hci_conn *conn)
hci_conn_del_sysfs(conn);
+ if (test_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags))
+ hci_conn_params_del(conn->hdev, &conn->dst, conn->dst_type);
+
hci_dev_put(hdev);
hci_conn_put(conn);
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index cb05d7f..6c162c8 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -200,31 +200,6 @@ static const struct file_operations blacklist_fops = {
.release = single_release,
};
-static int whitelist_show(struct seq_file *f, void *p)
-{
- struct hci_dev *hdev = f->private;
- struct bdaddr_list *b;
-
- hci_dev_lock(hdev);
- list_for_each_entry(b, &hdev->whitelist, list)
- seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
- hci_dev_unlock(hdev);
-
- return 0;
-}
-
-static int whitelist_open(struct inode *inode, struct file *file)
-{
- return single_open(file, whitelist_show, inode->i_private);
-}
-
-static const struct file_operations whitelist_fops = {
- .open = whitelist_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
static int uuids_show(struct seq_file *f, void *p)
{
struct hci_dev *hdev = f->private;
@@ -1030,10 +1005,13 @@ static int device_list_show(struct seq_file *f, void *ptr)
{
struct hci_dev *hdev = f->private;
struct hci_conn_params *p;
+ struct bdaddr_list *b;
hci_dev_lock(hdev);
+ list_for_each_entry(b, &hdev->whitelist, list)
+ seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
list_for_each_entry(p, &hdev->le_conn_params, list) {
- seq_printf(f, "%pMR %u %u\n", &p->addr, p->addr_type,
+ seq_printf(f, "%pMR (type %u) %u\n", &p->addr, p->addr_type,
p->auto_connect);
}
hci_dev_unlock(hdev);
@@ -1147,13 +1125,15 @@ struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
hdev->req_status = HCI_REQ_PEND;
- err = hci_req_run(&req, hci_req_sync_complete);
- if (err < 0)
- return ERR_PTR(err);
-
add_wait_queue(&hdev->req_wait_q, &wait);
set_current_state(TASK_INTERRUPTIBLE);
+ err = hci_req_run(&req, hci_req_sync_complete);
+ if (err < 0) {
+ remove_wait_queue(&hdev->req_wait_q, &wait);
+ return ERR_PTR(err);
+ }
+
schedule_timeout(timeout);
remove_wait_queue(&hdev->req_wait_q, &wait);
@@ -1211,10 +1191,15 @@ static int __hci_req_sync(struct hci_dev *hdev,
func(&req, opt);
+ add_wait_queue(&hdev->req_wait_q, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
err = hci_req_run(&req, hci_req_sync_complete);
if (err < 0) {
hdev->req_status = 0;
+ remove_wait_queue(&hdev->req_wait_q, &wait);
+
/* ENODATA means the HCI request command queue is empty.
* This can happen when a request with conditionals doesn't
* trigger any commands to be sent. This is normal behavior
@@ -1226,9 +1211,6 @@ static int __hci_req_sync(struct hci_dev *hdev,
return err;
}
- add_wait_queue(&hdev->req_wait_q, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
-
schedule_timeout(timeout);
remove_wait_queue(&hdev->req_wait_q, &wait);
@@ -1811,10 +1793,10 @@ static int __hci_init(struct hci_dev *hdev)
&hdev->manufacturer);
debugfs_create_u8("hci_version", 0444, hdev->debugfs, &hdev->hci_ver);
debugfs_create_u16("hci_revision", 0444, hdev->debugfs, &hdev->hci_rev);
+ debugfs_create_file("device_list", 0444, hdev->debugfs, hdev,
+ &device_list_fops);
debugfs_create_file("blacklist", 0444, hdev->debugfs, hdev,
&blacklist_fops);
- debugfs_create_file("whitelist", 0444, hdev->debugfs, hdev,
- &whitelist_fops);
debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
debugfs_create_file("conn_info_min_age", 0644, hdev->debugfs, hdev,
@@ -1893,8 +1875,6 @@ static int __hci_init(struct hci_dev *hdev)
hdev, &adv_min_interval_fops);
debugfs_create_file("adv_max_interval", 0644, hdev->debugfs,
hdev, &adv_max_interval_fops);
- debugfs_create_file("device_list", 0444, hdev->debugfs, hdev,
- &device_list_fops);
debugfs_create_u16("discov_interleaved_timeout", 0644,
hdev->debugfs,
&hdev->discov_interleaved_timeout);
@@ -4244,6 +4224,24 @@ int hci_resume_dev(struct hci_dev *hdev)
}
EXPORT_SYMBOL(hci_resume_dev);
+/* Reset HCI device */
+int hci_reset_dev(struct hci_dev *hdev)
+{
+ const u8 hw_err[] = { HCI_EV_HARDWARE_ERROR, 0x01, 0x00 };
+ struct sk_buff *skb;
+
+ skb = bt_skb_alloc(3, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+ memcpy(skb_put(skb, 3), hw_err, 3);
+
+ /* Send Hardware Error to upper stack */
+ return hci_recv_frame(hdev, skb);
+}
+EXPORT_SYMBOL(hci_reset_dev);
+
/* Receive frame from HCI drivers */
int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
@@ -4477,7 +4475,7 @@ int hci_req_run(struct hci_request *req, hci_req_complete_t complete)
BT_DBG("length %u", skb_queue_len(&req->cmd_q));
- /* If an error occured during request building, remove all HCI
+ /* If an error occurred during request building, remove all HCI
* commands queued on the HCI request queue.
*/
if (req->err) {
@@ -4546,7 +4544,7 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
return -ENOMEM;
}
- /* Stand-alone HCI commands must be flaged as
+ /* Stand-alone HCI commands must be flagged as
* single-command requests.
*/
bt_cb(skb)->req.start = true;
@@ -4566,7 +4564,7 @@ void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen);
- /* If an error occured during request building, there is no point in
+ /* If an error occurred during request building, there is no point in
* queueing the HCI command. We can simply return.
*/
if (req->err)
@@ -4661,8 +4659,12 @@ static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue,
skb_shinfo(skb)->frag_list = NULL;
- /* Queue all fragments atomically */
- spin_lock(&queue->lock);
+ /* Queue all fragments atomically. We need to use spin_lock_bh
+ * here because of 6LoWPAN links, as there this function is
+ * called from softirq and using normal spin lock could cause
+ * deadlocks.
+ */
+ spin_lock_bh(&queue->lock);
__skb_queue_tail(queue, skb);
@@ -4679,7 +4681,7 @@ static void hci_queue_acl(struct hci_chan *chan, struct sk_buff_head *queue,
__skb_queue_tail(queue, skb);
} while (list);
- spin_unlock(&queue->lock);
+ spin_unlock_bh(&queue->lock);
}
}
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 8b0a2a6..5e7be80 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -189,6 +189,9 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
clear_bit(HCI_RESET, &hdev->flags);
+ if (status)
+ return;
+
/* Reset all non-persistent flags */
hdev->dev_flags &= ~HCI_PERSISTENT_MASK;
@@ -205,6 +208,8 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb)
hdev->le_scan_type = LE_SCAN_PASSIVE;
hdev->ssp_debug_mode = 0;
+
+ hci_bdaddr_list_clear(&hdev->le_white_list);
}
static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
@@ -1045,7 +1050,7 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb)
hci_dev_lock(hdev);
- /* If we're doing connection initation as peripheral. Set a
+ /* If we're doing connection initiation as peripheral. Set a
* timeout in case something goes wrong.
*/
if (*sent) {
@@ -1577,8 +1582,7 @@ static void hci_check_pending_name(struct hci_dev *hdev, struct hci_conn *conn,
struct inquiry_entry *e;
if (conn && !test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
- mgmt_device_connected(hdev, bdaddr, ACL_LINK, 0x00, 0, name,
- name_len, conn->dev_class);
+ mgmt_device_connected(hdev, conn, 0, name, name_len);
if (discov->state == DISCOVERY_STOPPED)
return;
@@ -1943,6 +1947,29 @@ unlock:
hci_dev_unlock(hdev);
}
+static void hci_cs_switch_role(struct hci_dev *hdev, u8 status)
+{
+ struct hci_cp_switch_role *cp;
+ struct hci_conn *conn;
+
+ BT_DBG("%s status 0x%2.2x", hdev->name, status);
+
+ if (!status)
+ return;
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_SWITCH_ROLE);
+ if (!cp)
+ return;
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
+ if (conn)
+ clear_bit(HCI_CONN_RSWITCH_PEND, &conn->flags);
+
+ hci_dev_unlock(hdev);
+}
+
static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
@@ -2536,9 +2563,7 @@ static void hci_remote_features_evt(struct hci_dev *hdev,
cp.pscan_rep_mode = 0x02;
hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp);
} else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
- mgmt_device_connected(hdev, &conn->dst, conn->type,
- conn->dst_type, 0, NULL, 0,
- conn->dev_class);
+ mgmt_device_connected(hdev, conn, 0, NULL, 0);
if (!hci_outgoing_auth_needed(hdev, conn)) {
conn->state = BT_CONNECTED;
@@ -2848,6 +2873,10 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cs_create_conn(hdev, ev->status);
break;
+ case HCI_OP_DISCONNECT:
+ hci_cs_disconnect(hdev, ev->status);
+ break;
+
case HCI_OP_ADD_SCO:
hci_cs_add_sco(hdev, ev->status);
break;
@@ -2876,24 +2905,24 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
hci_cs_setup_sync_conn(hdev, ev->status);
break;
- case HCI_OP_SNIFF_MODE:
- hci_cs_sniff_mode(hdev, ev->status);
+ case HCI_OP_CREATE_PHY_LINK:
+ hci_cs_create_phylink(hdev, ev->status);
break;
- case HCI_OP_EXIT_SNIFF_MODE:
- hci_cs_exit_sniff_mode(hdev, ev->status);
+ case HCI_OP_ACCEPT_PHY_LINK:
+ hci_cs_accept_phylink(hdev, ev->status);
break;
- case HCI_OP_DISCONNECT:
- hci_cs_disconnect(hdev, ev->status);
+ case HCI_OP_SNIFF_MODE:
+ hci_cs_sniff_mode(hdev, ev->status);
break;
- case HCI_OP_CREATE_PHY_LINK:
- hci_cs_create_phylink(hdev, ev->status);
+ case HCI_OP_EXIT_SNIFF_MODE:
+ hci_cs_exit_sniff_mode(hdev, ev->status);
break;
- case HCI_OP_ACCEPT_PHY_LINK:
- hci_cs_accept_phylink(hdev, ev->status);
+ case HCI_OP_SWITCH_ROLE:
+ hci_cs_switch_role(hdev, ev->status);
break;
case HCI_OP_LE_CREATE_CONN:
@@ -2923,6 +2952,13 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
}
}
+static void hci_hardware_error_evt(struct hci_dev *hdev, struct sk_buff *skb)
+{
+ struct hci_ev_hardware_error *ev = (void *) skb->data;
+
+ BT_ERR("%s hardware error 0x%2.2x", hdev->name, ev->code);
+}
+
static void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct hci_ev_role_change *ev = (void *) skb->data;
@@ -3434,9 +3470,7 @@ static void hci_remote_ext_features_evt(struct hci_dev *hdev,
cp.pscan_rep_mode = 0x02;
hci_send_cmd(hdev, HCI_OP_REMOTE_NAME_REQ, sizeof(cp), &cp);
} else if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
- mgmt_device_connected(hdev, &conn->dst, conn->type,
- conn->dst_type, 0, NULL, 0,
- conn->dev_class);
+ mgmt_device_connected(hdev, conn, 0, NULL, 0);
if (!hci_outgoing_auth_needed(hdev, conn)) {
conn->state = BT_CONNECTED;
@@ -4214,8 +4248,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
}
if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags))
- mgmt_device_connected(hdev, &conn->dst, conn->type,
- conn->dst_type, 0, NULL, 0, NULL);
+ mgmt_device_connected(hdev, conn, 0, NULL, 0);
conn->sec_level = BT_SECURITY_LOW;
conn->handle = __le16_to_cpu(ev->handle);
@@ -4269,25 +4302,26 @@ static void hci_le_conn_update_complete_evt(struct hci_dev *hdev,
}
/* This function requires the caller holds hdev->lock */
-static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
- u8 addr_type, u8 adv_type)
+static struct hci_conn *check_pending_le_conn(struct hci_dev *hdev,
+ bdaddr_t *addr,
+ u8 addr_type, u8 adv_type)
{
struct hci_conn *conn;
struct hci_conn_params *params;
/* If the event is not connectable don't proceed further */
if (adv_type != LE_ADV_IND && adv_type != LE_ADV_DIRECT_IND)
- return;
+ return NULL;
/* Ignore if the device is blocked */
if (hci_bdaddr_list_lookup(&hdev->blacklist, addr, addr_type))
- return;
+ return NULL;
/* Most controller will fail if we try to create new connections
* while we have an existing one in slave role.
*/
if (hdev->conn_hash.le_num_slave > 0)
- return;
+ return NULL;
/* If we're not connectable only connect devices that we have in
* our pend_le_conns list.
@@ -4295,7 +4329,7 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
params = hci_pend_le_action_lookup(&hdev->pend_le_conns,
addr, addr_type);
if (!params)
- return;
+ return NULL;
switch (params->auto_connect) {
case HCI_AUTO_CONN_DIRECT:
@@ -4304,7 +4338,7 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
* incoming connections from slave devices.
*/
if (adv_type != LE_ADV_DIRECT_IND)
- return;
+ return NULL;
break;
case HCI_AUTO_CONN_ALWAYS:
/* Devices advertising with ADV_IND or ADV_DIRECT_IND
@@ -4315,7 +4349,7 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
*/
break;
default:
- return;
+ return NULL;
}
conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW,
@@ -4328,7 +4362,7 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
* count consistent once the connection is established.
*/
params->conn = hci_conn_get(conn);
- return;
+ return conn;
}
switch (PTR_ERR(conn)) {
@@ -4341,7 +4375,10 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr,
break;
default:
BT_DBG("Failed to connect: err %ld", PTR_ERR(conn));
+ return NULL;
}
+
+ return NULL;
}
static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
@@ -4349,6 +4386,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
{
struct discovery_state *d = &hdev->discovery;
struct smp_irk *irk;
+ struct hci_conn *conn;
bool match;
u32 flags;
@@ -4360,7 +4398,14 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
}
/* Check if we have been requested to connect to this device */
- check_pending_le_conn(hdev, bdaddr, bdaddr_type, type);
+ conn = check_pending_le_conn(hdev, bdaddr, bdaddr_type, type);
+ if (conn && type == LE_ADV_IND) {
+ /* Store report for later inclusion by
+ * mgmt_device_connected
+ */
+ memcpy(conn->le_adv_data, data, len);
+ conn->le_adv_data_len = len;
+ }
/* Passive scanning shouldn't trigger any device found events,
* except for devices marked as CONN_REPORT for which we do send
@@ -4735,6 +4780,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
hci_cmd_status_evt(hdev, skb);
break;
+ case HCI_EV_HARDWARE_ERROR:
+ hci_hardware_error_evt(hdev, skb);
+ break;
+
case HCI_EV_ROLE_CHANGE:
hci_role_change_evt(hdev, skb);
break;
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 115f149..bbc4ac7 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -987,7 +987,7 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
skb_queue_tail(&hdev->raw_q, skb);
queue_work(hdev->workqueue, &hdev->tx_work);
} else {
- /* Stand-alone HCI commands must be flaged as
+ /* Stand-alone HCI commands must be flagged as
* single-command requests.
*/
bt_cb(skb)->req.start = true;
diff --git a/net/bluetooth/hidp/Kconfig b/net/bluetooth/hidp/Kconfig
index 9332bc7..bc8610b 100644
--- a/net/bluetooth/hidp/Kconfig
+++ b/net/bluetooth/hidp/Kconfig
@@ -1,6 +1,6 @@
config BT_HIDP
tristate "HIDP protocol support"
- depends on BT && INPUT
+ depends on BT_BREDR && INPUT
select HID
help
HIDP (Human Interface Device Protocol) is a transport layer
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index b6f9777..fc15174 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -3873,9 +3873,7 @@ static int l2cap_connect_req(struct l2cap_conn *conn,
hci_dev_lock(hdev);
if (test_bit(HCI_MGMT, &hdev->dev_flags) &&
!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &hcon->flags))
- mgmt_device_connected(hdev, &hcon->dst, hcon->type,
- hcon->dst_type, 0, NULL, 0,
- hcon->dev_class);
+ mgmt_device_connected(hdev, hcon, 0, NULL, 0);
hci_dev_unlock(hdev);
l2cap_connect(conn, cmd, data, L2CAP_CONN_RSP, 0);
@@ -4084,7 +4082,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn,
chan->num_conf_req++;
}
- /* Got Conf Rsp PENDING from remote side and asume we sent
+ /* Got Conf Rsp PENDING from remote side and assume we sent
Conf Rsp PENDING in the code above */
if (test_bit(CONF_REM_CONF_PEND, &chan->conf_state) &&
test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) {
@@ -5494,6 +5492,7 @@ static inline int l2cap_le_credits(struct l2cap_conn *conn,
if (credits > max_credits) {
BT_ERR("LE credits overflow");
l2cap_send_disconn_req(chan, ECONNRESET);
+ l2cap_chan_unlock(chan);
/* Return 0 so that we don't trigger an unnecessary
* command reject packet.
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index efb71b0..ce0272c 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -2725,10 +2725,40 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
}
if (cp->addr.type == BDADDR_BREDR) {
+ /* If disconnection is requested, then look up the
+ * connection. If the remote device is connected, it
+ * will be later used to terminate the link.
+ *
+ * Setting it to NULL explicitly will cause no
+ * termination of the link.
+ */
+ if (cp->disconnect)
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
+ &cp->addr.bdaddr);
+ else
+ conn = NULL;
+
err = hci_remove_link_key(hdev, &cp->addr.bdaddr);
} else {
u8 addr_type;
+ conn = hci_conn_hash_lookup_ba(hdev, LE_LINK,
+ &cp->addr.bdaddr);
+ if (conn) {
+ /* Defer clearing up the connection parameters
+ * until closing to give a chance of keeping
+ * them if a repairing happens.
+ */
+ set_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags);
+
+ /* If disconnection is not requested, then
+ * clear the connection variable so that the
+ * link is not terminated.
+ */
+ if (!cp->disconnect)
+ conn = NULL;
+ }
+
if (cp->addr.type == BDADDR_LE_PUBLIC)
addr_type = ADDR_LE_DEV_PUBLIC;
else
@@ -2736,8 +2766,6 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
hci_remove_irk(hdev, &cp->addr.bdaddr, addr_type);
- hci_conn_params_del(hdev, &cp->addr.bdaddr, addr_type);
-
err = hci_remove_ltk(hdev, &cp->addr.bdaddr, addr_type);
}
@@ -2747,17 +2775,9 @@ static int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data,
goto unlock;
}
- if (cp->disconnect) {
- if (cp->addr.type == BDADDR_BREDR)
- conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
- &cp->addr.bdaddr);
- else
- conn = hci_conn_hash_lookup_ba(hdev, LE_LINK,
- &cp->addr.bdaddr);
- } else {
- conn = NULL;
- }
-
+ /* If the connection variable is set, then termination of the
+ * link is requested.
+ */
if (!conn) {
err = cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, 0,
&rp, sizeof(rp));
@@ -3062,6 +3082,11 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status)
hci_conn_put(conn);
mgmt_pending_remove(cmd);
+
+ /* The device is paired so there is no need to remove
+ * its connection parameters anymore.
+ */
+ clear_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags);
}
void mgmt_smp_complete(struct hci_conn *conn, bool complete)
@@ -3702,20 +3727,23 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
hci_dev_lock(hdev);
if (!hdev_is_powered(hdev)) {
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_NOT_POWERED);
+ err = cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+ MGMT_STATUS_NOT_POWERED,
+ &cp->type, sizeof(cp->type));
goto failed;
}
if (test_bit(HCI_PERIODIC_INQ, &hdev->dev_flags)) {
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_BUSY);
+ err = cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+ MGMT_STATUS_BUSY, &cp->type,
+ sizeof(cp->type));
goto failed;
}
if (hdev->discovery.state != DISCOVERY_STOPPED) {
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_BUSY);
+ err = cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+ MGMT_STATUS_BUSY, &cp->type,
+ sizeof(cp->type));
goto failed;
}
@@ -3733,15 +3761,18 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
case DISCOV_TYPE_BREDR:
status = mgmt_bredr_support(hdev);
if (status) {
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- status);
+ err = cmd_complete(sk, hdev->id,
+ MGMT_OP_START_DISCOVERY, status,
+ &cp->type, sizeof(cp->type));
mgmt_pending_remove(cmd);
goto failed;
}
if (test_bit(HCI_INQUIRY, &hdev->flags)) {
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_BUSY);
+ err = cmd_complete(sk, hdev->id,
+ MGMT_OP_START_DISCOVERY,
+ MGMT_STATUS_BUSY, &cp->type,
+ sizeof(cp->type));
mgmt_pending_remove(cmd);
goto failed;
}
@@ -3758,16 +3789,19 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
case DISCOV_TYPE_INTERLEAVED:
status = mgmt_le_support(hdev);
if (status) {
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- status);
+ err = cmd_complete(sk, hdev->id,
+ MGMT_OP_START_DISCOVERY, status,
+ &cp->type, sizeof(cp->type));
mgmt_pending_remove(cmd);
goto failed;
}
if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED &&
!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) {
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_NOT_SUPPORTED);
+ err = cmd_complete(sk, hdev->id,
+ MGMT_OP_START_DISCOVERY,
+ MGMT_STATUS_NOT_SUPPORTED,
+ &cp->type, sizeof(cp->type));
mgmt_pending_remove(cmd);
goto failed;
}
@@ -3779,9 +3813,11 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
*/
if (hci_conn_hash_lookup_state(hdev, LE_LINK,
BT_CONNECT)) {
- err = cmd_status(sk, hdev->id,
- MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_REJECTED);
+ err = cmd_complete(sk, hdev->id,
+ MGMT_OP_START_DISCOVERY,
+ MGMT_STATUS_REJECTED,
+ &cp->type,
+ sizeof(cp->type));
mgmt_pending_remove(cmd);
goto failed;
}
@@ -3804,8 +3840,10 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
*/
err = hci_update_random_address(&req, true, &own_addr_type);
if (err < 0) {
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_FAILED);
+ err = cmd_complete(sk, hdev->id,
+ MGMT_OP_START_DISCOVERY,
+ MGMT_STATUS_FAILED,
+ &cp->type, sizeof(cp->type));
mgmt_pending_remove(cmd);
goto failed;
}
@@ -3825,8 +3863,9 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
break;
default:
- err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
- MGMT_STATUS_INVALID_PARAMS);
+ err = cmd_complete(sk, hdev->id, MGMT_OP_START_DISCOVERY,
+ MGMT_STATUS_INVALID_PARAMS,
+ &cp->type, sizeof(cp->type));
mgmt_pending_remove(cmd);
goto failed;
}
@@ -6171,26 +6210,36 @@ static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data,
return eir_len;
}
-void mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
- u8 addr_type, u32 flags, u8 *name, u8 name_len,
- u8 *dev_class)
+void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn,
+ u32 flags, u8 *name, u8 name_len)
{
char buf[512];
struct mgmt_ev_device_connected *ev = (void *) buf;
u16 eir_len = 0;
- bacpy(&ev->addr.bdaddr, bdaddr);
- ev->addr.type = link_to_bdaddr(link_type, addr_type);
+ bacpy(&ev->addr.bdaddr, &conn->dst);
+ ev->addr.type = link_to_bdaddr(conn->type, conn->dst_type);
ev->flags = __cpu_to_le32(flags);
- if (name_len > 0)
- eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE,
- name, name_len);
+ /* We must ensure that the EIR Data fields are ordered and
+ * unique. Keep it simple for now and avoid the problem by not
+ * adding any BR/EDR data to the LE adv.
+ */
+ if (conn->le_adv_data_len > 0) {
+ memcpy(&ev->eir[eir_len],
+ conn->le_adv_data, conn->le_adv_data_len);
+ eir_len = conn->le_adv_data_len;
+ } else {
+ if (name_len > 0)
+ eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE,
+ name, name_len);
- if (dev_class && memcmp(dev_class, "\0\0\0", 3) != 0)
- eir_len = eir_append_data(ev->eir, eir_len,
- EIR_CLASS_OF_DEV, dev_class, 3);
+ if (memcmp(conn->dev_class, "\0\0\0", 3) != 0)
+ eir_len = eir_append_data(ev->eir, eir_len,
+ EIR_CLASS_OF_DEV,
+ conn->dev_class, 3);
+ }
ev->eir_len = cpu_to_le16(eir_len);
diff --git a/net/bluetooth/rfcomm/Kconfig b/net/bluetooth/rfcomm/Kconfig
index 18d352e..335df75 100644
--- a/net/bluetooth/rfcomm/Kconfig
+++ b/net/bluetooth/rfcomm/Kconfig
@@ -1,6 +1,6 @@
config BT_RFCOMM
tristate "RFCOMM protocol support"
- depends on BT
+ depends on BT_BREDR
help
RFCOMM provides connection oriented stream transport. RFCOMM
support is required for Dialup Networking, OBEX and other Bluetooth
diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
index af73bc3..64e20dd 100644
--- a/net/bluetooth/rfcomm/core.c
+++ b/net/bluetooth/rfcomm/core.c
@@ -78,8 +78,10 @@ static struct rfcomm_session *rfcomm_session_del(struct rfcomm_session *s);
#define __get_type(b) ((b & 0xef))
#define __test_ea(b) ((b & 0x01))
-#define __test_cr(b) ((b & 0x02))
-#define __test_pf(b) ((b & 0x10))
+#define __test_cr(b) (!!(b & 0x02))
+#define __test_pf(b) (!!(b & 0x10))
+
+#define __session_dir(s) ((s)->initiator ? 0x00 : 0x01)
#define __addr(cr, dlci) (((dlci & 0x3f) << 2) | (cr << 1) | 0x01)
#define __ctrl(type, pf) (((type & 0xef) | (pf << 4)))
@@ -388,7 +390,7 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst,
return err;
}
- dlci = __dlci(!s->initiator, channel);
+ dlci = __dlci(__session_dir(s), channel);
/* Check if DLCI already exists */
if (rfcomm_dlc_get(s, dlci))
@@ -543,7 +545,7 @@ struct rfcomm_dlc *rfcomm_dlc_exists(bdaddr_t *src, bdaddr_t *dst, u8 channel)
rfcomm_lock();
s = rfcomm_session_get(src, dst);
if (s) {
- dlci = __dlci(!s->initiator, channel);
+ dlci = __dlci(__session_dir(s), channel);
dlc = rfcomm_dlc_get(s, dlci);
}
rfcomm_unlock();
@@ -904,7 +906,7 @@ static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type)
hdr->len = __len8(sizeof(*mcc) + 1);
mcc = (void *) ptr; ptr += sizeof(*mcc);
- mcc->type = __mcc_type(cr, RFCOMM_NSC);
+ mcc->type = __mcc_type(0, RFCOMM_NSC);
mcc->len = __len8(1);
/* Type that we didn't like */
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index f09b6b6..3ebf65b 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -191,16 +191,13 @@ int smp_generate_rpa(struct hci_dev *hdev, u8 irk[16], bdaddr_t *rpa)
return 0;
}
-static int smp_c1(struct smp_chan *smp, u8 k[16], u8 r[16], u8 preq[7],
- u8 pres[7], u8 _iat, bdaddr_t *ia, u8 _rat, bdaddr_t *ra,
- u8 res[16])
+static int smp_c1(struct crypto_blkcipher *tfm_aes, u8 k[16], u8 r[16],
+ u8 preq[7], u8 pres[7], u8 _iat, bdaddr_t *ia, u8 _rat,
+ bdaddr_t *ra, u8 res[16])
{
- struct hci_dev *hdev = smp->conn->hcon->hdev;
u8 p1[16], p2[16];
int err;
- BT_DBG("%s", hdev->name);
-
memset(p1, 0, 16);
/* p1 = pres || preq || _rat || _iat */
@@ -218,7 +215,7 @@ static int smp_c1(struct smp_chan *smp, u8 k[16], u8 r[16], u8 preq[7],
u128_xor((u128 *) res, (u128 *) r, (u128 *) p1);
/* res = e(k, res) */
- err = smp_e(smp->tfm_aes, k, res);
+ err = smp_e(tfm_aes, k, res);
if (err) {
BT_ERR("Encrypt data error");
return err;
@@ -228,26 +225,23 @@ static int smp_c1(struct smp_chan *smp, u8 k[16], u8 r[16], u8 preq[7],
u128_xor((u128 *) res, (u128 *) res, (u128 *) p2);
/* res = e(k, res) */
- err = smp_e(smp->tfm_aes, k, res);
+ err = smp_e(tfm_aes, k, res);
if (err)
BT_ERR("Encrypt data error");
return err;
}
-static int smp_s1(struct smp_chan *smp, u8 k[16], u8 r1[16], u8 r2[16],
- u8 _r[16])
+static int smp_s1(struct crypto_blkcipher *tfm_aes, u8 k[16], u8 r1[16],
+ u8 r2[16], u8 _r[16])
{
- struct hci_dev *hdev = smp->conn->hcon->hdev;
int err;
- BT_DBG("%s", hdev->name);
-
/* Just least significant octets from r1 and r2 are considered */
memcpy(_r, r2, 8);
memcpy(_r + 8, r1, 8);
- err = smp_e(smp->tfm_aes, k, _r);
+ err = smp_e(tfm_aes, k, _r);
if (err)
BT_ERR("Encrypt data error");
@@ -547,7 +541,7 @@ static u8 smp_confirm(struct smp_chan *smp)
BT_DBG("conn %p", conn);
- ret = smp_c1(smp, smp->tk, smp->prnd, smp->preq, smp->prsp,
+ ret = smp_c1(smp->tfm_aes, smp->tk, smp->prnd, smp->preq, smp->prsp,
conn->hcon->init_addr_type, &conn->hcon->init_addr,
conn->hcon->resp_addr_type, &conn->hcon->resp_addr,
cp.confirm_val);
@@ -578,7 +572,7 @@ static u8 smp_random(struct smp_chan *smp)
BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave");
- ret = smp_c1(smp, smp->tk, smp->rrnd, smp->preq, smp->prsp,
+ ret = smp_c1(smp->tfm_aes, smp->tk, smp->rrnd, smp->preq, smp->prsp,
hcon->init_addr_type, &hcon->init_addr,
hcon->resp_addr_type, &hcon->resp_addr, confirm);
if (ret)
@@ -594,7 +588,7 @@ static u8 smp_random(struct smp_chan *smp)
__le64 rand = 0;
__le16 ediv = 0;
- smp_s1(smp, smp->tk, smp->rrnd, smp->prnd, stk);
+ smp_s1(smp->tfm_aes, smp->tk, smp->rrnd, smp->prnd, stk);
memset(stk + smp->enc_key_size, 0,
SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
@@ -613,7 +607,7 @@ static u8 smp_random(struct smp_chan *smp)
smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd),
smp->prnd);
- smp_s1(smp, smp->tk, smp->prnd, smp->rrnd, stk);
+ smp_s1(smp->tfm_aes, smp->tk, smp->prnd, smp->rrnd, stk);
memset(stk + smp->enc_key_size, 0,
SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
@@ -970,7 +964,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb)
if (sec_level > conn->hcon->pending_sec_level)
conn->hcon->pending_sec_level = sec_level;
- /* If we need MITM check that it can be acheived */
+ /* If we need MITM check that it can be achieved */
if (conn->hcon->pending_sec_level >= BT_SECURITY_HIGH) {
u8 method;
@@ -1028,7 +1022,7 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb)
auth = rsp->auth_req & AUTH_REQ_MASK;
- /* If we need MITM check that it can be acheived */
+ /* If we need MITM check that it can be achieved */
if (conn->hcon->pending_sec_level >= BT_SECURITY_HIGH) {
u8 method;
diff --git a/net/caif/caif_usb.c b/net/caif/caif_usb.c
index ba02db02..5cd44f0 100644
--- a/net/caif/caif_usb.c
+++ b/net/caif/caif_usb.c
@@ -87,13 +87,12 @@ static struct cflayer *cfusbl_create(int phyid, u8 ethaddr[ETH_ALEN],
{
struct cfusbl *this = kmalloc(sizeof(struct cfusbl), GFP_ATOMIC);
- if (!this) {
- pr_warn("Out of memory\n");
+ if (!this)
return NULL;
- }
+
caif_assert(offsetof(struct cfusbl, layer) == 0);
- memset(this, 0, sizeof(struct cflayer));
+ memset(&this->layer, 0, sizeof(this->layer));
this->layer.receive = cfusbl_receive;
this->layer.transmit = cfusbl_transmit;
this->layer.ctrlcmd = cfusbl_ctrlcmd;
diff --git a/net/caif/cfmuxl.c b/net/caif/cfmuxl.c
index 8c5d638..510aa5a 100644
--- a/net/caif/cfmuxl.c
+++ b/net/caif/cfmuxl.c
@@ -47,10 +47,10 @@ static struct cflayer *get_up(struct cfmuxl *muxl, u16 id);
struct cflayer *cfmuxl_create(void)
{
- struct cfmuxl *this = kmalloc(sizeof(struct cfmuxl), GFP_ATOMIC);
+ struct cfmuxl *this = kzalloc(sizeof(struct cfmuxl), GFP_ATOMIC);
+
if (!this)
return NULL;
- memset(this, 0, sizeof(*this));
this->layer.receive = cfmuxl_receive;
this->layer.transmit = cfmuxl_transmit;
this->layer.ctrlcmd = cfmuxl_ctrlcmd;
diff --git a/net/ceph/Kconfig b/net/ceph/Kconfig
index e50cc69..f8cceb9 100644
--- a/net/ceph/Kconfig
+++ b/net/ceph/Kconfig
@@ -3,6 +3,7 @@ config CEPH_LIB
depends on INET
select LIBCRC32C
select CRYPTO_AES
+ select CRYPTO_CBC
select CRYPTO
select KEYS
default n
diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c
index 1675021..58fbfe1 100644
--- a/net/ceph/ceph_common.c
+++ b/net/ceph/ceph_common.c
@@ -293,17 +293,20 @@ static int get_secret(struct ceph_crypto_key *dst, const char *name) {
key_err = PTR_ERR(ukey);
switch (key_err) {
case -ENOKEY:
- pr_warning("ceph: Mount failed due to key not found: %s\n", name);
+ pr_warn("ceph: Mount failed due to key not found: %s\n",
+ name);
break;
case -EKEYEXPIRED:
- pr_warning("ceph: Mount failed due to expired key: %s\n", name);
+ pr_warn("ceph: Mount failed due to expired key: %s\n",
+ name);
break;
case -EKEYREVOKED:
- pr_warning("ceph: Mount failed due to revoked key: %s\n", name);
+ pr_warn("ceph: Mount failed due to revoked key: %s\n",
+ name);
break;
default:
- pr_warning("ceph: Mount failed due to unknown key error"
- " %d: %s\n", key_err, name);
+ pr_warn("ceph: Mount failed due to unknown key error %d: %s\n",
+ key_err, name);
}
err = -EPERM;
goto out;
@@ -433,7 +436,7 @@ ceph_parse_options(char *options, const char *dev_name,
/* misc */
case Opt_osdtimeout:
- pr_warning("ignoring deprecated osdtimeout option\n");
+ pr_warn("ignoring deprecated osdtimeout option\n");
break;
case Opt_osdkeepalivetimeout:
opt->osd_keepalive_timeout = intval;
diff --git a/net/ceph/ceph_strings.c b/net/ceph/ceph_strings.c
index 1348df9..3056020 100644
--- a/net/ceph/ceph_strings.c
+++ b/net/ceph/ceph_strings.c
@@ -19,77 +19,12 @@ const char *ceph_entity_type_name(int type)
const char *ceph_osd_op_name(int op)
{
switch (op) {
- case CEPH_OSD_OP_READ: return "read";
- case CEPH_OSD_OP_STAT: return "stat";
- case CEPH_OSD_OP_MAPEXT: return "mapext";
- case CEPH_OSD_OP_SPARSE_READ: return "sparse-read";
- case CEPH_OSD_OP_NOTIFY: return "notify";
- case CEPH_OSD_OP_NOTIFY_ACK: return "notify-ack";
- case CEPH_OSD_OP_ASSERT_VER: return "assert-version";
-
- case CEPH_OSD_OP_MASKTRUNC: return "masktrunc";
-
- case CEPH_OSD_OP_CREATE: return "create";
- case CEPH_OSD_OP_WRITE: return "write";
- case CEPH_OSD_OP_DELETE: return "delete";
- case CEPH_OSD_OP_TRUNCATE: return "truncate";
- case CEPH_OSD_OP_ZERO: return "zero";
- case CEPH_OSD_OP_WRITEFULL: return "writefull";
- case CEPH_OSD_OP_ROLLBACK: return "rollback";
-
- case CEPH_OSD_OP_APPEND: return "append";
- case CEPH_OSD_OP_STARTSYNC: return "startsync";
- case CEPH_OSD_OP_SETTRUNC: return "settrunc";
- case CEPH_OSD_OP_TRIMTRUNC: return "trimtrunc";
-
- case CEPH_OSD_OP_TMAPUP: return "tmapup";
- case CEPH_OSD_OP_TMAPGET: return "tmapget";
- case CEPH_OSD_OP_TMAPPUT: return "tmapput";
- case CEPH_OSD_OP_WATCH: return "watch";
-
- case CEPH_OSD_OP_CLONERANGE: return "clonerange";
- case CEPH_OSD_OP_ASSERT_SRC_VERSION: return "assert-src-version";
- case CEPH_OSD_OP_SRC_CMPXATTR: return "src-cmpxattr";
-
- case CEPH_OSD_OP_GETXATTR: return "getxattr";
- case CEPH_OSD_OP_GETXATTRS: return "getxattrs";
- case CEPH_OSD_OP_SETXATTR: return "setxattr";
- case CEPH_OSD_OP_SETXATTRS: return "setxattrs";
- case CEPH_OSD_OP_RESETXATTRS: return "resetxattrs";
- case CEPH_OSD_OP_RMXATTR: return "rmxattr";
- case CEPH_OSD_OP_CMPXATTR: return "cmpxattr";
-
- case CEPH_OSD_OP_PULL: return "pull";
- case CEPH_OSD_OP_PUSH: return "push";
- case CEPH_OSD_OP_BALANCEREADS: return "balance-reads";
- case CEPH_OSD_OP_UNBALANCEREADS: return "unbalance-reads";
- case CEPH_OSD_OP_SCRUB: return "scrub";
- case CEPH_OSD_OP_SCRUB_RESERVE: return "scrub-reserve";
- case CEPH_OSD_OP_SCRUB_UNRESERVE: return "scrub-unreserve";
- case CEPH_OSD_OP_SCRUB_STOP: return "scrub-stop";
- case CEPH_OSD_OP_SCRUB_MAP: return "scrub-map";
-
- case CEPH_OSD_OP_WRLOCK: return "wrlock";
- case CEPH_OSD_OP_WRUNLOCK: return "wrunlock";
- case CEPH_OSD_OP_RDLOCK: return "rdlock";
- case CEPH_OSD_OP_RDUNLOCK: return "rdunlock";
- case CEPH_OSD_OP_UPLOCK: return "uplock";
- case CEPH_OSD_OP_DNLOCK: return "dnlock";
-
- case CEPH_OSD_OP_CALL: return "call";
-
- case CEPH_OSD_OP_PGLS: return "pgls";
- case CEPH_OSD_OP_PGLS_FILTER: return "pgls-filter";
- case CEPH_OSD_OP_OMAPGETKEYS: return "omap-get-keys";
- case CEPH_OSD_OP_OMAPGETVALS: return "omap-get-vals";
- case CEPH_OSD_OP_OMAPGETHEADER: return "omap-get-header";
- case CEPH_OSD_OP_OMAPGETVALSBYKEYS: return "omap-get-vals-by-keys";
- case CEPH_OSD_OP_OMAPSETVALS: return "omap-set-vals";
- case CEPH_OSD_OP_OMAPSETHEADER: return "omap-set-header";
- case CEPH_OSD_OP_OMAPCLEAR: return "omap-clear";
- case CEPH_OSD_OP_OMAPRMKEYS: return "omap-rm-keys";
+#define GENERATE_CASE(op, opcode, str) case CEPH_OSD_OP_##op: return (str);
+__CEPH_FORALL_OSD_OPS(GENERATE_CASE)
+#undef GENERATE_CASE
+ default:
+ return "???";
}
- return "???";
}
const char *ceph_osd_state_name(int s)
diff --git a/net/ceph/crypto.c b/net/ceph/crypto.c
index ffeba8f..62fc5e7 100644
--- a/net/ceph/crypto.c
+++ b/net/ceph/crypto.c
@@ -476,7 +476,6 @@ struct key_type key_type_ceph = {
.preparse = ceph_key_preparse,
.free_preparse = ceph_key_free_preparse,
.instantiate = generic_key_instantiate,
- .match = user_match,
.destroy = ceph_key_destroy,
};
diff --git a/net/ceph/debugfs.c b/net/ceph/debugfs.c
index d1a62c6..d2d5255 100644
--- a/net/ceph/debugfs.c
+++ b/net/ceph/debugfs.c
@@ -169,7 +169,8 @@ static int osdc_show(struct seq_file *s, void *pp)
for (i = 0; i < req->r_num_ops; i++) {
opcode = req->r_ops[i].op;
- seq_printf(s, "\t%s", ceph_osd_op_name(opcode));
+ seq_printf(s, "%s%s", (i == 0 ? "\t" : ","),
+ ceph_osd_op_name(opcode));
}
seq_printf(s, "\n");
diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c
index b2f571d..559c9f6 100644
--- a/net/ceph/messenger.c
+++ b/net/ceph/messenger.c
@@ -292,7 +292,11 @@ int ceph_msgr_init(void)
if (ceph_msgr_slab_init())
return -ENOMEM;
- ceph_msgr_wq = alloc_workqueue("ceph-msgr", 0, 0);
+ /*
+ * The number of active work items is limited by the number of
+ * connections, so leave @max_active at default.
+ */
+ ceph_msgr_wq = alloc_workqueue("ceph-msgr", WQ_MEM_RECLAIM, 0);
if (ceph_msgr_wq)
return 0;
@@ -1937,11 +1941,11 @@ static int process_banner(struct ceph_connection *con)
sizeof(con->peer_addr)) != 0 &&
!(addr_is_blank(&con->actual_peer_addr.in_addr) &&
con->actual_peer_addr.nonce == con->peer_addr.nonce)) {
- pr_warning("wrong peer, want %s/%d, got %s/%d\n",
- ceph_pr_addr(&con->peer_addr.in_addr),
- (int)le32_to_cpu(con->peer_addr.nonce),
- ceph_pr_addr(&con->actual_peer_addr.in_addr),
- (int)le32_to_cpu(con->actual_peer_addr.nonce));
+ pr_warn("wrong peer, want %s/%d, got %s/%d\n",
+ ceph_pr_addr(&con->peer_addr.in_addr),
+ (int)le32_to_cpu(con->peer_addr.nonce),
+ ceph_pr_addr(&con->actual_peer_addr.in_addr),
+ (int)le32_to_cpu(con->actual_peer_addr.nonce));
con->error_msg = "wrong peer at address";
return -1;
}
@@ -2302,7 +2306,7 @@ static int read_partial_message(struct ceph_connection *con)
BUG_ON(!con->in_msg ^ skip);
if (con->in_msg && data_len > con->in_msg->data_length) {
- pr_warning("%s skipping long message (%u > %zd)\n",
+ pr_warn("%s skipping long message (%u > %zd)\n",
__func__, data_len, con->in_msg->data_length);
ceph_msg_put(con->in_msg);
con->in_msg = NULL;
@@ -2712,7 +2716,7 @@ static bool con_sock_closed(struct ceph_connection *con)
CASE(OPEN);
CASE(STANDBY);
default:
- pr_warning("%s con %p unrecognized state %lu\n",
+ pr_warn("%s con %p unrecognized state %lu\n",
__func__, con, con->state);
con->error_msg = "unrecognized con state";
BUG();
@@ -2828,8 +2832,8 @@ static void con_work(struct work_struct *work)
*/
static void con_fault(struct ceph_connection *con)
{
- pr_warning("%s%lld %s %s\n", ENTITY_NAME(con->peer_name),
- ceph_pr_addr(&con->peer_addr.in_addr), con->error_msg);
+ pr_warn("%s%lld %s %s\n", ENTITY_NAME(con->peer_name),
+ ceph_pr_addr(&con->peer_addr.in_addr), con->error_msg);
dout("fault %p state %lu to peer %s\n",
con, con->state, ceph_pr_addr(&con->peer_addr.in_addr));
@@ -3071,10 +3075,8 @@ static void ceph_msg_data_destroy(struct ceph_msg_data *data)
return;
WARN_ON(!list_empty(&data->links));
- if (data->type == CEPH_MSG_DATA_PAGELIST) {
+ if (data->type == CEPH_MSG_DATA_PAGELIST)
ceph_pagelist_release(data->pagelist);
- kfree(data->pagelist);
- }
kmem_cache_free(ceph_msg_data_cache, data);
}
diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c
index 61fcfc3..a83062c 100644
--- a/net/ceph/mon_client.c
+++ b/net/ceph/mon_client.c
@@ -1182,10 +1182,10 @@ static struct ceph_msg *mon_alloc_msg(struct ceph_connection *con,
pr_info("alloc_msg unknown type %d\n", type);
*skip = 1;
} else if (front_len > m->front_alloc_len) {
- pr_warning("mon_alloc_msg front %d > prealloc %d (%u#%llu)\n",
- front_len, m->front_alloc_len,
- (unsigned int)con->peer_name.type,
- le64_to_cpu(con->peer_name.num));
+ pr_warn("mon_alloc_msg front %d > prealloc %d (%u#%llu)\n",
+ front_len, m->front_alloc_len,
+ (unsigned int)con->peer_name.type,
+ le64_to_cpu(con->peer_name.num));
ceph_msg_put(m);
m = ceph_msg_new(type, front_len, GFP_NOFS, false);
}
diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c
index 30f6faf..f3fc54e 100644
--- a/net/ceph/osd_client.c
+++ b/net/ceph/osd_client.c
@@ -30,8 +30,11 @@ static void __send_queued(struct ceph_osd_client *osdc);
static int __reset_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd);
static void __register_request(struct ceph_osd_client *osdc,
struct ceph_osd_request *req);
+static void __unregister_request(struct ceph_osd_client *osdc,
+ struct ceph_osd_request *req);
static void __unregister_linger_request(struct ceph_osd_client *osdc,
struct ceph_osd_request *req);
+static void __enqueue_request(struct ceph_osd_request *req);
static void __send_request(struct ceph_osd_client *osdc,
struct ceph_osd_request *req);
@@ -428,68 +431,9 @@ EXPORT_SYMBOL(ceph_osdc_alloc_request);
static bool osd_req_opcode_valid(u16 opcode)
{
switch (opcode) {
- case CEPH_OSD_OP_READ:
- case CEPH_OSD_OP_STAT:
- case CEPH_OSD_OP_MAPEXT:
- case CEPH_OSD_OP_MASKTRUNC:
- case CEPH_OSD_OP_SPARSE_READ:
- case CEPH_OSD_OP_NOTIFY:
- case CEPH_OSD_OP_NOTIFY_ACK:
- case CEPH_OSD_OP_ASSERT_VER:
- case CEPH_OSD_OP_WRITE:
- case CEPH_OSD_OP_WRITEFULL:
- case CEPH_OSD_OP_TRUNCATE:
- case CEPH_OSD_OP_ZERO:
- case CEPH_OSD_OP_DELETE:
- case CEPH_OSD_OP_APPEND:
- case CEPH_OSD_OP_STARTSYNC:
- case CEPH_OSD_OP_SETTRUNC:
- case CEPH_OSD_OP_TRIMTRUNC:
- case CEPH_OSD_OP_TMAPUP:
- case CEPH_OSD_OP_TMAPPUT:
- case CEPH_OSD_OP_TMAPGET:
- case CEPH_OSD_OP_CREATE:
- case CEPH_OSD_OP_ROLLBACK:
- case CEPH_OSD_OP_WATCH:
- case CEPH_OSD_OP_OMAPGETKEYS:
- case CEPH_OSD_OP_OMAPGETVALS:
- case CEPH_OSD_OP_OMAPGETHEADER:
- case CEPH_OSD_OP_OMAPGETVALSBYKEYS:
- case CEPH_OSD_OP_OMAPSETVALS:
- case CEPH_OSD_OP_OMAPSETHEADER:
- case CEPH_OSD_OP_OMAPCLEAR:
- case CEPH_OSD_OP_OMAPRMKEYS:
- case CEPH_OSD_OP_OMAP_CMP:
- case CEPH_OSD_OP_SETALLOCHINT:
- case CEPH_OSD_OP_CLONERANGE:
- case CEPH_OSD_OP_ASSERT_SRC_VERSION:
- case CEPH_OSD_OP_SRC_CMPXATTR:
- case CEPH_OSD_OP_GETXATTR:
- case CEPH_OSD_OP_GETXATTRS:
- case CEPH_OSD_OP_CMPXATTR:
- case CEPH_OSD_OP_SETXATTR:
- case CEPH_OSD_OP_SETXATTRS:
- case CEPH_OSD_OP_RESETXATTRS:
- case CEPH_OSD_OP_RMXATTR:
- case CEPH_OSD_OP_PULL:
- case CEPH_OSD_OP_PUSH:
- case CEPH_OSD_OP_BALANCEREADS:
- case CEPH_OSD_OP_UNBALANCEREADS:
- case CEPH_OSD_OP_SCRUB:
- case CEPH_OSD_OP_SCRUB_RESERVE:
- case CEPH_OSD_OP_SCRUB_UNRESERVE:
- case CEPH_OSD_OP_SCRUB_STOP:
- case CEPH_OSD_OP_SCRUB_MAP:
- case CEPH_OSD_OP_WRLOCK:
- case CEPH_OSD_OP_WRUNLOCK:
- case CEPH_OSD_OP_RDLOCK:
- case CEPH_OSD_OP_RDUNLOCK:
- case CEPH_OSD_OP_UPLOCK:
- case CEPH_OSD_OP_DNLOCK:
- case CEPH_OSD_OP_CALL:
- case CEPH_OSD_OP_PGLS:
- case CEPH_OSD_OP_PGLS_FILTER:
- return true;
+#define GENERATE_CASE(op, opcode, str) case CEPH_OSD_OP_##op: return true;
+__CEPH_FORALL_OSD_OPS(GENERATE_CASE)
+#undef GENERATE_CASE
default:
return false;
}
@@ -892,6 +836,37 @@ __lookup_request_ge(struct ceph_osd_client *osdc,
return NULL;
}
+static void __kick_linger_request(struct ceph_osd_request *req)
+{
+ struct ceph_osd_client *osdc = req->r_osdc;
+ struct ceph_osd *osd = req->r_osd;
+
+ /*
+ * Linger requests need to be resent with a new tid to avoid
+ * the dup op detection logic on the OSDs. Achieve this with
+ * a re-register dance instead of open-coding.
+ */
+ ceph_osdc_get_request(req);
+ if (!list_empty(&req->r_linger_item))
+ __unregister_linger_request(osdc, req);
+ else
+ __unregister_request(osdc, req);
+ __register_request(osdc, req);
+ ceph_osdc_put_request(req);
+
+ /*
+ * Unless request has been registered as both normal and
+ * lingering, __unregister{,_linger}_request clears r_osd.
+ * However, here we need to preserve r_osd to make sure we
+ * requeue on the same OSD.
+ */
+ WARN_ON(req->r_osd || !osd);
+ req->r_osd = osd;
+
+ dout("%s requeueing %p tid %llu\n", __func__, req, req->r_tid);
+ __enqueue_request(req);
+}
+
/*
* Resubmit requests pending on the given osd.
*/
@@ -900,12 +875,14 @@ static void __kick_osd_requests(struct ceph_osd_client *osdc,
{
struct ceph_osd_request *req, *nreq;
LIST_HEAD(resend);
+ LIST_HEAD(resend_linger);
int err;
- dout("__kick_osd_requests osd%d\n", osd->o_osd);
+ dout("%s osd%d\n", __func__, osd->o_osd);
err = __reset_osd(osdc, osd);
if (err)
return;
+
/*
* Build up a list of requests to resend by traversing the
* osd's list of requests. Requests for a given object are
@@ -926,33 +903,32 @@ static void __kick_osd_requests(struct ceph_osd_client *osdc,
list_for_each_entry(req, &osd->o_requests, r_osd_item) {
if (!req->r_sent)
break;
- list_move_tail(&req->r_req_lru_item, &resend);
- dout("requeueing %p tid %llu osd%d\n", req, req->r_tid,
- osd->o_osd);
- if (!req->r_linger)
+
+ if (!req->r_linger) {
+ dout("%s requeueing %p tid %llu\n", __func__, req,
+ req->r_tid);
+ list_move_tail(&req->r_req_lru_item, &resend);
req->r_flags |= CEPH_OSD_FLAG_RETRY;
+ } else {
+ list_move_tail(&req->r_req_lru_item, &resend_linger);
+ }
}
list_splice(&resend, &osdc->req_unsent);
/*
- * Linger requests are re-registered before sending, which
- * sets up a new tid for each. We add them to the unsent
- * list at the end to keep things in tid order.
+ * Both registered and not yet registered linger requests are
+ * enqueued with a new tid on the same OSD. We add/move them
+ * to req_unsent/o_requests at the end to keep things in tid
+ * order.
*/
list_for_each_entry_safe(req, nreq, &osd->o_linger_requests,
r_linger_osd_item) {
- /*
- * reregister request prior to unregistering linger so
- * that r_osd is preserved.
- */
- BUG_ON(!list_empty(&req->r_req_lru_item));
- __register_request(osdc, req);
- list_add_tail(&req->r_req_lru_item, &osdc->req_unsent);
- list_add_tail(&req->r_osd_item, &req->r_osd->o_requests);
- __unregister_linger_request(osdc, req);
- dout("requeued lingering %p tid %llu osd%d\n", req, req->r_tid,
- osd->o_osd);
+ WARN_ON(!list_empty(&req->r_req_lru_item));
+ __kick_linger_request(req);
}
+
+ list_for_each_entry_safe(req, nreq, &resend_linger, r_req_lru_item)
+ __kick_linger_request(req);
}
/*
@@ -1346,6 +1322,22 @@ static int __calc_request_pg(struct ceph_osdmap *osdmap,
&req->r_target_oid, pg_out);
}
+static void __enqueue_request(struct ceph_osd_request *req)
+{
+ struct ceph_osd_client *osdc = req->r_osdc;
+
+ dout("%s %p tid %llu to osd%d\n", __func__, req, req->r_tid,
+ req->r_osd ? req->r_osd->o_osd : -1);
+
+ if (req->r_osd) {
+ __remove_osd_from_lru(req->r_osd);
+ list_add_tail(&req->r_osd_item, &req->r_osd->o_requests);
+ list_move_tail(&req->r_req_lru_item, &osdc->req_unsent);
+ } else {
+ list_move_tail(&req->r_req_lru_item, &osdc->req_notarget);
+ }
+}
+
/*
* Pick an osd (the first 'up' osd in the pg), allocate the osd struct
* (as needed), and set the request r_osd appropriately. If there is
@@ -1423,13 +1415,7 @@ static int __map_request(struct ceph_osd_client *osdc,
&osdc->osdmap->osd_addr[o]);
}
- if (req->r_osd) {
- __remove_osd_from_lru(req->r_osd);
- list_add_tail(&req->r_osd_item, &req->r_osd->o_requests);
- list_move_tail(&req->r_req_lru_item, &osdc->req_unsent);
- } else {
- list_move_tail(&req->r_req_lru_item, &osdc->req_notarget);
- }
+ __enqueue_request(req);
err = 1; /* osd or pg changed */
out:
@@ -1774,8 +1760,8 @@ static void handle_reply(struct ceph_osd_client *osdc, struct ceph_msg *msg,
}
bytes = le32_to_cpu(msg->hdr.data_len);
if (payload_len != bytes) {
- pr_warning("sum of op payload lens %d != data_len %d",
- payload_len, bytes);
+ pr_warn("sum of op payload lens %d != data_len %d\n",
+ payload_len, bytes);
goto bad_put;
}
@@ -2313,24 +2299,19 @@ static void handle_watch_notify(struct ceph_osd_client *osdc,
if (event) {
event_work = kmalloc(sizeof(*event_work), GFP_NOIO);
if (!event_work) {
- dout("ERROR: could not allocate event_work\n");
- goto done_err;
+ pr_err("couldn't allocate event_work\n");
+ ceph_osdc_put_event(event);
+ return;
}
INIT_WORK(&event_work->work, do_event_work);
event_work->event = event;
event_work->ver = ver;
event_work->notify_id = notify_id;
event_work->opcode = opcode;
- if (!queue_work(osdc->notify_wq, &event_work->work)) {
- dout("WARNING: failed to queue notify event work\n");
- goto done_err;
- }
- }
- return;
+ queue_work(osdc->notify_wq, &event_work->work);
+ }
-done_err:
- ceph_osdc_put_event(event);
return;
bad:
@@ -2797,10 +2778,10 @@ static struct ceph_msg *get_reply(struct ceph_connection *con,
ceph_msg_revoke_incoming(req->r_reply);
if (front_len > req->r_reply->front_alloc_len) {
- pr_warning("get_reply front %d > preallocated %d (%u#%llu)\n",
- front_len, req->r_reply->front_alloc_len,
- (unsigned int)con->peer_name.type,
- le64_to_cpu(con->peer_name.num));
+ pr_warn("get_reply front %d > preallocated %d (%u#%llu)\n",
+ front_len, req->r_reply->front_alloc_len,
+ (unsigned int)con->peer_name.type,
+ le64_to_cpu(con->peer_name.num));
m = ceph_msg_new(CEPH_MSG_OSD_OPREPLY, front_len, GFP_NOFS,
false);
if (!m)
@@ -2823,8 +2804,7 @@ static struct ceph_msg *get_reply(struct ceph_connection *con,
if (osd_data->pages &&
unlikely(osd_data->length < data_len)) {
- pr_warning("tid %lld reply has %d bytes "
- "we had only %llu bytes ready\n",
+ pr_warn("tid %lld reply has %d bytes we had only %llu bytes ready\n",
tid, data_len, osd_data->length);
*skip = 1;
ceph_msg_put(m);
diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c
index c547e46..b8c3fde 100644
--- a/net/ceph/osdmap.c
+++ b/net/ceph/osdmap.c
@@ -521,11 +521,11 @@ static int decode_pool(void **p, void *end, struct ceph_pg_pool_info *pi)
ev = ceph_decode_8(p); /* encoding version */
cv = ceph_decode_8(p); /* compat version */
if (ev < 5) {
- pr_warning("got v %d < 5 cv %d of ceph_pg_pool\n", ev, cv);
+ pr_warn("got v %d < 5 cv %d of ceph_pg_pool\n", ev, cv);
return -EINVAL;
}
if (cv > 9) {
- pr_warning("got v %d cv %d > 9 of ceph_pg_pool\n", ev, cv);
+ pr_warn("got v %d cv %d > 9 of ceph_pg_pool\n", ev, cv);
return -EINVAL;
}
len = ceph_decode_32(p);
@@ -671,26 +671,26 @@ static int osdmap_set_max_osd(struct ceph_osdmap *map, int max)
int i;
state = krealloc(map->osd_state, max*sizeof(*state), GFP_NOFS);
+ if (!state)
+ return -ENOMEM;
+ map->osd_state = state;
+
weight = krealloc(map->osd_weight, max*sizeof(*weight), GFP_NOFS);
- addr = krealloc(map->osd_addr, max*sizeof(*addr), GFP_NOFS);
- if (!state || !weight || !addr) {
- kfree(state);
- kfree(weight);
- kfree(addr);
+ if (!weight)
+ return -ENOMEM;
+ map->osd_weight = weight;
+ addr = krealloc(map->osd_addr, max*sizeof(*addr), GFP_NOFS);
+ if (!addr)
return -ENOMEM;
- }
+ map->osd_addr = addr;
for (i = map->max_osd; i < max; i++) {
- state[i] = 0;
- weight[i] = CEPH_OSD_OUT;
- memset(addr + i, 0, sizeof(*addr));
+ map->osd_state[i] = 0;
+ map->osd_weight[i] = CEPH_OSD_OUT;
+ memset(map->osd_addr + i, 0, sizeof(*map->osd_addr));
}
- map->osd_state = state;
- map->osd_weight = weight;
- map->osd_addr = addr;
-
if (map->osd_primary_affinity) {
u32 *affinity;
@@ -698,11 +698,11 @@ static int osdmap_set_max_osd(struct ceph_osdmap *map, int max)
max*sizeof(*affinity), GFP_NOFS);
if (!affinity)
return -ENOMEM;
+ map->osd_primary_affinity = affinity;
for (i = map->max_osd; i < max; i++)
- affinity[i] = CEPH_OSD_DEFAULT_PRIMARY_AFFINITY;
-
- map->osd_primary_affinity = affinity;
+ map->osd_primary_affinity[i] =
+ CEPH_OSD_DEFAULT_PRIMARY_AFFINITY;
}
map->max_osd = max;
@@ -729,9 +729,9 @@ static int get_osdmap_client_data_v(void **p, void *end,
ceph_decode_8_safe(p, end, struct_compat, e_inval);
if (struct_compat > OSDMAP_WRAPPER_COMPAT_VER) {
- pr_warning("got v %d cv %d > %d of %s ceph_osdmap\n",
- struct_v, struct_compat,
- OSDMAP_WRAPPER_COMPAT_VER, prefix);
+ pr_warn("got v %d cv %d > %d of %s ceph_osdmap\n",
+ struct_v, struct_compat,
+ OSDMAP_WRAPPER_COMPAT_VER, prefix);
return -EINVAL;
}
*p += 4; /* ignore wrapper struct_len */
@@ -739,9 +739,9 @@ static int get_osdmap_client_data_v(void **p, void *end,
ceph_decode_8_safe(p, end, struct_v, e_inval);
ceph_decode_8_safe(p, end, struct_compat, e_inval);
if (struct_compat > OSDMAP_CLIENT_DATA_COMPAT_VER) {
- pr_warning("got v %d cv %d > %d of %s ceph_osdmap client data\n",
- struct_v, struct_compat,
- OSDMAP_CLIENT_DATA_COMPAT_VER, prefix);
+ pr_warn("got v %d cv %d > %d of %s ceph_osdmap client data\n",
+ struct_v, struct_compat,
+ OSDMAP_CLIENT_DATA_COMPAT_VER, prefix);
return -EINVAL;
}
*p += 4; /* ignore client data struct_len */
@@ -751,8 +751,8 @@ static int get_osdmap_client_data_v(void **p, void *end,
*p -= 1;
ceph_decode_16_safe(p, end, version, e_inval);
if (version < 6) {
- pr_warning("got v %d < 6 of %s ceph_osdmap\n", version,
- prefix);
+ pr_warn("got v %d < 6 of %s ceph_osdmap\n",
+ version, prefix);
return -EINVAL;
}
diff --git a/net/ceph/pagelist.c b/net/ceph/pagelist.c
index 92866be..c7c220a 100644
--- a/net/ceph/pagelist.c
+++ b/net/ceph/pagelist.c
@@ -1,5 +1,6 @@
#include <linux/module.h>
#include <linux/gfp.h>
+#include <linux/slab.h>
#include <linux/pagemap.h>
#include <linux/highmem.h>
#include <linux/ceph/pagelist.h>
@@ -13,8 +14,10 @@ static void ceph_pagelist_unmap_tail(struct ceph_pagelist *pl)
}
}
-int ceph_pagelist_release(struct ceph_pagelist *pl)
+void ceph_pagelist_release(struct ceph_pagelist *pl)
{
+ if (!atomic_dec_and_test(&pl->refcnt))
+ return;
ceph_pagelist_unmap_tail(pl);
while (!list_empty(&pl->head)) {
struct page *page = list_first_entry(&pl->head, struct page,
@@ -23,7 +26,7 @@ int ceph_pagelist_release(struct ceph_pagelist *pl)
__free_page(page);
}
ceph_pagelist_free_reserve(pl);
- return 0;
+ kfree(pl);
}
EXPORT_SYMBOL(ceph_pagelist_release);
diff --git a/net/core/dev.c b/net/core/dev.c
index 4699dcf..b793e35 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2153,7 +2153,7 @@ static inline void __netif_reschedule(struct Qdisc *q)
unsigned long flags;
local_irq_save(flags);
- sd = &__get_cpu_var(softnet_data);
+ sd = this_cpu_ptr(&softnet_data);
q->next_sched = NULL;
*sd->output_queue_tailp = q;
sd->output_queue_tailp = &q->next_sched;
@@ -2675,7 +2675,7 @@ static struct sk_buff *validate_xmit_skb(struct sk_buff *skb, struct net_device
if (skb->encapsulation)
features &= dev->hw_enc_features;
- if (netif_needs_gso(skb, features)) {
+ if (netif_needs_gso(dev, skb, features)) {
struct sk_buff *segs;
segs = skb_gso_segment(skb, features);
@@ -3233,7 +3233,7 @@ static void rps_trigger_softirq(void *data)
static int rps_ipi_queued(struct softnet_data *sd)
{
#ifdef CONFIG_RPS
- struct softnet_data *mysd = &__get_cpu_var(softnet_data);
+ struct softnet_data *mysd = this_cpu_ptr(&softnet_data);
if (sd != mysd) {
sd->rps_ipi_next = mysd->rps_ipi_list;
@@ -3260,7 +3260,7 @@ static bool skb_flow_limit(struct sk_buff *skb, unsigned int qlen)
if (qlen < (netdev_max_backlog >> 1))
return false;
- sd = &__get_cpu_var(softnet_data);
+ sd = this_cpu_ptr(&softnet_data);
rcu_read_lock();
fl = rcu_dereference(sd->flow_limit);
@@ -3407,7 +3407,7 @@ EXPORT_SYMBOL(netif_rx_ni);
static void net_tx_action(struct softirq_action *h)
{
- struct softnet_data *sd = &__get_cpu_var(softnet_data);
+ struct softnet_data *sd = this_cpu_ptr(&softnet_data);
if (sd->completion_queue) {
struct sk_buff *clist;
@@ -3832,7 +3832,7 @@ EXPORT_SYMBOL(netif_receive_skb);
static void flush_backlog(void *arg)
{
struct net_device *dev = arg;
- struct softnet_data *sd = &__get_cpu_var(softnet_data);
+ struct softnet_data *sd = this_cpu_ptr(&softnet_data);
struct sk_buff *skb, *tmp;
rps_lock(sd);
@@ -4379,7 +4379,7 @@ void __napi_schedule(struct napi_struct *n)
unsigned long flags;
local_irq_save(flags);
- ____napi_schedule(&__get_cpu_var(softnet_data), n);
+ ____napi_schedule(this_cpu_ptr(&softnet_data), n);
local_irq_restore(flags);
}
EXPORT_SYMBOL(__napi_schedule);
@@ -4500,7 +4500,7 @@ EXPORT_SYMBOL(netif_napi_del);
static void net_rx_action(struct softirq_action *h)
{
- struct softnet_data *sd = &__get_cpu_var(softnet_data);
+ struct softnet_data *sd = this_cpu_ptr(&softnet_data);
unsigned long time_limit = jiffies + 2;
int budget = netdev_budget;
void *have;
diff --git a/net/core/drop_monitor.c b/net/core/drop_monitor.c
index 50f9a9db..252e155 100644
--- a/net/core/drop_monitor.c
+++ b/net/core/drop_monitor.c
@@ -146,7 +146,7 @@ static void trace_drop_common(struct sk_buff *skb, void *location)
unsigned long flags;
local_irq_save(flags);
- data = &__get_cpu_var(dm_cpu_data);
+ data = this_cpu_ptr(&dm_cpu_data);
spin_lock(&data->lock);
dskb = data->skb;
diff --git a/net/core/filter.c b/net/core/filter.c
index fcd3f67..647b122 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -51,9 +51,9 @@
* @skb: buffer to filter
*
* Run the filter code and then cut skb->data to correct size returned by
- * sk_run_filter. If pkt_len is 0 we toss packet. If skb->len is smaller
+ * SK_RUN_FILTER. If pkt_len is 0 we toss packet. If skb->len is smaller
* than pkt_len we keep whole skb->data. This is the socket level
- * wrapper to sk_run_filter. It returns 0 if the packet should
+ * wrapper to SK_RUN_FILTER. It returns 0 if the packet should
* be accepted or -EPERM if the packet should be tossed.
*
*/
@@ -566,11 +566,8 @@ err:
/* Security:
*
- * A BPF program is able to use 16 cells of memory to store intermediate
- * values (check u32 mem[BPF_MEMWORDS] in sk_run_filter()).
- *
* As we dont want to clear mem[] array for each packet going through
- * sk_run_filter(), we check that filter loaded by user never try to read
+ * __bpf_prog_run(), we check that filter loaded by user never try to read
* a cell if not previously written, and we check all branches to be sure
* a malicious user doesn't try to abuse us.
*/
diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c
index 8560dea..4508493 100644
--- a/net/core/flow_dissector.c
+++ b/net/core/flow_dissector.c
@@ -100,6 +100,13 @@ ip:
if (ip_is_fragment(iph))
ip_proto = 0;
+ /* skip the address processing if skb is NULL. The assumption
+ * here is that if there is no skb we are not looking for flow
+ * info but lengths and protocols.
+ */
+ if (!skb)
+ break;
+
iph_to_flow_copy_addrs(flow, iph);
break;
}
@@ -114,17 +121,15 @@ ipv6:
return false;
ip_proto = iph->nexthdr;
- flow->src = (__force __be32)ipv6_addr_hash(&iph->saddr);
- flow->dst = (__force __be32)ipv6_addr_hash(&iph->daddr);
nhoff += sizeof(struct ipv6hdr);
- /* skip the flow label processing if skb is NULL. The
- * assumption here is that if there is no skb we are not
- * looking for flow info as much as we are length.
- */
+ /* see comment above in IPv4 section */
if (!skb)
break;
+ flow->src = (__force __be32)ipv6_addr_hash(&iph->saddr);
+ flow->dst = (__force __be32)ipv6_addr_hash(&iph->daddr);
+
flow_label = ip6_flowlabel(iph);
if (flow_label) {
/* Awesome, IPv6 packet has a flow label so we can
@@ -231,9 +236,13 @@ ipv6:
flow->n_proto = proto;
flow->ip_proto = ip_proto;
- flow->ports = __skb_flow_get_ports(skb, nhoff, ip_proto, data, hlen);
flow->thoff = (u16) nhoff;
+ /* unless skb is set we don't need to record port info */
+ if (skb)
+ flow->ports = __skb_flow_get_ports(skb, nhoff, ip_proto,
+ data, hlen);
+
return true;
}
EXPORT_SYMBOL(__skb_flow_dissect);
@@ -334,15 +343,16 @@ u32 __skb_get_poff(const struct sk_buff *skb, void *data,
switch (keys->ip_proto) {
case IPPROTO_TCP: {
- const struct tcphdr *tcph;
- struct tcphdr _tcph;
+ /* access doff as u8 to avoid unaligned access */
+ const u8 *doff;
+ u8 _doff;
- tcph = __skb_header_pointer(skb, poff, sizeof(_tcph),
- data, hlen, &_tcph);
- if (!tcph)
+ doff = __skb_header_pointer(skb, poff + 12, sizeof(_doff),
+ data, hlen, &_doff);
+ if (!doff)
return poff;
- poff += max_t(u32, sizeof(struct tcphdr), tcph->doff * 4);
+ poff += max_t(u32, sizeof(struct tcphdr), (*doff & 0xF0) >> 2);
break;
}
case IPPROTO_UDP:
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 7b3df0d..61059a0 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -345,7 +345,7 @@ static void *__netdev_alloc_frag(unsigned int fragsz, gfp_t gfp_mask)
unsigned long flags;
local_irq_save(flags);
- nc = &__get_cpu_var(netdev_alloc_cache);
+ nc = this_cpu_ptr(&netdev_alloc_cache);
if (unlikely(!nc->frag.page)) {
refill:
for (order = NETDEV_FRAG_PAGE_MAX_ORDER; ;) {
@@ -360,18 +360,29 @@ refill:
goto end;
}
nc->frag.size = PAGE_SIZE << order;
-recycle:
- atomic_set(&nc->frag.page->_count, NETDEV_PAGECNT_MAX_BIAS);
+ /* Even if we own the page, we do not use atomic_set().
+ * This would break get_page_unless_zero() users.
+ */
+ atomic_add(NETDEV_PAGECNT_MAX_BIAS - 1,
+ &nc->frag.page->_count);
nc->pagecnt_bias = NETDEV_PAGECNT_MAX_BIAS;
nc->frag.offset = 0;
}
if (nc->frag.offset + fragsz > nc->frag.size) {
- /* avoid unnecessary locked operations if possible */
- if ((atomic_read(&nc->frag.page->_count) == nc->pagecnt_bias) ||
- atomic_sub_and_test(nc->pagecnt_bias, &nc->frag.page->_count))
- goto recycle;
- goto refill;
+ if (atomic_read(&nc->frag.page->_count) != nc->pagecnt_bias) {
+ if (!atomic_sub_and_test(nc->pagecnt_bias,
+ &nc->frag.page->_count))
+ goto refill;
+ /* OK, page count is 0, we can safely set it */
+ atomic_set(&nc->frag.page->_count,
+ NETDEV_PAGECNT_MAX_BIAS);
+ } else {
+ atomic_add(NETDEV_PAGECNT_MAX_BIAS - nc->pagecnt_bias,
+ &nc->frag.page->_count);
+ }
+ nc->pagecnt_bias = NETDEV_PAGECNT_MAX_BIAS;
+ nc->frag.offset = 0;
}
data = page_address(nc->frag.page) + nc->frag.offset;
@@ -4126,11 +4137,11 @@ EXPORT_SYMBOL(skb_vlan_untag);
/**
* alloc_skb_with_frags - allocate skb with page frags
*
- * header_len: size of linear part
- * data_len: needed length in frags
- * max_page_order: max page order desired.
- * errcode: pointer to error code if any
- * gfp_mask: allocation mask
+ * @header_len: size of linear part
+ * @data_len: needed length in frags
+ * @max_page_order: max page order desired.
+ * @errcode: pointer to error code if any
+ * @gfp_mask: allocation mask
*
* This can be used to allocate a paged skb, given a maximal order for frags.
*/
diff --git a/net/core/sock.c b/net/core/sock.c
index b4f3ea2..15e0c67 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1718,6 +1718,8 @@ EXPORT_SYMBOL(sock_kmalloc);
*/
void sock_kfree_s(struct sock *sk, void *mem, int size)
{
+ if (WARN_ON_ONCE(!mem))
+ return;
kfree(mem);
atomic_sub(size, &sk->sk_omem_alloc);
}
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index ad2acfe..6bcaa33 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -757,7 +757,8 @@ static int dccp_v6_rcv(struct sk_buff *skb)
/* Step 2:
* Look up flow ID in table and get corresponding socket */
sk = __inet6_lookup_skb(&dccp_hashinfo, skb,
- dh->dccph_sport, dh->dccph_dport);
+ dh->dccph_sport, dh->dccph_dport,
+ inet6_iif(skb));
/*
* Step 2:
* If no socket ...
diff --git a/net/dccp/proto.c b/net/dccp/proto.c
index 97b0fcc..5ab6627 100644
--- a/net/dccp/proto.c
+++ b/net/dccp/proto.c
@@ -1115,7 +1115,7 @@ static int __init dccp_init(void)
BUILD_BUG_ON(sizeof(struct dccp_skb_cb) >
FIELD_SIZEOF(struct sk_buff, cb));
- rc = percpu_counter_init(&dccp_orphan_count, 0);
+ rc = percpu_counter_init(&dccp_orphan_count, 0, GFP_KERNEL);
if (rc)
goto out_fail;
rc = -ENOBUFS;
diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c
index f380b2c..31cd4fd 100644
--- a/net/dns_resolver/dns_key.c
+++ b/net/dns_resolver/dns_key.c
@@ -176,11 +176,11 @@ static void dns_resolver_free_preparse(struct key_preparsed_payload *prep)
* The domain name may be a simple name or an absolute domain name (which
* should end with a period). The domain name is case-independent.
*/
-static int
-dns_resolver_match(const struct key *key, const void *description)
+static bool dns_resolver_cmp(const struct key *key,
+ const struct key_match_data *match_data)
{
int slen, dlen, ret = 0;
- const char *src = key->description, *dsp = description;
+ const char *src = key->description, *dsp = match_data->raw_data;
kenter("%s,%s", src, dsp);
@@ -209,6 +209,16 @@ no_match:
}
/*
+ * Preparse the match criterion.
+ */
+static int dns_resolver_match_preparse(struct key_match_data *match_data)
+{
+ match_data->lookup_type = KEYRING_SEARCH_LOOKUP_ITERATE;
+ match_data->cmp = dns_resolver_cmp;
+ return 0;
+}
+
+/*
* Describe a DNS key
*/
static void dns_resolver_describe(const struct key *key, struct seq_file *m)
@@ -242,7 +252,7 @@ struct key_type key_type_dns_resolver = {
.preparse = dns_resolver_preparse,
.free_preparse = dns_resolver_free_preparse,
.instantiate = generic_key_instantiate,
- .match = dns_resolver_match,
+ .match_preparse = dns_resolver_match_preparse,
.revoke = user_revoke,
.destroy = user_destroy,
.describe = dns_resolver_describe,
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 8030489..6d18174 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -11,6 +11,7 @@
#include <linux/list.h>
#include <linux/etherdevice.h>
#include <linux/phy.h>
+#include <linux/phy_fixed.h>
#include <linux/of_net.h>
#include <linux/of_mdio.h>
#include "dsa_priv.h"
@@ -598,7 +599,7 @@ dsa_slave_create(struct dsa_switch *ds, struct device *parent,
netif_carrier_off(slave_dev);
if (p->phy != NULL) {
- if (ds->drv->get_phy_flags(ds, port))
+ if (ds->drv->get_phy_flags)
p->phy->dev_flags |= ds->drv->get_phy_flags(ds, port);
phy_attach(slave_dev, dev_name(&p->phy->dev),
diff --git a/net/ieee802154/6lowpan_rtnl.c b/net/ieee802154/6lowpan_rtnl.c
index 4413629..290e14f 100644
--- a/net/ieee802154/6lowpan_rtnl.c
+++ b/net/ieee802154/6lowpan_rtnl.c
@@ -49,8 +49,8 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/netdevice.h>
+#include <linux/ieee802154.h>
#include <net/af_ieee802154.h>
-#include <net/ieee802154.h>
#include <net/ieee802154_netdev.h>
#include <net/6lowpan.h>
#include <net/ipv6.h>
@@ -58,12 +58,13 @@
#include "reassembly.h"
static LIST_HEAD(lowpan_devices);
+static int lowpan_open_count;
/* private device info */
struct lowpan_dev_info {
struct net_device *real_dev; /* real WPAN device ptr */
struct mutex dev_list_mtx; /* mutex for list ops */
- __be16 fragment_tag;
+ u16 fragment_tag;
};
struct lowpan_dev_record {
@@ -140,24 +141,33 @@ static int lowpan_give_skb_to_devices(struct sk_buff *skb,
struct sk_buff *skb_cp;
int stat = NET_RX_SUCCESS;
+ skb->protocol = htons(ETH_P_IPV6);
+ skb->pkt_type = PACKET_HOST;
+
rcu_read_lock();
list_for_each_entry_rcu(entry, &lowpan_devices, list)
if (lowpan_dev_info(entry->ldev)->real_dev == skb->dev) {
skb_cp = skb_copy(skb, GFP_ATOMIC);
if (!skb_cp) {
- stat = -ENOMEM;
- break;
+ kfree_skb(skb);
+ rcu_read_unlock();
+ return NET_RX_DROP;
}
skb_cp->dev = entry->ldev;
stat = netif_rx(skb_cp);
+ if (stat == NET_RX_DROP)
+ break;
}
rcu_read_unlock();
+ consume_skb(skb);
+
return stat;
}
-static int process_data(struct sk_buff *skb, const struct ieee802154_hdr *hdr)
+static int
+iphc_decompress(struct sk_buff *skb, const struct ieee802154_hdr *hdr)
{
u8 iphc0, iphc1;
struct ieee802154_addr_sa sa, da;
@@ -166,13 +176,13 @@ static int process_data(struct sk_buff *skb, const struct ieee802154_hdr *hdr)
raw_dump_table(__func__, "raw skb data dump", skb->data, skb->len);
/* at least two bytes will be used for the encoding */
if (skb->len < 2)
- goto drop;
+ return -EINVAL;
if (lowpan_fetch_skb_u8(skb, &iphc0))
- goto drop;
+ return -EINVAL;
if (lowpan_fetch_skb_u8(skb, &iphc1))
- goto drop;
+ return -EINVAL;
ieee802154_addr_to_sa(&sa, &hdr->source);
ieee802154_addr_to_sa(&da, &hdr->dest);
@@ -187,27 +197,9 @@ static int process_data(struct sk_buff *skb, const struct ieee802154_hdr *hdr)
else
dap = &da.hwaddr;
- return lowpan_process_data(skb, skb->dev, sap, sa.addr_type,
- IEEE802154_ADDR_LEN, dap, da.addr_type,
- IEEE802154_ADDR_LEN, iphc0, iphc1,
- lowpan_give_skb_to_devices);
-
-drop:
- kfree_skb(skb);
- return -EINVAL;
-}
-
-static int lowpan_set_address(struct net_device *dev, void *p)
-{
- struct sockaddr *sa = p;
-
- if (netif_running(dev))
- return -EBUSY;
-
- /* TODO: validate addr */
- memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
-
- return 0;
+ return lowpan_header_decompress(skb, skb->dev, sap, sa.addr_type,
+ IEEE802154_ADDR_LEN, dap, da.addr_type,
+ IEEE802154_ADDR_LEN, iphc0, iphc1);
}
static struct sk_buff*
@@ -233,7 +225,7 @@ lowpan_alloc_frag(struct sk_buff *skb, int size,
&master_hdr->source, size);
if (rc < 0) {
kfree_skb(frag);
- return ERR_PTR(-rc);
+ return ERR_PTR(rc);
}
} else {
frag = ERR_PTR(-ENOMEM);
@@ -275,7 +267,8 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev,
dgram_size = lowpan_uncompress_size(skb, &dgram_offset) -
skb->mac_len;
- frag_tag = lowpan_dev_info(dev)->fragment_tag++;
+ frag_tag = htons(lowpan_dev_info(dev)->fragment_tag);
+ lowpan_dev_info(dev)->fragment_tag++;
frag_hdr[0] = LOWPAN_DISPATCH_FRAG1 | ((dgram_size >> 8) & 0x07);
frag_hdr[1] = dgram_size & 0xff;
@@ -294,7 +287,7 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev,
frag_len + skb_network_header_len(skb));
if (rc) {
pr_debug("%s unable to send FRAG1 packet (tag: %d)",
- __func__, frag_tag);
+ __func__, ntohs(frag_tag));
goto err;
}
@@ -315,7 +308,7 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *dev,
frag_len);
if (rc) {
pr_debug("%s unable to send a FRAGN packet. (tag: %d, offset: %d)\n",
- __func__, frag_tag, skb_offset);
+ __func__, ntohs(frag_tag), skb_offset);
goto err;
}
} while (skb_unprocessed > frag_cap);
@@ -410,13 +403,6 @@ static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev)
}
}
-static struct wpan_phy *lowpan_get_phy(const struct net_device *dev)
-{
- struct net_device *real_dev = lowpan_dev_info(dev)->real_dev;
-
- return ieee802154_mlme_ops(real_dev)->get_phy(real_dev);
-}
-
static __le16 lowpan_get_pan_id(const struct net_device *dev)
{
struct net_device *real_dev = lowpan_dev_info(dev)->real_dev;
@@ -464,12 +450,10 @@ static int lowpan_dev_init(struct net_device *dev)
static const struct net_device_ops lowpan_netdev_ops = {
.ndo_init = lowpan_dev_init,
.ndo_start_xmit = lowpan_xmit,
- .ndo_set_mac_address = lowpan_set_address,
};
static struct ieee802154_mlme_ops lowpan_mlme = {
.get_pan_id = lowpan_get_pan_id,
- .get_phy = lowpan_get_phy,
.get_short_addr = lowpan_get_short_addr,
.get_dsn = lowpan_get_dsn,
};
@@ -515,6 +499,9 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev,
if (!netif_running(dev))
goto drop_skb;
+ if (skb->pkt_type == PACKET_OTHERHOST)
+ goto drop_skb;
+
if (dev->type != ARPHRD_IEEE802154)
goto drop_skb;
@@ -523,55 +510,67 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev,
/* check that it's our buffer */
if (skb->data[0] == LOWPAN_DISPATCH_IPV6) {
- skb->protocol = htons(ETH_P_IPV6);
- skb->pkt_type = PACKET_HOST;
-
/* Pull off the 1-byte of 6lowpan header. */
skb_pull(skb, 1);
-
- ret = lowpan_give_skb_to_devices(skb, NULL);
- if (ret == NET_RX_DROP)
- goto drop;
+ return lowpan_give_skb_to_devices(skb, NULL);
} else {
switch (skb->data[0] & 0xe0) {
case LOWPAN_DISPATCH_IPHC: /* ipv6 datagram */
- ret = process_data(skb, &hdr);
- if (ret == NET_RX_DROP)
- goto drop;
- break;
+ ret = iphc_decompress(skb, &hdr);
+ if (ret < 0)
+ goto drop_skb;
+
+ return lowpan_give_skb_to_devices(skb, NULL);
case LOWPAN_DISPATCH_FRAG1: /* first fragment header */
ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAG1);
if (ret == 1) {
- ret = process_data(skb, &hdr);
- if (ret == NET_RX_DROP)
- goto drop;
+ ret = iphc_decompress(skb, &hdr);
+ if (ret < 0)
+ goto drop_skb;
+
+ return lowpan_give_skb_to_devices(skb, NULL);
+ } else if (ret == -1) {
+ return NET_RX_DROP;
+ } else {
+ return NET_RX_SUCCESS;
}
- break;
case LOWPAN_DISPATCH_FRAGN: /* next fragments headers */
ret = lowpan_frag_rcv(skb, LOWPAN_DISPATCH_FRAGN);
if (ret == 1) {
- ret = process_data(skb, &hdr);
- if (ret == NET_RX_DROP)
- goto drop;
+ ret = iphc_decompress(skb, &hdr);
+ if (ret < 0)
+ goto drop_skb;
+
+ return lowpan_give_skb_to_devices(skb, NULL);
+ } else if (ret == -1) {
+ return NET_RX_DROP;
+ } else {
+ return NET_RX_SUCCESS;
}
- break;
default:
break;
}
}
- return NET_RX_SUCCESS;
drop_skb:
kfree_skb(skb);
drop:
return NET_RX_DROP;
}
+static struct packet_type lowpan_packet_type = {
+ .type = htons(ETH_P_IEEE802154),
+ .func = lowpan_rcv,
+};
+
static int lowpan_newlink(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[])
{
struct net_device *real_dev;
struct lowpan_dev_record *entry;
+ int ret;
+
+ ASSERT_RTNL();
pr_debug("adding new link\n");
@@ -606,9 +605,14 @@ static int lowpan_newlink(struct net *src_net, struct net_device *dev,
list_add_tail(&entry->list, &lowpan_devices);
mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx);
- register_netdevice(dev);
+ ret = register_netdevice(dev);
+ if (ret >= 0) {
+ if (!lowpan_open_count)
+ dev_add_pack(&lowpan_packet_type);
+ lowpan_open_count++;
+ }
- return 0;
+ return ret;
}
static void lowpan_dellink(struct net_device *dev, struct list_head *head)
@@ -619,6 +623,10 @@ static void lowpan_dellink(struct net_device *dev, struct list_head *head)
ASSERT_RTNL();
+ lowpan_open_count--;
+ if (!lowpan_open_count)
+ dev_remove_pack(&lowpan_packet_type);
+
mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx);
list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) {
if (entry->ldev == dev) {
@@ -681,11 +689,6 @@ static struct notifier_block lowpan_dev_notifier = {
.notifier_call = lowpan_device_event,
};
-static struct packet_type lowpan_packet_type = {
- .type = htons(ETH_P_IEEE802154),
- .func = lowpan_rcv,
-};
-
static int __init lowpan_init_module(void)
{
int err = 0;
@@ -698,8 +701,6 @@ static int __init lowpan_init_module(void)
if (err < 0)
goto out_frag;
- dev_add_pack(&lowpan_packet_type);
-
err = register_netdevice_notifier(&lowpan_dev_notifier);
if (err < 0)
goto out_pack;
@@ -707,7 +708,6 @@ static int __init lowpan_init_module(void)
return 0;
out_pack:
- dev_remove_pack(&lowpan_packet_type);
lowpan_netlink_fini();
out_frag:
lowpan_net_frag_exit();
@@ -719,8 +719,6 @@ static void __exit lowpan_cleanup_module(void)
{
lowpan_netlink_fini();
- dev_remove_pack(&lowpan_packet_type);
-
lowpan_net_frag_exit();
unregister_netdevice_notifier(&lowpan_dev_notifier);
diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile
index 3914b1e..38354d4 100644
--- a/net/ieee802154/Makefile
+++ b/net/ieee802154/Makefile
@@ -2,8 +2,8 @@ obj-$(CONFIG_IEEE802154) += ieee802154.o af_802154.o
obj-$(CONFIG_IEEE802154_6LOWPAN) += ieee802154_6lowpan.o
ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o
-ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o \
- header_ops.o
+ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o core.o \
+ header_ops.o sysfs.o
af_802154-y := af_ieee802154.o raw.o dgram.o
ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/ieee802154/af802154.h b/net/ieee802154/af802154.h
index 8330a09..343b63e 100644
--- a/net/ieee802154/af802154.h
+++ b/net/ieee802154/af802154.h
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
diff --git a/net/ieee802154/af_ieee802154.c b/net/ieee802154/af_ieee802154.c
index 29e0de6..26da1e1 100644
--- a/net/ieee802154/af_ieee802154.c
+++ b/net/ieee802154/af_ieee802154.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Maxim Gorbachyov <maxim.gorbachev@siemens.com>
diff --git a/net/ieee802154/core.c b/net/ieee802154/core.c
new file mode 100644
index 0000000..d1cd0ed
--- /dev/null
+++ b/net/ieee802154/core.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2007, 2008, 2009 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+
+#include <net/cfg802154.h>
+
+#include "ieee802154.h"
+#include "sysfs.h"
+#include "core.h"
+
+static int wpan_phy_match(struct device *dev, const void *data)
+{
+ return !strcmp(dev_name(dev), (const char *)data);
+}
+
+struct wpan_phy *wpan_phy_find(const char *str)
+{
+ struct device *dev;
+
+ if (WARN_ON(!str))
+ return NULL;
+
+ dev = class_find_device(&wpan_phy_class, NULL, str, wpan_phy_match);
+ if (!dev)
+ return NULL;
+
+ return container_of(dev, struct wpan_phy, dev);
+}
+EXPORT_SYMBOL(wpan_phy_find);
+
+struct wpan_phy_iter_data {
+ int (*fn)(struct wpan_phy *phy, void *data);
+ void *data;
+};
+
+static int wpan_phy_iter(struct device *dev, void *_data)
+{
+ struct wpan_phy_iter_data *wpid = _data;
+ struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev);
+
+ return wpid->fn(phy, wpid->data);
+}
+
+int wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data),
+ void *data)
+{
+ struct wpan_phy_iter_data wpid = {
+ .fn = fn,
+ .data = data,
+ };
+
+ return class_for_each_device(&wpan_phy_class, NULL,
+ &wpid, wpan_phy_iter);
+}
+EXPORT_SYMBOL(wpan_phy_for_each);
+
+struct wpan_phy *
+wpan_phy_alloc(const struct cfg802154_ops *ops, size_t priv_size)
+{
+ static atomic_t wpan_phy_counter = ATOMIC_INIT(0);
+ struct cfg802154_registered_device *rdev;
+ size_t alloc_size;
+
+ alloc_size = sizeof(*rdev) + priv_size;
+ rdev = kzalloc(alloc_size, GFP_KERNEL);
+ if (!rdev)
+ return NULL;
+
+ rdev->ops = ops;
+
+ rdev->wpan_phy_idx = atomic_inc_return(&wpan_phy_counter);
+
+ if (unlikely(rdev->wpan_phy_idx < 0)) {
+ /* ugh, wrapped! */
+ atomic_dec(&wpan_phy_counter);
+ kfree(rdev);
+ return NULL;
+ }
+
+ /* atomic_inc_return makes it start at 1, make it start at 0 */
+ rdev->wpan_phy_idx--;
+
+ mutex_init(&rdev->wpan_phy.pib_lock);
+
+ device_initialize(&rdev->wpan_phy.dev);
+ dev_set_name(&rdev->wpan_phy.dev, "wpan-phy%d", rdev->wpan_phy_idx);
+
+ rdev->wpan_phy.dev.class = &wpan_phy_class;
+ rdev->wpan_phy.dev.platform_data = rdev;
+
+ return &rdev->wpan_phy;
+}
+EXPORT_SYMBOL(wpan_phy_alloc);
+
+int wpan_phy_register(struct wpan_phy *phy)
+{
+ return device_add(&phy->dev);
+}
+EXPORT_SYMBOL(wpan_phy_register);
+
+void wpan_phy_unregister(struct wpan_phy *phy)
+{
+ device_del(&phy->dev);
+}
+EXPORT_SYMBOL(wpan_phy_unregister);
+
+void wpan_phy_free(struct wpan_phy *phy)
+{
+ put_device(&phy->dev);
+}
+EXPORT_SYMBOL(wpan_phy_free);
+
+void cfg802154_dev_free(struct cfg802154_registered_device *rdev)
+{
+ kfree(rdev);
+}
+
+static int __init wpan_phy_class_init(void)
+{
+ int rc;
+
+ rc = wpan_phy_sysfs_init();
+ if (rc)
+ goto err;
+
+ rc = ieee802154_nl_init();
+ if (rc)
+ goto err_nl;
+
+ return 0;
+err_nl:
+ wpan_phy_sysfs_exit();
+err:
+ return rc;
+}
+subsys_initcall(wpan_phy_class_init);
+
+static void __exit wpan_phy_class_exit(void)
+{
+ ieee802154_nl_exit();
+ wpan_phy_sysfs_exit();
+}
+module_exit(wpan_phy_class_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("IEEE 802.15.4 configuration interface");
+MODULE_AUTHOR("Dmitry Eremin-Solenikov");
+
diff --git a/net/ieee802154/core.h b/net/ieee802154/core.h
new file mode 100644
index 0000000..fea60b3
--- /dev/null
+++ b/net/ieee802154/core.h
@@ -0,0 +1,29 @@
+#ifndef __IEEE802154_CORE_H
+#define __IEEE802154_CORE_H
+
+#include <net/cfg802154.h>
+
+struct cfg802154_registered_device {
+ const struct cfg802154_ops *ops;
+
+ /* wpan_phy index, internal only */
+ int wpan_phy_idx;
+
+ /* must be last because of the way we do wpan_phy_priv(),
+ * and it should at least be aligned to NETDEV_ALIGN
+ */
+ struct wpan_phy wpan_phy __aligned(NETDEV_ALIGN);
+};
+
+static inline struct cfg802154_registered_device *
+wpan_phy_to_rdev(struct wpan_phy *wpan_phy)
+{
+ BUG_ON(!wpan_phy);
+ return container_of(wpan_phy, struct cfg802154_registered_device,
+ wpan_phy);
+}
+
+/* free object */
+void cfg802154_dev_free(struct cfg802154_registered_device *rdev);
+
+#endif /* __IEEE802154_CORE_H */
diff --git a/net/ieee802154/dgram.c b/net/ieee802154/dgram.c
index ef2ad8a..3d58bef 100644
--- a/net/ieee802154/dgram.c
+++ b/net/ieee802154/dgram.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
@@ -27,9 +23,9 @@
#include <linux/if_arp.h>
#include <linux/list.h>
#include <linux/slab.h>
+#include <linux/ieee802154.h>
#include <net/sock.h>
#include <net/af_ieee802154.h>
-#include <net/ieee802154.h>
#include <net/ieee802154_netdev.h>
#include <asm/ioctls.h>
diff --git a/net/ieee802154/header_ops.c b/net/ieee802154/header_ops.c
index c09294e..a051b69 100644
--- a/net/ieee802154/header_ops.c
+++ b/net/ieee802154/header_ops.c
@@ -14,8 +14,9 @@
* Phoebe Buckheister <phoebe.buckheister@itwm.fraunhofer.de>
*/
+#include <linux/ieee802154.h>
+
#include <net/mac802154.h>
-#include <net/ieee802154.h>
#include <net/ieee802154_netdev.h>
static int
diff --git a/net/ieee802154/ieee802154.h b/net/ieee802154/ieee802154.h
index 5d352f8..42ae63a 100644
--- a/net/ieee802154/ieee802154.h
+++ b/net/ieee802154/ieee802154.h
@@ -10,10 +10,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#ifndef IEEE_802154_LOCAL_H
#define IEEE_802154_LOCAL_H
diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c
index 9222966..6c3c2595 100644
--- a/net/ieee802154/netlink.c
+++ b/net/ieee802154/netlink.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
diff --git a/net/ieee802154/nl-mac.c b/net/ieee802154/nl-mac.c
index c6bfe22..7127b9d 100644
--- a/net/ieee802154/nl-mac.c
+++ b/net/ieee802154/nl-mac.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
@@ -26,16 +22,15 @@
#include <linux/kernel.h>
#include <linux/if_arp.h>
#include <linux/netdevice.h>
+#include <linux/ieee802154.h>
#include <net/netlink.h>
#include <net/genetlink.h>
#include <net/sock.h>
#include <linux/nl802154.h>
#include <linux/export.h>
#include <net/af_ieee802154.h>
-#include <net/nl802154.h>
-#include <net/ieee802154.h>
#include <net/ieee802154_netdev.h>
-#include <net/wpan-phy.h>
+#include <net/cfg802154.h>
#include "ieee802154.h"
@@ -59,186 +54,7 @@ static __le16 nla_get_shortaddr(const struct nlattr *nla)
return cpu_to_le16(nla_get_u16(nla));
}
-int ieee802154_nl_assoc_indic(struct net_device *dev,
- struct ieee802154_addr *addr,
- u8 cap)
-{
- struct sk_buff *msg;
-
- pr_debug("%s\n", __func__);
-
- if (addr->mode != IEEE802154_ADDR_LONG) {
- pr_err("%s: received non-long source address!\n", __func__);
- return -EINVAL;
- }
-
- msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_INDIC);
- if (!msg)
- return -ENOBUFS;
-
- if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
- nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
- nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
- dev->dev_addr) ||
- nla_put_hwaddr(msg, IEEE802154_ATTR_SRC_HW_ADDR,
- addr->extended_addr) ||
- nla_put_u8(msg, IEEE802154_ATTR_CAPABILITY, cap))
- goto nla_put_failure;
-
- return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
-
-nla_put_failure:
- nlmsg_free(msg);
- return -ENOBUFS;
-}
-EXPORT_SYMBOL(ieee802154_nl_assoc_indic);
-
-int ieee802154_nl_assoc_confirm(struct net_device *dev, __le16 short_addr,
- u8 status)
-{
- struct sk_buff *msg;
-
- pr_debug("%s\n", __func__);
-
- msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_CONF);
- if (!msg)
- return -ENOBUFS;
-
- if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
- nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
- nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
- dev->dev_addr) ||
- nla_put_shortaddr(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) ||
- nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
- goto nla_put_failure;
- return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
-
-nla_put_failure:
- nlmsg_free(msg);
- return -ENOBUFS;
-}
-EXPORT_SYMBOL(ieee802154_nl_assoc_confirm);
-
-int ieee802154_nl_disassoc_indic(struct net_device *dev,
- struct ieee802154_addr *addr,
- u8 reason)
-{
- struct sk_buff *msg;
-
- pr_debug("%s\n", __func__);
-
- msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_INDIC);
- if (!msg)
- return -ENOBUFS;
-
- if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
- nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
- nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
- dev->dev_addr))
- goto nla_put_failure;
- if (addr->mode == IEEE802154_ADDR_LONG) {
- if (nla_put_hwaddr(msg, IEEE802154_ATTR_SRC_HW_ADDR,
- addr->extended_addr))
- goto nla_put_failure;
- } else {
- if (nla_put_shortaddr(msg, IEEE802154_ATTR_SRC_SHORT_ADDR,
- addr->short_addr))
- goto nla_put_failure;
- }
- if (nla_put_u8(msg, IEEE802154_ATTR_REASON, reason))
- goto nla_put_failure;
- return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
-
-nla_put_failure:
- nlmsg_free(msg);
- return -ENOBUFS;
-}
-EXPORT_SYMBOL(ieee802154_nl_disassoc_indic);
-
-int ieee802154_nl_disassoc_confirm(struct net_device *dev, u8 status)
-{
- struct sk_buff *msg;
-
- pr_debug("%s\n", __func__);
-
- msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_CONF);
- if (!msg)
- return -ENOBUFS;
-
- if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
- nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
- nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
- dev->dev_addr) ||
- nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
- goto nla_put_failure;
- return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
-
-nla_put_failure:
- nlmsg_free(msg);
- return -ENOBUFS;
-}
-EXPORT_SYMBOL(ieee802154_nl_disassoc_confirm);
-
-int ieee802154_nl_beacon_indic(struct net_device *dev, __le16 panid,
- __le16 coord_addr)
-{
- struct sk_buff *msg;
-
- pr_debug("%s\n", __func__);
-
- msg = ieee802154_nl_create(0, IEEE802154_BEACON_NOTIFY_INDIC);
- if (!msg)
- return -ENOBUFS;
-
- if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
- nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
- nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
- dev->dev_addr) ||
- nla_put_shortaddr(msg, IEEE802154_ATTR_COORD_SHORT_ADDR,
- coord_addr) ||
- nla_put_shortaddr(msg, IEEE802154_ATTR_COORD_PAN_ID, panid))
- goto nla_put_failure;
- return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
-
-nla_put_failure:
- nlmsg_free(msg);
- return -ENOBUFS;
-}
-EXPORT_SYMBOL(ieee802154_nl_beacon_indic);
-
-int ieee802154_nl_scan_confirm(struct net_device *dev,
- u8 status, u8 scan_type,
- u32 unscanned, u8 page,
- u8 *edl/* , struct list_head *pan_desc_list */)
-{
- struct sk_buff *msg;
-
- pr_debug("%s\n", __func__);
-
- msg = ieee802154_nl_create(0, IEEE802154_SCAN_CONF);
- if (!msg)
- return -ENOBUFS;
-
- if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
- nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
- nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
- dev->dev_addr) ||
- nla_put_u8(msg, IEEE802154_ATTR_STATUS, status) ||
- nla_put_u8(msg, IEEE802154_ATTR_SCAN_TYPE, scan_type) ||
- nla_put_u32(msg, IEEE802154_ATTR_CHANNELS, unscanned) ||
- nla_put_u8(msg, IEEE802154_ATTR_PAGE, page) ||
- (edl &&
- nla_put(msg, IEEE802154_ATTR_ED_LIST, 27, edl)))
- goto nla_put_failure;
- return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
-
-nla_put_failure:
- nlmsg_free(msg);
- return -ENOBUFS;
-}
-EXPORT_SYMBOL(ieee802154_nl_scan_confirm);
-
-int ieee802154_nl_start_confirm(struct net_device *dev, u8 status)
+static int ieee802154_nl_start_confirm(struct net_device *dev, u8 status)
{
struct sk_buff *msg;
@@ -278,8 +94,9 @@ static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid,
goto out;
ops = ieee802154_mlme_ops(dev);
- phy = ops->get_phy(dev);
+ phy = dev->ieee802154_ptr->wpan_phy;
BUG_ON(!phy);
+ get_device(&phy->dev);
short_addr = ops->get_short_addr(dev);
pan_id = ops->get_pan_id(dev);
@@ -481,7 +298,7 @@ int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
u8 channel, bcn_ord, sf_ord;
u8 page;
int pan_coord, blx, coord_realign;
- int ret = -EOPNOTSUPP;
+ int ret = -EBUSY;
if (!info->attrs[IEEE802154_ATTR_COORD_PAN_ID] ||
!info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR] ||
@@ -497,8 +314,14 @@ int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
dev = ieee802154_nl_get_dev(info);
if (!dev)
return -ENODEV;
- if (!ieee802154_mlme_ops(dev)->start_req)
+
+ if (netif_running(dev))
+ goto out;
+
+ if (!ieee802154_mlme_ops(dev)->start_req) {
+ ret = -EOPNOTSUPP;
goto out;
+ }
addr.mode = IEEE802154_ADDR_SHORT;
addr.short_addr = nla_get_shortaddr(
@@ -528,6 +351,11 @@ int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page,
bcn_ord, sf_ord, pan_coord, blx, coord_realign);
+ /* FIXME: add validation for unused parameters to be sane
+ * for SoftMAC
+ */
+ ieee802154_nl_start_confirm(dev, IEEE802154_SUCCESS);
+
out:
dev_put(dev);
return ret;
@@ -666,21 +494,8 @@ int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info)
!info->attrs[IEEE802154_ATTR_FRAME_RETRIES])
goto out;
- phy = ops->get_phy(dev);
-
- if ((!phy->set_lbt && info->attrs[IEEE802154_ATTR_LBT_ENABLED]) ||
- (!phy->set_cca_mode && info->attrs[IEEE802154_ATTR_CCA_MODE]) ||
- (!phy->set_cca_ed_level &&
- info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]) ||
- (!phy->set_csma_params &&
- (info->attrs[IEEE802154_ATTR_CSMA_RETRIES] ||
- info->attrs[IEEE802154_ATTR_CSMA_MIN_BE] ||
- info->attrs[IEEE802154_ATTR_CSMA_MAX_BE])) ||
- (!phy->set_frame_retries &&
- info->attrs[IEEE802154_ATTR_FRAME_RETRIES])) {
- rc = -EOPNOTSUPP;
- goto out_phy;
- }
+ phy = dev->ieee802154_ptr->wpan_phy;
+ get_device(&phy->dev);
ops->get_mac_params(dev, &params);
@@ -712,10 +527,9 @@ int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info)
wpan_phy_put(phy);
dev_put(dev);
- return rc;
-out_phy:
- wpan_phy_put(phy);
+ return 0;
+
out:
dev_put(dev);
return rc;
diff --git a/net/ieee802154/nl-phy.c b/net/ieee802154/nl-phy.c
index 972baf8..80a946d 100644
--- a/net/ieee802154/nl-phy.c
+++ b/net/ieee802154/nl-phy.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
@@ -27,13 +23,15 @@
#include <linux/if_arp.h>
#include <net/netlink.h>
#include <net/genetlink.h>
-#include <net/wpan-phy.h>
+#include <net/cfg802154.h>
#include <net/af_ieee802154.h>
#include <net/ieee802154_netdev.h>
#include <net/rtnetlink.h> /* for rtnl_{un,}lock */
#include <linux/nl802154.h>
#include "ieee802154.h"
+#include "rdev-ops.h"
+#include "core.h"
static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid,
u32 seq, int flags, struct wpan_phy *phy)
@@ -207,11 +205,6 @@ int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info)
if (!msg)
goto out_dev;
- if (!phy->add_iface) {
- rc = -EINVAL;
- goto nla_put_failure;
- }
-
if (info->attrs[IEEE802154_ATTR_HW_ADDR] &&
nla_len(info->attrs[IEEE802154_ATTR_HW_ADDR]) !=
IEEE802154_ADDR_LEN) {
@@ -227,11 +220,13 @@ int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info)
}
}
- dev = phy->add_iface(phy, devname, type);
+ dev = rdev_add_virtual_intf_deprecated(wpan_phy_to_rdev(phy), devname,
+ type);
if (IS_ERR(dev)) {
rc = PTR_ERR(dev);
goto nla_put_failure;
}
+ dev_hold(dev);
if (info->attrs[IEEE802154_ATTR_HW_ADDR]) {
struct sockaddr addr;
@@ -261,7 +256,7 @@ int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info)
dev_unregister:
rtnl_lock(); /* del_iface must be called with RTNL lock */
- phy->del_iface(phy, dev);
+ rdev_del_virtual_intf_deprecated(wpan_phy_to_rdev(phy), dev);
dev_put(dev);
rtnl_unlock();
nla_put_failure:
@@ -292,8 +287,9 @@ int ieee802154_del_iface(struct sk_buff *skb, struct genl_info *info)
if (!dev)
return -ENODEV;
- phy = ieee802154_mlme_ops(dev)->get_phy(dev);
+ phy = dev->ieee802154_ptr->wpan_phy;
BUG_ON(!phy);
+ get_device(&phy->dev);
rc = -EINVAL;
/* phy name is optional, but should be checked if it's given */
@@ -323,13 +319,8 @@ int ieee802154_del_iface(struct sk_buff *skb, struct genl_info *info)
if (!msg)
goto out_dev;
- if (!phy->del_iface) {
- rc = -EINVAL;
- goto nla_put_failure;
- }
-
rtnl_lock();
- phy->del_iface(phy, dev);
+ rdev_del_virtual_intf_deprecated(wpan_phy_to_rdev(phy), dev);
/* We don't have device anymore */
dev_put(dev);
diff --git a/net/ieee802154/nl_policy.c b/net/ieee802154/nl_policy.c
index 3a703ab8..35c4326 100644
--- a/net/ieee802154/nl_policy.c
+++ b/net/ieee802154/nl_policy.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <linux/kernel.h>
diff --git a/net/ieee802154/raw.c b/net/ieee802154/raw.c
index 9d1f648..3ffcf4a 100644
--- a/net/ieee802154/raw.c
+++ b/net/ieee802154/raw.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
diff --git a/net/ieee802154/rdev-ops.h b/net/ieee802154/rdev-ops.h
new file mode 100644
index 0000000..ac8824e
--- /dev/null
+++ b/net/ieee802154/rdev-ops.h
@@ -0,0 +1,23 @@
+#ifndef __CFG802154_RDEV_OPS
+#define __CFG802154_RDEV_OPS
+
+#include <net/cfg802154.h>
+
+#include "core.h"
+
+static inline struct net_device *
+rdev_add_virtual_intf_deprecated(struct cfg802154_registered_device *rdev,
+ const char *name, int type)
+{
+ return rdev->ops->add_virtual_intf_deprecated(&rdev->wpan_phy, name,
+ type);
+}
+
+static inline void
+rdev_del_virtual_intf_deprecated(struct cfg802154_registered_device *rdev,
+ struct net_device *dev)
+{
+ rdev->ops->del_virtual_intf_deprecated(&rdev->wpan_phy, dev);
+}
+
+#endif /* __CFG802154_RDEV_OPS */
diff --git a/net/ieee802154/reassembly.c b/net/ieee802154/reassembly.c
index 7cfcd68..9d980ed 100644
--- a/net/ieee802154/reassembly.c
+++ b/net/ieee802154/reassembly.c
@@ -33,7 +33,7 @@
static const char lowpan_frags_cache_name[] = "lowpan-frags";
struct lowpan_frag_info {
- __be16 d_tag;
+ u16 d_tag;
u16 d_size;
u8 d_offset;
};
@@ -48,7 +48,7 @@ static struct inet_frags lowpan_frags;
static int lowpan_frag_reasm(struct lowpan_frag_queue *fq,
struct sk_buff *prev, struct net_device *dev);
-static unsigned int lowpan_hash_frag(__be16 tag, u16 d_size,
+static unsigned int lowpan_hash_frag(u16 tag, u16 d_size,
const struct ieee802154_addr *saddr,
const struct ieee802154_addr *daddr)
{
@@ -330,11 +330,13 @@ static int lowpan_get_frag_info(struct sk_buff *skb, const u8 frag_type,
{
bool fail;
u8 pattern = 0, low = 0;
+ __be16 d_tag = 0;
fail = lowpan_fetch_skb(skb, &pattern, 1);
fail |= lowpan_fetch_skb(skb, &low, 1);
frag_info->d_size = (pattern & 7) << 8 | low;
- fail |= lowpan_fetch_skb(skb, &frag_info->d_tag, 2);
+ fail |= lowpan_fetch_skb(skb, &d_tag, 2);
+ frag_info->d_tag = ntohs(d_tag);
if (frag_type == LOWPAN_DISPATCH_FRAGN) {
fail |= lowpan_fetch_skb(skb, &frag_info->d_offset, 1);
diff --git a/net/ieee802154/reassembly.h b/net/ieee802154/reassembly.h
index 74e4a7c..836b16f 100644
--- a/net/ieee802154/reassembly.h
+++ b/net/ieee802154/reassembly.h
@@ -4,7 +4,7 @@
#include <net/inet_frag.h>
struct lowpan_create_arg {
- __be16 tag;
+ u16 tag;
u16 d_size;
const struct ieee802154_addr *src;
const struct ieee802154_addr *dst;
@@ -15,7 +15,7 @@ struct lowpan_create_arg {
struct lowpan_frag_queue {
struct inet_frag_queue q;
- __be16 tag;
+ u16 tag;
u16 d_size;
struct ieee802154_addr saddr;
struct ieee802154_addr daddr;
diff --git a/net/ieee802154/sysfs.c b/net/ieee802154/sysfs.c
new file mode 100644
index 0000000..8819998
--- /dev/null
+++ b/net/ieee802154/sysfs.c
@@ -0,0 +1,104 @@
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Authors:
+ * Alexander Aring <aar@pengutronix.de>
+ *
+ * Based on: net/wireless/sysfs.c
+ */
+
+#include <linux/device.h>
+
+#include <net/cfg802154.h>
+
+#include "core.h"
+#include "sysfs.h"
+
+static inline struct cfg802154_registered_device *
+dev_to_rdev(struct device *dev)
+{
+ return container_of(dev, struct cfg802154_registered_device,
+ wpan_phy.dev);
+}
+
+#define MASTER_SHOW_COMPLEX(name, format_string, args...) \
+static ssize_t name ## _show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev); \
+ int ret; \
+ \
+ mutex_lock(&phy->pib_lock); \
+ ret = snprintf(buf, PAGE_SIZE, format_string "\n", args); \
+ mutex_unlock(&phy->pib_lock); \
+ return ret; \
+} \
+static DEVICE_ATTR_RO(name)
+
+#define MASTER_SHOW(field, format_string) \
+ MASTER_SHOW_COMPLEX(field, format_string, phy->field)
+
+MASTER_SHOW(current_channel, "%d");
+MASTER_SHOW(current_page, "%d");
+MASTER_SHOW(transmit_power, "%d +- 1 dB");
+MASTER_SHOW(cca_mode, "%d");
+
+static ssize_t channels_supported_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev);
+ int ret;
+ int i, len = 0;
+
+ mutex_lock(&phy->pib_lock);
+ for (i = 0; i < 32; i++) {
+ ret = snprintf(buf + len, PAGE_SIZE - len,
+ "%#09x\n", phy->channels_supported[i]);
+ if (ret < 0)
+ break;
+ len += ret;
+ }
+ mutex_unlock(&phy->pib_lock);
+ return len;
+}
+static DEVICE_ATTR_RO(channels_supported);
+
+static void wpan_phy_release(struct device *dev)
+{
+ struct cfg802154_registered_device *rdev = dev_to_rdev(dev);
+
+ cfg802154_dev_free(rdev);
+}
+
+static struct attribute *pmib_attrs[] = {
+ &dev_attr_current_channel.attr,
+ &dev_attr_current_page.attr,
+ &dev_attr_channels_supported.attr,
+ &dev_attr_transmit_power.attr,
+ &dev_attr_cca_mode.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(pmib);
+
+struct class wpan_phy_class = {
+ .name = "ieee802154",
+ .dev_release = wpan_phy_release,
+ .dev_groups = pmib_groups,
+};
+
+int wpan_phy_sysfs_init(void)
+{
+ return class_register(&wpan_phy_class);
+}
+
+void wpan_phy_sysfs_exit(void)
+{
+ class_unregister(&wpan_phy_class);
+}
diff --git a/net/ieee802154/sysfs.h b/net/ieee802154/sysfs.h
new file mode 100644
index 0000000..aa42e39
--- /dev/null
+++ b/net/ieee802154/sysfs.h
@@ -0,0 +1,9 @@
+#ifndef __IEEE802154_SYSFS_H
+#define __IEEE802154_SYSFS_H
+
+int wpan_phy_sysfs_init(void);
+void wpan_phy_sysfs_exit(void);
+
+extern struct class wpan_phy_class;
+
+#endif /* __IEEE802154_SYSFS_H */
diff --git a/net/ieee802154/wpan-class.c b/net/ieee802154/wpan-class.c
deleted file mode 100644
index 4955e0f..0000000
--- a/net/ieee802154/wpan-class.c
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Copyright (C) 2007, 2008, 2009 Siemens AG
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-#include <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/device.h>
-
-#include <net/wpan-phy.h>
-
-#include "ieee802154.h"
-
-#define MASTER_SHOW_COMPLEX(name, format_string, args...) \
-static ssize_t name ## _show(struct device *dev, \
- struct device_attribute *attr, char *buf) \
-{ \
- struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev); \
- int ret; \
- \
- mutex_lock(&phy->pib_lock); \
- ret = snprintf(buf, PAGE_SIZE, format_string "\n", args); \
- mutex_unlock(&phy->pib_lock); \
- return ret; \
-} \
-static DEVICE_ATTR_RO(name);
-
-#define MASTER_SHOW(field, format_string) \
- MASTER_SHOW_COMPLEX(field, format_string, phy->field)
-
-MASTER_SHOW(current_channel, "%d");
-MASTER_SHOW(current_page, "%d");
-MASTER_SHOW(transmit_power, "%d +- 1 dB");
-MASTER_SHOW(cca_mode, "%d");
-
-static ssize_t channels_supported_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev);
- int ret;
- int i, len = 0;
-
- mutex_lock(&phy->pib_lock);
- for (i = 0; i < 32; i++) {
- ret = snprintf(buf + len, PAGE_SIZE - len,
- "%#09x\n", phy->channels_supported[i]);
- if (ret < 0)
- break;
- len += ret;
- }
- mutex_unlock(&phy->pib_lock);
- return len;
-}
-static DEVICE_ATTR_RO(channels_supported);
-
-static struct attribute *pmib_attrs[] = {
- &dev_attr_current_channel.attr,
- &dev_attr_current_page.attr,
- &dev_attr_channels_supported.attr,
- &dev_attr_transmit_power.attr,
- &dev_attr_cca_mode.attr,
- NULL,
-};
-ATTRIBUTE_GROUPS(pmib);
-
-static void wpan_phy_release(struct device *d)
-{
- struct wpan_phy *phy = container_of(d, struct wpan_phy, dev);
-
- kfree(phy);
-}
-
-static struct class wpan_phy_class = {
- .name = "ieee802154",
- .dev_release = wpan_phy_release,
- .dev_groups = pmib_groups,
-};
-
-static DEFINE_MUTEX(wpan_phy_mutex);
-static int wpan_phy_idx;
-
-static int wpan_phy_match(struct device *dev, const void *data)
-{
- return !strcmp(dev_name(dev), (const char *)data);
-}
-
-struct wpan_phy *wpan_phy_find(const char *str)
-{
- struct device *dev;
-
- if (WARN_ON(!str))
- return NULL;
-
- dev = class_find_device(&wpan_phy_class, NULL, str, wpan_phy_match);
- if (!dev)
- return NULL;
-
- return container_of(dev, struct wpan_phy, dev);
-}
-EXPORT_SYMBOL(wpan_phy_find);
-
-struct wpan_phy_iter_data {
- int (*fn)(struct wpan_phy *phy, void *data);
- void *data;
-};
-
-static int wpan_phy_iter(struct device *dev, void *_data)
-{
- struct wpan_phy_iter_data *wpid = _data;
- struct wpan_phy *phy = container_of(dev, struct wpan_phy, dev);
-
- return wpid->fn(phy, wpid->data);
-}
-
-int wpan_phy_for_each(int (*fn)(struct wpan_phy *phy, void *data),
- void *data)
-{
- struct wpan_phy_iter_data wpid = {
- .fn = fn,
- .data = data,
- };
-
- return class_for_each_device(&wpan_phy_class, NULL,
- &wpid, wpan_phy_iter);
-}
-EXPORT_SYMBOL(wpan_phy_for_each);
-
-static int wpan_phy_idx_valid(int idx)
-{
- return idx >= 0;
-}
-
-struct wpan_phy *wpan_phy_alloc(size_t priv_size)
-{
- struct wpan_phy *phy = kzalloc(sizeof(*phy) + priv_size,
- GFP_KERNEL);
-
- if (!phy)
- goto out;
- mutex_lock(&wpan_phy_mutex);
- phy->idx = wpan_phy_idx++;
- if (unlikely(!wpan_phy_idx_valid(phy->idx))) {
- wpan_phy_idx--;
- mutex_unlock(&wpan_phy_mutex);
- kfree(phy);
- goto out;
- }
- mutex_unlock(&wpan_phy_mutex);
-
- mutex_init(&phy->pib_lock);
-
- device_initialize(&phy->dev);
- dev_set_name(&phy->dev, "wpan-phy%d", phy->idx);
-
- phy->dev.class = &wpan_phy_class;
-
- phy->current_channel = -1; /* not initialised */
- phy->current_page = 0; /* for compatibility */
-
- return phy;
-
-out:
- return NULL;
-}
-EXPORT_SYMBOL(wpan_phy_alloc);
-
-int wpan_phy_register(struct wpan_phy *phy)
-{
- return device_add(&phy->dev);
-}
-EXPORT_SYMBOL(wpan_phy_register);
-
-void wpan_phy_unregister(struct wpan_phy *phy)
-{
- device_del(&phy->dev);
-}
-EXPORT_SYMBOL(wpan_phy_unregister);
-
-void wpan_phy_free(struct wpan_phy *phy)
-{
- put_device(&phy->dev);
-}
-EXPORT_SYMBOL(wpan_phy_free);
-
-static int __init wpan_phy_class_init(void)
-{
- int rc;
-
- rc = class_register(&wpan_phy_class);
- if (rc)
- goto err;
-
- rc = ieee802154_nl_init();
- if (rc)
- goto err_nl;
-
- return 0;
-err_nl:
- class_unregister(&wpan_phy_class);
-err:
- return rc;
-}
-subsys_initcall(wpan_phy_class_init);
-
-static void __exit wpan_phy_class_exit(void)
-{
- ieee802154_nl_exit();
- class_unregister(&wpan_phy_class);
-}
-module_exit(wpan_phy_class_exit);
-
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("IEEE 802.15.4 configuration interface");
-MODULE_AUTHOR("Dmitry Eremin-Solenikov");
-
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index 5b6efb3..f99f41b 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -537,7 +537,7 @@ int fib_nh_match(struct fib_config *cfg, struct fib_info *fi)
return 1;
attrlen = rtnh_attrlen(rtnh);
- if (attrlen < 0) {
+ if (attrlen > 0) {
struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
nla = nla_find(attrs, attrlen, RTA_GATEWAY);
diff --git a/net/ipv4/fou.c b/net/ipv4/fou.c
index efa70ad..32e7892 100644
--- a/net/ipv4/fou.c
+++ b/net/ipv4/fou.c
@@ -87,6 +87,9 @@ static int gue_udp_recv(struct sock *sk, struct sk_buff *skb)
if (!pskb_may_pull(skb, len))
goto drop;
+ uh = udp_hdr(skb);
+ guehdr = (struct guehdr *)&uh[1];
+
if (guehdr->version != 0)
goto drop;
diff --git a/net/ipv4/gre_offload.c b/net/ipv4/gre_offload.c
index a777295..ccda096 100644
--- a/net/ipv4/gre_offload.c
+++ b/net/ipv4/gre_offload.c
@@ -55,13 +55,13 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb,
if (csum)
skb->encap_hdr_csum = 1;
- if (unlikely(!pskb_may_pull(skb, ghl)))
- goto out;
-
/* setup inner skb. */
skb->protocol = greh->protocol;
skb->encapsulation = 0;
+ if (unlikely(!pskb_may_pull(skb, ghl)))
+ goto out;
+
__skb_pull(skb, ghl);
skb_reset_mac_header(skb);
skb_set_network_header(skb, skb_inner_network_offset(skb));
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index e35b712..88e5ef2 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -1535,6 +1535,7 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb,
struct sk_buff *nskb;
struct sock *sk;
struct inet_sock *inet;
+ int err;
if (__ip_options_echo(&replyopts.opt.opt, skb, sopt))
return;
@@ -1574,8 +1575,13 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb,
sock_net_set(sk, net);
__skb_queue_head_init(&sk->sk_write_queue);
sk->sk_sndbuf = sysctl_wmem_default;
- ip_append_data(sk, &fl4, ip_reply_glue_bits, arg->iov->iov_base, len, 0,
- &ipc, &rt, MSG_DONTWAIT);
+ err = ip_append_data(sk, &fl4, ip_reply_glue_bits, arg->iov->iov_base,
+ len, 0, &ipc, &rt, MSG_DONTWAIT);
+ if (unlikely(err)) {
+ ip_flush_pending_frames(sk);
+ goto out;
+ }
+
nskb = skb_peek(&sk->sk_write_queue);
if (nskb) {
if (arg->csumoffset >= 0)
@@ -1587,7 +1593,7 @@ void ip_send_unicast_reply(struct net *net, struct sk_buff *skb,
skb_set_queue_mapping(nskb, skb_get_queue_mapping(skb));
ip_push_pending_frames(sk, &fl4);
}
-
+out:
put_cpu_var(unicast_sock);
ip_rt_put(rt);
diff --git a/net/ipv4/ip_tunnel_core.c b/net/ipv4/ip_tunnel_core.c
index f4c987b..88c386c 100644
--- a/net/ipv4/ip_tunnel_core.c
+++ b/net/ipv4/ip_tunnel_core.c
@@ -91,11 +91,12 @@ int iptunnel_pull_header(struct sk_buff *skb, int hdr_len, __be16 inner_proto)
skb_pull_rcsum(skb, hdr_len);
if (inner_proto == htons(ETH_P_TEB)) {
- struct ethhdr *eh = (struct ethhdr *)skb->data;
+ struct ethhdr *eh;
if (unlikely(!pskb_may_pull(skb, ETH_HLEN)))
return -ENOMEM;
+ eh = (struct ethhdr *)skb->data;
if (likely(ntohs(eh->h_proto) >= ETH_P_802_3_MIN))
skb->protocol = eh->h_proto;
else
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 793c0bb..2d4ae46 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1311,7 +1311,7 @@ static bool rt_cache_route(struct fib_nh *nh, struct rtable *rt)
if (rt_is_input_route(rt)) {
p = (struct rtable **)&nh->nh_rth_input;
} else {
- p = (struct rtable **)__this_cpu_ptr(nh->nh_pcpu_rth_output);
+ p = (struct rtable **)raw_cpu_ptr(nh->nh_pcpu_rth_output);
}
orig = *p;
@@ -1939,7 +1939,7 @@ static struct rtable *__mkroute_output(const struct fib_result *res,
do_cache = false;
goto add;
}
- prth = __this_cpu_ptr(nh->nh_pcpu_rth_output);
+ prth = raw_cpu_ptr(nh->nh_pcpu_rth_output);
}
rth = rcu_dereference(*prth);
if (rt_cache_valid(rth)) {
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index 0431a8f..32b98d0 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -40,7 +40,7 @@ static u32 cookie_hash(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport,
net_get_random_once(syncookie_secret, sizeof(syncookie_secret));
- tmp = __get_cpu_var(ipv4_cookie_scratch);
+ tmp = this_cpu_ptr(ipv4_cookie_scratch);
memcpy(tmp + 4, syncookie_secret[c], sizeof(syncookie_secret[c]));
tmp[0] = (__force u32)saddr;
tmp[1] = (__force u32)daddr;
@@ -255,9 +255,9 @@ bool cookie_check_timestamp(struct tcp_options_received *tcp_opt,
}
EXPORT_SYMBOL(cookie_check_timestamp);
-struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
- struct ip_options *opt)
+struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb)
{
+ struct ip_options *opt = &TCP_SKB_CB(skb)->header.h4.opt;
struct tcp_options_received tcp_opt;
struct inet_request_sock *ireq;
struct tcp_request_sock *treq;
@@ -317,15 +317,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
/* We throwed the options of the initial SYN away, so we hope
* the ACK carries the same options again (see RFC1122 4.2.3.8)
*/
- if (opt && opt->optlen) {
- int opt_size = sizeof(struct ip_options_rcu) + opt->optlen;
-
- ireq->opt = kmalloc(opt_size, GFP_ATOMIC);
- if (ireq->opt != NULL && ip_options_echo(&ireq->opt->opt, skb)) {
- kfree(ireq->opt);
- ireq->opt = NULL;
- }
- }
+ ireq->opt = tcp_v4_save_options(skb);
if (security_inet_conn_request(sk, skb, req)) {
reqsk_free(req);
@@ -344,7 +336,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
flowi4_init_output(&fl4, sk->sk_bound_dev_if, ireq->ir_mark,
RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE, IPPROTO_TCP,
inet_sk_flowi_flags(sk),
- (opt && opt->srr) ? opt->faddr : ireq->ir_rmt_addr,
+ opt->srr ? opt->faddr : ireq->ir_rmt_addr,
ireq->ir_loc_addr, th->source, th->dest);
security_req_classify_flow(req, flowi4_to_flowi(&fl4));
rt = ip_route_output_key(sock_net(sk), &fl4);
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 461003d..1bec4e7 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -2941,7 +2941,7 @@ struct tcp_md5sig_pool *tcp_get_md5sig_pool(void)
local_bh_disable();
p = ACCESS_ONCE(tcp_md5sig_pool);
if (p)
- return __this_cpu_ptr(p);
+ return raw_cpu_ptr(p);
local_bh_enable();
return NULL;
@@ -3071,8 +3071,8 @@ void __init tcp_init(void)
BUILD_BUG_ON(sizeof(struct tcp_skb_cb) > sizeof(skb->cb));
- percpu_counter_init(&tcp_sockets_allocated, 0);
- percpu_counter_init(&tcp_orphan_count, 0);
+ percpu_counter_init(&tcp_sockets_allocated, 0, GFP_KERNEL);
+ percpu_counter_init(&tcp_orphan_count, 0, GFP_KERNEL);
tcp_hashinfo.bind_bucket_cachep =
kmem_cache_create("tcp_bind_bucket",
sizeof(struct inet_bind_bucket), 0,
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 00a4149..a12b455 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -68,6 +68,7 @@
#include <linux/module.h>
#include <linux/sysctl.h>
#include <linux/kernel.h>
+#include <linux/prefetch.h>
#include <net/dst.h>
#include <net/tcp.h>
#include <net/inet_common.h>
@@ -3029,6 +3030,21 @@ static u32 tcp_tso_acked(struct sock *sk, struct sk_buff *skb)
return packets_acked;
}
+static void tcp_ack_tstamp(struct sock *sk, struct sk_buff *skb,
+ u32 prior_snd_una)
+{
+ const struct skb_shared_info *shinfo;
+
+ /* Avoid cache line misses to get skb_shinfo() and shinfo->tx_flags */
+ if (likely(!(sk->sk_tsflags & SOF_TIMESTAMPING_TX_ACK)))
+ return;
+
+ shinfo = skb_shinfo(skb);
+ if ((shinfo->tx_flags & SKBTX_ACK_TSTAMP) &&
+ between(shinfo->tskey, prior_snd_una, tcp_sk(sk)->snd_una - 1))
+ __skb_tstamp_tx(skb, NULL, sk, SCM_TSTAMP_ACK);
+}
+
/* Remove acknowledged frames from the retransmission queue. If our packet
* is before the ack sequence we can discard it as it's confirmed to have
* arrived at the other end.
@@ -3052,14 +3068,11 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
first_ackt.v64 = 0;
while ((skb = tcp_write_queue_head(sk)) && skb != tcp_send_head(sk)) {
- struct skb_shared_info *shinfo = skb_shinfo(skb);
struct tcp_skb_cb *scb = TCP_SKB_CB(skb);
u8 sacked = scb->sacked;
u32 acked_pcount;
- if (unlikely(shinfo->tx_flags & SKBTX_ACK_TSTAMP) &&
- between(shinfo->tskey, prior_snd_una, tp->snd_una - 1))
- __skb_tstamp_tx(skb, NULL, sk, SCM_TSTAMP_ACK);
+ tcp_ack_tstamp(sk, skb, prior_snd_una);
/* Determine how many packets and what bytes were acked, tso and else */
if (after(scb->end_seq, tp->snd_una)) {
@@ -3073,10 +3086,12 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
fully_acked = false;
} else {
+ /* Speedup tcp_unlink_write_queue() and next loop */
+ prefetchw(skb->next);
acked_pcount = tcp_skb_pcount(skb);
}
- if (sacked & TCPCB_RETRANS) {
+ if (unlikely(sacked & TCPCB_RETRANS)) {
if (sacked & TCPCB_SACKED_RETRANS)
tp->retrans_out -= acked_pcount;
flag |= FLAG_RETRANS_DATA_ACKED;
@@ -3107,7 +3122,7 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
* connection startup slow start one packet too
* quickly. This is severely frowned upon behavior.
*/
- if (!(scb->tcp_flags & TCPHDR_SYN)) {
+ if (likely(!(scb->tcp_flags & TCPHDR_SYN))) {
flag |= FLAG_DATA_ACKED;
} else {
flag |= FLAG_SYN_ACKED;
@@ -3119,9 +3134,9 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
tcp_unlink_write_queue(skb, sk);
sk_wmem_free_skb(sk, skb);
- if (skb == tp->retransmit_skb_hint)
+ if (unlikely(skb == tp->retransmit_skb_hint))
tp->retransmit_skb_hint = NULL;
- if (skb == tp->lost_skb_hint)
+ if (unlikely(skb == tp->lost_skb_hint))
tp->lost_skb_hint = NULL;
}
@@ -3132,7 +3147,7 @@ static int tcp_clean_rtx_queue(struct sock *sk, int prior_fackets,
flag |= FLAG_SACK_RENEGING;
skb_mstamp_get(&now);
- if (first_ackt.v64) {
+ if (likely(first_ackt.v64)) {
seq_rtt_us = skb_mstamp_us_delta(&now, &first_ackt);
ca_seq_rtt_us = skb_mstamp_us_delta(&now, &last_ackt);
}
@@ -3394,6 +3409,9 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag)
int acked = 0; /* Number of packets newly acked */
long sack_rtt_us = -1L;
+ /* We very likely will need to access write queue head. */
+ prefetchw(sk->sk_write_queue.next);
+
/* If the ack is older than previous acks
* then we can probably ignore it.
*/
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 552e87e..94d1a77 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -880,26 +880,6 @@ bool tcp_syn_flood_action(struct sock *sk,
}
EXPORT_SYMBOL(tcp_syn_flood_action);
-/*
- * Save and compile IPv4 options into the request_sock if needed.
- */
-static struct ip_options_rcu *tcp_v4_save_options(struct sk_buff *skb)
-{
- const struct ip_options *opt = &TCP_SKB_CB(skb)->header.h4.opt;
- struct ip_options_rcu *dopt = NULL;
-
- if (opt && opt->optlen) {
- int opt_size = sizeof(*dopt) + opt->optlen;
-
- dopt = kmalloc(opt_size, GFP_ATOMIC);
- if (dopt && __ip_options_echo(&dopt->opt, skb, opt)) {
- kfree(dopt);
- dopt = NULL;
- }
- }
- return dopt;
-}
-
#ifdef CONFIG_TCP_MD5SIG
/*
* RFC2385 MD5 checksumming requires a mapping of
@@ -1428,7 +1408,7 @@ static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb)
#ifdef CONFIG_SYN_COOKIES
if (!th->syn)
- sk = cookie_v4_check(sk, skb, &TCP_SKB_CB(skb)->header.h4.opt);
+ sk = cookie_v4_check(sk, skb);
#endif
return sk;
}
diff --git a/net/ipv4/tcp_memcontrol.c b/net/ipv4/tcp_memcontrol.c
index 3af5226..1d19135 100644
--- a/net/ipv4/tcp_memcontrol.c
+++ b/net/ipv4/tcp_memcontrol.c
@@ -32,7 +32,7 @@ int tcp_init_cgroup(struct mem_cgroup *memcg, struct cgroup_subsys *ss)
res_parent = &parent_cg->memory_allocated;
res_counter_init(&cg_proto->memory_allocated, res_parent);
- percpu_counter_init(&cg_proto->sockets_allocated, 0);
+ percpu_counter_init(&cg_proto->sockets_allocated, 0, GFP_KERNEL);
return 0;
}
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 8d4eac7..3af2129 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -839,26 +839,38 @@ void tcp_wfree(struct sk_buff *skb)
{
struct sock *sk = skb->sk;
struct tcp_sock *tp = tcp_sk(sk);
+ int wmem;
+
+ /* Keep one reference on sk_wmem_alloc.
+ * Will be released by sk_free() from here or tcp_tasklet_func()
+ */
+ wmem = atomic_sub_return(skb->truesize - 1, &sk->sk_wmem_alloc);
+
+ /* If this softirq is serviced by ksoftirqd, we are likely under stress.
+ * Wait until our queues (qdisc + devices) are drained.
+ * This gives :
+ * - less callbacks to tcp_write_xmit(), reducing stress (batches)
+ * - chance for incoming ACK (processed by another cpu maybe)
+ * to migrate this flow (skb->ooo_okay will be eventually set)
+ */
+ if (wmem >= SKB_TRUESIZE(1) && this_cpu_ksoftirqd() == current)
+ goto out;
if (test_and_clear_bit(TSQ_THROTTLED, &tp->tsq_flags) &&
!test_and_set_bit(TSQ_QUEUED, &tp->tsq_flags)) {
unsigned long flags;
struct tsq_tasklet *tsq;
- /* Keep a ref on socket.
- * This last ref will be released in tcp_tasklet_func()
- */
- atomic_sub(skb->truesize - 1, &sk->sk_wmem_alloc);
-
/* queue this socket to tasklet queue */
local_irq_save(flags);
- tsq = &__get_cpu_var(tsq_tasklet);
+ tsq = this_cpu_ptr(&tsq_tasklet);
list_add(&tp->tsq_node, &tsq->head);
tasklet_schedule(&tsq->tasklet);
local_irq_restore(flags);
- } else {
- sock_wfree(skb);
+ return;
}
+out:
+ sk_free(sk);
}
/* This routine actually transmits TCP packets queued in by
@@ -914,9 +926,13 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
tcp_ca_event(sk, CA_EVENT_TX_START);
/* if no packet is in qdisc/device queue, then allow XPS to select
- * another queue.
+ * another queue. We can be called from tcp_tsq_handler()
+ * which holds one reference to sk_wmem_alloc.
+ *
+ * TODO: Ideally, in-flight pure ACK packets should not matter here.
+ * One way to get this would be to set skb->truesize = 2 on them.
*/
- skb->ooo_okay = sk_wmem_alloc_get(sk) == 0;
+ skb->ooo_okay = sk_wmem_alloc_get(sk) < SKB_TRUESIZE(1);
skb_push(skb, tcp_header_size);
skb_reset_transport_header(skb);
diff --git a/net/ipv6/anycast.c b/net/ipv6/anycast.c
index f5e319a..baf2742 100644
--- a/net/ipv6/anycast.c
+++ b/net/ipv6/anycast.c
@@ -235,7 +235,6 @@ static struct ifacaddr6 *aca_alloc(struct rt6_info *rt,
/* aca_tstamp should be updated upon changes */
aca->aca_cstamp = aca->aca_tstamp = jiffies;
atomic_set(&aca->aca_refcnt, 1);
- spin_lock_init(&aca->aca_lock);
return aca;
}
diff --git a/net/ipv6/ip6_offload.c b/net/ipv6/ip6_offload.c
index 9034f76..91014d3 100644
--- a/net/ipv6/ip6_offload.c
+++ b/net/ipv6/ip6_offload.c
@@ -46,6 +46,7 @@ static int ipv6_gso_pull_exthdrs(struct sk_buff *skb, int proto)
if (unlikely(!pskb_may_pull(skb, len)))
break;
+ opth = (void *)skb->data;
proto = opth->nexthdr;
__skb_pull(skb, len);
}
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index 6eab37c..58e5b47 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -485,11 +485,11 @@ static void ipip6_tunnel_uninit(struct net_device *dev)
*/
static int ipip6_err_gen_icmpv6_unreach(struct sk_buff *skb)
{
- const struct iphdr *iph = (const struct iphdr *) skb->data;
+ int ihl = ((const struct iphdr *)skb->data)->ihl*4;
struct rt6_info *rt;
struct sk_buff *skb2;
- if (!pskb_may_pull(skb, iph->ihl * 4 + sizeof(struct ipv6hdr) + 8))
+ if (!pskb_may_pull(skb, ihl + sizeof(struct ipv6hdr) + 8))
return 1;
skb2 = skb_clone(skb, GFP_ATOMIC);
@@ -498,7 +498,7 @@ static int ipip6_err_gen_icmpv6_unreach(struct sk_buff *skb)
return 1;
skb_dst_drop(skb2);
- skb_pull(skb2, iph->ihl * 4);
+ skb_pull(skb2, ihl);
skb_reset_network_header(skb2);
rt = rt6_lookup(dev_net(skb->dev), &ipv6_hdr(skb2)->saddr, NULL, 0, 0);
diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c
index 9a2838e..2f25cb6 100644
--- a/net/ipv6/syncookies.c
+++ b/net/ipv6/syncookies.c
@@ -67,7 +67,7 @@ static u32 cookie_hash(const struct in6_addr *saddr, const struct in6_addr *dadd
net_get_random_once(syncookie6_secret, sizeof(syncookie6_secret));
- tmp = __get_cpu_var(ipv6_cookie_scratch);
+ tmp = this_cpu_ptr(ipv6_cookie_scratch);
/*
* we have 320 bits of information to hash, copy in the remaining
@@ -214,7 +214,7 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
/* So that link locals have meaning */
if (!sk->sk_bound_dev_if &&
ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL)
- ireq->ir_iif = inet6_iif(skb);
+ ireq->ir_iif = tcp_v6_iif(skb);
ireq->ir_mark = inet_request_mark(sk, skb);
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index cf2e45a..8314955 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -424,6 +424,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
if (sock_owned_by_user(sk))
goto out;
+ /* Note : We use inet6_iif() here, not tcp_v6_iif() */
req = inet6_csk_search_req(sk, &prev, th->dest, &hdr->daddr,
&hdr->saddr, inet6_iif(skb));
if (!req)
@@ -738,7 +739,7 @@ static void tcp_v6_init_req(struct request_sock *req, struct sock *sk,
/* So that link locals have meaning */
if (!sk->sk_bound_dev_if &&
ipv6_addr_type(&ireq->ir_v6_rmt_addr) & IPV6_ADDR_LINKLOCAL)
- ireq->ir_iif = inet6_iif(skb);
+ ireq->ir_iif = tcp_v6_iif(skb);
if (!TCP_SKB_CB(skb)->tcp_tw_isn &&
(ipv6_opt_accepted(sk, skb, &TCP_SKB_CB(skb)->header.h6) ||
@@ -860,7 +861,7 @@ static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win,
fl6.flowi6_proto = IPPROTO_TCP;
if (rt6_need_strict(&fl6.daddr) && !oif)
- fl6.flowi6_oif = inet6_iif(skb);
+ fl6.flowi6_oif = tcp_v6_iif(skb);
else
fl6.flowi6_oif = oif;
fl6.flowi6_mark = IP6_REPLY_MARK(net, skb->mark);
@@ -918,7 +919,7 @@ static void tcp_v6_send_reset(struct sock *sk, struct sk_buff *skb)
sk1 = inet6_lookup_listener(dev_net(skb_dst(skb)->dev),
&tcp_hashinfo, &ipv6h->saddr,
th->source, &ipv6h->daddr,
- ntohs(th->source), inet6_iif(skb));
+ ntohs(th->source), tcp_v6_iif(skb));
if (!sk1)
return;
@@ -1000,13 +1001,14 @@ static struct sock *tcp_v6_hnd_req(struct sock *sk, struct sk_buff *skb)
/* Find possible connection requests. */
req = inet6_csk_search_req(sk, &prev, th->source,
&ipv6_hdr(skb)->saddr,
- &ipv6_hdr(skb)->daddr, inet6_iif(skb));
+ &ipv6_hdr(skb)->daddr, tcp_v6_iif(skb));
if (req)
return tcp_check_req(sk, skb, req, prev, false);
nsk = __inet6_lookup_established(sock_net(sk), &tcp_hashinfo,
- &ipv6_hdr(skb)->saddr, th->source,
- &ipv6_hdr(skb)->daddr, ntohs(th->dest), inet6_iif(skb));
+ &ipv6_hdr(skb)->saddr, th->source,
+ &ipv6_hdr(skb)->daddr, ntohs(th->dest),
+ tcp_v6_iif(skb));
if (nsk) {
if (nsk->sk_state != TCP_TIME_WAIT) {
@@ -1090,7 +1092,7 @@ static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
newnp->ipv6_fl_list = NULL;
newnp->pktoptions = NULL;
newnp->opt = NULL;
- newnp->mcast_oif = inet6_iif(skb);
+ newnp->mcast_oif = tcp_v6_iif(skb);
newnp->mcast_hops = ipv6_hdr(skb)->hop_limit;
newnp->rcv_flowinfo = ip6_flowinfo(ipv6_hdr(skb));
if (np->repflow)
@@ -1174,7 +1176,7 @@ static struct sock *tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
skb_set_owner_r(newnp->pktoptions, newsk);
}
newnp->opt = NULL;
- newnp->mcast_oif = inet6_iif(skb);
+ newnp->mcast_oif = tcp_v6_iif(skb);
newnp->mcast_hops = ipv6_hdr(skb)->hop_limit;
newnp->rcv_flowinfo = ip6_flowinfo(ipv6_hdr(skb));
if (np->repflow)
@@ -1360,7 +1362,7 @@ ipv6_pktoptions:
if (TCP_SKB_CB(opt_skb)->end_seq == tp->rcv_nxt &&
!((1 << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN))) {
if (np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo)
- np->mcast_oif = inet6_iif(opt_skb);
+ np->mcast_oif = tcp_v6_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.rxflow || np->rxopt.bits.rxtclass)
@@ -1427,7 +1429,8 @@ static int tcp_v6_rcv(struct sk_buff *skb)
TCP_SKB_CB(skb)->ip_dsfield = ipv6_get_dsfield(hdr);
TCP_SKB_CB(skb)->sacked = 0;
- sk = __inet6_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest);
+ sk = __inet6_lookup_skb(&tcp_hashinfo, skb, th->source, th->dest,
+ tcp_v6_iif(skb));
if (!sk)
goto no_tcp_socket;
@@ -1514,7 +1517,7 @@ do_time_wait:
sk2 = inet6_lookup_listener(dev_net(skb->dev), &tcp_hashinfo,
&ipv6_hdr(skb)->saddr, th->source,
&ipv6_hdr(skb)->daddr,
- ntohs(th->dest), inet6_iif(skb));
+ ntohs(th->dest), tcp_v6_iif(skb));
if (sk2 != NULL) {
struct inet_timewait_sock *tw = inet_twsk(sk);
inet_twsk_deschedule(tw, &tcp_death_row);
@@ -1553,6 +1556,7 @@ static void tcp_v6_early_demux(struct sk_buff *skb)
if (th->doff < sizeof(struct tcphdr) / 4)
return;
+ /* Note : We use inet6_iif() here, not tcp_v6_iif() */
sk = __inet6_lookup_established(dev_net(skb->dev), &tcp_hashinfo,
&hdr->saddr, th->source,
&hdr->daddr, ntohs(th->dest),
diff --git a/net/mac802154/Kconfig b/net/mac802154/Kconfig
index 1818a99..aa462b4 100644
--- a/net/mac802154/Kconfig
+++ b/net/mac802154/Kconfig
@@ -16,5 +16,5 @@ config MAC802154
been tested yet!
If you plan to use HardMAC IEEE 802.15.4 devices, you can
- say N here. Alternatievly you can say M to compile it as
+ say N here. Alternatively you can say M to compile it as
module.
diff --git a/net/mac802154/Makefile b/net/mac802154/Makefile
index 9723d6f..702d8b4 100644
--- a/net/mac802154/Makefile
+++ b/net/mac802154/Makefile
@@ -1,5 +1,5 @@
obj-$(CONFIG_MAC802154) += mac802154.o
-mac802154-objs := ieee802154_dev.o rx.o tx.o mac_cmd.o mib.o \
- monitor.o wpan.o llsec.o
+mac802154-objs := main.o rx.o tx.o mac_cmd.o mib.o \
+ iface.o llsec.o util.o cfg.o
ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/mac802154/cfg.c b/net/mac802154/cfg.c
new file mode 100644
index 0000000..d2c4e8f8
--- /dev/null
+++ b/net/mac802154/cfg.c
@@ -0,0 +1,47 @@
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Authors:
+ * Alexander Aring <aar@pengutronix.de>
+ *
+ * Based on: net/mac80211/cfg.c
+ */
+
+#include <net/rtnetlink.h>
+#include <net/cfg802154.h>
+
+#include "ieee802154_i.h"
+#include "cfg.h"
+
+static struct net_device *
+ieee802154_add_iface_deprecated(struct wpan_phy *wpan_phy,
+ const char *name, int type)
+{
+ struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
+ struct net_device *dev;
+
+ rtnl_lock();
+ dev = ieee802154_if_add(local, name, NULL, type);
+ rtnl_unlock();
+
+ return dev;
+}
+
+static void ieee802154_del_iface_deprecated(struct wpan_phy *wpan_phy,
+ struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+
+ ieee802154_if_remove(sdata);
+}
+
+const struct cfg802154_ops mac802154_config_ops = {
+ .add_virtual_intf_deprecated = ieee802154_add_iface_deprecated,
+ .del_virtual_intf_deprecated = ieee802154_del_iface_deprecated,
+};
diff --git a/net/mac802154/cfg.h b/net/mac802154/cfg.h
new file mode 100644
index 0000000..e2718f9
--- /dev/null
+++ b/net/mac802154/cfg.h
@@ -0,0 +1,9 @@
+/* mac802154 configuration hooks for cfg802154
+ */
+
+#ifndef __CFG_H
+#define __CFG_H
+
+extern const struct cfg802154_ops mac802154_config_ops;
+
+#endif /* __CFG_H */
diff --git a/net/mac802154/driver-ops.h b/net/mac802154/driver-ops.h
new file mode 100644
index 0000000..dfd29ff
--- /dev/null
+++ b/net/mac802154/driver-ops.h
@@ -0,0 +1,226 @@
+#ifndef __MAC802154_DRVIER_OPS
+#define __MAC802154_DRIVER_OPS
+
+#include <linux/types.h>
+#include <linux/rtnetlink.h>
+
+#include <net/mac802154.h>
+
+#include "ieee802154_i.h"
+
+static inline int
+drv_xmit_async(struct ieee802154_local *local, struct sk_buff *skb)
+{
+ return local->ops->xmit_async(&local->hw, skb);
+}
+
+static inline int
+drv_xmit_sync(struct ieee802154_local *local, struct sk_buff *skb)
+{
+ /* don't allow other operations while sync xmit */
+ ASSERT_RTNL();
+
+ might_sleep();
+
+ return local->ops->xmit_sync(&local->hw, skb);
+}
+
+static inline int drv_start(struct ieee802154_local *local)
+{
+ might_sleep();
+
+ local->started = true;
+ smp_mb();
+
+ return local->ops->start(&local->hw);
+}
+
+static inline void drv_stop(struct ieee802154_local *local)
+{
+ might_sleep();
+
+ local->ops->stop(&local->hw);
+
+ /* sync away all work on the tasklet before clearing started */
+ tasklet_disable(&local->tasklet);
+ tasklet_enable(&local->tasklet);
+
+ barrier();
+
+ local->started = false;
+}
+
+static inline int drv_set_channel(struct ieee802154_local *local,
+ const u8 page, const u8 channel)
+{
+ might_sleep();
+
+ return local->ops->set_channel(&local->hw, page, channel);
+}
+
+static inline int drv_set_tx_power(struct ieee802154_local *local,
+ const s8 dbm)
+{
+ might_sleep();
+
+ if (!local->ops->set_txpower) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ return local->ops->set_txpower(&local->hw, dbm);
+}
+
+static inline int drv_set_cca_mode(struct ieee802154_local *local,
+ const u8 cca_mode)
+{
+ might_sleep();
+
+ if (!local->ops->set_cca_mode) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ return local->ops->set_cca_mode(&local->hw, cca_mode);
+}
+
+static inline int drv_set_lbt_mode(struct ieee802154_local *local,
+ const bool mode)
+{
+ might_sleep();
+
+ if (!local->ops->set_lbt) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ return local->ops->set_lbt(&local->hw, mode);
+}
+
+static inline int drv_set_cca_ed_level(struct ieee802154_local *local,
+ const s32 ed_level)
+{
+ might_sleep();
+
+ if (!local->ops->set_cca_ed_level) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ return local->ops->set_cca_ed_level(&local->hw, ed_level);
+}
+
+static inline int drv_set_pan_id(struct ieee802154_local *local,
+ const __le16 pan_id)
+{
+ struct ieee802154_hw_addr_filt filt;
+
+ might_sleep();
+
+ if (!local->ops->set_hw_addr_filt) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ filt.pan_id = pan_id;
+
+ return local->ops->set_hw_addr_filt(&local->hw, &filt,
+ IEEE802154_AFILT_PANID_CHANGED);
+}
+
+static inline int drv_set_extended_addr(struct ieee802154_local *local,
+ const __le64 extended_addr)
+{
+ struct ieee802154_hw_addr_filt filt;
+
+ might_sleep();
+
+ if (!local->ops->set_hw_addr_filt) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ filt.ieee_addr = extended_addr;
+
+ return local->ops->set_hw_addr_filt(&local->hw, &filt,
+ IEEE802154_AFILT_IEEEADDR_CHANGED);
+}
+
+static inline int drv_set_short_addr(struct ieee802154_local *local,
+ const __le16 short_addr)
+{
+ struct ieee802154_hw_addr_filt filt;
+
+ might_sleep();
+
+ if (!local->ops->set_hw_addr_filt) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ filt.short_addr = short_addr;
+
+ return local->ops->set_hw_addr_filt(&local->hw, &filt,
+ IEEE802154_AFILT_SADDR_CHANGED);
+}
+
+static inline int drv_set_pan_coord(struct ieee802154_local *local,
+ const bool is_coord)
+{
+ struct ieee802154_hw_addr_filt filt;
+
+ might_sleep();
+
+ if (!local->ops->set_hw_addr_filt) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ filt.pan_coord = is_coord;
+
+ return local->ops->set_hw_addr_filt(&local->hw, &filt,
+ IEEE802154_AFILT_PANC_CHANGED);
+}
+
+static inline int drv_set_csma_params(struct ieee802154_local *local,
+ u8 min_be, u8 max_be,
+ u8 max_csma_backoffs)
+{
+ might_sleep();
+
+ if (!local->ops->set_csma_params) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ return local->ops->set_csma_params(&local->hw, min_be, max_be,
+ max_csma_backoffs);
+}
+
+static inline int drv_set_max_frame_retries(struct ieee802154_local *local,
+ s8 max_frame_retries)
+{
+ might_sleep();
+
+ if (!local->ops->set_frame_retries) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ return local->ops->set_frame_retries(&local->hw, max_frame_retries);
+}
+
+static inline int drv_set_promiscuous_mode(struct ieee802154_local *local,
+ const bool on)
+{
+ might_sleep();
+
+ if (!local->ops->set_promiscuous_mode) {
+ WARN_ON(1);
+ return -EOPNOTSUPP;
+ }
+
+ return local->ops->set_promiscuous_mode(&local->hw, on);
+}
+
+#endif /* __MAC802154_DRVIER_OPS */
diff --git a/net/mac802154/ieee802154_dev.c b/net/mac802154/ieee802154_dev.c
deleted file mode 100644
index b36b2b9..0000000
--- a/net/mac802154/ieee802154_dev.c
+++ /dev/null
@@ -1,415 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Siemens AG
- *
- * Written by:
- * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
- *
- * Based on the code from 'linux-zigbee.sourceforge.net' project.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/netdevice.h>
-
-#include <net/netlink.h>
-#include <linux/nl802154.h>
-#include <net/mac802154.h>
-#include <net/ieee802154_netdev.h>
-#include <net/route.h>
-#include <net/wpan-phy.h>
-
-#include "mac802154.h"
-
-int mac802154_slave_open(struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct mac802154_sub_if_data *subif;
- struct mac802154_priv *ipriv = priv->hw;
- int res = 0;
-
- ASSERT_RTNL();
-
- if (priv->type == IEEE802154_DEV_WPAN) {
- mutex_lock(&priv->hw->slaves_mtx);
- list_for_each_entry(subif, &priv->hw->slaves, list) {
- if (subif != priv && subif->type == priv->type &&
- subif->running) {
- mutex_unlock(&priv->hw->slaves_mtx);
- return -EBUSY;
- }
- }
- mutex_unlock(&priv->hw->slaves_mtx);
- }
-
- mutex_lock(&priv->hw->slaves_mtx);
- priv->running = true;
- mutex_unlock(&priv->hw->slaves_mtx);
-
- if (ipriv->open_count++ == 0) {
- res = ipriv->ops->start(&ipriv->hw);
- WARN_ON(res);
- if (res)
- goto err;
- }
-
- if (ipriv->ops->ieee_addr) {
- __le64 addr = ieee802154_devaddr_from_raw(dev->dev_addr);
-
- res = ipriv->ops->ieee_addr(&ipriv->hw, addr);
- WARN_ON(res);
- if (res)
- goto err;
- mac802154_dev_set_ieee_addr(dev);
- }
-
- netif_start_queue(dev);
- return 0;
-err:
- priv->hw->open_count--;
-
- return res;
-}
-
-int mac802154_slave_close(struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct mac802154_priv *ipriv = priv->hw;
-
- ASSERT_RTNL();
-
- netif_stop_queue(dev);
-
- mutex_lock(&priv->hw->slaves_mtx);
- priv->running = false;
- mutex_unlock(&priv->hw->slaves_mtx);
-
- if (!--ipriv->open_count)
- ipriv->ops->stop(&ipriv->hw);
-
- return 0;
-}
-
-static int
-mac802154_netdev_register(struct wpan_phy *phy, struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv;
- struct mac802154_priv *ipriv;
- int err;
-
- ipriv = wpan_phy_priv(phy);
-
- priv = netdev_priv(dev);
- priv->dev = dev;
- priv->hw = ipriv;
-
- dev->needed_headroom = ipriv->hw.extra_tx_headroom;
-
- SET_NETDEV_DEV(dev, &ipriv->phy->dev);
-
- mutex_lock(&ipriv->slaves_mtx);
- if (!ipriv->running) {
- mutex_unlock(&ipriv->slaves_mtx);
- return -ENODEV;
- }
- mutex_unlock(&ipriv->slaves_mtx);
-
- err = register_netdev(dev);
- if (err < 0)
- return err;
-
- rtnl_lock();
- mutex_lock(&ipriv->slaves_mtx);
- list_add_tail_rcu(&priv->list, &ipriv->slaves);
- mutex_unlock(&ipriv->slaves_mtx);
- rtnl_unlock();
-
- return 0;
-}
-
-static void
-mac802154_del_iface(struct wpan_phy *phy, struct net_device *dev)
-{
- struct mac802154_sub_if_data *sdata;
-
- ASSERT_RTNL();
-
- sdata = netdev_priv(dev);
-
- BUG_ON(sdata->hw->phy != phy);
-
- mutex_lock(&sdata->hw->slaves_mtx);
- list_del_rcu(&sdata->list);
- mutex_unlock(&sdata->hw->slaves_mtx);
-
- synchronize_rcu();
- unregister_netdevice(sdata->dev);
-}
-
-static struct net_device *
-mac802154_add_iface(struct wpan_phy *phy, const char *name, int type)
-{
- struct net_device *dev;
- int err = -ENOMEM;
-
- switch (type) {
- case IEEE802154_DEV_MONITOR:
- dev = alloc_netdev(sizeof(struct mac802154_sub_if_data),
- name, NET_NAME_UNKNOWN,
- mac802154_monitor_setup);
- break;
- case IEEE802154_DEV_WPAN:
- dev = alloc_netdev(sizeof(struct mac802154_sub_if_data),
- name, NET_NAME_UNKNOWN,
- mac802154_wpan_setup);
- break;
- default:
- dev = NULL;
- err = -EINVAL;
- break;
- }
- if (!dev)
- goto err;
-
- err = mac802154_netdev_register(phy, dev);
- if (err)
- goto err_free;
-
- dev_hold(dev); /* we return an incremented device refcount */
- return dev;
-
-err_free:
- free_netdev(dev);
-err:
- return ERR_PTR(err);
-}
-
-static int mac802154_set_txpower(struct wpan_phy *phy, int db)
-{
- struct mac802154_priv *priv = wpan_phy_priv(phy);
-
- return priv->ops->set_txpower(&priv->hw, db);
-}
-
-static int mac802154_set_lbt(struct wpan_phy *phy, bool on)
-{
- struct mac802154_priv *priv = wpan_phy_priv(phy);
-
- return priv->ops->set_lbt(&priv->hw, on);
-}
-
-static int mac802154_set_cca_mode(struct wpan_phy *phy, u8 mode)
-{
- struct mac802154_priv *priv = wpan_phy_priv(phy);
-
- return priv->ops->set_cca_mode(&priv->hw, mode);
-}
-
-static int mac802154_set_cca_ed_level(struct wpan_phy *phy, s32 level)
-{
- struct mac802154_priv *priv = wpan_phy_priv(phy);
-
- return priv->ops->set_cca_ed_level(&priv->hw, level);
-}
-
-static int mac802154_set_csma_params(struct wpan_phy *phy, u8 min_be,
- u8 max_be, u8 retries)
-{
- struct mac802154_priv *priv = wpan_phy_priv(phy);
-
- return priv->ops->set_csma_params(&priv->hw, min_be, max_be, retries);
-}
-
-static int mac802154_set_frame_retries(struct wpan_phy *phy, s8 retries)
-{
- struct mac802154_priv *priv = wpan_phy_priv(phy);
-
- return priv->ops->set_frame_retries(&priv->hw, retries);
-}
-
-struct ieee802154_dev *
-ieee802154_alloc_device(size_t priv_data_len, struct ieee802154_ops *ops)
-{
- struct wpan_phy *phy;
- struct mac802154_priv *priv;
- size_t priv_size;
-
- if (!ops || !ops->xmit || !ops->ed || !ops->start ||
- !ops->stop || !ops->set_channel) {
- pr_err("undefined IEEE802.15.4 device operations\n");
- return NULL;
- }
-
- /* Ensure 32-byte alignment of our private data and hw private data.
- * We use the wpan_phy priv data for both our mac802154_priv and for
- * the driver's private data
- *
- * in memory it'll be like this:
- *
- * +-----------------------+
- * | struct wpan_phy |
- * +-----------------------+
- * | struct mac802154_priv |
- * +-----------------------+
- * | driver's private data |
- * +-----------------------+
- *
- * Due to ieee802154 layer isn't aware of driver and MAC structures,
- * so lets allign them here.
- */
-
- priv_size = ALIGN(sizeof(*priv), NETDEV_ALIGN) + priv_data_len;
-
- phy = wpan_phy_alloc(priv_size);
- if (!phy) {
- pr_err("failure to allocate master IEEE802.15.4 device\n");
- return NULL;
- }
-
- priv = wpan_phy_priv(phy);
- priv->phy = phy;
- priv->hw.phy = priv->phy;
- priv->hw.priv = (char *)priv + ALIGN(sizeof(*priv), NETDEV_ALIGN);
- priv->ops = ops;
-
- INIT_LIST_HEAD(&priv->slaves);
- mutex_init(&priv->slaves_mtx);
-
- return &priv->hw;
-}
-EXPORT_SYMBOL(ieee802154_alloc_device);
-
-void ieee802154_free_device(struct ieee802154_dev *hw)
-{
- struct mac802154_priv *priv = mac802154_to_priv(hw);
-
- BUG_ON(!list_empty(&priv->slaves));
-
- mutex_destroy(&priv->slaves_mtx);
-
- wpan_phy_free(priv->phy);
-}
-EXPORT_SYMBOL(ieee802154_free_device);
-
-int ieee802154_register_device(struct ieee802154_dev *dev)
-{
- struct mac802154_priv *priv = mac802154_to_priv(dev);
- int rc = -ENOSYS;
-
- if (dev->flags & IEEE802154_HW_TXPOWER) {
- if (!priv->ops->set_txpower)
- goto out;
-
- priv->phy->set_txpower = mac802154_set_txpower;
- }
-
- if (dev->flags & IEEE802154_HW_LBT) {
- if (!priv->ops->set_lbt)
- goto out;
-
- priv->phy->set_lbt = mac802154_set_lbt;
- }
-
- if (dev->flags & IEEE802154_HW_CCA_MODE) {
- if (!priv->ops->set_cca_mode)
- goto out;
-
- priv->phy->set_cca_mode = mac802154_set_cca_mode;
- }
-
- if (dev->flags & IEEE802154_HW_CCA_ED_LEVEL) {
- if (!priv->ops->set_cca_ed_level)
- goto out;
-
- priv->phy->set_cca_ed_level = mac802154_set_cca_ed_level;
- }
-
- if (dev->flags & IEEE802154_HW_CSMA_PARAMS) {
- if (!priv->ops->set_csma_params)
- goto out;
-
- priv->phy->set_csma_params = mac802154_set_csma_params;
- }
-
- if (dev->flags & IEEE802154_HW_FRAME_RETRIES) {
- if (!priv->ops->set_frame_retries)
- goto out;
-
- priv->phy->set_frame_retries = mac802154_set_frame_retries;
- }
-
- priv->dev_workqueue =
- create_singlethread_workqueue(wpan_phy_name(priv->phy));
- if (!priv->dev_workqueue) {
- rc = -ENOMEM;
- goto out;
- }
-
- wpan_phy_set_dev(priv->phy, priv->hw.parent);
-
- priv->phy->add_iface = mac802154_add_iface;
- priv->phy->del_iface = mac802154_del_iface;
-
- rc = wpan_phy_register(priv->phy);
- if (rc < 0)
- goto out_wq;
-
- rtnl_lock();
-
- mutex_lock(&priv->slaves_mtx);
- priv->running = MAC802154_DEVICE_RUN;
- mutex_unlock(&priv->slaves_mtx);
-
- rtnl_unlock();
-
- return 0;
-
-out_wq:
- destroy_workqueue(priv->dev_workqueue);
-out:
- return rc;
-}
-EXPORT_SYMBOL(ieee802154_register_device);
-
-void ieee802154_unregister_device(struct ieee802154_dev *dev)
-{
- struct mac802154_priv *priv = mac802154_to_priv(dev);
- struct mac802154_sub_if_data *sdata, *next;
-
- flush_workqueue(priv->dev_workqueue);
- destroy_workqueue(priv->dev_workqueue);
-
- rtnl_lock();
-
- mutex_lock(&priv->slaves_mtx);
- priv->running = MAC802154_DEVICE_STOPPED;
- mutex_unlock(&priv->slaves_mtx);
-
- list_for_each_entry_safe(sdata, next, &priv->slaves, list) {
- mutex_lock(&sdata->hw->slaves_mtx);
- list_del(&sdata->list);
- mutex_unlock(&sdata->hw->slaves_mtx);
-
- unregister_netdevice(sdata->dev);
- }
-
- rtnl_unlock();
-
- wpan_phy_unregister(priv->phy);
-}
-EXPORT_SYMBOL(ieee802154_unregister_device);
-
-MODULE_DESCRIPTION("IEEE 802.15.4 implementation");
-MODULE_LICENSE("GPL v2");
diff --git a/net/mac802154/mac802154.h b/net/mac802154/ieee802154_i.h
index 762a6f8..4acacea 100644
--- a/net/mac802154/mac802154.h
+++ b/net/mac802154/ieee802154_i.h
@@ -10,29 +10,26 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Written by:
* Pavel Smolenskiy <pavel.smolenskiy@gmail.com>
* Maxim Gorbachyov <maxim.gorbachev@siemens.com>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
* Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
*/
-#ifndef MAC802154_H
-#define MAC802154_H
+#ifndef __IEEE802154_I_H
+#define __IEEE802154_I_H
#include <linux/mutex.h>
+#include <net/cfg802154.h>
#include <net/mac802154.h>
#include <net/ieee802154_netdev.h>
#include "llsec.h"
/* mac802154 device private data */
-struct mac802154_priv {
- struct ieee802154_dev hw;
- struct ieee802154_ops *ops;
+struct ieee802154_local {
+ struct ieee802154_hw hw;
+ const struct ieee802154_ops *ops;
/* ieee802154 phy */
struct wpan_phy *phy;
@@ -46,23 +43,27 @@ struct mac802154_priv {
*
* So atomic readers can use any of this protection methods.
*/
- struct list_head slaves;
- struct mutex slaves_mtx;
+ struct list_head interfaces;
+ struct mutex iflist_mtx;
/* This one is used for scanning and other jobs not to be interfered
* with serial driver.
*/
- struct workqueue_struct *dev_workqueue;
+ struct workqueue_struct *workqueue;
- /* SoftMAC device is registered and running. One can add subinterfaces.
- * This flag should be modified under slaves_mtx and RTNL, so you can
- * read them using any of protection methods.
- */
- bool running;
+ bool started;
+
+ struct tasklet_struct tasklet;
+ struct sk_buff_head skb_queue;
+};
+
+enum {
+ IEEE802154_RX_MSG = 1,
};
-#define MAC802154_DEVICE_STOPPED 0x00
-#define MAC802154_DEVICE_RUN 0x01
+enum ieee802154_sdata_state_bits {
+ SDATA_STATE_RUNNING,
+};
/* Slave interface definition.
*
@@ -70,23 +71,23 @@ struct mac802154_priv {
* Each ieee802154 device/transceiver may have several slaves and able
* to be associated with several networks at the same time.
*/
-struct mac802154_sub_if_data {
+struct ieee802154_sub_if_data {
struct list_head list; /* the ieee802154_priv->slaves list */
- struct mac802154_priv *hw;
+ struct wpan_dev wpan_dev;
+
+ struct ieee802154_local *local;
struct net_device *dev;
- int type;
- bool running;
+ unsigned long state;
+ char name[IFNAMSIZ];
spinlock_t mib_lock;
__le16 pan_id;
__le16 short_addr;
__le64 extended_addr;
-
- u8 chan;
- u8 page;
+ bool promiscuous_mode;
struct ieee802154_mac_params mac_params;
@@ -101,41 +102,49 @@ struct mac802154_sub_if_data {
struct mutex sec_mtx;
struct mac802154_llsec sec;
+ /* must be last, dynamically sized area in this! */
+ struct ieee802154_vif vif;
};
-#define mac802154_to_priv(_hw) container_of(_hw, struct mac802154_priv, hw)
-
#define MAC802154_CHAN_NONE 0xff /* No channel is assigned */
+static inline struct ieee802154_local *
+hw_to_local(struct ieee802154_hw *hw)
+{
+ return container_of(hw, struct ieee802154_local, hw);
+}
+
+static inline struct ieee802154_sub_if_data *
+IEEE802154_DEV_TO_SUB_IF(const struct net_device *dev)
+{
+ return netdev_priv(dev);
+}
+
+static inline bool
+ieee802154_sdata_running(struct ieee802154_sub_if_data *sdata)
+{
+ return test_bit(SDATA_STATE_RUNNING, &sdata->state);
+}
+
extern struct ieee802154_reduced_mlme_ops mac802154_mlme_reduced;
extern struct ieee802154_mlme_ops mac802154_mlme_wpan;
-int mac802154_slave_open(struct net_device *dev);
-int mac802154_slave_close(struct net_device *dev);
-
-void mac802154_monitors_rx(struct mac802154_priv *priv, struct sk_buff *skb);
void mac802154_monitor_setup(struct net_device *dev);
+netdev_tx_t
+ieee802154_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev);
-void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb);
void mac802154_wpan_setup(struct net_device *dev);
-
-netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb,
- u8 page, u8 chan);
+netdev_tx_t
+ieee802154_subif_start_xmit(struct sk_buff *skb, struct net_device *dev);
/* MIB callbacks */
void mac802154_dev_set_short_addr(struct net_device *dev, __le16 val);
__le16 mac802154_dev_get_short_addr(const struct net_device *dev);
-void mac802154_dev_set_ieee_addr(struct net_device *dev);
__le16 mac802154_dev_get_pan_id(const struct net_device *dev);
void mac802154_dev_set_pan_id(struct net_device *dev, __le16 val);
void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan);
u8 mac802154_dev_get_dsn(const struct net_device *dev);
-int mac802154_set_mac_params(struct net_device *dev,
- const struct ieee802154_mac_params *params);
-void mac802154_get_mac_params(struct net_device *dev,
- struct ieee802154_mac_params *params);
-
int mac802154_get_params(struct net_device *dev,
struct ieee802154_llsec_params *params);
int mac802154_set_params(struct net_device *dev,
@@ -169,4 +178,11 @@ void mac802154_get_table(struct net_device *dev,
struct ieee802154_llsec_table **t);
void mac802154_unlock_table(struct net_device *dev);
-#endif /* MAC802154_H */
+struct net_device *
+mac802154_add_iface(struct wpan_phy *phy, const char *name, int type);
+void ieee802154_if_remove(struct ieee802154_sub_if_data *sdata);
+struct net_device *
+ieee802154_if_add(struct ieee802154_local *local, const char *name,
+ struct wpan_dev **new_wpan_dev, int type);
+
+#endif /* __IEEE802154_I_H */
diff --git a/net/mac802154/iface.c b/net/mac802154/iface.c
new file mode 100644
index 0000000..384f4bb
--- /dev/null
+++ b/net/mac802154/iface.c
@@ -0,0 +1,521 @@
+/*
+ * Copyright 2007-2012 Siemens AG
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Written by:
+ * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
+ * Sergey Lapin <slapin@ossfans.org>
+ * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
+ * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
+ */
+
+#include <linux/netdevice.h>
+#include <linux/module.h>
+#include <linux/if_arp.h>
+#include <linux/ieee802154.h>
+
+#include <net/rtnetlink.h>
+#include <linux/nl802154.h>
+#include <net/mac802154.h>
+#include <net/ieee802154_netdev.h>
+#include <net/cfg802154.h>
+
+#include "ieee802154_i.h"
+#include "driver-ops.h"
+
+static int mac802154_wpan_update_llsec(struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
+ int rc = 0;
+
+ if (ops->llsec) {
+ struct ieee802154_llsec_params params;
+ int changed = 0;
+
+ params.pan_id = sdata->pan_id;
+ changed |= IEEE802154_LLSEC_PARAM_PAN_ID;
+
+ params.hwaddr = sdata->extended_addr;
+ changed |= IEEE802154_LLSEC_PARAM_HWADDR;
+
+ rc = ops->llsec->set_params(dev, &params, changed);
+ }
+
+ return rc;
+}
+
+static int
+mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct sockaddr_ieee802154 *sa =
+ (struct sockaddr_ieee802154 *)&ifr->ifr_addr;
+ int err = -ENOIOCTLCMD;
+
+ spin_lock_bh(&sdata->mib_lock);
+
+ switch (cmd) {
+ case SIOCGIFADDR:
+ {
+ u16 pan_id, short_addr;
+
+ pan_id = le16_to_cpu(sdata->pan_id);
+ short_addr = le16_to_cpu(sdata->short_addr);
+ if (pan_id == IEEE802154_PANID_BROADCAST ||
+ short_addr == IEEE802154_ADDR_BROADCAST) {
+ err = -EADDRNOTAVAIL;
+ break;
+ }
+
+ sa->family = AF_IEEE802154;
+ sa->addr.addr_type = IEEE802154_ADDR_SHORT;
+ sa->addr.pan_id = pan_id;
+ sa->addr.short_addr = short_addr;
+
+ err = 0;
+ break;
+ }
+ case SIOCSIFADDR:
+ dev_warn(&dev->dev,
+ "Using DEBUGing ioctl SIOCSIFADDR isn't recommended!\n");
+ if (sa->family != AF_IEEE802154 ||
+ sa->addr.addr_type != IEEE802154_ADDR_SHORT ||
+ sa->addr.pan_id == IEEE802154_PANID_BROADCAST ||
+ sa->addr.short_addr == IEEE802154_ADDR_BROADCAST ||
+ sa->addr.short_addr == IEEE802154_ADDR_UNDEF) {
+ err = -EINVAL;
+ break;
+ }
+
+ sdata->pan_id = cpu_to_le16(sa->addr.pan_id);
+ sdata->short_addr = cpu_to_le16(sa->addr.short_addr);
+
+ err = mac802154_wpan_update_llsec(dev);
+ break;
+ }
+
+ spin_unlock_bh(&sdata->mib_lock);
+ return err;
+}
+
+static int mac802154_wpan_mac_addr(struct net_device *dev, void *p)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct sockaddr *addr = p;
+ __le64 extended_addr;
+
+ if (netif_running(dev))
+ return -EBUSY;
+
+ ieee802154_be64_to_le64(&extended_addr, addr->sa_data);
+ if (!ieee802154_is_valid_extended_addr(extended_addr))
+ return -EINVAL;
+
+ memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+ sdata->extended_addr = extended_addr;
+
+ return mac802154_wpan_update_llsec(dev);
+}
+
+static int mac802154_slave_open(struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct ieee802154_sub_if_data *subif;
+ struct ieee802154_local *local = sdata->local;
+ int res = 0;
+
+ ASSERT_RTNL();
+
+ if (sdata->vif.type == IEEE802154_DEV_WPAN) {
+ mutex_lock(&sdata->local->iflist_mtx);
+ list_for_each_entry(subif, &sdata->local->interfaces, list) {
+ if (subif != sdata &&
+ subif->vif.type == sdata->vif.type &&
+ ieee802154_sdata_running(subif)) {
+ mutex_unlock(&sdata->local->iflist_mtx);
+ return -EBUSY;
+ }
+ }
+ mutex_unlock(&sdata->local->iflist_mtx);
+ }
+
+ set_bit(SDATA_STATE_RUNNING, &sdata->state);
+
+ if (!local->open_count) {
+ res = drv_start(local);
+ WARN_ON(res);
+ if (res)
+ goto err;
+ }
+
+ local->open_count++;
+ netif_start_queue(dev);
+ return 0;
+err:
+ /* might already be clear but that doesn't matter */
+ clear_bit(SDATA_STATE_RUNNING, &sdata->state);
+
+ return res;
+}
+
+static int mac802154_wpan_open(struct net_device *dev)
+{
+ int rc;
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct ieee802154_local *local = sdata->local;
+ struct wpan_phy *phy = sdata->local->phy;
+
+ rc = mac802154_slave_open(dev);
+ if (rc < 0)
+ return rc;
+
+ mutex_lock(&phy->pib_lock);
+
+ if (local->hw.flags & IEEE802154_HW_PROMISCUOUS) {
+ rc = drv_set_promiscuous_mode(local, sdata->promiscuous_mode);
+ if (rc < 0)
+ goto out;
+ }
+
+ if (local->hw.flags & IEEE802154_HW_AFILT) {
+ rc = drv_set_pan_id(local, sdata->pan_id);
+ if (rc < 0)
+ goto out;
+
+ rc = drv_set_extended_addr(local, sdata->extended_addr);
+ if (rc < 0)
+ goto out;
+
+ rc = drv_set_short_addr(local, sdata->short_addr);
+ if (rc < 0)
+ goto out;
+ }
+
+ if (local->hw.flags & IEEE802154_HW_LBT) {
+ rc = drv_set_lbt_mode(local, sdata->mac_params.lbt);
+ if (rc < 0)
+ goto out;
+ }
+
+ if (local->hw.flags & IEEE802154_HW_CSMA_PARAMS) {
+ rc = drv_set_csma_params(local, sdata->mac_params.min_be,
+ sdata->mac_params.max_be,
+ sdata->mac_params.csma_retries);
+ if (rc < 0)
+ goto out;
+ }
+
+ if (local->hw.flags & IEEE802154_HW_FRAME_RETRIES) {
+ rc = drv_set_max_frame_retries(local,
+ sdata->mac_params.frame_retries);
+ if (rc < 0)
+ goto out;
+ }
+
+ mutex_unlock(&phy->pib_lock);
+ return 0;
+
+out:
+ mutex_unlock(&phy->pib_lock);
+ return rc;
+}
+
+static int mac802154_slave_close(struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct ieee802154_local *local = sdata->local;
+
+ ASSERT_RTNL();
+
+ netif_stop_queue(dev);
+ local->open_count--;
+
+ clear_bit(SDATA_STATE_RUNNING, &sdata->state);
+
+ if (!local->open_count)
+ drv_stop(local);
+
+ return 0;
+}
+
+static int mac802154_set_header_security(struct ieee802154_sub_if_data *sdata,
+ struct ieee802154_hdr *hdr,
+ const struct ieee802154_mac_cb *cb)
+{
+ struct ieee802154_llsec_params params;
+ u8 level;
+
+ mac802154_llsec_get_params(&sdata->sec, &params);
+
+ if (!params.enabled && cb->secen_override && cb->secen)
+ return -EINVAL;
+ if (!params.enabled ||
+ (cb->secen_override && !cb->secen) ||
+ !params.out_level)
+ return 0;
+ if (cb->seclevel_override && !cb->seclevel)
+ return -EINVAL;
+
+ level = cb->seclevel_override ? cb->seclevel : params.out_level;
+
+ hdr->fc.security_enabled = 1;
+ hdr->sec.level = level;
+ hdr->sec.key_id_mode = params.out_key.mode;
+ if (params.out_key.mode == IEEE802154_SCF_KEY_SHORT_INDEX)
+ hdr->sec.short_src = params.out_key.short_source;
+ else if (params.out_key.mode == IEEE802154_SCF_KEY_HW_INDEX)
+ hdr->sec.extended_src = params.out_key.extended_source;
+ hdr->sec.key_id = params.out_key.id;
+
+ return 0;
+}
+
+static int mac802154_header_create(struct sk_buff *skb,
+ struct net_device *dev,
+ unsigned short type,
+ const void *daddr,
+ const void *saddr,
+ unsigned len)
+{
+ struct ieee802154_hdr hdr;
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct ieee802154_mac_cb *cb = mac_cb(skb);
+ int hlen;
+
+ if (!daddr)
+ return -EINVAL;
+
+ memset(&hdr.fc, 0, sizeof(hdr.fc));
+ hdr.fc.type = cb->type;
+ hdr.fc.security_enabled = cb->secen;
+ hdr.fc.ack_request = cb->ackreq;
+ hdr.seq = ieee802154_mlme_ops(dev)->get_dsn(dev);
+
+ if (mac802154_set_header_security(sdata, &hdr, cb) < 0)
+ return -EINVAL;
+
+ if (!saddr) {
+ spin_lock_bh(&sdata->mib_lock);
+
+ if (sdata->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST) ||
+ sdata->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) ||
+ sdata->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) {
+ hdr.source.mode = IEEE802154_ADDR_LONG;
+ hdr.source.extended_addr = sdata->extended_addr;
+ } else {
+ hdr.source.mode = IEEE802154_ADDR_SHORT;
+ hdr.source.short_addr = sdata->short_addr;
+ }
+
+ hdr.source.pan_id = sdata->pan_id;
+
+ spin_unlock_bh(&sdata->mib_lock);
+ } else {
+ hdr.source = *(const struct ieee802154_addr *)saddr;
+ }
+
+ hdr.dest = *(const struct ieee802154_addr *)daddr;
+
+ hlen = ieee802154_hdr_push(skb, &hdr);
+ if (hlen < 0)
+ return -EINVAL;
+
+ skb_reset_mac_header(skb);
+ skb->mac_len = hlen;
+
+ if (len > ieee802154_max_payload(&hdr))
+ return -EMSGSIZE;
+
+ return hlen;
+}
+
+static int
+mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr)
+{
+ struct ieee802154_hdr hdr;
+ struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr;
+
+ if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) {
+ pr_debug("malformed packet\n");
+ return 0;
+ }
+
+ *addr = hdr.source;
+ return sizeof(*addr);
+}
+
+static struct header_ops mac802154_header_ops = {
+ .create = mac802154_header_create,
+ .parse = mac802154_header_parse,
+};
+
+static const struct net_device_ops mac802154_wpan_ops = {
+ .ndo_open = mac802154_wpan_open,
+ .ndo_stop = mac802154_slave_close,
+ .ndo_start_xmit = ieee802154_subif_start_xmit,
+ .ndo_do_ioctl = mac802154_wpan_ioctl,
+ .ndo_set_mac_address = mac802154_wpan_mac_addr,
+};
+
+static const struct net_device_ops mac802154_monitor_ops = {
+ .ndo_open = mac802154_wpan_open,
+ .ndo_stop = mac802154_slave_close,
+ .ndo_start_xmit = ieee802154_monitor_start_xmit,
+};
+
+static void mac802154_wpan_free(struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+
+ mac802154_llsec_destroy(&sdata->sec);
+
+ free_netdev(dev);
+}
+
+static void ieee802154_if_setup(struct net_device *dev)
+{
+ dev->addr_len = IEEE802154_EXTENDED_ADDR_LEN;
+ memset(dev->broadcast, 0xff, IEEE802154_EXTENDED_ADDR_LEN);
+
+ dev->hard_header_len = MAC802154_FRAME_HARD_HEADER_LEN;
+ dev->needed_tailroom = 2 + 16; /* FCS + MIC */
+ dev->mtu = IEEE802154_MTU;
+ dev->tx_queue_len = 300;
+ dev->flags = IFF_NOARP | IFF_BROADCAST;
+}
+
+static int
+ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata, int type)
+{
+ /* set some type-dependent values */
+ sdata->vif.type = type;
+
+ get_random_bytes(&sdata->bsn, 1);
+ get_random_bytes(&sdata->dsn, 1);
+
+ /* defaults per 802.15.4-2011 */
+ sdata->mac_params.min_be = 3;
+ sdata->mac_params.max_be = 5;
+ sdata->mac_params.csma_retries = 4;
+ /* for compatibility, actual default is 3 */
+ sdata->mac_params.frame_retries = -1;
+
+ ieee802154_be64_to_le64(&sdata->extended_addr, sdata->dev->dev_addr);
+ sdata->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
+ sdata->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
+
+ switch (type) {
+ case IEEE802154_DEV_WPAN:
+ sdata->dev->header_ops = &mac802154_header_ops;
+ sdata->dev->destructor = mac802154_wpan_free;
+ sdata->dev->netdev_ops = &mac802154_wpan_ops;
+ sdata->dev->ml_priv = &mac802154_mlme_wpan;
+ sdata->promiscuous_mode = false;
+
+ spin_lock_init(&sdata->mib_lock);
+ mutex_init(&sdata->sec_mtx);
+
+ mac802154_llsec_init(&sdata->sec);
+ break;
+ case IEEE802154_DEV_MONITOR:
+ sdata->dev->destructor = free_netdev;
+ sdata->dev->netdev_ops = &mac802154_monitor_ops;
+ sdata->promiscuous_mode = true;
+ break;
+ default:
+ BUG();
+ }
+
+ return 0;
+}
+
+struct net_device *
+ieee802154_if_add(struct ieee802154_local *local, const char *name,
+ struct wpan_dev **new_wpan_dev, int type)
+{
+ struct net_device *ndev = NULL;
+ struct ieee802154_sub_if_data *sdata = NULL;
+ int ret = -ENOMEM;
+
+ ASSERT_RTNL();
+
+ ndev = alloc_netdev(sizeof(*sdata) + local->hw.vif_data_size, name,
+ NET_NAME_UNKNOWN, ieee802154_if_setup);
+ if (!ndev)
+ return ERR_PTR(-ENOMEM);
+
+ ndev->needed_headroom = local->hw.extra_tx_headroom;
+
+ ret = dev_alloc_name(ndev, ndev->name);
+ if (ret < 0)
+ goto err;
+
+ switch (type) {
+ case IEEE802154_DEV_WPAN:
+ ndev->type = ARPHRD_IEEE802154;
+ break;
+ case IEEE802154_DEV_MONITOR:
+ ndev->type = ARPHRD_IEEE802154_MONITOR;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ieee802154_le64_to_be64(ndev->perm_addr,
+ &local->hw.phy->perm_extended_addr);
+ memcpy(ndev->dev_addr, ndev->perm_addr, IEEE802154_EXTENDED_ADDR_LEN);
+ /* TODO check this */
+ SET_NETDEV_DEV(ndev, &local->phy->dev);
+ sdata = netdev_priv(ndev);
+ ndev->ieee802154_ptr = &sdata->wpan_dev;
+ memcpy(sdata->name, ndev->name, IFNAMSIZ);
+ sdata->dev = ndev;
+ sdata->wpan_dev.wpan_phy = local->hw.phy;
+ sdata->local = local;
+
+ /* setup type-dependent data */
+ ret = ieee802154_setup_sdata(sdata, type);
+ if (ret)
+ goto err;
+
+ if (ndev) {
+ ret = register_netdevice(ndev);
+ if (ret < 0)
+ goto err;
+ }
+
+ mutex_lock(&local->iflist_mtx);
+ list_add_tail_rcu(&sdata->list, &local->interfaces);
+ mutex_unlock(&local->iflist_mtx);
+
+ if (new_wpan_dev)
+ *new_wpan_dev = &sdata->wpan_dev;
+
+ return ndev;
+
+err:
+ free_netdev(ndev);
+ return ERR_PTR(ret);
+}
+
+void ieee802154_if_remove(struct ieee802154_sub_if_data *sdata)
+{
+ ASSERT_RTNL();
+
+ mutex_lock(&sdata->local->iflist_mtx);
+ list_del_rcu(&sdata->list);
+ mutex_unlock(&sdata->local->iflist_mtx);
+
+ synchronize_rcu();
+ unregister_netdevice(sdata->dev);
+}
diff --git a/net/mac802154/llsec.c b/net/mac802154/llsec.c
index 4570581..fa0d523 100644
--- a/net/mac802154/llsec.c
+++ b/net/mac802154/llsec.c
@@ -17,10 +17,10 @@
#include <linux/err.h>
#include <linux/bug.h>
#include <linux/completion.h>
-#include <net/ieee802154.h>
+#include <linux/ieee802154.h>
#include <crypto/algapi.h>
-#include "mac802154.h"
+#include "ieee802154_i.h"
#include "llsec.h"
static void llsec_key_put(struct mac802154_llsec_key *key);
diff --git a/net/mac802154/mac_cmd.c b/net/mac802154/mac_cmd.c
index bf80913..00b2b21 100644
--- a/net/mac802154/mac_cmd.c
+++ b/net/mac802154/mac_cmd.c
@@ -12,10 +12,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Written by:
* Sergey Lapin <slapin@ossfans.org>
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
@@ -24,14 +20,14 @@
#include <linux/skbuff.h>
#include <linux/if_arp.h>
+#include <linux/ieee802154.h>
-#include <net/ieee802154.h>
#include <net/ieee802154_netdev.h>
-#include <net/wpan-phy.h>
+#include <net/cfg802154.h>
#include <net/mac802154.h>
-#include <net/nl802154.h>
-#include "mac802154.h"
+#include "ieee802154_i.h"
+#include "driver-ops.h"
static int mac802154_mlme_start_req(struct net_device *dev,
struct ieee802154_addr *addr,
@@ -47,7 +43,6 @@ static int mac802154_mlme_start_req(struct net_device *dev,
mac802154_dev_set_pan_id(dev, addr->pan_id);
mac802154_dev_set_short_addr(dev, addr->short_addr);
- mac802154_dev_set_ieee_addr(dev);
mac802154_dev_set_page_channel(dev, page, channel);
if (ops->llsec) {
@@ -69,21 +64,49 @@ static int mac802154_mlme_start_req(struct net_device *dev,
rc = ops->llsec->set_params(dev, &params, changed);
}
- /* FIXME: add validation for unused parameters to be sane
- * for SoftMAC
- */
- ieee802154_nl_start_confirm(dev, IEEE802154_SUCCESS);
-
return rc;
}
-static struct wpan_phy *mac802154_get_phy(const struct net_device *dev)
+static int mac802154_set_mac_params(struct net_device *dev,
+ const struct ieee802154_mac_params *params)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct ieee802154_local *local = sdata->local;
+ int ret;
+
+ mutex_lock(&sdata->local->iflist_mtx);
+ sdata->mac_params = *params;
+ mutex_unlock(&sdata->local->iflist_mtx);
+
+ if (local->hw.flags & IEEE802154_HW_TXPOWER) {
+ ret = drv_set_tx_power(local, params->transmit_power);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (local->hw.flags & IEEE802154_HW_CCA_MODE) {
+ ret = drv_set_cca_mode(local, params->cca_mode);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (local->hw.flags & IEEE802154_HW_CCA_ED_LEVEL) {
+ ret = drv_set_cca_ed_level(local, params->cca_ed_level);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
- BUG_ON(dev->type != ARPHRD_IEEE802154);
+static void mac802154_get_mac_params(struct net_device *dev,
+ struct ieee802154_mac_params *params)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
- return to_phy(get_device(&priv->hw->phy->dev));
+ mutex_lock(&sdata->local->iflist_mtx);
+ *params = sdata->mac_params;
+ mutex_unlock(&sdata->local->iflist_mtx);
}
static struct ieee802154_llsec_ops mac802154_llsec_ops = {
@@ -102,12 +125,7 @@ static struct ieee802154_llsec_ops mac802154_llsec_ops = {
.unlock_table = mac802154_unlock_table,
};
-struct ieee802154_reduced_mlme_ops mac802154_mlme_reduced = {
- .get_phy = mac802154_get_phy,
-};
-
struct ieee802154_mlme_ops mac802154_mlme_wpan = {
- .get_phy = mac802154_get_phy,
.start_req = mac802154_mlme_start_req,
.get_pan_id = mac802154_dev_get_pan_id,
.get_short_addr = mac802154_dev_get_short_addr,
diff --git a/net/mac802154/main.c b/net/mac802154/main.c
new file mode 100644
index 0000000..7d0ff7f
--- /dev/null
+++ b/net/mac802154/main.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2007-2012 Siemens AG
+ *
+ * Written by:
+ * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
+ *
+ * Based on the code from 'linux-zigbee.sourceforge.net' project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+
+#include <net/netlink.h>
+#include <linux/nl802154.h>
+#include <net/mac802154.h>
+#include <net/ieee802154_netdev.h>
+#include <net/route.h>
+#include <net/cfg802154.h>
+
+#include "ieee802154_i.h"
+#include "cfg.h"
+
+static void ieee802154_tasklet_handler(unsigned long data)
+{
+ struct ieee802154_local *local = (struct ieee802154_local *)data;
+ struct sk_buff *skb;
+
+ while ((skb = skb_dequeue(&local->skb_queue))) {
+ switch (skb->pkt_type) {
+ case IEEE802154_RX_MSG:
+ /* Clear skb->pkt_type in order to not confuse kernel
+ * netstack.
+ */
+ skb->pkt_type = 0;
+ ieee802154_rx(&local->hw, skb);
+ break;
+ default:
+ WARN(1, "mac802154: Packet is of unknown type %d\n",
+ skb->pkt_type);
+ kfree_skb(skb);
+ break;
+ }
+ }
+}
+
+struct ieee802154_hw *
+ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops)
+{
+ struct wpan_phy *phy;
+ struct ieee802154_local *local;
+ size_t priv_size;
+
+ if (!ops || !(ops->xmit_async || ops->xmit_sync) || !ops->ed ||
+ !ops->start || !ops->stop || !ops->set_channel) {
+ pr_err("undefined IEEE802.15.4 device operations\n");
+ return NULL;
+ }
+
+ /* Ensure 32-byte alignment of our private data and hw private data.
+ * We use the wpan_phy priv data for both our ieee802154_local and for
+ * the driver's private data
+ *
+ * in memory it'll be like this:
+ *
+ * +-------------------------+
+ * | struct wpan_phy |
+ * +-------------------------+
+ * | struct ieee802154_local |
+ * +-------------------------+
+ * | driver's private data |
+ * +-------------------------+
+ *
+ * Due to ieee802154 layer isn't aware of driver and MAC structures,
+ * so lets align them here.
+ */
+
+ priv_size = ALIGN(sizeof(*local), NETDEV_ALIGN) + priv_data_len;
+
+ phy = wpan_phy_alloc(&mac802154_config_ops, priv_size);
+ if (!phy) {
+ pr_err("failure to allocate master IEEE802.15.4 device\n");
+ return NULL;
+ }
+
+ local = wpan_phy_priv(phy);
+ local->phy = phy;
+ local->hw.phy = local->phy;
+ local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN);
+ local->ops = ops;
+
+ INIT_LIST_HEAD(&local->interfaces);
+ mutex_init(&local->iflist_mtx);
+
+ tasklet_init(&local->tasklet,
+ ieee802154_tasklet_handler,
+ (unsigned long)local);
+
+ skb_queue_head_init(&local->skb_queue);
+
+ return &local->hw;
+}
+EXPORT_SYMBOL(ieee802154_alloc_hw);
+
+void ieee802154_free_hw(struct ieee802154_hw *hw)
+{
+ struct ieee802154_local *local = hw_to_local(hw);
+
+ BUG_ON(!list_empty(&local->interfaces));
+
+ mutex_destroy(&local->iflist_mtx);
+
+ wpan_phy_free(local->phy);
+}
+EXPORT_SYMBOL(ieee802154_free_hw);
+
+int ieee802154_register_hw(struct ieee802154_hw *hw)
+{
+ struct ieee802154_local *local = hw_to_local(hw);
+ struct net_device *dev;
+ int rc = -ENOSYS;
+
+ local->workqueue =
+ create_singlethread_workqueue(wpan_phy_name(local->phy));
+ if (!local->workqueue) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ wpan_phy_set_dev(local->phy, local->hw.parent);
+
+ rc = wpan_phy_register(local->phy);
+ if (rc < 0)
+ goto out_wq;
+
+ rtnl_lock();
+
+ dev = ieee802154_if_add(local, "wpan%d", NULL, IEEE802154_DEV_WPAN);
+ if (IS_ERR(dev)) {
+ rtnl_unlock();
+ rc = PTR_ERR(dev);
+ goto out_wq;
+ }
+
+ rtnl_unlock();
+
+ return 0;
+
+out_wq:
+ destroy_workqueue(local->workqueue);
+out:
+ return rc;
+}
+EXPORT_SYMBOL(ieee802154_register_hw);
+
+void ieee802154_unregister_hw(struct ieee802154_hw *hw)
+{
+ struct ieee802154_local *local = hw_to_local(hw);
+ struct ieee802154_sub_if_data *sdata, *next;
+
+ tasklet_kill(&local->tasklet);
+ flush_workqueue(local->workqueue);
+ destroy_workqueue(local->workqueue);
+
+ rtnl_lock();
+
+ list_for_each_entry_safe(sdata, next, &local->interfaces, list) {
+ mutex_lock(&sdata->local->iflist_mtx);
+ list_del(&sdata->list);
+ mutex_unlock(&sdata->local->iflist_mtx);
+
+ unregister_netdevice(sdata->dev);
+ }
+
+ rtnl_unlock();
+
+ wpan_phy_unregister(local->phy);
+}
+EXPORT_SYMBOL(ieee802154_unregister_hw);
+
+MODULE_DESCRIPTION("IEEE 802.15.4 implementation");
+MODULE_LICENSE("GPL v2");
diff --git a/net/mac802154/mib.c b/net/mac802154/mib.c
index 868a040..6fa7491 100644
--- a/net/mac802154/mib.c
+++ b/net/mac802154/mib.c
@@ -10,10 +10,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Written by:
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
* Sergey Lapin <slapin@ossfans.org>
@@ -25,193 +21,86 @@
#include <net/mac802154.h>
#include <net/ieee802154_netdev.h>
-#include <net/wpan-phy.h>
-
-#include "mac802154.h"
-
-struct phy_chan_notify_work {
- struct work_struct work;
- struct net_device *dev;
-};
-
-struct hw_addr_filt_notify_work {
- struct work_struct work;
- struct net_device *dev;
- unsigned long changed;
-};
-
-static struct mac802154_priv *mac802154_slave_get_priv(struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+#include <net/cfg802154.h>
- BUG_ON(dev->type != ARPHRD_IEEE802154);
-
- return priv->hw;
-}
-
-static void hw_addr_notify(struct work_struct *work)
-{
- struct hw_addr_filt_notify_work *nw = container_of(work,
- struct hw_addr_filt_notify_work, work);
- struct mac802154_priv *hw = mac802154_slave_get_priv(nw->dev);
- int res;
-
- res = hw->ops->set_hw_addr_filt(&hw->hw,
- &hw->hw.hw_filt,
- nw->changed);
- if (res)
- pr_debug("failed changed mask %lx\n", nw->changed);
-
- kfree(nw);
-}
-
-static void set_hw_addr_filt(struct net_device *dev, unsigned long changed)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct hw_addr_filt_notify_work *work;
-
- work = kzalloc(sizeof(*work), GFP_ATOMIC);
- if (!work)
- return;
-
- INIT_WORK(&work->work, hw_addr_notify);
- work->dev = dev;
- work->changed = changed;
- queue_work(priv->hw->dev_workqueue, &work->work);
-}
+#include "ieee802154_i.h"
+#include "driver-ops.h"
void mac802154_dev_set_short_addr(struct net_device *dev, __le16 val)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
BUG_ON(dev->type != ARPHRD_IEEE802154);
- spin_lock_bh(&priv->mib_lock);
- priv->short_addr = val;
- spin_unlock_bh(&priv->mib_lock);
-
- if ((priv->hw->ops->set_hw_addr_filt) &&
- (priv->hw->hw.hw_filt.short_addr != priv->short_addr)) {
- priv->hw->hw.hw_filt.short_addr = priv->short_addr;
- set_hw_addr_filt(dev, IEEE802515_AFILT_SADDR_CHANGED);
- }
+ spin_lock_bh(&sdata->mib_lock);
+ sdata->short_addr = val;
+ spin_unlock_bh(&sdata->mib_lock);
}
__le16 mac802154_dev_get_short_addr(const struct net_device *dev)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
__le16 ret;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- spin_lock_bh(&priv->mib_lock);
- ret = priv->short_addr;
- spin_unlock_bh(&priv->mib_lock);
+ spin_lock_bh(&sdata->mib_lock);
+ ret = sdata->short_addr;
+ spin_unlock_bh(&sdata->mib_lock);
return ret;
}
-void mac802154_dev_set_ieee_addr(struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct mac802154_priv *mac = priv->hw;
-
- priv->extended_addr = ieee802154_devaddr_from_raw(dev->dev_addr);
-
- if (mac->ops->set_hw_addr_filt &&
- mac->hw.hw_filt.ieee_addr != priv->extended_addr) {
- mac->hw.hw_filt.ieee_addr = priv->extended_addr;
- set_hw_addr_filt(dev, IEEE802515_AFILT_IEEEADDR_CHANGED);
- }
-}
-
__le16 mac802154_dev_get_pan_id(const struct net_device *dev)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
__le16 ret;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- spin_lock_bh(&priv->mib_lock);
- ret = priv->pan_id;
- spin_unlock_bh(&priv->mib_lock);
+ spin_lock_bh(&sdata->mib_lock);
+ ret = sdata->pan_id;
+ spin_unlock_bh(&sdata->mib_lock);
return ret;
}
void mac802154_dev_set_pan_id(struct net_device *dev, __le16 val)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
BUG_ON(dev->type != ARPHRD_IEEE802154);
- spin_lock_bh(&priv->mib_lock);
- priv->pan_id = val;
- spin_unlock_bh(&priv->mib_lock);
-
- if ((priv->hw->ops->set_hw_addr_filt) &&
- (priv->hw->hw.hw_filt.pan_id != priv->pan_id)) {
- priv->hw->hw.hw_filt.pan_id = priv->pan_id;
- set_hw_addr_filt(dev, IEEE802515_AFILT_PANID_CHANGED);
- }
+ spin_lock_bh(&sdata->mib_lock);
+ sdata->pan_id = val;
+ spin_unlock_bh(&sdata->mib_lock);
}
u8 mac802154_dev_get_dsn(const struct net_device *dev)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
BUG_ON(dev->type != ARPHRD_IEEE802154);
- return priv->dsn++;
-}
-
-static void phy_chan_notify(struct work_struct *work)
-{
- struct phy_chan_notify_work *nw = container_of(work,
- struct phy_chan_notify_work, work);
- struct mac802154_priv *hw = mac802154_slave_get_priv(nw->dev);
- struct mac802154_sub_if_data *priv = netdev_priv(nw->dev);
- int res;
-
- mutex_lock(&priv->hw->phy->pib_lock);
- res = hw->ops->set_channel(&hw->hw, priv->page, priv->chan);
- if (res) {
- pr_debug("set_channel failed\n");
- } else {
- priv->hw->phy->current_channel = priv->chan;
- priv->hw->phy->current_page = priv->page;
- }
- mutex_unlock(&priv->hw->phy->pib_lock);
-
- kfree(nw);
+ return sdata->dsn++;
}
void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct phy_chan_notify_work *work;
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ struct ieee802154_local *local = sdata->local;
+ int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- spin_lock_bh(&priv->mib_lock);
- priv->page = page;
- priv->chan = chan;
- spin_unlock_bh(&priv->mib_lock);
-
- mutex_lock(&priv->hw->phy->pib_lock);
- if (priv->hw->phy->current_channel != priv->chan ||
- priv->hw->phy->current_page != priv->page) {
- mutex_unlock(&priv->hw->phy->pib_lock);
-
- work = kzalloc(sizeof(*work), GFP_ATOMIC);
- if (!work)
- return;
-
- INIT_WORK(&work->work, phy_chan_notify);
- work->dev = dev;
- queue_work(priv->hw->dev_workqueue, &work->work);
+ res = drv_set_channel(local, page, chan);
+ if (res) {
+ pr_debug("set_channel failed\n");
} else {
- mutex_unlock(&priv->hw->phy->pib_lock);
+ mutex_lock(&local->phy->pib_lock);
+ local->phy->current_channel = chan;
+ local->phy->current_page = page;
+ mutex_unlock(&local->phy->pib_lock);
}
}
@@ -219,14 +108,14 @@ void mac802154_dev_set_page_channel(struct net_device *dev, u8 page, u8 chan)
int mac802154_get_params(struct net_device *dev,
struct ieee802154_llsec_params *params)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_get_params(&priv->sec, params);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_get_params(&sdata->sec, params);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
@@ -235,14 +124,14 @@ int mac802154_set_params(struct net_device *dev,
const struct ieee802154_llsec_params *params,
int changed)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_set_params(&priv->sec, params, changed);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_set_params(&sdata->sec, params, changed);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
@@ -252,14 +141,14 @@ int mac802154_add_key(struct net_device *dev,
const struct ieee802154_llsec_key_id *id,
const struct ieee802154_llsec_key *key)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_key_add(&priv->sec, id, key);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_key_add(&sdata->sec, id, key);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
@@ -267,14 +156,14 @@ int mac802154_add_key(struct net_device *dev,
int mac802154_del_key(struct net_device *dev,
const struct ieee802154_llsec_key_id *id)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_key_del(&priv->sec, id);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_key_del(&sdata->sec, id);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
@@ -283,28 +172,28 @@ int mac802154_del_key(struct net_device *dev,
int mac802154_add_dev(struct net_device *dev,
const struct ieee802154_llsec_device *llsec_dev)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_dev_add(&priv->sec, llsec_dev);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_dev_add(&sdata->sec, llsec_dev);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
int mac802154_del_dev(struct net_device *dev, __le64 dev_addr)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_dev_del(&priv->sec, dev_addr);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_dev_del(&sdata->sec, dev_addr);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
@@ -314,14 +203,14 @@ int mac802154_add_devkey(struct net_device *dev,
__le64 device_addr,
const struct ieee802154_llsec_device_key *key)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_devkey_add(&priv->sec, device_addr, key);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_devkey_add(&sdata->sec, device_addr, key);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
@@ -330,14 +219,14 @@ int mac802154_del_devkey(struct net_device *dev,
__le64 device_addr,
const struct ieee802154_llsec_device_key *key)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_devkey_del(&priv->sec, device_addr, key);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_devkey_del(&sdata->sec, device_addr, key);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
@@ -346,14 +235,14 @@ int mac802154_del_devkey(struct net_device *dev,
int mac802154_add_seclevel(struct net_device *dev,
const struct ieee802154_llsec_seclevel *sl)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_seclevel_add(&priv->sec, sl);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_seclevel_add(&sdata->sec, sl);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
@@ -361,14 +250,14 @@ int mac802154_add_seclevel(struct net_device *dev,
int mac802154_del_seclevel(struct net_device *dev,
const struct ieee802154_llsec_seclevel *sl)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
int res;
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
- res = mac802154_llsec_seclevel_del(&priv->sec, sl);
- mutex_unlock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
+ res = mac802154_llsec_seclevel_del(&sdata->sec, sl);
+ mutex_unlock(&sdata->sec_mtx);
return res;
}
@@ -376,28 +265,28 @@ int mac802154_del_seclevel(struct net_device *dev,
void mac802154_lock_table(struct net_device *dev)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_lock(&priv->sec_mtx);
+ mutex_lock(&sdata->sec_mtx);
}
void mac802154_get_table(struct net_device *dev,
struct ieee802154_llsec_table **t)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
BUG_ON(dev->type != ARPHRD_IEEE802154);
- *t = &priv->sec.table;
+ *t = &sdata->sec.table;
}
void mac802154_unlock_table(struct net_device *dev)
{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
BUG_ON(dev->type != ARPHRD_IEEE802154);
- mutex_unlock(&priv->sec_mtx);
+ mutex_unlock(&sdata->sec_mtx);
}
diff --git a/net/mac802154/monitor.c b/net/mac802154/monitor.c
deleted file mode 100644
index a68230e..0000000
--- a/net/mac802154/monitor.c
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright 2007, 2008, 2009 Siemens AG
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Written by:
- * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
- * Sergey Lapin <slapin@ossfans.org>
- * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
- * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
- */
-
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-#include <linux/crc-ccitt.h>
-
-#include <net/ieee802154.h>
-#include <net/mac802154.h>
-#include <net/netlink.h>
-#include <net/wpan-phy.h>
-#include <linux/nl802154.h>
-
-#include "mac802154.h"
-
-static netdev_tx_t mac802154_monitor_xmit(struct sk_buff *skb,
- struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv;
- u8 chan, page;
-
- priv = netdev_priv(dev);
-
- /* FIXME: locking */
- chan = priv->hw->phy->current_channel;
- page = priv->hw->phy->current_page;
-
- if (chan == MAC802154_CHAN_NONE) /* not initialized */
- return NETDEV_TX_OK;
-
- if (WARN_ON(page >= WPAN_NUM_PAGES) ||
- WARN_ON(chan >= WPAN_NUM_CHANNELS))
- return NETDEV_TX_OK;
-
- skb->skb_iif = dev->ifindex;
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += skb->len;
-
- return mac802154_tx(priv->hw, skb, page, chan);
-}
-
-
-void mac802154_monitors_rx(struct mac802154_priv *priv, struct sk_buff *skb)
-{
- struct sk_buff *skb2;
- struct mac802154_sub_if_data *sdata;
- u16 crc = crc_ccitt(0, skb->data, skb->len);
- u8 *data;
-
- rcu_read_lock();
- list_for_each_entry_rcu(sdata, &priv->slaves, list) {
- if (sdata->type != IEEE802154_DEV_MONITOR ||
- !netif_running(sdata->dev))
- continue;
-
- skb2 = skb_clone(skb, GFP_ATOMIC);
- skb2->dev = sdata->dev;
- skb2->pkt_type = PACKET_HOST;
- data = skb_put(skb2, 2);
- data[0] = crc & 0xff;
- data[1] = crc >> 8;
-
- netif_rx_ni(skb2);
- }
- rcu_read_unlock();
-}
-
-static const struct net_device_ops mac802154_monitor_ops = {
- .ndo_open = mac802154_slave_open,
- .ndo_stop = mac802154_slave_close,
- .ndo_start_xmit = mac802154_monitor_xmit,
-};
-
-void mac802154_monitor_setup(struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv;
-
- dev->addr_len = 0;
- dev->hard_header_len = 0;
- dev->needed_tailroom = 2; /* room for FCS */
- dev->mtu = IEEE802154_MTU;
- dev->tx_queue_len = 10;
- dev->type = ARPHRD_IEEE802154_MONITOR;
- dev->flags = IFF_NOARP | IFF_BROADCAST;
- dev->watchdog_timeo = 0;
-
- dev->destructor = free_netdev;
- dev->netdev_ops = &mac802154_monitor_ops;
- dev->ml_priv = &mac802154_mlme_reduced;
-
- priv = netdev_priv(dev);
- priv->type = IEEE802154_DEV_MONITOR;
-
- priv->chan = MAC802154_CHAN_NONE; /* not initialized */
- priv->page = 0;
-}
diff --git a/net/mac802154/rx.c b/net/mac802154/rx.c
index a14cf9e..4b54cf3 100644
--- a/net/mac802154/rx.c
+++ b/net/mac802154/rx.c
@@ -10,10 +10,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Written by:
* Pavel Smolenskiy <pavel.smolenskiy@gmail.com>
* Maxim Gorbachyov <maxim.gorbachev@siemens.com>
@@ -23,92 +19,285 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/workqueue.h>
#include <linux/netdevice.h>
#include <linux/crc-ccitt.h>
+#include <asm/unaligned.h>
#include <net/mac802154.h>
#include <net/ieee802154_netdev.h>
+#include <net/rtnetlink.h>
+#include <linux/nl802154.h>
-#include "mac802154.h"
+#include "ieee802154_i.h"
-/* The IEEE 802.15.4 standard defines 4 MAC packet types:
- * - beacon frame
- * - MAC command frame
- * - acknowledgement frame
- * - data frame
- *
- * and only the data frame should be pushed to the upper layers, other types
- * are just internal MAC layer management information. So only data packets
- * are going to be sent to the networking queue, all other will be processed
- * right here by using the device workqueue.
- */
-struct rx_work {
- struct sk_buff *skb;
- struct work_struct work;
- struct ieee802154_dev *dev;
- u8 lqi;
-};
+static int ieee802154_deliver_skb(struct sk_buff *skb)
+{
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->protocol = htons(ETH_P_IEEE802154);
-static void
-mac802154_subif_rx(struct ieee802154_dev *hw, struct sk_buff *skb, u8 lqi)
+ return netif_receive_skb(skb);
+}
+
+static int
+ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,
+ struct sk_buff *skb, const struct ieee802154_hdr *hdr)
{
- struct mac802154_priv *priv = mac802154_to_priv(hw);
+ __le16 span, sshort;
+ int rc;
- mac_cb(skb)->lqi = lqi;
- skb->protocol = htons(ETH_P_IEEE802154);
- skb_reset_mac_header(skb);
+ pr_debug("getting packet via slave interface %s\n", sdata->dev->name);
- if (!(priv->hw.flags & IEEE802154_HW_OMIT_CKSUM)) {
- u16 crc;
+ spin_lock_bh(&sdata->mib_lock);
- if (skb->len < 2) {
- pr_debug("got invalid frame\n");
- goto fail;
- }
- crc = crc_ccitt(0, skb->data, skb->len);
- if (crc) {
- pr_debug("CRC mismatch\n");
- goto fail;
- }
- skb_trim(skb, skb->len - 2); /* CRC */
+ span = sdata->pan_id;
+ sshort = sdata->short_addr;
+
+ switch (mac_cb(skb)->dest.mode) {
+ case IEEE802154_ADDR_NONE:
+ if (mac_cb(skb)->dest.mode != IEEE802154_ADDR_NONE)
+ /* FIXME: check if we are PAN coordinator */
+ skb->pkt_type = PACKET_OTHERHOST;
+ else
+ /* ACK comes with both addresses empty */
+ skb->pkt_type = PACKET_HOST;
+ break;
+ case IEEE802154_ADDR_LONG:
+ if (mac_cb(skb)->dest.pan_id != span &&
+ mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST))
+ skb->pkt_type = PACKET_OTHERHOST;
+ else if (mac_cb(skb)->dest.extended_addr == sdata->extended_addr)
+ skb->pkt_type = PACKET_HOST;
+ else
+ skb->pkt_type = PACKET_OTHERHOST;
+ break;
+ case IEEE802154_ADDR_SHORT:
+ if (mac_cb(skb)->dest.pan_id != span &&
+ mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST))
+ skb->pkt_type = PACKET_OTHERHOST;
+ else if (mac_cb(skb)->dest.short_addr == sshort)
+ skb->pkt_type = PACKET_HOST;
+ else if (mac_cb(skb)->dest.short_addr ==
+ cpu_to_le16(IEEE802154_ADDR_BROADCAST))
+ skb->pkt_type = PACKET_BROADCAST;
+ else
+ skb->pkt_type = PACKET_OTHERHOST;
+ break;
+ default:
+ spin_unlock_bh(&sdata->mib_lock);
+ pr_debug("invalid dest mode\n");
+ kfree_skb(skb);
+ return NET_RX_DROP;
}
- mac802154_monitors_rx(priv, skb);
- mac802154_wpans_rx(priv, skb);
+ spin_unlock_bh(&sdata->mib_lock);
- return;
+ skb->dev = sdata->dev;
+
+ rc = mac802154_llsec_decrypt(&sdata->sec, skb);
+ if (rc) {
+ pr_debug("decryption failed: %i\n", rc);
+ goto fail;
+ }
+
+ sdata->dev->stats.rx_packets++;
+ sdata->dev->stats.rx_bytes += skb->len;
+
+ switch (mac_cb(skb)->type) {
+ case IEEE802154_FC_TYPE_DATA:
+ return ieee802154_deliver_skb(skb);
+ default:
+ pr_warn("ieee802154: bad frame received (type = %d)\n",
+ mac_cb(skb)->type);
+ goto fail;
+ }
fail:
kfree_skb(skb);
+ return NET_RX_DROP;
}
-static void mac802154_rx_worker(struct work_struct *work)
+static void
+ieee802154_print_addr(const char *name, const struct ieee802154_addr *addr)
{
- struct rx_work *rw = container_of(work, struct rx_work, work);
+ if (addr->mode == IEEE802154_ADDR_NONE)
+ pr_debug("%s not present\n", name);
+
+ pr_debug("%s PAN ID: %04x\n", name, le16_to_cpu(addr->pan_id));
+ if (addr->mode == IEEE802154_ADDR_SHORT) {
+ pr_debug("%s is short: %04x\n", name,
+ le16_to_cpu(addr->short_addr));
+ } else {
+ u64 hw = swab64((__force u64)addr->extended_addr);
- mac802154_subif_rx(rw->dev, rw->skb, rw->lqi);
- kfree(rw);
+ pr_debug("%s is hardware: %8phC\n", name, &hw);
+ }
}
-void
-ieee802154_rx_irqsafe(struct ieee802154_dev *dev, struct sk_buff *skb, u8 lqi)
+static int
+ieee802154_parse_frame_start(struct sk_buff *skb, struct ieee802154_hdr *hdr)
{
- struct mac802154_priv *priv = mac802154_to_priv(dev);
- struct rx_work *work;
+ int hlen;
+ struct ieee802154_mac_cb *cb = mac_cb_init(skb);
- if (!skb)
- return;
+ skb_reset_mac_header(skb);
+
+ hlen = ieee802154_hdr_pull(skb, hdr);
+ if (hlen < 0)
+ return -EINVAL;
+
+ skb->mac_len = hlen;
+
+ pr_debug("fc: %04x dsn: %02x\n", le16_to_cpup((__le16 *)&hdr->fc),
+ hdr->seq);
+
+ cb->type = hdr->fc.type;
+ cb->ackreq = hdr->fc.ack_request;
+ cb->secen = hdr->fc.security_enabled;
+
+ ieee802154_print_addr("destination", &hdr->dest);
+ ieee802154_print_addr("source", &hdr->source);
+
+ cb->source = hdr->source;
+ cb->dest = hdr->dest;
+
+ if (hdr->fc.security_enabled) {
+ u64 key;
+
+ pr_debug("seclevel %i\n", hdr->sec.level);
+
+ switch (hdr->sec.key_id_mode) {
+ case IEEE802154_SCF_KEY_IMPLICIT:
+ pr_debug("implicit key\n");
+ break;
+
+ case IEEE802154_SCF_KEY_INDEX:
+ pr_debug("key %02x\n", hdr->sec.key_id);
+ break;
+
+ case IEEE802154_SCF_KEY_SHORT_INDEX:
+ pr_debug("key %04x:%04x %02x\n",
+ le32_to_cpu(hdr->sec.short_src) >> 16,
+ le32_to_cpu(hdr->sec.short_src) & 0xffff,
+ hdr->sec.key_id);
+ break;
+
+ case IEEE802154_SCF_KEY_HW_INDEX:
+ key = swab64((__force u64)hdr->sec.extended_src);
+ pr_debug("key source %8phC %02x\n", &key,
+ hdr->sec.key_id);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void
+__ieee802154_rx_handle_packet(struct ieee802154_local *local,
+ struct sk_buff *skb)
+{
+ int ret;
+ struct ieee802154_sub_if_data *sdata;
+ struct ieee802154_hdr hdr;
- work = kzalloc(sizeof(*work), GFP_ATOMIC);
- if (!work)
+ ret = ieee802154_parse_frame_start(skb, &hdr);
+ if (ret) {
+ pr_debug("got invalid frame\n");
+ kfree_skb(skb);
return;
+ }
+
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (sdata->vif.type != IEEE802154_DEV_WPAN ||
+ !netif_running(sdata->dev))
+ continue;
+
+ ieee802154_subif_frame(sdata, skb, &hdr);
+ skb = NULL;
+ break;
+ }
+
+ if (skb)
+ kfree_skb(skb);
+}
+
+static void
+ieee802154_monitors_rx(struct ieee802154_local *local, struct sk_buff *skb)
+{
+ struct sk_buff *skb2;
+ struct ieee802154_sub_if_data *sdata;
+
+ skb_reset_mac_header(skb);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->pkt_type = PACKET_OTHERHOST;
+ skb->protocol = htons(ETH_P_IEEE802154);
+
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (sdata->vif.type != IEEE802154_DEV_MONITOR)
+ continue;
+
+ if (!ieee802154_sdata_running(sdata))
+ continue;
+
+ skb2 = skb_clone(skb, GFP_ATOMIC);
+ if (skb2) {
+ skb2->dev = sdata->dev;
+ ieee802154_deliver_skb(skb2);
+
+ sdata->dev->stats.rx_packets++;
+ sdata->dev->stats.rx_bytes += skb->len;
+ }
+ }
+}
+
+void ieee802154_rx(struct ieee802154_hw *hw, struct sk_buff *skb)
+{
+ struct ieee802154_local *local = hw_to_local(hw);
+ u16 crc;
- INIT_WORK(&work->work, mac802154_rx_worker);
- work->skb = skb;
- work->dev = dev;
- work->lqi = lqi;
+ WARN_ON_ONCE(softirq_count() == 0);
- queue_work(priv->dev_workqueue, &work->work);
+ /* TODO: When a transceiver omits the checksum here, we
+ * add an own calculated one. This is currently an ugly
+ * solution because the monitor needs a crc here.
+ */
+ if (local->hw.flags & IEEE802154_HW_RX_OMIT_CKSUM) {
+ crc = crc_ccitt(0, skb->data, skb->len);
+ put_unaligned_le16(crc, skb_put(skb, 2));
+ }
+
+ rcu_read_lock();
+
+ ieee802154_monitors_rx(local, skb);
+
+ /* Check if transceiver doesn't validate the checksum.
+ * If not we validate the checksum here.
+ */
+ if (local->hw.flags & IEEE802154_HW_RX_DROP_BAD_CKSUM) {
+ crc = crc_ccitt(0, skb->data, skb->len);
+ if (crc) {
+ rcu_read_unlock();
+ kfree_skb(skb);
+ return;
+ }
+ }
+ /* remove crc */
+ skb_trim(skb, skb->len - 2);
+
+ __ieee802154_rx_handle_packet(local, skb);
+
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL(ieee802154_rx);
+
+void
+ieee802154_rx_irqsafe(struct ieee802154_hw *hw, struct sk_buff *skb, u8 lqi)
+{
+ struct ieee802154_local *local = hw_to_local(hw);
+
+ mac_cb(skb)->lqi = lqi;
+ skb->pkt_type = IEEE802154_RX_MSG;
+ skb_queue_tail(&local->skb_queue, skb);
+ tasklet_schedule(&local->tasklet);
}
EXPORT_SYMBOL(ieee802154_rx_irqsafe);
diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c
index fdf4c0e6..cc37b77 100644
--- a/net/mac802154/tx.c
+++ b/net/mac802154/tx.c
@@ -10,10 +10,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
* Written by:
* Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
* Sergey Lapin <slapin@ossfans.org>
@@ -24,106 +20,98 @@
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/crc-ccitt.h>
+#include <asm/unaligned.h>
+#include <net/rtnetlink.h>
#include <net/ieee802154_netdev.h>
#include <net/mac802154.h>
-#include <net/wpan-phy.h>
+#include <net/cfg802154.h>
-#include "mac802154.h"
+#include "ieee802154_i.h"
+#include "driver-ops.h"
/* IEEE 802.15.4 transceivers can sleep during the xmit session, so process
* packets through the workqueue.
*/
-struct xmit_work {
+struct ieee802154_xmit_cb {
struct sk_buff *skb;
struct work_struct work;
- struct mac802154_priv *priv;
- u8 chan;
- u8 page;
+ struct ieee802154_local *local;
};
-static void mac802154_xmit_worker(struct work_struct *work)
+static struct ieee802154_xmit_cb ieee802154_xmit_cb;
+
+static void ieee802154_xmit_worker(struct work_struct *work)
{
- struct xmit_work *xw = container_of(work, struct xmit_work, work);
- struct mac802154_sub_if_data *sdata;
+ struct ieee802154_xmit_cb *cb =
+ container_of(work, struct ieee802154_xmit_cb, work);
+ struct ieee802154_local *local = cb->local;
+ struct sk_buff *skb = cb->skb;
+ struct net_device *dev = skb->dev;
int res;
- mutex_lock(&xw->priv->phy->pib_lock);
- if (xw->priv->phy->current_channel != xw->chan ||
- xw->priv->phy->current_page != xw->page) {
- res = xw->priv->ops->set_channel(&xw->priv->hw,
- xw->page,
- xw->chan);
- if (res) {
- pr_debug("set_channel failed\n");
- goto out;
- }
+ rtnl_lock();
- xw->priv->phy->current_channel = xw->chan;
- xw->priv->phy->current_page = xw->page;
- }
+ /* check if ifdown occurred while schedule */
+ if (!netif_running(dev))
+ goto err_tx;
- res = xw->priv->ops->xmit(&xw->priv->hw, xw->skb);
+ res = drv_xmit_sync(local, skb);
if (res)
- pr_debug("transmission failed\n");
+ goto err_tx;
-out:
- mutex_unlock(&xw->priv->phy->pib_lock);
+ ieee802154_xmit_complete(&local->hw, skb);
- /* Restart the netif queue on each sub_if_data object. */
- rcu_read_lock();
- list_for_each_entry_rcu(sdata, &xw->priv->slaves, list)
- netif_wake_queue(sdata->dev);
- rcu_read_unlock();
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
- dev_kfree_skb(xw->skb);
+ rtnl_unlock();
- kfree(xw);
+ return;
+
+err_tx:
+ /* Restart the netif queue on each sub_if_data object. */
+ ieee802154_wake_queue(&local->hw);
+ rtnl_unlock();
+ kfree_skb(skb);
+ netdev_dbg(dev, "transmission failed\n");
}
-netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb,
- u8 page, u8 chan)
+static netdev_tx_t
+ieee802154_tx(struct ieee802154_local *local, struct sk_buff *skb)
{
- struct xmit_work *work;
- struct mac802154_sub_if_data *sdata;
-
- if (!(priv->phy->channels_supported[page] & (1 << chan))) {
- WARN_ON(1);
- goto err_tx;
- }
-
- mac802154_monitors_rx(mac802154_to_priv(&priv->hw), skb);
+ struct net_device *dev = skb->dev;
+ int ret;
- if (!(priv->hw.flags & IEEE802154_HW_OMIT_CKSUM)) {
+ if (!(local->hw.flags & IEEE802154_HW_TX_OMIT_CKSUM)) {
u16 crc = crc_ccitt(0, skb->data, skb->len);
- u8 *data = skb_put(skb, 2);
- data[0] = crc & 0xff;
- data[1] = crc >> 8;
+ put_unaligned_le16(crc, skb_put(skb, 2));
}
- if (skb_cow_head(skb, priv->hw.extra_tx_headroom))
+ if (skb_cow_head(skb, local->hw.extra_tx_headroom))
goto err_tx;
- work = kzalloc(sizeof(*work), GFP_ATOMIC);
- if (!work) {
- kfree_skb(skb);
- return NETDEV_TX_BUSY;
- }
-
/* Stop the netif queue on each sub_if_data object. */
- rcu_read_lock();
- list_for_each_entry_rcu(sdata, &priv->slaves, list)
- netif_stop_queue(sdata->dev);
- rcu_read_unlock();
+ ieee802154_stop_queue(&local->hw);
+
+ /* async is priority, otherwise sync is fallback */
+ if (local->ops->xmit_async) {
+ ret = drv_xmit_async(local, skb);
+ if (ret) {
+ ieee802154_wake_queue(&local->hw);
+ goto err_tx;
+ }
- INIT_WORK(&work->work, mac802154_xmit_worker);
- work->skb = skb;
- work->priv = priv;
- work->page = page;
- work->chan = chan;
+ dev->stats.tx_packets++;
+ dev->stats.tx_bytes += skb->len;
+ } else {
+ INIT_WORK(&ieee802154_xmit_cb.work, ieee802154_xmit_worker);
+ ieee802154_xmit_cb.skb = skb;
+ ieee802154_xmit_cb.local = local;
- queue_work(priv->dev_workqueue, &work->work);
+ queue_work(local->workqueue, &ieee802154_xmit_cb.work);
+ }
return NETDEV_TX_OK;
@@ -131,3 +119,31 @@ err_tx:
kfree_skb(skb);
return NETDEV_TX_OK;
}
+
+netdev_tx_t
+ieee802154_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+
+ skb->skb_iif = dev->ifindex;
+
+ return ieee802154_tx(sdata->local, skb);
+}
+
+netdev_tx_t
+ieee802154_subif_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+ int rc;
+
+ rc = mac802154_llsec_encrypt(&sdata->sec, skb);
+ if (rc) {
+ netdev_warn(dev, "encryption failed: %i\n", rc);
+ kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ skb->skb_iif = dev->ifindex;
+
+ return ieee802154_tx(sdata->local, skb);
+}
diff --git a/net/mac802154/util.c b/net/mac802154/util.c
new file mode 100644
index 0000000..117e4ef
--- /dev/null
+++ b/net/mac802154/util.c
@@ -0,0 +1,55 @@
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Authors:
+ * Alexander Aring <aar@pengutronix.de>
+ *
+ * Based on: net/mac80211/util.c
+ */
+
+#include "ieee802154_i.h"
+
+void ieee802154_wake_queue(struct ieee802154_hw *hw)
+{
+ struct ieee802154_local *local = hw_to_local(hw);
+ struct ieee802154_sub_if_data *sdata;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (!sdata->dev)
+ continue;
+
+ netif_wake_queue(sdata->dev);
+ }
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL(ieee802154_wake_queue);
+
+void ieee802154_stop_queue(struct ieee802154_hw *hw)
+{
+ struct ieee802154_local *local = hw_to_local(hw);
+ struct ieee802154_sub_if_data *sdata;
+
+ rcu_read_lock();
+ list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ if (!sdata->dev)
+ continue;
+
+ netif_stop_queue(sdata->dev);
+ }
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL(ieee802154_stop_queue);
+
+void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb)
+{
+ ieee802154_wake_queue(hw);
+ consume_skb(skb);
+}
+EXPORT_SYMBOL(ieee802154_xmit_complete);
diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c
deleted file mode 100644
index 4ab86a5..0000000
--- a/net/mac802154/wpan.c
+++ /dev/null
@@ -1,599 +0,0 @@
-/*
- * Copyright 2007-2012 Siemens AG
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Written by:
- * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
- * Sergey Lapin <slapin@ossfans.org>
- * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
- * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
- */
-
-#include <linux/netdevice.h>
-#include <linux/module.h>
-#include <linux/if_arp.h>
-
-#include <net/rtnetlink.h>
-#include <linux/nl802154.h>
-#include <net/af_ieee802154.h>
-#include <net/mac802154.h>
-#include <net/ieee802154_netdev.h>
-#include <net/ieee802154.h>
-#include <net/wpan-phy.h>
-
-#include "mac802154.h"
-
-static int mac802154_wpan_update_llsec(struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
- int rc = 0;
-
- if (ops->llsec) {
- struct ieee802154_llsec_params params;
- int changed = 0;
-
- params.pan_id = priv->pan_id;
- changed |= IEEE802154_LLSEC_PARAM_PAN_ID;
-
- params.hwaddr = priv->extended_addr;
- changed |= IEEE802154_LLSEC_PARAM_HWADDR;
-
- rc = ops->llsec->set_params(dev, &params, changed);
- }
-
- return rc;
-}
-
-static int
-mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct sockaddr_ieee802154 *sa =
- (struct sockaddr_ieee802154 *)&ifr->ifr_addr;
- int err = -ENOIOCTLCMD;
-
- spin_lock_bh(&priv->mib_lock);
-
- switch (cmd) {
- case SIOCGIFADDR:
- {
- u16 pan_id, short_addr;
-
- pan_id = le16_to_cpu(priv->pan_id);
- short_addr = le16_to_cpu(priv->short_addr);
- if (pan_id == IEEE802154_PANID_BROADCAST ||
- short_addr == IEEE802154_ADDR_BROADCAST) {
- err = -EADDRNOTAVAIL;
- break;
- }
-
- sa->family = AF_IEEE802154;
- sa->addr.addr_type = IEEE802154_ADDR_SHORT;
- sa->addr.pan_id = pan_id;
- sa->addr.short_addr = short_addr;
-
- err = 0;
- break;
- }
- case SIOCSIFADDR:
- dev_warn(&dev->dev,
- "Using DEBUGing ioctl SIOCSIFADDR isn't recommended!\n");
- if (sa->family != AF_IEEE802154 ||
- sa->addr.addr_type != IEEE802154_ADDR_SHORT ||
- sa->addr.pan_id == IEEE802154_PANID_BROADCAST ||
- sa->addr.short_addr == IEEE802154_ADDR_BROADCAST ||
- sa->addr.short_addr == IEEE802154_ADDR_UNDEF) {
- err = -EINVAL;
- break;
- }
-
- priv->pan_id = cpu_to_le16(sa->addr.pan_id);
- priv->short_addr = cpu_to_le16(sa->addr.short_addr);
-
- err = mac802154_wpan_update_llsec(dev);
- break;
- }
-
- spin_unlock_bh(&priv->mib_lock);
- return err;
-}
-
-static int mac802154_wpan_mac_addr(struct net_device *dev, void *p)
-{
- struct sockaddr *addr = p;
-
- if (netif_running(dev))
- return -EBUSY;
-
- /* FIXME: validate addr */
- memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
- mac802154_dev_set_ieee_addr(dev);
- return mac802154_wpan_update_llsec(dev);
-}
-
-int mac802154_set_mac_params(struct net_device *dev,
- const struct ieee802154_mac_params *params)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
-
- mutex_lock(&priv->hw->slaves_mtx);
- priv->mac_params = *params;
- mutex_unlock(&priv->hw->slaves_mtx);
-
- return 0;
-}
-
-void mac802154_get_mac_params(struct net_device *dev,
- struct ieee802154_mac_params *params)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
-
- mutex_lock(&priv->hw->slaves_mtx);
- *params = priv->mac_params;
- mutex_unlock(&priv->hw->slaves_mtx);
-}
-
-static int mac802154_wpan_open(struct net_device *dev)
-{
- int rc;
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct wpan_phy *phy = priv->hw->phy;
-
- rc = mac802154_slave_open(dev);
- if (rc < 0)
- return rc;
-
- mutex_lock(&phy->pib_lock);
-
- if (phy->set_txpower) {
- rc = phy->set_txpower(phy, priv->mac_params.transmit_power);
- if (rc < 0)
- goto out;
- }
-
- if (phy->set_lbt) {
- rc = phy->set_lbt(phy, priv->mac_params.lbt);
- if (rc < 0)
- goto out;
- }
-
- if (phy->set_cca_mode) {
- rc = phy->set_cca_mode(phy, priv->mac_params.cca_mode);
- if (rc < 0)
- goto out;
- }
-
- if (phy->set_cca_ed_level) {
- rc = phy->set_cca_ed_level(phy, priv->mac_params.cca_ed_level);
- if (rc < 0)
- goto out;
- }
-
- if (phy->set_csma_params) {
- rc = phy->set_csma_params(phy, priv->mac_params.min_be,
- priv->mac_params.max_be,
- priv->mac_params.csma_retries);
- if (rc < 0)
- goto out;
- }
-
- if (phy->set_frame_retries) {
- rc = phy->set_frame_retries(phy,
- priv->mac_params.frame_retries);
- if (rc < 0)
- goto out;
- }
-
- mutex_unlock(&phy->pib_lock);
- return 0;
-
-out:
- mutex_unlock(&phy->pib_lock);
- return rc;
-}
-
-static int mac802154_set_header_security(struct mac802154_sub_if_data *priv,
- struct ieee802154_hdr *hdr,
- const struct ieee802154_mac_cb *cb)
-{
- struct ieee802154_llsec_params params;
- u8 level;
-
- mac802154_llsec_get_params(&priv->sec, &params);
-
- if (!params.enabled && cb->secen_override && cb->secen)
- return -EINVAL;
- if (!params.enabled ||
- (cb->secen_override && !cb->secen) ||
- !params.out_level)
- return 0;
- if (cb->seclevel_override && !cb->seclevel)
- return -EINVAL;
-
- level = cb->seclevel_override ? cb->seclevel : params.out_level;
-
- hdr->fc.security_enabled = 1;
- hdr->sec.level = level;
- hdr->sec.key_id_mode = params.out_key.mode;
- if (params.out_key.mode == IEEE802154_SCF_KEY_SHORT_INDEX)
- hdr->sec.short_src = params.out_key.short_source;
- else if (params.out_key.mode == IEEE802154_SCF_KEY_HW_INDEX)
- hdr->sec.extended_src = params.out_key.extended_source;
- hdr->sec.key_id = params.out_key.id;
-
- return 0;
-}
-
-static int mac802154_header_create(struct sk_buff *skb,
- struct net_device *dev,
- unsigned short type,
- const void *daddr,
- const void *saddr,
- unsigned len)
-{
- struct ieee802154_hdr hdr;
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
- struct ieee802154_mac_cb *cb = mac_cb(skb);
- int hlen;
-
- if (!daddr)
- return -EINVAL;
-
- memset(&hdr.fc, 0, sizeof(hdr.fc));
- hdr.fc.type = cb->type;
- hdr.fc.security_enabled = cb->secen;
- hdr.fc.ack_request = cb->ackreq;
- hdr.seq = ieee802154_mlme_ops(dev)->get_dsn(dev);
-
- if (mac802154_set_header_security(priv, &hdr, cb) < 0)
- return -EINVAL;
-
- if (!saddr) {
- spin_lock_bh(&priv->mib_lock);
-
- if (priv->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST) ||
- priv->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) ||
- priv->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) {
- hdr.source.mode = IEEE802154_ADDR_LONG;
- hdr.source.extended_addr = priv->extended_addr;
- } else {
- hdr.source.mode = IEEE802154_ADDR_SHORT;
- hdr.source.short_addr = priv->short_addr;
- }
-
- hdr.source.pan_id = priv->pan_id;
-
- spin_unlock_bh(&priv->mib_lock);
- } else {
- hdr.source = *(const struct ieee802154_addr *)saddr;
- }
-
- hdr.dest = *(const struct ieee802154_addr *)daddr;
-
- hlen = ieee802154_hdr_push(skb, &hdr);
- if (hlen < 0)
- return -EINVAL;
-
- skb_reset_mac_header(skb);
- skb->mac_len = hlen;
-
- if (len > ieee802154_max_payload(&hdr))
- return -EMSGSIZE;
-
- return hlen;
-}
-
-static int
-mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr)
-{
- struct ieee802154_hdr hdr;
- struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr;
-
- if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) {
- pr_debug("malformed packet\n");
- return 0;
- }
-
- *addr = hdr.source;
- return sizeof(*addr);
-}
-
-static netdev_tx_t
-mac802154_wpan_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv;
- u8 chan, page;
- int rc;
-
- priv = netdev_priv(dev);
-
- spin_lock_bh(&priv->mib_lock);
- chan = priv->chan;
- page = priv->page;
- spin_unlock_bh(&priv->mib_lock);
-
- if (chan == MAC802154_CHAN_NONE ||
- page >= WPAN_NUM_PAGES ||
- chan >= WPAN_NUM_CHANNELS) {
- kfree_skb(skb);
- return NETDEV_TX_OK;
- }
-
- rc = mac802154_llsec_encrypt(&priv->sec, skb);
- if (rc) {
- pr_warn("encryption failed: %i\n", rc);
- kfree_skb(skb);
- return NETDEV_TX_OK;
- }
-
- skb->skb_iif = dev->ifindex;
- dev->stats.tx_packets++;
- dev->stats.tx_bytes += skb->len;
-
- return mac802154_tx(priv->hw, skb, page, chan);
-}
-
-static struct header_ops mac802154_header_ops = {
- .create = mac802154_header_create,
- .parse = mac802154_header_parse,
-};
-
-static const struct net_device_ops mac802154_wpan_ops = {
- .ndo_open = mac802154_wpan_open,
- .ndo_stop = mac802154_slave_close,
- .ndo_start_xmit = mac802154_wpan_xmit,
- .ndo_do_ioctl = mac802154_wpan_ioctl,
- .ndo_set_mac_address = mac802154_wpan_mac_addr,
-};
-
-static void mac802154_wpan_free(struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv = netdev_priv(dev);
-
- mac802154_llsec_destroy(&priv->sec);
-
- free_netdev(dev);
-}
-
-void mac802154_wpan_setup(struct net_device *dev)
-{
- struct mac802154_sub_if_data *priv;
-
- dev->addr_len = IEEE802154_ADDR_LEN;
- memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN);
-
- dev->hard_header_len = MAC802154_FRAME_HARD_HEADER_LEN;
- dev->header_ops = &mac802154_header_ops;
- dev->needed_tailroom = 2 + 16; /* FCS + MIC */
- dev->mtu = IEEE802154_MTU;
- dev->tx_queue_len = 300;
- dev->type = ARPHRD_IEEE802154;
- dev->flags = IFF_NOARP | IFF_BROADCAST;
- dev->watchdog_timeo = 0;
-
- dev->destructor = mac802154_wpan_free;
- dev->netdev_ops = &mac802154_wpan_ops;
- dev->ml_priv = &mac802154_mlme_wpan;
-
- priv = netdev_priv(dev);
- priv->type = IEEE802154_DEV_WPAN;
-
- priv->chan = MAC802154_CHAN_NONE;
- priv->page = 0;
-
- spin_lock_init(&priv->mib_lock);
- mutex_init(&priv->sec_mtx);
-
- get_random_bytes(&priv->bsn, 1);
- get_random_bytes(&priv->dsn, 1);
-
- /* defaults per 802.15.4-2011 */
- priv->mac_params.min_be = 3;
- priv->mac_params.max_be = 5;
- priv->mac_params.csma_retries = 4;
- priv->mac_params.frame_retries = -1; /* for compatibility, actual default is 3 */
-
- priv->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
- priv->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
-
- mac802154_llsec_init(&priv->sec);
-}
-
-static int mac802154_process_data(struct net_device *dev, struct sk_buff *skb)
-{
- return netif_rx_ni(skb);
-}
-
-static int
-mac802154_subif_frame(struct mac802154_sub_if_data *sdata, struct sk_buff *skb,
- const struct ieee802154_hdr *hdr)
-{
- __le16 span, sshort;
- int rc;
-
- pr_debug("getting packet via slave interface %s\n", sdata->dev->name);
-
- spin_lock_bh(&sdata->mib_lock);
-
- span = sdata->pan_id;
- sshort = sdata->short_addr;
-
- switch (mac_cb(skb)->dest.mode) {
- case IEEE802154_ADDR_NONE:
- if (mac_cb(skb)->dest.mode != IEEE802154_ADDR_NONE)
- /* FIXME: check if we are PAN coordinator */
- skb->pkt_type = PACKET_OTHERHOST;
- else
- /* ACK comes with both addresses empty */
- skb->pkt_type = PACKET_HOST;
- break;
- case IEEE802154_ADDR_LONG:
- if (mac_cb(skb)->dest.pan_id != span &&
- mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST))
- skb->pkt_type = PACKET_OTHERHOST;
- else if (mac_cb(skb)->dest.extended_addr == sdata->extended_addr)
- skb->pkt_type = PACKET_HOST;
- else
- skb->pkt_type = PACKET_OTHERHOST;
- break;
- case IEEE802154_ADDR_SHORT:
- if (mac_cb(skb)->dest.pan_id != span &&
- mac_cb(skb)->dest.pan_id != cpu_to_le16(IEEE802154_PANID_BROADCAST))
- skb->pkt_type = PACKET_OTHERHOST;
- else if (mac_cb(skb)->dest.short_addr == sshort)
- skb->pkt_type = PACKET_HOST;
- else if (mac_cb(skb)->dest.short_addr ==
- cpu_to_le16(IEEE802154_ADDR_BROADCAST))
- skb->pkt_type = PACKET_BROADCAST;
- else
- skb->pkt_type = PACKET_OTHERHOST;
- break;
- default:
- spin_unlock_bh(&sdata->mib_lock);
- pr_debug("invalid dest mode\n");
- kfree_skb(skb);
- return NET_RX_DROP;
- }
-
- spin_unlock_bh(&sdata->mib_lock);
-
- skb->dev = sdata->dev;
-
- rc = mac802154_llsec_decrypt(&sdata->sec, skb);
- if (rc) {
- pr_debug("decryption failed: %i\n", rc);
- goto fail;
- }
-
- sdata->dev->stats.rx_packets++;
- sdata->dev->stats.rx_bytes += skb->len;
-
- switch (mac_cb(skb)->type) {
- case IEEE802154_FC_TYPE_DATA:
- return mac802154_process_data(sdata->dev, skb);
- default:
- pr_warn("ieee802154: bad frame received (type = %d)\n",
- mac_cb(skb)->type);
- goto fail;
- }
-
-fail:
- kfree_skb(skb);
- return NET_RX_DROP;
-}
-
-static void mac802154_print_addr(const char *name,
- const struct ieee802154_addr *addr)
-{
- if (addr->mode == IEEE802154_ADDR_NONE)
- pr_debug("%s not present\n", name);
-
- pr_debug("%s PAN ID: %04x\n", name, le16_to_cpu(addr->pan_id));
- if (addr->mode == IEEE802154_ADDR_SHORT) {
- pr_debug("%s is short: %04x\n", name,
- le16_to_cpu(addr->short_addr));
- } else {
- u64 hw = swab64((__force u64) addr->extended_addr);
-
- pr_debug("%s is hardware: %8phC\n", name, &hw);
- }
-}
-
-static int mac802154_parse_frame_start(struct sk_buff *skb,
- struct ieee802154_hdr *hdr)
-{
- int hlen;
- struct ieee802154_mac_cb *cb = mac_cb_init(skb);
-
- hlen = ieee802154_hdr_pull(skb, hdr);
- if (hlen < 0)
- return -EINVAL;
-
- skb->mac_len = hlen;
-
- pr_debug("fc: %04x dsn: %02x\n", le16_to_cpup((__le16 *)&hdr->fc),
- hdr->seq);
-
- cb->type = hdr->fc.type;
- cb->ackreq = hdr->fc.ack_request;
- cb->secen = hdr->fc.security_enabled;
-
- mac802154_print_addr("destination", &hdr->dest);
- mac802154_print_addr("source", &hdr->source);
-
- cb->source = hdr->source;
- cb->dest = hdr->dest;
-
- if (hdr->fc.security_enabled) {
- u64 key;
-
- pr_debug("seclevel %i\n", hdr->sec.level);
-
- switch (hdr->sec.key_id_mode) {
- case IEEE802154_SCF_KEY_IMPLICIT:
- pr_debug("implicit key\n");
- break;
-
- case IEEE802154_SCF_KEY_INDEX:
- pr_debug("key %02x\n", hdr->sec.key_id);
- break;
-
- case IEEE802154_SCF_KEY_SHORT_INDEX:
- pr_debug("key %04x:%04x %02x\n",
- le32_to_cpu(hdr->sec.short_src) >> 16,
- le32_to_cpu(hdr->sec.short_src) & 0xffff,
- hdr->sec.key_id);
- break;
-
- case IEEE802154_SCF_KEY_HW_INDEX:
- key = swab64((__force u64) hdr->sec.extended_src);
- pr_debug("key source %8phC %02x\n", &key,
- hdr->sec.key_id);
- break;
- }
- }
-
- return 0;
-}
-
-void mac802154_wpans_rx(struct mac802154_priv *priv, struct sk_buff *skb)
-{
- int ret;
- struct mac802154_sub_if_data *sdata;
- struct ieee802154_hdr hdr;
-
- ret = mac802154_parse_frame_start(skb, &hdr);
- if (ret) {
- pr_debug("got invalid frame\n");
- kfree_skb(skb);
- return;
- }
-
- rcu_read_lock();
- list_for_each_entry_rcu(sdata, &priv->slaves, list) {
- if (sdata->type != IEEE802154_DEV_WPAN ||
- !netif_running(sdata->dev))
- continue;
-
- mac802154_subif_frame(sdata, skb, &hdr);
- skb = NULL;
- break;
- }
- rcu_read_unlock();
-
- if (skb)
- kfree_skb(skb);
-}
diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c
index a64fa15..1d5341f 100644
--- a/net/netfilter/ipvs/ip_vs_ftp.c
+++ b/net/netfilter/ipvs/ip_vs_ftp.c
@@ -96,13 +96,13 @@ static int ip_vs_ftp_get_addrport(char *data, char *data_limit,
if (data_limit - data < plen) {
/* check if there is partial match */
- if (strnicmp(data, pattern, data_limit - data) == 0)
+ if (strncasecmp(data, pattern, data_limit - data) == 0)
return -1;
else
return 0;
}
- if (strnicmp(data, pattern, plen) != 0) {
+ if (strncasecmp(data, pattern, plen) != 0) {
return 0;
}
s = data + plen;
@@ -354,7 +354,7 @@ static int ip_vs_ftp_in(struct ip_vs_app *app, struct ip_vs_conn *cp,
data_limit = skb_tail_pointer(skb);
while (data <= data_limit - 6) {
- if (strnicmp(data, "PASV\r\n", 6) == 0) {
+ if (strncasecmp(data, "PASV\r\n", 6) == 0) {
/* Passive mode on */
IP_VS_DBG(7, "got PASV at %td of %td\n",
data - data_start,
diff --git a/net/netfilter/nf_conntrack_ftp.c b/net/netfilter/nf_conntrack_ftp.c
index b8a0924..b666959 100644
--- a/net/netfilter/nf_conntrack_ftp.c
+++ b/net/netfilter/nf_conntrack_ftp.c
@@ -304,12 +304,12 @@ static int find_pattern(const char *data, size_t dlen,
if (dlen <= plen) {
/* Short packet: try for partial? */
- if (strnicmp(data, pattern, dlen) == 0)
+ if (strncasecmp(data, pattern, dlen) == 0)
return -1;
else return 0;
}
- if (strnicmp(data, pattern, plen) != 0) {
+ if (strncasecmp(data, pattern, plen) != 0) {
#if 0
size_t i;
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
index 4c3ba1c..885b4ab 100644
--- a/net/netfilter/nf_conntrack_sip.c
+++ b/net/netfilter/nf_conntrack_sip.c
@@ -247,7 +247,7 @@ int ct_sip_parse_request(const struct nf_conn *ct,
for (; dptr < limit - strlen("sip:"); dptr++) {
if (*dptr == '\r' || *dptr == '\n')
return -1;
- if (strnicmp(dptr, "sip:", strlen("sip:")) == 0) {
+ if (strncasecmp(dptr, "sip:", strlen("sip:")) == 0) {
dptr += strlen("sip:");
break;
}
@@ -350,7 +350,7 @@ static const char *ct_sip_header_search(const char *dptr, const char *limit,
continue;
}
- if (strnicmp(dptr, needle, len) == 0)
+ if (strncasecmp(dptr, needle, len) == 0)
return dptr;
}
return NULL;
@@ -383,10 +383,10 @@ int ct_sip_get_header(const struct nf_conn *ct, const char *dptr,
/* Find header. Compact headers must be followed by a
* non-alphabetic character to avoid mismatches. */
if (limit - dptr >= hdr->len &&
- strnicmp(dptr, hdr->name, hdr->len) == 0)
+ strncasecmp(dptr, hdr->name, hdr->len) == 0)
dptr += hdr->len;
else if (hdr->cname && limit - dptr >= hdr->clen + 1 &&
- strnicmp(dptr, hdr->cname, hdr->clen) == 0 &&
+ strncasecmp(dptr, hdr->cname, hdr->clen) == 0 &&
!isalpha(*(dptr + hdr->clen)))
dptr += hdr->clen;
else
@@ -620,9 +620,9 @@ static int ct_sip_parse_transport(struct nf_conn *ct, const char *dptr,
if (ct_sip_parse_param(ct, dptr, dataoff, datalen, "transport=",
&matchoff, &matchlen)) {
- if (!strnicmp(dptr + matchoff, "TCP", strlen("TCP")))
+ if (!strncasecmp(dptr + matchoff, "TCP", strlen("TCP")))
*proto = IPPROTO_TCP;
- else if (!strnicmp(dptr + matchoff, "UDP", strlen("UDP")))
+ else if (!strncasecmp(dptr + matchoff, "UDP", strlen("UDP")))
*proto = IPPROTO_UDP;
else
return 0;
@@ -743,10 +743,10 @@ int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr,
if (term != SDP_HDR_UNSPEC &&
limit - dptr >= thdr->len &&
- strnicmp(dptr, thdr->name, thdr->len) == 0)
+ strncasecmp(dptr, thdr->name, thdr->len) == 0)
break;
else if (limit - dptr >= hdr->len &&
- strnicmp(dptr, hdr->name, hdr->len) == 0)
+ strncasecmp(dptr, hdr->name, hdr->len) == 0)
dptr += hdr->len;
else
continue;
@@ -1394,7 +1394,7 @@ static int process_sip_response(struct sk_buff *skb, unsigned int protoff,
if (handler->response == NULL)
continue;
if (*datalen < matchend + handler->len ||
- strnicmp(*dptr + matchend, handler->method, handler->len))
+ strncasecmp(*dptr + matchend, handler->method, handler->len))
continue;
return handler->response(skb, protoff, dataoff, dptr, datalen,
cseq, code);
@@ -1435,7 +1435,7 @@ static int process_sip_request(struct sk_buff *skb, unsigned int protoff,
if (handler->request == NULL)
continue;
if (*datalen < handler->len ||
- strnicmp(*dptr, handler->method, handler->len))
+ strncasecmp(*dptr, handler->method, handler->len))
continue;
if (ct_sip_get_header(ct, *dptr, 0, *datalen, SIP_HDR_CSEQ,
@@ -1462,7 +1462,7 @@ static int process_sip_msg(struct sk_buff *skb, struct nf_conn *ct,
const struct nf_nat_sip_hooks *hooks;
int ret;
- if (strnicmp(*dptr, "SIP/2.0 ", strlen("SIP/2.0 ")) != 0)
+ if (strncasecmp(*dptr, "SIP/2.0 ", strlen("SIP/2.0 ")) != 0)
ret = process_sip_request(skb, protoff, dataoff, dptr, datalen);
else
ret = process_sip_response(skb, protoff, dataoff, dptr, datalen);
diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c
index daad602..d719764 100644
--- a/net/netfilter/nf_log.c
+++ b/net/netfilter/nf_log.c
@@ -30,7 +30,7 @@ static struct nf_logger *__find_logger(int pf, const char *str_logger)
log = rcu_dereference_protected(loggers[pf][i],
lockdep_is_held(&nf_log_mutex));
- if (!strnicmp(str_logger, log->name, strlen(log->name)))
+ if (!strncasecmp(str_logger, log->name, strlen(log->name)))
return log;
}
diff --git a/net/netfilter/nf_nat_sip.c b/net/netfilter/nf_nat_sip.c
index b4d691d..791fac4 100644
--- a/net/netfilter/nf_nat_sip.c
+++ b/net/netfilter/nf_nat_sip.c
@@ -155,7 +155,7 @@ static unsigned int nf_nat_sip(struct sk_buff *skb, unsigned int protoff,
int request, in_header;
/* Basic rules: requests and responses. */
- if (strnicmp(*dptr, "SIP/2.0", strlen("SIP/2.0")) != 0) {
+ if (strncasecmp(*dptr, "SIP/2.0", strlen("SIP/2.0")) != 0) {
if (ct_sip_parse_request(ct, *dptr, *datalen,
&matchoff, &matchlen,
&addr, &port) > 0 &&
diff --git a/net/netfilter/nft_reject.c b/net/netfilter/nft_reject.c
index ec8a456..57d3e1a 100644
--- a/net/netfilter/nft_reject.c
+++ b/net/netfilter/nft_reject.c
@@ -72,7 +72,7 @@ nla_put_failure:
}
EXPORT_SYMBOL_GPL(nft_reject_dump);
-static u8 icmp_code_v4[NFT_REJECT_ICMPX_MAX] = {
+static u8 icmp_code_v4[NFT_REJECT_ICMPX_MAX + 1] = {
[NFT_REJECT_ICMPX_NO_ROUTE] = ICMP_NET_UNREACH,
[NFT_REJECT_ICMPX_PORT_UNREACH] = ICMP_PORT_UNREACH,
[NFT_REJECT_ICMPX_HOST_UNREACH] = ICMP_HOST_UNREACH,
@@ -81,8 +81,7 @@ static u8 icmp_code_v4[NFT_REJECT_ICMPX_MAX] = {
int nft_reject_icmp_code(u8 code)
{
- if (code > NFT_REJECT_ICMPX_MAX)
- return -EINVAL;
+ BUG_ON(code > NFT_REJECT_ICMPX_MAX);
return icmp_code_v4[code];
}
@@ -90,7 +89,7 @@ int nft_reject_icmp_code(u8 code)
EXPORT_SYMBOL_GPL(nft_reject_icmp_code);
-static u8 icmp_code_v6[NFT_REJECT_ICMPX_MAX] = {
+static u8 icmp_code_v6[NFT_REJECT_ICMPX_MAX + 1] = {
[NFT_REJECT_ICMPX_NO_ROUTE] = ICMPV6_NOROUTE,
[NFT_REJECT_ICMPX_PORT_UNREACH] = ICMPV6_PORT_UNREACH,
[NFT_REJECT_ICMPX_HOST_UNREACH] = ICMPV6_ADDR_UNREACH,
@@ -99,8 +98,7 @@ static u8 icmp_code_v6[NFT_REJECT_ICMPX_MAX] = {
int nft_reject_icmpv6_code(u8 code)
{
- if (code > NFT_REJECT_ICMPX_MAX)
- return -EINVAL;
+ BUG_ON(code > NFT_REJECT_ICMPX_MAX);
return icmp_code_v6[code];
}
diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c
index 0b4692d..a845cd4 100644
--- a/net/netlabel/netlabel_kapi.c
+++ b/net/netlabel/netlabel_kapi.c
@@ -246,7 +246,6 @@ int netlbl_cfg_unlbl_static_add(struct net *net,
* @addr: IP address in network byte order (struct in[6]_addr)
* @mask: address mask in network byte order (struct in[6]_addr)
* @family: address family
- * @secid: LSM secid value for the entry
* @audit_info: NetLabel audit information
*
* Description:
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index c416725..7a186e7 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -715,7 +715,7 @@ static int netlink_mmap_sendmsg(struct sock *sk, struct msghdr *msg,
* after validation, the socket and the ring may only be used by a
* single process, otherwise we fall back to copying.
*/
- if (atomic_long_read(&sk->sk_socket->file->f_count) > 2 ||
+ if (atomic_long_read(&sk->sk_socket->file->f_count) > 1 ||
atomic_read(&nlk->mapped) > 1)
excl = false;
diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c
index 71cf1bf..1b06a1f 100644
--- a/net/netrom/af_netrom.c
+++ b/net/netrom/af_netrom.c
@@ -30,7 +30,7 @@
#include <linux/skbuff.h>
#include <net/net_namespace.h>
#include <net/sock.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <linux/fcntl.h>
#include <linux/termios.h> /* For TIOCINQ/OUTQ */
#include <linux/mm.h>
diff --git a/net/netrom/nr_dev.c b/net/netrom/nr_dev.c
index 743262b..6ae063c 100644
--- a/net/netrom/nr_dev.c
+++ b/net/netrom/nr_dev.c
@@ -20,8 +20,8 @@
#include <linux/in.h>
#include <linux/if_ether.h> /* For the statistics structure. */
#include <linux/slab.h>
+#include <linux/uaccess.h>
-#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/inet.h>
diff --git a/net/netrom/nr_in.c b/net/netrom/nr_in.c
index c3073a2..80dbd0b 100644
--- a/net/netrom/nr_in.c
+++ b/net/netrom/nr_in.c
@@ -23,7 +23,7 @@
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/tcp_states.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
diff --git a/net/netrom/nr_out.c b/net/netrom/nr_out.c
index 0b4bcb2..00fbf14 100644
--- a/net/netrom/nr_out.c
+++ b/net/netrom/nr_out.c
@@ -22,7 +22,7 @@
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <net/sock.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c
index b976d5e..96b64d2 100644
--- a/net/netrom/nr_route.c
+++ b/net/netrom/nr_route.c
@@ -25,7 +25,7 @@
#include <linux/if_arp.h>
#include <linux/skbuff.h>
#include <net/sock.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <linux/fcntl.h>
#include <linux/termios.h> /* For TIOCINQ/OUTQ */
#include <linux/mm.h>
diff --git a/net/netrom/nr_subr.c b/net/netrom/nr_subr.c
index ca40e22..029c8bb 100644
--- a/net/netrom/nr_subr.c
+++ b/net/netrom/nr_subr.c
@@ -22,7 +22,7 @@
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/tcp_states.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
diff --git a/net/netrom/nr_timer.c b/net/netrom/nr_timer.c
index ff2c1b1..94d05806 100644
--- a/net/netrom/nr_timer.c
+++ b/net/netrom/nr_timer.c
@@ -23,7 +23,7 @@
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/tcp_states.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c
index 62db02b..2b78789 100644
--- a/net/openvswitch/flow.c
+++ b/net/openvswitch/flow.c
@@ -274,6 +274,8 @@ static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key)
key->ip.frag = OVS_FRAG_TYPE_LATER;
else
key->ip.frag = OVS_FRAG_TYPE_FIRST;
+ } else {
+ key->ip.frag = OVS_FRAG_TYPE_NONE;
}
nh_len = payload_ofs - nh_ofs;
@@ -358,6 +360,7 @@ static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key,
*/
key->tp.src = htons(icmp->icmp6_type);
key->tp.dst = htons(icmp->icmp6_code);
+ memset(&key->ipv6.nd, 0, sizeof(key->ipv6.nd));
if (icmp->icmp6_code == 0 &&
(icmp->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION ||
@@ -557,10 +560,11 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
} else if (key->eth.type == htons(ETH_P_ARP) ||
key->eth.type == htons(ETH_P_RARP)) {
struct arp_eth_header *arp;
+ bool arp_available = arphdr_ok(skb);
arp = (struct arp_eth_header *)skb_network_header(skb);
- if (arphdr_ok(skb) &&
+ if (arp_available &&
arp->ar_hrd == htons(ARPHRD_ETHER) &&
arp->ar_pro == htons(ETH_P_IP) &&
arp->ar_hln == ETH_ALEN &&
@@ -673,9 +677,6 @@ int ovs_flow_key_extract(struct ovs_tunnel_info *tun_info,
key->ovs_flow_hash = 0;
key->recirc_id = 0;
- /* Flags are always used as part of stats */
- key->tp.flags = 0;
-
return key_extract(skb, key);
}
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c
index 368f233..939bcb3 100644
--- a/net/openvswitch/flow_netlink.c
+++ b/net/openvswitch/flow_netlink.c
@@ -103,10 +103,19 @@ static void update_range__(struct sw_flow_match *match,
SW_FLOW_KEY_MEMCPY_OFFSET(match, offsetof(struct sw_flow_key, field), \
value_p, len, is_mask)
-static u16 range_n_bytes(const struct sw_flow_key_range *range)
-{
- return range->end - range->start;
-}
+#define SW_FLOW_KEY_MEMSET_FIELD(match, field, value, is_mask) \
+ do { \
+ update_range__(match, offsetof(struct sw_flow_key, field), \
+ sizeof((match)->key->field), is_mask); \
+ if (is_mask) { \
+ if ((match)->mask) \
+ memset((u8 *)&(match)->mask->key.field, value,\
+ sizeof((match)->mask->key.field)); \
+ } else { \
+ memset((u8 *)&(match)->key->field, value, \
+ sizeof((match)->key->field)); \
+ } \
+ } while (0)
static bool match_validate(const struct sw_flow_match *match,
u64 key_attrs, u64 mask_attrs)
@@ -809,13 +818,26 @@ static int ovs_key_from_nlattrs(struct sw_flow_match *match, u64 attrs,
return 0;
}
-static void sw_flow_mask_set(struct sw_flow_mask *mask,
- struct sw_flow_key_range *range, u8 val)
+static void nlattr_set(struct nlattr *attr, u8 val, bool is_attr_mask_key)
{
- u8 *m = (u8 *)&mask->key + range->start;
+ struct nlattr *nla;
+ int rem;
+
+ /* The nlattr stream should already have been validated */
+ nla_for_each_nested(nla, attr, rem) {
+ /* We assume that ovs_key_lens[type] == -1 means that type is a
+ * nested attribute
+ */
+ if (is_attr_mask_key && ovs_key_lens[nla_type(nla)] == -1)
+ nlattr_set(nla, val, false);
+ else
+ memset(nla_data(nla), val, nla_len(nla));
+ }
+}
- mask->range = *range;
- memset(m, val, range_n_bytes(range));
+static void mask_set_nlattr(struct nlattr *attr, u8 val)
+{
+ nlattr_set(attr, val, true);
}
/**
@@ -836,6 +858,7 @@ int ovs_nla_get_match(struct sw_flow_match *match,
{
const struct nlattr *a[OVS_KEY_ATTR_MAX + 1];
const struct nlattr *encap;
+ struct nlattr *newmask = NULL;
u64 key_attrs = 0;
u64 mask_attrs = 0;
bool encap_valid = false;
@@ -882,18 +905,44 @@ int ovs_nla_get_match(struct sw_flow_match *match,
if (err)
return err;
+ if (match->mask && !mask) {
+ /* Create an exact match mask. We need to set to 0xff all the
+ * 'match->mask' fields that have been touched in 'match->key'.
+ * We cannot simply memset 'match->mask', because padding bytes
+ * and fields not specified in 'match->key' should be left to 0.
+ * Instead, we use a stream of netlink attributes, copied from
+ * 'key' and set to 0xff: ovs_key_from_nlattrs() will take care
+ * of filling 'match->mask' appropriately.
+ */
+ newmask = kmemdup(key, nla_total_size(nla_len(key)),
+ GFP_KERNEL);
+ if (!newmask)
+ return -ENOMEM;
+
+ mask_set_nlattr(newmask, 0xff);
+
+ /* The userspace does not send tunnel attributes that are 0,
+ * but we should not wildcard them nonetheless.
+ */
+ if (match->key->tun_key.ipv4_dst)
+ SW_FLOW_KEY_MEMSET_FIELD(match, tun_key, 0xff, true);
+
+ mask = newmask;
+ }
+
if (mask) {
err = parse_flow_mask_nlattrs(mask, a, &mask_attrs);
if (err)
- return err;
+ goto free_newmask;
- if (mask_attrs & 1 << OVS_KEY_ATTR_ENCAP) {
+ if (mask_attrs & 1 << OVS_KEY_ATTR_ENCAP) {
__be16 eth_type = 0;
__be16 tci = 0;
if (!encap_valid) {
OVS_NLERR("Encap mask attribute is set for non-VLAN frame.\n");
- return -EINVAL;
+ err = -EINVAL;
+ goto free_newmask;
}
mask_attrs &= ~(1 << OVS_KEY_ATTR_ENCAP);
@@ -904,10 +953,13 @@ int ovs_nla_get_match(struct sw_flow_match *match,
mask_attrs &= ~(1 << OVS_KEY_ATTR_ETHERTYPE);
encap = a[OVS_KEY_ATTR_ENCAP];
err = parse_flow_mask_nlattrs(encap, a, &mask_attrs);
+ if (err)
+ goto free_newmask;
} else {
OVS_NLERR("VLAN frames must have an exact match on the TPID (mask=%x).\n",
ntohs(eth_type));
- return -EINVAL;
+ err = -EINVAL;
+ goto free_newmask;
}
if (a[OVS_KEY_ATTR_VLAN])
@@ -915,23 +967,22 @@ int ovs_nla_get_match(struct sw_flow_match *match,
if (!(tci & htons(VLAN_TAG_PRESENT))) {
OVS_NLERR("VLAN tag present bit must have an exact match (tci_mask=%x).\n", ntohs(tci));
- return -EINVAL;
+ err = -EINVAL;
+ goto free_newmask;
}
}
err = ovs_key_from_nlattrs(match, mask_attrs, a, true);
if (err)
- return err;
- } else {
- /* Populate exact match flow's key mask. */
- if (match->mask)
- sw_flow_mask_set(match->mask, &match->range, 0xff);
+ goto free_newmask;
}
if (!match_validate(match, key_attrs, mask_attrs))
- return -EINVAL;
+ err = -EINVAL;
- return 0;
+free_newmask:
+ kfree(newmask);
+ return err;
}
/**
diff --git a/net/openvswitch/vport-geneve.c b/net/openvswitch/vport-geneve.c
index 910b3ef..106a9d8 100644
--- a/net/openvswitch/vport-geneve.c
+++ b/net/openvswitch/vport-geneve.c
@@ -30,7 +30,7 @@
/**
* struct geneve_port - Keeps track of open UDP ports
- * @sock: The socket created for this port number.
+ * @gs: The socket created for this port number.
* @name: vport name.
*/
struct geneve_port {
diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c
index 53001b0..6015802 100644
--- a/net/openvswitch/vport.c
+++ b/net/openvswitch/vport.c
@@ -408,13 +408,13 @@ int ovs_vport_get_upcall_portids(const struct vport *vport,
*
* Returns the portid of the target socket. Must be called with rcu_read_lock.
*/
-u32 ovs_vport_find_upcall_portid(const struct vport *p, struct sk_buff *skb)
+u32 ovs_vport_find_upcall_portid(const struct vport *vport, struct sk_buff *skb)
{
struct vport_portids *ids;
u32 ids_index;
u32 hash;
- ids = rcu_dereference(p->upcall_portids);
+ ids = rcu_dereference(vport->upcall_portids);
if (ids->n_ids == 1 && ids->ids[0] == 0)
return 0;
diff --git a/net/rds/ib_rdma.c b/net/rds/ib_rdma.c
index e8fdb17..273b8bf 100644
--- a/net/rds/ib_rdma.c
+++ b/net/rds/ib_rdma.c
@@ -267,7 +267,7 @@ static inline struct rds_ib_mr *rds_ib_reuse_fmr(struct rds_ib_mr_pool *pool)
unsigned long *flag;
preempt_disable();
- flag = &__get_cpu_var(clean_list_grace);
+ flag = this_cpu_ptr(&clean_list_grace);
set_bit(CLEAN_LIST_BUSY_BIT, flag);
ret = llist_del_first(&pool->clean_list);
if (ret)
diff --git a/net/rds/rdma.c b/net/rds/rdma.c
index 4e37c1c..40084d8 100644
--- a/net/rds/rdma.c
+++ b/net/rds/rdma.c
@@ -564,12 +564,12 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm,
if (rs->rs_bound_addr == 0) {
ret = -ENOTCONN; /* XXX not a great errno */
- goto out;
+ goto out_ret;
}
if (args->nr_local > UIO_MAXIOV) {
ret = -EMSGSIZE;
- goto out;
+ goto out_ret;
}
/* Check whether to allocate the iovec area */
@@ -578,7 +578,7 @@ int rds_cmsg_rdma_args(struct rds_sock *rs, struct rds_message *rm,
iovs = sock_kmalloc(rds_rs_to_sk(rs), iov_size, GFP_KERNEL);
if (!iovs) {
ret = -ENOMEM;
- goto out;
+ goto out_ret;
}
}
@@ -696,6 +696,7 @@ out:
if (iovs != iovstack)
sock_kfree_s(rds_rs_to_sk(rs), iovs, iov_size);
kfree(pages);
+out_ret:
if (ret)
rds_rdma_free_op(op);
else
diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c
index 1b24191..db0f39f 100644
--- a/net/rxrpc/ar-key.c
+++ b/net/rxrpc/ar-key.c
@@ -44,7 +44,6 @@ struct key_type key_type_rxrpc = {
.preparse = rxrpc_preparse,
.free_preparse = rxrpc_free_preparse,
.instantiate = generic_key_instantiate,
- .match = user_match,
.destroy = rxrpc_destroy,
.describe = rxrpc_describe,
.read = rxrpc_read,
@@ -61,7 +60,6 @@ struct key_type key_type_rxrpc_s = {
.preparse = rxrpc_preparse_s,
.free_preparse = rxrpc_free_preparse_s,
.instantiate = generic_key_instantiate,
- .match = user_match,
.destroy = rxrpc_destroy_s,
.describe = rxrpc_describe,
};
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index 38d58e6..6efca30 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -57,7 +57,8 @@ static inline int dev_requeue_skb(struct sk_buff *skb, struct Qdisc *q)
static void try_bulk_dequeue_skb(struct Qdisc *q,
struct sk_buff *skb,
- const struct netdev_queue *txq)
+ const struct netdev_queue *txq,
+ int *packets)
{
int bytelimit = qdisc_avail_bulklimit(txq) - skb->len;
@@ -70,6 +71,7 @@ static void try_bulk_dequeue_skb(struct Qdisc *q,
bytelimit -= nskb->len; /* covers GSO len */
skb->next = nskb;
skb = nskb;
+ (*packets)++; /* GSO counts as one pkt */
}
skb->next = NULL;
}
@@ -77,11 +79,13 @@ static void try_bulk_dequeue_skb(struct Qdisc *q,
/* Note that dequeue_skb can possibly return a SKB list (via skb->next).
* A requeued skb (via q->gso_skb) can also be a SKB list.
*/
-static struct sk_buff *dequeue_skb(struct Qdisc *q, bool *validate)
+static struct sk_buff *dequeue_skb(struct Qdisc *q, bool *validate,
+ int *packets)
{
struct sk_buff *skb = q->gso_skb;
const struct netdev_queue *txq = q->dev_queue;
+ *packets = 1;
*validate = true;
if (unlikely(skb)) {
/* check the reason of requeuing without tx lock first */
@@ -98,7 +102,7 @@ static struct sk_buff *dequeue_skb(struct Qdisc *q, bool *validate)
!netif_xmit_frozen_or_stopped(txq)) {
skb = q->dequeue(q);
if (skb && qdisc_may_bulk(q))
- try_bulk_dequeue_skb(q, skb, txq);
+ try_bulk_dequeue_skb(q, skb, txq, packets);
}
}
return skb;
@@ -204,7 +208,7 @@ int sch_direct_xmit(struct sk_buff *skb, struct Qdisc *q,
* >0 - queue is not empty.
*
*/
-static inline int qdisc_restart(struct Qdisc *q)
+static inline int qdisc_restart(struct Qdisc *q, int *packets)
{
struct netdev_queue *txq;
struct net_device *dev;
@@ -213,7 +217,7 @@ static inline int qdisc_restart(struct Qdisc *q)
bool validate;
/* Dequeue packet */
- skb = dequeue_skb(q, &validate);
+ skb = dequeue_skb(q, &validate, packets);
if (unlikely(!skb))
return 0;
@@ -227,14 +231,16 @@ static inline int qdisc_restart(struct Qdisc *q)
void __qdisc_run(struct Qdisc *q)
{
int quota = weight_p;
+ int packets;
- while (qdisc_restart(q)) {
+ while (qdisc_restart(q, &packets)) {
/*
* Ordered by possible occurrence: Postpone processing if
* 1. we've exceeded packet quota
* 2. another process needs the CPU;
*/
- if (--quota <= 0 || need_resched()) {
+ quota -= packets;
+ if (quota <= 0 || need_resched()) {
__netif_schedule(q);
break;
}
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index a88b852..f791edd 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -1668,6 +1668,8 @@ struct sctp_chunk *sctp_assoc_lookup_asconf_ack(
* ack chunk whose serial number matches that of the request.
*/
list_for_each_entry(ack, &asoc->asconf_ack_list, transmitted_list) {
+ if (sctp_chunk_pending(ack))
+ continue;
if (ack->subh.addip_hdr->serial == serial) {
sctp_chunk_hold(ack);
return ack;
diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c
index 4de12af..7e8a16c 100644
--- a/net/sctp/inqueue.c
+++ b/net/sctp/inqueue.c
@@ -140,18 +140,9 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
} else {
/* Nothing to do. Next chunk in the packet, please. */
ch = (sctp_chunkhdr_t *) chunk->chunk_end;
-
/* Force chunk->skb->data to chunk->chunk_end. */
- skb_pull(chunk->skb,
- chunk->chunk_end - chunk->skb->data);
-
- /* Verify that we have at least chunk headers
- * worth of buffer left.
- */
- if (skb_headlen(chunk->skb) < sizeof(sctp_chunkhdr_t)) {
- sctp_chunk_free(chunk);
- chunk = queue->in_progress = NULL;
- }
+ skb_pull(chunk->skb, chunk->chunk_end - chunk->skb->data);
+ /* We are guaranteed to pull a SCTP header. */
}
}
@@ -187,24 +178,14 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
skb_pull(chunk->skb, sizeof(sctp_chunkhdr_t));
chunk->subh.v = NULL; /* Subheader is no longer valid. */
- if (chunk->chunk_end < skb_tail_pointer(chunk->skb)) {
+ if (chunk->chunk_end + sizeof(sctp_chunkhdr_t) <
+ skb_tail_pointer(chunk->skb)) {
/* This is not a singleton */
chunk->singleton = 0;
} else if (chunk->chunk_end > skb_tail_pointer(chunk->skb)) {
- /* RFC 2960, Section 6.10 Bundling
- *
- * Partial chunks MUST NOT be placed in an SCTP packet.
- * If the receiver detects a partial chunk, it MUST drop
- * the chunk.
- *
- * Since the end of the chunk is past the end of our buffer
- * (which contains the whole packet, we can freely discard
- * the whole packet.
- */
- sctp_chunk_free(chunk);
- chunk = queue->in_progress = NULL;
-
- return NULL;
+ /* Discard inside state machine. */
+ chunk->pdiscard = 1;
+ chunk->chunk_end = skb_tail_pointer(chunk->skb);
} else {
/* We are at the end of the packet, so mark the chunk
* in case we need to send a SACK.
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 9d2c6c9..8f34b27 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -1341,7 +1341,7 @@ static __init int sctp_init(void)
if (!sctp_chunk_cachep)
goto err_chunk_cachep;
- status = percpu_counter_init(&sctp_sockets_allocated, 0);
+ status = percpu_counter_init(&sctp_sockets_allocated, 0, GFP_KERNEL);
if (status)
goto err_percpu_counter_init;
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index ae0e616..ab734be 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -3110,50 +3110,63 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
return SCTP_ERROR_NO_ERROR;
}
-/* Verify the ASCONF packet before we process it. */
-int sctp_verify_asconf(const struct sctp_association *asoc,
- struct sctp_paramhdr *param_hdr, void *chunk_end,
- struct sctp_paramhdr **errp) {
- sctp_addip_param_t *asconf_param;
+/* Verify the ASCONF packet before we process it. */
+bool sctp_verify_asconf(const struct sctp_association *asoc,
+ struct sctp_chunk *chunk, bool addr_param_needed,
+ struct sctp_paramhdr **errp)
+{
+ sctp_addip_chunk_t *addip = (sctp_addip_chunk_t *) chunk->chunk_hdr;
union sctp_params param;
- int length, plen;
-
- param.v = (sctp_paramhdr_t *) param_hdr;
- while (param.v <= chunk_end - sizeof(sctp_paramhdr_t)) {
- length = ntohs(param.p->length);
- *errp = param.p;
+ bool addr_param_seen = false;
- if (param.v > chunk_end - length ||
- length < sizeof(sctp_paramhdr_t))
- return 0;
+ sctp_walk_params(param, addip, addip_hdr.params) {
+ size_t length = ntohs(param.p->length);
+ *errp = param.p;
switch (param.p->type) {
+ case SCTP_PARAM_ERR_CAUSE:
+ break;
+ case SCTP_PARAM_IPV4_ADDRESS:
+ if (length != sizeof(sctp_ipv4addr_param_t))
+ return false;
+ addr_param_seen = true;
+ break;
+ case SCTP_PARAM_IPV6_ADDRESS:
+ if (length != sizeof(sctp_ipv6addr_param_t))
+ return false;
+ addr_param_seen = true;
+ break;
case SCTP_PARAM_ADD_IP:
case SCTP_PARAM_DEL_IP:
case SCTP_PARAM_SET_PRIMARY:
- asconf_param = (sctp_addip_param_t *)param.v;
- plen = ntohs(asconf_param->param_hdr.length);
- if (plen < sizeof(sctp_addip_param_t) +
- sizeof(sctp_paramhdr_t))
- return 0;
+ /* In ASCONF chunks, these need to be first. */
+ if (addr_param_needed && !addr_param_seen)
+ return false;
+ length = ntohs(param.addip->param_hdr.length);
+ if (length < sizeof(sctp_addip_param_t) +
+ sizeof(sctp_paramhdr_t))
+ return false;
break;
case SCTP_PARAM_SUCCESS_REPORT:
case SCTP_PARAM_ADAPTATION_LAYER_IND:
if (length != sizeof(sctp_addip_param_t))
- return 0;
-
+ return false;
break;
default:
- break;
+ /* This is unkown to us, reject! */
+ return false;
}
-
- param.v += WORD_ROUND(length);
}
- if (param.v != chunk_end)
- return 0;
+ /* Remaining sanity checks. */
+ if (addr_param_needed && !addr_param_seen)
+ return false;
+ if (!addr_param_needed && addr_param_seen)
+ return false;
+ if (param.v != chunk->chunk_end)
+ return false;
- return 1;
+ return true;
}
/* Process an incoming ASCONF chunk with the next expected serial no. and
@@ -3162,16 +3175,17 @@ int sctp_verify_asconf(const struct sctp_association *asoc,
struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
struct sctp_chunk *asconf)
{
+ sctp_addip_chunk_t *addip = (sctp_addip_chunk_t *) asconf->chunk_hdr;
+ bool all_param_pass = true;
+ union sctp_params param;
sctp_addiphdr_t *hdr;
union sctp_addr_param *addr_param;
sctp_addip_param_t *asconf_param;
struct sctp_chunk *asconf_ack;
-
__be16 err_code;
int length = 0;
int chunk_len;
__u32 serial;
- int all_param_pass = 1;
chunk_len = ntohs(asconf->chunk_hdr->length) - sizeof(sctp_chunkhdr_t);
hdr = (sctp_addiphdr_t *)asconf->skb->data;
@@ -3199,9 +3213,14 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
goto done;
/* Process the TLVs contained within the ASCONF chunk. */
- while (chunk_len > 0) {
+ sctp_walk_params(param, addip, addip_hdr.params) {
+ /* Skip preceeding address parameters. */
+ if (param.p->type == SCTP_PARAM_IPV4_ADDRESS ||
+ param.p->type == SCTP_PARAM_IPV6_ADDRESS)
+ continue;
+
err_code = sctp_process_asconf_param(asoc, asconf,
- asconf_param);
+ param.addip);
/* ADDIP 4.1 A7)
* If an error response is received for a TLV parameter,
* all TLVs with no response before the failed TLV are
@@ -3209,28 +3228,20 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
* the failed response are considered unsuccessful unless
* a specific success indication is present for the parameter.
*/
- if (SCTP_ERROR_NO_ERROR != err_code)
- all_param_pass = 0;
-
+ if (err_code != SCTP_ERROR_NO_ERROR)
+ all_param_pass = false;
if (!all_param_pass)
- sctp_add_asconf_response(asconf_ack,
- asconf_param->crr_id, err_code,
- asconf_param);
+ sctp_add_asconf_response(asconf_ack, param.addip->crr_id,
+ err_code, param.addip);
/* ADDIP 4.3 D11) When an endpoint receiving an ASCONF to add
* an IP address sends an 'Out of Resource' in its response, it
* MUST also fail any subsequent add or delete requests bundled
* in the ASCONF.
*/
- if (SCTP_ERROR_RSRC_LOW == err_code)
+ if (err_code == SCTP_ERROR_RSRC_LOW)
goto done;
-
- /* Move to the next ASCONF param. */
- length = ntohs(asconf_param->param_hdr.length);
- asconf_param = (void *)asconf_param + length;
- chunk_len -= length;
}
-
done:
asoc->peer.addip_serial++;
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index c8f6063..3ee27b7 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -170,6 +170,9 @@ sctp_chunk_length_valid(struct sctp_chunk *chunk,
{
__u16 chunk_length = ntohs(chunk->chunk_hdr->length);
+ /* Previously already marked? */
+ if (unlikely(chunk->pdiscard))
+ return 0;
if (unlikely(chunk_length < required_length))
return 0;
@@ -3591,9 +3594,7 @@ sctp_disposition_t sctp_sf_do_asconf(struct net *net,
struct sctp_chunk *asconf_ack = NULL;
struct sctp_paramhdr *err_param = NULL;
sctp_addiphdr_t *hdr;
- union sctp_addr_param *addr_param;
__u32 serial;
- int length;
if (!sctp_vtag_verify(chunk, asoc)) {
sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG,
@@ -3618,17 +3619,8 @@ sctp_disposition_t sctp_sf_do_asconf(struct net *net,
hdr = (sctp_addiphdr_t *)chunk->skb->data;
serial = ntohl(hdr->serial);
- addr_param = (union sctp_addr_param *)hdr->params;
- length = ntohs(addr_param->p.length);
- if (length < sizeof(sctp_paramhdr_t))
- return sctp_sf_violation_paramlen(net, ep, asoc, type, arg,
- (void *)addr_param, commands);
-
/* Verify the ASCONF chunk before processing it. */
- if (!sctp_verify_asconf(asoc,
- (sctp_paramhdr_t *)((void *)addr_param + length),
- (void *)chunk->chunk_end,
- &err_param))
+ if (!sctp_verify_asconf(asoc, chunk, true, &err_param))
return sctp_sf_violation_paramlen(net, ep, asoc, type, arg,
(void *)err_param, commands);
@@ -3745,10 +3737,7 @@ sctp_disposition_t sctp_sf_do_asconf_ack(struct net *net,
rcvd_serial = ntohl(addip_hdr->serial);
/* Verify the ASCONF-ACK chunk before processing it. */
- if (!sctp_verify_asconf(asoc,
- (sctp_paramhdr_t *)addip_hdr->params,
- (void *)asconf_ack->chunk_end,
- &err_param))
+ if (!sctp_verify_asconf(asoc, asconf_ack, false, &err_param))
return sctp_sf_violation_paramlen(net, ep, asoc, type, arg,
(void *)err_param, commands);
diff --git a/net/socket.c b/net/socket.c
index ffd9cb4..fe20c31 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1065,7 +1065,8 @@ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg)
err = -EFAULT;
if (get_user(pid, (int __user *)argp))
break;
- err = f_setown(sock->file, pid, 1);
+ f_setown(sock->file, pid, 1);
+ err = 0;
break;
case FIOGETOWN:
case SIOCGPGRP:
diff --git a/net/tipc/link.c b/net/tipc/link.c
index 65410e1..1db162a 100644
--- a/net/tipc/link.c
+++ b/net/tipc/link.c
@@ -1924,7 +1924,12 @@ void tipc_link_bundle_rcv(struct sk_buff *buf)
}
omsg = buf_msg(obuf);
pos += align(msg_size(omsg));
- if (msg_isdata(omsg) || (msg_user(omsg) == CONN_MANAGER)) {
+ if (msg_isdata(omsg)) {
+ if (unlikely(msg_type(omsg) == TIPC_MCAST_MSG))
+ tipc_sk_mcast_rcv(obuf);
+ else
+ tipc_sk_rcv(obuf);
+ } else if (msg_user(omsg) == CONN_MANAGER) {
tipc_sk_rcv(obuf);
} else if (msg_user(omsg) == NAME_DISTRIBUTOR) {
tipc_named_rcv(obuf);
diff --git a/net/wireless/lib80211.c b/net/wireless/lib80211.c
index a55c27b..4596115 100644
--- a/net/wireless/lib80211.c
+++ b/net/wireless/lib80211.c
@@ -46,38 +46,6 @@ static void lib80211_crypt_deinit_entries(struct lib80211_crypt_info *info,
static void lib80211_crypt_quiescing(struct lib80211_crypt_info *info);
static void lib80211_crypt_deinit_handler(unsigned long data);
-const char *print_ssid(char *buf, const char *ssid, u8 ssid_len)
-{
- const char *s = ssid;
- char *d = buf;
-
- ssid_len = min_t(u8, ssid_len, IEEE80211_MAX_SSID_LEN);
- while (ssid_len--) {
- if (isprint(*s)) {
- *d++ = *s++;
- continue;
- }
-
- *d++ = '\\';
- if (*s == '\0')
- *d++ = '0';
- else if (*s == '\n')
- *d++ = 'n';
- else if (*s == '\r')
- *d++ = 'r';
- else if (*s == '\t')
- *d++ = 't';
- else if (*s == '\\')
- *d++ = '\\';
- else
- d += snprintf(d, 3, "%03o", *s);
- s++;
- }
- *d = '\0';
- return buf;
-}
-EXPORT_SYMBOL(print_ssid);
-
int lib80211_crypt_info_init(struct lib80211_crypt_info *info, char *name,
spinlock_t *lock)
{
OpenPOWER on IntegriCloud